From 03b62f3315fe23506869eba8a1dfbb0cff4ec583 Mon Sep 17 00:00:00 2001 From: Alexander Deynichenko Date: Mon, 25 Feb 2013 07:23:44 +0400 Subject: [PATCH] PLIB 1.8.5+ r2173 from http://plib.svn.sourceforge.net/svnroot/plib/trunk --- AUTHORS | 66 + COPYING | 496 + ChangeLog | 1102 ++ INSTALL | 218 + KNOWN_BUGS | 25 + Makefile.am | 7 + NEWS | 4 + NOTICE | 23 + README | 182 + README.GLUT | 3 + README.JOYSTICKS.FREEBSD | 96 + README.mac | 19 + README.win32 | 52 + TODO-1.6 | 69 + TODO-2.0 | 28 + TODO_AFTER135 | 138 + autogen.sh | 19 + configure.in | 426 + demos/README | 16 + demos/build_all_demos | 22 + demos/exposer/AUTHORS | 2 + demos/exposer/COPYING | 340 + demos/exposer/ChangeLog | 16 + demos/exposer/INSTALL | 182 + demos/exposer/Makefile.am | 4 + demos/exposer/NEWS | 0 demos/exposer/README | 44 + demos/exposer/autogen.sh | 19 + demos/exposer/configure.in | 167 + demos/exposer/data/Makefile.am | 7 + demos/exposer/data/dog.ac | 1539 ++ demos/exposer/data/dog.bones | 150 + demos/exposer/data/human.ac | 1556 ++ demos/exposer/data/human.bones | 38 + demos/exposer/data/human_die.bones | 133 + demos/exposer/data/human_walk.bones | 171 + demos/exposer/data/teeshirt_human.rgb | Bin 0 -> 61392 bytes demos/exposer/doc/Makefile.am | 7 + demos/exposer/doc/bone.png | Bin 0 -> 3210 bytes demos/exposer/doc/exposer.png | Bin 0 -> 20543 bytes demos/exposer/doc/exposer_logo.png | Bin 0 -> 3673 bytes demos/exposer/doc/index.html | 451 + demos/exposer/doc/menu.png | Bin 0 -> 329 bytes demos/exposer/doc/timeline.png | Bin 0 -> 365 bytes demos/exposer/doc/translate.png | Bin 0 -> 2474 bytes demos/exposer/doc/vcr.png | Bin 0 -> 2251 bytes demos/exposer/doc/viewer.png | Bin 0 -> 2764 bytes demos/exposer/exposer.dsw | 29 + demos/exposer/src/Makefile.am | 22 + demos/exposer/src/boneGUI.cxx | 305 + demos/exposer/src/boneGUI.h | 32 + demos/exposer/src/bones.cxx | 278 + demos/exposer/src/bones.h | 84 + demos/exposer/src/event.cxx | 300 + demos/exposer/src/event.h | 102 + demos/exposer/src/exposer.cxx | 480 + demos/exposer/src/exposer.dsp | 156 + demos/exposer/src/exposer.h | 65 + demos/exposer/src/floor.cxx | 108 + demos/exposer/src/floor.h | 39 + demos/exposer/src/load_save.cxx | 621 + demos/exposer/src/load_save.h | 36 + demos/exposer/src/merge_tweens.cxx | 179 + demos/exposer/src/merge_tweens.dsp | 92 + demos/exposer/src/merge_tweens.h | 12 + demos/exposer/src/model.cxx | 465 + demos/exposer/src/model.h | 36 + demos/exposer/src/timebox.cxx | 386 + demos/exposer/src/timebox.h | 117 + demos/exposer/src/vertices.cxx | 189 + demos/exposer/src/vertices.h | 28 + demos/p-guide/AUTHORS | 3 + demos/p-guide/COPYING | 340 + demos/p-guide/ChangeLog | 1 + demos/p-guide/INSTALL | 182 + demos/p-guide/Makefile.am | 4 + demos/p-guide/NEWS | 0 demos/p-guide/README | 34 + demos/p-guide/TODO | 30 + demos/p-guide/autogen.sh | 19 + demos/p-guide/configure.in | 162 + demos/p-guide/doc/p-guide.html | 146 + demos/p-guide/pGuide.dsw | 29 + demos/p-guide/src/LoadSave.cxx | 756 + demos/p-guide/src/Makefile.am | 9 + demos/p-guide/src/PropertiesWindow.cxx | 610 + demos/p-guide/src/StatusWindow.cxx | 1146 ++ demos/p-guide/src/WidgetList.h | 75 + demos/p-guide/src/WidgetWindow.cxx | 460 + demos/p-guide/src/WriteCode.cxx | 616 + demos/p-guide/src/example.xml | 504 + demos/p-guide/src/pGuide.cxx | 916 ++ demos/p-guide/src/pGuide.dsp | 128 + demos/simon/AUTHORS | 3 + demos/simon/COPYING | 340 + demos/simon/Makefile | 13 + demos/simon/README | 60 + demos/simon/astropong/astropong.psl | 249 + demos/simon/astropong/data/Penguin_beak.rgb | Bin 0 -> 11194 bytes demos/simon/astropong/data/Penguin_body.rgb | Bin 0 -> 18495 bytes demos/simon/astropong/data/Penguin_body2.rgb | Bin 0 -> 21709 bytes demos/simon/astropong/data/Penguin_foot.rgba | Bin 0 -> 17046 bytes demos/simon/astropong/data/Scoreboard.ac | 2294 +++ demos/simon/astropong/data/Scoreboard.rgb | Bin 0 -> 47150 bytes demos/simon/astropong/data/arena2.ac | 272 + demos/simon/astropong/data/ball3.ac | 2474 +++ demos/simon/astropong/data/bluepaddle2.ac | 12964 +++++++++++++++ .../simon/astropong/data/embossed_herring.rgb | Bin 0 -> 26229 bytes demos/simon/astropong/data/redpaddle2.ac | 12965 +++++++++++++++ demos/simon/astropong/data/shadow2.ac | 577 + demos/simon/doc/index.html | 256 + demos/simon/src/Makefile | 31 + demos/simon/src/simon.cxx | 231 + demos/simon/src/simon.dsp | 113 + demos/simon/src/simon.dsw | 29 + demos/simon/src/simon.h | 83 + demos/simon/src/simon_api.cxx | 365 + demos/ttt3d/AUTHORS | 2 + demos/ttt3d/COPYING | 340 + demos/ttt3d/ChangeLog | 8 + demos/ttt3d/INSTALL | 182 + demos/ttt3d/Makefile.am | 4 + demos/ttt3d/NEWS | 0 demos/ttt3d/README | 54 + demos/ttt3d/TTT3D.dsp | 172 + demos/ttt3d/TTT3D.dsw | 29 + demos/ttt3d/acinclude.m4 | 8 + demos/ttt3d/autogen.sh | 19 + demos/ttt3d/configure.in | 174 + demos/ttt3d/data/Makefile.am | 7 + demos/ttt3d/data/levels.dat | 2 + demos/ttt3d/doc/Makefile.am | 5 + demos/ttt3d/doc/index.html | 95 + demos/ttt3d/fonts/Makefile.am | 7 + demos/ttt3d/fonts/sorority.txf | Bin 0 -> 9304 bytes demos/ttt3d/images/Makefile.am | 7 + demos/ttt3d/images/O.rgb | Bin 0 -> 15104 bytes demos/ttt3d/images/X.rgb | Bin 0 -> 15081 bytes demos/ttt3d/images/controls.rgb | Bin 0 -> 15646 bytes demos/ttt3d/images/ground.rgb | Bin 0 -> 15020 bytes demos/ttt3d/mods/Makefile.am | 7 + demos/ttt3d/mods/mfarmer.mod | Bin 0 -> 47098 bytes demos/ttt3d/src/Makefile.am | 12 + demos/ttt3d/src/cell.cxx | 291 + demos/ttt3d/src/cell.h | 55 + demos/ttt3d/src/game.cxx | 483 + demos/ttt3d/src/game.h | 33 + demos/ttt3d/src/gfx.cxx | 143 + demos/ttt3d/src/gfx.h | 33 + demos/ttt3d/src/gui.cxx | 246 + demos/ttt3d/src/gui.h | 46 + demos/ttt3d/src/level.h | 36 + demos/ttt3d/src/material.cxx | 175 + demos/ttt3d/src/material.h | 51 + demos/ttt3d/src/p3d.h | 95 + demos/ttt3d/src/puzzle.cxx | 102 + demos/ttt3d/src/puzzle.h | 90 + demos/ttt3d/src/sound.cxx | 122 + demos/ttt3d/src/sound.h | 56 + demos/ttt3d/src/status.cxx | 358 + demos/ttt3d/src/status.h | 40 + demos/ttt3d/src/ttt3d.cxx | 286 + demos/ttt3d/wavs/Makefile.am | 7 + demos/ttt3d/wavs/ahooga.wav | Bin 0 -> 9588 bytes demos/ttt3d/wavs/clap.wav | Bin 0 -> 30764 bytes demos/ttt3d/wavs/frog.wav | Bin 0 -> 4338 bytes demos/ttt3d/wavs/glasbk.wav | Bin 0 -> 26975 bytes demos/ttt3d/wavs/pop.wav | Bin 0 -> 4486 bytes demos/ttt3d/wavs/ugh.wav | Bin 0 -> 6564 bytes demos/ttt3d/wavs/who_else.wav | Bin 0 -> 4764 bytes demos/widgy/README | 2 + doc/FDS_logo.png | Bin 0 -> 8710 bytes doc/FNT.png | Bin 0 -> 1580 bytes doc/JS.png | Bin 0 -> 1580 bytes doc/MinigolfManiacs.png | Bin 0 -> 4385 bytes doc/NET.png | Bin 0 -> 1563 bytes doc/PSL.png | Bin 0 -> 1812 bytes doc/PUI.png | Bin 0 -> 1550 bytes doc/PW.png | Bin 0 -> 1939 bytes doc/SG.png | Bin 0 -> 1609 bytes doc/SL.png | Bin 0 -> 1775 bytes doc/SSG.png | Bin 0 -> 1685 bytes doc/UL.png | Bin 0 -> 1530 bytes doc/auxlibs/index.html | 55 + doc/contacts.html | 114 + doc/contacts.png | Bin 0 -> 5264 bytes doc/download.html | 225 + doc/download.png | Bin 0 -> 5452 bytes doc/examples.html | 191 + doc/examples.png | Bin 0 -> 5156 bytes doc/fgfs.png | Bin 0 -> 3990 bytes doc/fnt/index.html | 385 + doc/gallery.html | 188 + doc/gallery.png | Bin 0 -> 4738 bytes doc/home.png | Bin 0 -> 4622 bytes doc/index.html | 203 + doc/js/index.html | 187 + doc/license.html | 112 + doc/license.png | Bin 0 -> 5267 bytes doc/majik3d.png | Bin 0 -> 3081 bytes doc/marble.png | Bin 0 -> 7247 bytes doc/mote.png | Bin 0 -> 3323 bytes doc/net/index.html | 354 + doc/plib_logo.png | Bin 0 -> 29635 bytes doc/ppe_logo_1.png | Bin 0 -> 2786 bytes doc/psl/appl_guide.html | 430 + doc/psl/impl_guide.html | 463 + doc/psl/index.html | 55 + doc/psl/prog_guide.html | 209 + doc/puAux.png | Bin 0 -> 1803 bytes doc/puAux/index.html | 54 + doc/pui/BasicPUI.html | 636 + doc/pui/FAQ.html | 171 + doc/pui/complex.png | Bin 0 -> 28076 bytes doc/pui/index.html | 2210 +++ doc/pui/labelplaces.png | Bin 0 -> 13349 bytes doc/pui/legendplaces.png | Bin 0 -> 10769 bytes doc/pui/pooh.png | Bin 0 -> 3027 bytes doc/pui/puArrowButton.png | Bin 0 -> 2280 bytes doc/pui/puBiSlider.png | Bin 0 -> 2944 bytes doc/pui/puButton.png | Bin 0 -> 4666 bytes doc/pui/puButtonBox.png | Bin 0 -> 4488 bytes doc/pui/puComboBox.png | Bin 0 -> 6184 bytes doc/pui/puDial.png | Bin 0 -> 3108 bytes doc/pui/puDialogBox.png | Bin 0 -> 3368 bytes doc/pui/puFileSelector.png | Bin 0 -> 14745 bytes doc/pui/puFrame.png | Bin 0 -> 3164 bytes doc/pui/puInput.png | Bin 0 -> 1518 bytes doc/pui/puLargeInput.png | Bin 0 -> 7149 bytes doc/pui/puListBox.png | Bin 0 -> 4873 bytes doc/pui/puMenuBar.png | Bin 0 -> 2986 bytes doc/pui/puOneShot.png | Bin 0 -> 2165 bytes doc/pui/puPopupMenu.png | Bin 0 -> 1716 bytes doc/pui/puSelectBox.png | Bin 0 -> 2790 bytes doc/pui/puSlider.png | Bin 0 -> 2470 bytes doc/pui/puText.png | Bin 0 -> 609 bytes doc/pui/puTriSlider.png | Bin 0 -> 3275 bytes doc/pui/puVerticalMenu.png | Bin 0 -> 1370 bytes doc/pui/simple.png | Bin 0 -> 3109 bytes doc/pw/index.html | 203 + doc/requirements.html | 147 + doc/requirements.png | Bin 0 -> 5343 bytes doc/sg/index.html | 1107 ++ doc/sg/triangle_params.png | Bin 0 -> 6195 bytes doc/sl/index.html | 71 + doc/sl/mixer.png | Bin 0 -> 39578 bytes doc/sl/sl.html | 1048 ++ doc/sl/sl.png | Bin 0 -> 5958 bytes doc/sl/sm.html | 106 + doc/spacething-small.png | Bin 0 -> 1760 bytes doc/ssg/LoaderWriter.html | 315 + doc/ssg/branches.html | 319 + doc/ssg/index.html | 157 + doc/ssg/non_class.html | 936 ++ doc/ssg/ssgBase.html | 233 + doc/ssg/ssgContext.html | 183 + doc/ssg/ssgEntity.html | 146 + doc/ssg/ssgLeaf.html | 305 + doc/ssg/state.html | 255 + doc/ssgAux.png | Bin 0 -> 1671 bytes doc/ssgAux/index.html | 886 ++ doc/ssgAux/snapshot.png | Bin 0 -> 64063 bytes doc/ssgAux/ssgaSky.png | Bin 0 -> 84034 bytes doc/torcs_logo.png | Bin 0 -> 4563 bytes doc/tux_aqfh.png | Bin 0 -> 7704 bytes doc/tuxfleet_logo.png | Bin 0 -> 5565 bytes doc/tuxkart_logo.png | Bin 0 -> 6536 bytes doc/util/index.html | 626 + doc/valid-html40.png | Bin 0 -> 799 bytes doc/whats_inside.html | 196 + doc/whats_inside.png | Bin 0 -> 5137 bytes examples/AUTHORS | 62 + examples/COPYING | 340 + examples/COPYING.DATA | 5 + examples/ChangeLog | 34 + examples/INSTALL | 203 + examples/Makefile.am | 16 + examples/NEWS | 0 examples/README | 23 + examples/autogen.sh | 19 + examples/configure.in | 313 + examples/plib_examples.dsp | 61 + examples/plib_examples.dsw | 491 + examples/src/Makefile.am | 3 + examples/src/fnt/Makefile.am | 18 + examples/src/fnt/data/AvantGarde-Book.txf | Bin 0 -> 66708 bytes .../src/fnt/data/AvantGarde-BookOblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/AvantGarde-Demi.txf | Bin 0 -> 66708 bytes .../src/fnt/data/AvantGarde-DemiOblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Bookman-Demi.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Bookman-DemiItalic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Bookman-Light.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Bookman-LightItalic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Courier-Bold.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Courier-BoldOblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Courier-Oblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Courier.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Helvetica-Bold.txf | Bin 0 -> 66708 bytes .../src/fnt/data/Helvetica-BoldOblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Helvetica-Oblique.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Helvetica.txf | Bin 0 -> 66708 bytes .../src/fnt/data/NewCenturySchlbk-Bold.txf | Bin 0 -> 66708 bytes .../fnt/data/NewCenturySchlbk-BoldItalic.txf | Bin 0 -> 66708 bytes .../src/fnt/data/NewCenturySchlbk-Italic.txf | Bin 0 -> 66708 bytes .../src/fnt/data/NewCenturySchlbk-Roman.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Palatino-Bold.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Palatino-BoldItalic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Palatino-Italic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Palatino-Roman.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/README | 48 + examples/src/fnt/data/Times-Bold.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Times-BoldItalic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Times-Italic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/Times-Roman.txf | Bin 0 -> 66708 bytes .../fnt/data/ZapfChancery-MediumItalic.txf | Bin 0 -> 66708 bytes examples/src/fnt/data/old/README | 34 + examples/src/fnt/data/old/charter.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/courier-bold.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/courier_medium.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/curlfont.txf | Bin 0 -> 8968 bytes examples/src/fnt/data/old/default.txf | Bin 0 -> 9196 bytes examples/src/fnt/data/old/derniere.txf | Bin 0 -> 9124 bytes examples/src/fnt/data/old/haeberli.txf | Bin 0 -> 8968 bytes examples/src/fnt/data/old/helvetica_bold.txf | Bin 0 -> 9292 bytes .../src/fnt/data/old/helvetica_medium.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/junius.txf | Bin 0 -> 9196 bytes examples/src/fnt/data/old/ledfixed.txf | Bin 0 -> 9196 bytes examples/src/fnt/data/old/lucida.txf | Bin 0 -> 9292 bytes .../src/fnt/data/old/lucidabright_bold.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/schoolbook_bold.txf | Bin 0 -> 9292 bytes .../src/fnt/data/old/schoolbook_medium.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/sorority.txf | Bin 0 -> 9304 bytes examples/src/fnt/data/old/symbol.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/times_bold.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/times_medium.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/old/typewriter.txf | Bin 0 -> 9292 bytes examples/src/fnt/data/test_slideshow.txt | 36 + examples/src/fnt/fnt_test.cxx | 282 + examples/src/fnt/fnt_test.dsp | 88 + examples/src/fnt/hello_world.cxx | 65 + examples/src/fnt/slideshow.cxx | 235 + examples/src/fnt/slideshow.dsp | 88 + examples/src/js/Makefile.am | 12 + examples/src/js/js_demo.cxx | 124 + examples/src/js/js_demo.dsp | 88 + examples/src/net/Makefile.am | 3 + examples/src/net/client_server/Makefile.am | 14 + examples/src/net/client_server/README | 25 + .../src/net/client_server/net_udp_client.cxx | 105 + .../src/net/client_server/net_udp_client.dsp | 88 + .../src/net/client_server/net_udp_server.cxx | 87 + .../src/net/client_server/net_udp_server.dsp | 88 + examples/src/net/http_get/Makefile.am | 12 + examples/src/net/http_get/README | 6 + examples/src/net/http_get/http_get.cxx | 75 + examples/src/net/http_get/http_get.dsp | 88 + examples/src/net/net_echo/Makefile.am | 12 + examples/src/net/net_echo/README | 16 + examples/src/net/net_echo/net_echo.cxx | 97 + examples/src/net/net_echo/net_echo.dsp | 88 + examples/src/psl/Makefile.am | 13 + examples/src/psl/data/header.psh | 4 + examples/src/psl/data/test.psl | 9 + examples/src/psl/psl_demo.cxx | 129 + examples/src/psl/psl_demo.dsp | 90 + examples/src/pui/Makefile.am | 31 + examples/src/pui/PointPicker.cxx | 446 + examples/src/pui/PointPicker.dsp | 88 + examples/src/pui/complex.cxx | 636 + examples/src/pui/complex.dsp | 88 + examples/src/pui/fltk_demo/Makefile | 25 + examples/src/pui/fltk_demo/gui.fl | 48 + examples/src/pui/fltk_demo/main.cxx | 143 + examples/src/pui/fltk_demo/puf.cxx | 186 + examples/src/pui/fltk_demo/puf.h | 61 + examples/src/pui/sdl_example/Demo.cxx | 75 + examples/src/pui/sdl_example/Demo.h | 10 + examples/src/pui/sdl_example/EventLoopSDL.cxx | 180 + examples/src/pui/sdl_example/EventLoopSDL.h | 32 + examples/src/pui/sdl_example/Makefile | 38 + examples/src/pui/sdl_example/mainSDL.cxx | 58 + examples/src/pui/simple.cxx | 131 + examples/src/pui/simple.dsp | 88 + examples/src/pui/widget_list.cxx | 684 + examples/src/pui/widget_list.dsp | 88 + examples/src/pui/widget_test.cxx | 219 + examples/src/pui/widget_test.dep | 2 + examples/src/pui/widget_test.dsp | 101 + examples/src/pui/widget_test.mak | 143 + examples/src/pui/widget_test.plg | 33 + examples/src/pw/Makefile.am | 14 + examples/src/pw/pw_demo.cxx | 104 + examples/src/pw/pw_demo.dsp | 88 + examples/src/pw/pw_pui.cxx | 75 + examples/src/pw/pw_pui.dsp | 88 + examples/src/sg/Makefile.am | 12 + examples/src/sg/sg_quat_test.cxx | 287 + examples/src/sg/sg_quat_test.dsp | 88 + examples/src/sl/Makefile.am | 17 + examples/src/sl/README | 18 + examples/src/sl/cuckoo.au | Bin 0 -> 6400 bytes examples/src/sl/example.cxx | 118 + examples/src/sl/example.dsp | 88 + examples/src/sl/mod_demo.cxx | 73 + examples/src/sl/mod_demo.dsp | 88 + examples/src/sl/playsound.cxx | 81 + examples/src/sl/scream.ub | 1 + examples/src/sl/tuxr.mod | Bin 0 -> 55430 bytes examples/src/sl/wheeee.ub | Bin 0 -> 7596 bytes examples/src/sl/zzap.wav | Bin 0 -> 6246 bytes examples/src/ssg/Makefile.am | 3 + examples/src/ssg/README | 20 + examples/src/ssg/dynamics/Makefile.am | 12 + examples/src/ssg/dynamics/data/bw.rgb | Bin 0 -> 888 bytes examples/src/ssg/dynamics/data/mg.rgb | Bin 0 -> 4244 bytes examples/src/ssg/dynamics/dynamics.cxx | 450 + examples/src/ssg/dynamics/dynamics.dsp | 88 + examples/src/ssg/load_save/Makefile.am | 14 + .../src/ssg/load_save/data/Penguin_beak.rgb | Bin 0 -> 11194 bytes .../src/ssg/load_save/data/Penguin_body.rgb | Bin 0 -> 18495 bytes .../ssg/load_save/data/embossed_herring.bmp | Bin 0 -> 24630 bytes examples/src/ssg/load_save/data/pedestal.ac | 81 + examples/src/ssg/load_save/data/pedestal.safe | 81 + examples/src/ssg/load_save/data/tuxedo.ac | 1695 ++ examples/src/ssg/load_save/data/wood.rgb | Bin 0 -> 11453 bytes examples/src/ssg/load_save/load.cxx | 234 + examples/src/ssg/load_save/load.dsp | 88 + examples/src/ssg/load_save/save.cxx | 131 + examples/src/ssg/load_save/save.dsp | 88 + examples/src/ssg/majik/Makefile.am | 14 + examples/src/ssg/majik/data/Penguin_beak.rgb | Bin 0 -> 11194 bytes examples/src/ssg/majik/data/Penguin_body.rgb | Bin 0 -> 18495 bytes examples/src/ssg/majik/data/bumpnoise.rgb | Bin 0 -> 54254 bytes examples/src/ssg/majik/data/majik.rgb | Bin 0 -> 9885 bytes examples/src/ssg/majik/data/tuxedo.ac | 1695 ++ examples/src/ssg/majik/elevation_map.h | 3859 +++++ examples/src/ssg/majik/image_map.h | 13111 ++++++++++++++++ examples/src/ssg/majik/majik_demo.cxx | 433 + examples/src/ssg/majik/majik_demo.dsp | 96 + examples/src/ssg/shapes/Makefile.am | 10 + examples/src/ssg/shapes/shapes.cxx | 180 + examples/src/ssg/shrubs/Makefile.am | 12 + examples/src/ssg/shrubs/data/Courier-Bold.txf | Bin 0 -> 66708 bytes .../src/ssg/shrubs/data/deciduous-tree.rgb | Bin 0 -> 8496 bytes examples/src/ssg/shrubs/shrubs.cxx | 718 + examples/src/ssg/sky/Makefile.am | 12 + examples/src/ssg/sky/README | 39 + examples/src/ssg/sky/data/candy.rgb | Bin 0 -> 1568 bytes examples/src/ssg/sky/data/halo.rgba | Bin 0 -> 7539 bytes examples/src/ssg/sky/data/moon.rgba | Bin 0 -> 80170 bytes examples/src/ssg/sky/data/ocean.rgb | Bin 0 -> 206134 bytes examples/src/ssg/sky/data/scattered.rgba | Bin 0 -> 246992 bytes examples/src/ssg/sky/data/sorority.txf | Bin 0 -> 9304 bytes examples/src/ssg/sky/sky.cxx | 688 + examples/src/ssg/sky/sky.dsp | 87 + examples/src/ssg/state_test/Makefile.am | 12 + examples/src/ssg/state_test/herring.inta | Bin 0 -> 16896 bytes examples/src/ssg/state_test/stest.cxx | 323 + examples/src/ssg/state_test/stest.dsp | 88 + examples/src/ssg/tux/Makefile.am | 14 + examples/src/ssg/tux/WavingFlag.cxx | 203 + examples/src/ssg/tux/WavingFlag.dsp | 96 + examples/src/ssg/tux/WavingFlag.h | 44 + examples/src/ssg/tux/WavingFlagDemo.cxx | 194 + examples/src/ssg/tux/data/Penguin_beak.rgb | Bin 0 -> 11194 bytes examples/src/ssg/tux/data/Penguin_body.rgb | Bin 0 -> 18495 bytes .../src/ssg/tux/data/embossed_herring.bmp | Bin 0 -> 24630 bytes examples/src/ssg/tux/data/pedestal.ac | 81 + examples/src/ssg/tux/data/pedestal.safe | 81 + examples/src/ssg/tux/data/tuxedo.ac | 1695 ++ examples/src/ssg/tux/data/wood.rgb | Bin 0 -> 11453 bytes examples/src/ssg/tux/tux_example.cxx | 228 + examples/src/ssg/tux/tux_example.dsp | 88 + examples/src/ssg/tween_test/Makefile.am | 12 + examples/src/ssg/tween_test/tween_test.cxx | 255 + examples/src/ssg/tween_test/tween_test.dsp | 90 + examples/src/ssg/viewer/Makefile.am | 17 + examples/src/ssg/viewer/data/NOTES | 10 + examples/src/ssg/viewer/data/Pbody.rgb | Bin 0 -> 18495 bytes examples/src/ssg/viewer/data/Penguin_beak.rgb | Bin 0 -> 11194 bytes examples/src/ssg/viewer/data/Penguin_body.rgb | Bin 0 -> 18495 bytes examples/src/ssg/viewer/data/animated.ASE | 299 + examples/src/ssg/viewer/data/ant_vr.wrl | 571 + examples/src/ssg/viewer/data/brown.bmp | Bin 0 -> 12342 bytes examples/src/ssg/viewer/data/default.txf | Bin 0 -> 66708 bytes examples/src/ssg/viewer/data/door.ase | 2978 ++++ .../src/ssg/viewer/data/embossed_herring.bmp | Bin 0 -> 24630 bytes examples/src/ssg/viewer/data/gray.bmp | Bin 0 -> 12342 bytes examples/src/ssg/viewer/data/pedestal.ac | 81 + examples/src/ssg/viewer/data/plane.3ds | Bin 0 -> 504755 bytes examples/src/ssg/viewer/data/plib.ac | 589 + examples/src/ssg/viewer/data/sorority.txf | Bin 0 -> 9304 bytes examples/src/ssg/viewer/data/test.ac | 81 + examples/src/ssg/viewer/data/tuxedo.ac | 1695 ++ examples/src/ssg/viewer/data/wood.rgb | Bin 0 -> 11453 bytes examples/src/ssg/viewer/pview.cxx | 191 + examples/src/ssg/viewer/pview.dsp | 102 + examples/src/ssg/viewer/viewer.cxx | 859 + examples/src/ssg/viewer/viewer.dsp | 88 + examples/src/ssg/water/Makefile.am | 12 + examples/src/ssg/water/README | 56 + examples/src/ssg/water/data/droplet.rgb | Bin 0 -> 10601 bytes examples/src/ssg/water/data/ocean.rgb | Bin 0 -> 206134 bytes examples/src/ssg/water/data/pattern.rgb | Bin 0 -> 54031 bytes examples/src/ssg/water/data/pavement.rgb | Bin 0 -> 103424 bytes examples/src/ssg/water/data/sorority.txf | Bin 0 -> 9304 bytes examples/src/ssg/water/water.cxx | 991 ++ examples/src/ssg/water/water.dsp | 90 + examples/src/util/Makefile.am | 12 + examples/src/util/test_dir.cxx | 65 + examples/src/util/test_dir.dsp | 88 + plib.dsp | 61 + plib.dsw | 209 + src/Makefile.am | 1 + src/fnt/Makefile.am | 14 + src/fnt/fnt.cxx | 252 + src/fnt/fnt.dsp | 114 + src/fnt/fnt.h | 361 + src/fnt/fntBitmap.cxx | 1017 ++ src/fnt/fntLocal.h | 111 + src/fnt/fntTXF.cxx | 363 + src/js/Makefile.am | 17 + src/js/README | 30 + src/js/js.cxx | 86 + src/js/js.dsp | 126 + src/js/js.h | 98 + src/js/jsBSD.cxx | 522 + src/js/jsLinux.cxx | 201 + src/js/jsLinuxOld.cxx | 146 + src/js/jsMacOS.cxx | 210 + src/js/jsMacOSX.cxx | 466 + src/js/jsNone.cxx | 59 + src/js/jsWindows.cxx | 273 + src/net/Makefile.am | 16 + src/net/README | 47 + src/net/net.dsp | 144 + src/net/net.h | 12 + src/net/netBuffer.cxx | 69 + src/net/netBuffer.h | 234 + src/net/netChannel.cxx | 279 + src/net/netChannel.h | 116 + src/net/netChat.cxx | 125 + src/net/netChat.h | 86 + src/net/netMessage.cxx | 50 + src/net/netMessage.h | 294 + src/net/netMonitor.cxx | 180 + src/net/netMonitor.h | 106 + src/net/netSocket.cxx | 499 + src/net/netSocket.h | 128 + src/psl/Makefile.am | 20 + src/psl/psl.cxx | 66 + src/psl/psl.dsp | 166 + src/psl/psl.h | 348 + src/psl/pslCodeGen.cxx | 394 + src/psl/pslCompiler.cxx | 884 ++ src/psl/pslCompiler.h | 414 + src/psl/pslContext.cxx | 864 + src/psl/pslContext.h | 106 + src/psl/pslDump.cxx | 288 + src/psl/pslError.cxx | 126 + src/psl/pslExpression.cxx | 519 + src/psl/pslFileIO.cxx | 206 + src/psl/pslFileIO.h | 35 + src/psl/pslLocal.h | 63 + src/psl/pslOpcodes.h | 100 + src/psl/pslProgram.cxx | 127 + src/psl/pslSymbol.h | 49 + src/psl/pslSymbols.cxx | 168 + src/psl/pslToken.cxx | 608 + src/puAux/Makefile.am | 32 + src/puAux/puAux.cxx | 31 + src/puAux/puAux.dsp | 160 + src/puAux/puAux.h | 944 ++ src/puAux/puAuxBiSlider.cxx | 170 + src/puAux/puAuxBiSliderWithEnds.cxx | 99 + src/puAux/puAuxChooser.cxx | 104 + src/puAux/puAuxComboBox.cxx | 377 + src/puAux/puAuxCompass.cxx | 564 + src/puAux/puAuxFileSelector.cxx | 692 + src/puAux/puAuxLargeInput.cxx | 1313 ++ src/puAux/puAuxList.cxx | 317 + src/puAux/puAuxLocal.h | 4 + src/puAux/puAuxScrollBar.cxx | 278 + src/puAux/puAuxSelectBox.cxx | 206 + src/puAux/puAuxSliderWithInput.cxx | 89 + src/puAux/puAuxSpinBox.cxx | 97 + src/puAux/puAuxTriSlider.cxx | 211 + src/puAux/puAuxVerticalMenu.cxx | 174 + src/pui/Makefile.am | 23 + src/pui/pu.cxx | 482 + src/pui/pu.h | 1615 ++ src/pui/puArrowButton.cxx | 170 + src/pui/puBox.cxx | 266 + src/pui/puButton.cxx | 236 + src/pui/puButtonBox.cxx | 162 + src/pui/puDial.cxx | 185 + src/pui/puDialogBox.cxx | 28 + src/pui/puFLTK.h | 68 + src/pui/puFont.cxx | 135 + src/pui/puFrame.cxx | 44 + src/pui/puGLUT.h | 75 + src/pui/puGroup.cxx | 476 + src/pui/puInput.cxx | 423 + src/pui/puInputBase.cxx | 88 + src/pui/puInterface.cxx | 127 + src/pui/puListBox.cxx | 201 + src/pui/puLocal.h | 31 + src/pui/puMenuBar.cxx | 179 + src/pui/puNative.h | 101 + src/pui/puObject.cxx | 520 + src/pui/puOneShot.cxx | 35 + src/pui/puPW.h | 66 + src/pui/puPopup.cxx | 28 + src/pui/puPopupMenu.cxx | 245 + src/pui/puRange.cxx | 27 + src/pui/puSDL.h | 55 + src/pui/puSlider.cxx | 210 + src/pui/puText.cxx | 39 + src/pui/puValue.cxx | 206 + src/pui/pui.dsp | 218 + src/pw/Makefile.am | 14 + src/pw/pw.cxx | 6 + src/pw/pw.dsp | 106 + src/pw/pw.h | 111 + src/pw/pwMacOSX.cxx | 1034 ++ src/pw/pwWindows.cxx | 490 + src/pw/pwX11.cxx | 651 + src/sg/Makefile.am | 16 + src/sg/sg.cxx | 2080 +++ src/sg/sg.dsp | 118 + src/sg/sg.h | 3102 ++++ src/sg/sgIsect.cxx | 390 + src/sg/sgPerlinNoise.cxx | 315 + src/sg/sgd.cxx | 2064 +++ src/sg/sgdIsect.cxx | 354 + src/sl/Makefile.am | 20 + src/sl/sl.dsp | 162 + src/sl/sl.h | 731 + src/sl/slDSP.cxx | 1085 ++ src/sl/slEnvelope.cxx | 219 + src/sl/slMODPlayer.cxx | 101 + src/sl/slMODPrivate.h | 159 + src/sl/slMODdacio.cxx | 118 + src/sl/slMODfile.cxx | 687 + src/sl/slMODfile.h | 78 + src/sl/slMODinst.cxx | 1096 ++ src/sl/slMODnote.cxx | 469 + src/sl/slPlayer.cxx | 139 + src/sl/slPortability.h | 95 + src/sl/slSample.cxx | 527 + src/sl/slSamplePlayer.cxx | 120 + src/sl/slScheduler.cxx | 545 + src/sl/sm.h | 111 + src/sl/smMixer.cxx | 279 + src/ssg/Makefile.am | 37 + src/ssg/pcx.h | 86 + src/ssg/ssg.cxx | 523 + src/ssg/ssg.dsp | 466 + src/ssg/ssg.h | 2883 ++++ src/ssg/ssg3ds.h | 79 + src/ssg/ssgAnimTransform.cxx | 174 + src/ssg/ssgAnimation.cxx | 205 + src/ssg/ssgAxisTransform.cxx | 128 + src/ssg/ssgBase.cxx | 138 + src/ssg/ssgBaseTransform.cxx | 79 + src/ssg/ssgBranch.cxx | 631 + src/ssg/ssgContext.cxx | 292 + src/ssg/ssgCutout.cxx | 163 + src/ssg/ssgDList.cxx | 273 + src/ssg/ssgEntity.cxx | 464 + src/ssg/ssgIO.cxx | 244 + src/ssg/ssgInvisible.cxx | 67 + src/ssg/ssgIsect.cxx | 107 + src/ssg/ssgKeyFlier.h | 265 + src/ssg/ssgLeaf.cxx | 247 + src/ssg/ssgList.cxx | 133 + src/ssg/ssgLoad.cxx | 260 + src/ssg/ssgLoad3ds.cxx | 1315 ++ src/ssg/ssgLoadAC.cxx | 952 ++ src/ssg/ssgLoadASC.cxx | 1015 ++ src/ssg/ssgLoadASE.cxx | 1704 ++ src/ssg/ssgLoadATG.cxx | 497 + src/ssg/ssgLoadBMP.cxx | 383 + src/ssg/ssgLoadDOF.cxx | 638 + src/ssg/ssgLoadDXF.cxx | 952 ++ src/ssg/ssgLoadFLT.cxx | 3173 ++++ src/ssg/ssgLoadIV.cxx | 381 + src/ssg/ssgLoadM.cxx | 152 + src/ssg/ssgLoadMD2.cxx | 402 + src/ssg/ssgLoadMDL.cxx | 1950 +++ src/ssg/ssgLoadMDL.h | 504 + src/ssg/ssgLoadMDL_BGLTexture.cxx | 384 + src/ssg/ssgLoadOBJ.cxx | 761 + src/ssg/ssgLoadOFF.cxx | 344 + src/ssg/ssgLoadPCX.cxx | 188 + src/ssg/ssgLoadPNG.cxx | 57 + src/ssg/ssgLoadSGI.cxx | 538 + src/ssg/ssgLoadSSG.cxx | 277 + src/ssg/ssgLoadStrip.cxx | 119 + src/ssg/ssgLoadTGA.cxx | 452 + src/ssg/ssgLoadTRI.cxx | 128 + src/ssg/ssgLoadTexture.cxx | 364 + src/ssg/ssgLoadVRML.h | 150 + src/ssg/ssgLoadVRML1.cxx | 932 ++ src/ssg/ssgLoadX.cxx | 757 + src/ssg/ssgLoadXPlaneObj.cxx | 463 + src/ssg/ssgLoaderWriterStuff.cxx | 914 ++ src/ssg/ssgLoaderWriterStuff.h | 161 + src/ssg/ssgLocal.h | 136 + src/ssg/ssgMSFSPalette.h | 29 + src/ssg/ssgOptimiser.cxx | 1108 ++ src/ssg/ssgParser.cxx | 531 + src/ssg/ssgParser.h | 116 + src/ssg/ssgRangeSelector.cxx | 200 + src/ssg/ssgRoot.cxx | 61 + src/ssg/ssgSave3ds.cxx | 511 + src/ssg/ssgSaveAC.cxx | 257 + src/ssg/ssgSaveASC.cxx | 234 + src/ssg/ssgSaveASE.cxx | 343 + src/ssg/ssgSaveATG.cxx | 160 + src/ssg/ssgSaveDXF.cxx | 179 + src/ssg/ssgSaveFLT.cxx | 680 + src/ssg/ssgSaveIV.cxx | 301 + src/ssg/ssgSaveM.cxx | 70 + src/ssg/ssgSaveOBJ.cxx | 349 + src/ssg/ssgSaveOFF.cxx | 79 + src/ssg/ssgSaveQHI.cxx | 63 + src/ssg/ssgSaveTRI.cxx | 111 + src/ssg/ssgSaveVRML1.cxx | 449 + src/ssg/ssgSaveX.cxx | 39 + src/ssg/ssgSelector.cxx | 172 + src/ssg/ssgSimpleList.cxx | 201 + src/ssg/ssgSimpleState.cxx | 649 + src/ssg/ssgState.cxx | 136 + src/ssg/ssgStateSelector.cxx | 426 + src/ssg/ssgStateTables.cxx | 221 + src/ssg/ssgStatistics.cxx | 18 + src/ssg/ssgStats.cxx | 95 + src/ssg/ssgTexTrans.cxx | 117 + src/ssg/ssgTexture.cxx | 267 + src/ssg/ssgTransform.cxx | 242 + src/ssg/ssgTween.cxx | 500 + src/ssg/ssgTweenController.cxx | 98 + src/ssg/ssgVTable.cxx | 869 + src/ssg/ssgVertSplitter.cxx | 351 + src/ssg/ssgVertSplitter.h | 78 + src/ssg/ssgVtxArray.cxx | 433 + src/ssg/ssgVtxTable.cxx | 1018 ++ src/ssg/ssgconf.h | 55 + src/ssgAux/Makefile.am | 41 + src/ssgAux/ssgAux.cxx | 36 + src/ssgAux/ssgAux.dsp | 205 + src/ssgAux/ssgAux.h | 63 + src/ssgAux/ssgaBillboards.cxx | 405 + src/ssgAux/ssgaBillboards.h | 227 + src/ssgAux/ssgaCelestialBody.cxx | 406 + src/ssgAux/ssgaCloudLayer.cxx | 524 + src/ssgAux/ssgaFire.cxx | 256 + src/ssgAux/ssgaFire.h | 77 + src/ssgAux/ssgaLensFlare.cxx | 330 + src/ssgAux/ssgaLensFlare.h | 52 + src/ssgAux/ssgaLensFlareTexture.cxx | 2076 +++ src/ssgAux/ssgaParticleSystem.cxx | 205 + src/ssgAux/ssgaParticleSystem.h | 115 + src/ssgAux/ssgaPatch.cxx | 238 + src/ssgAux/ssgaScreenDump.cxx | 204 + src/ssgAux/ssgaScreenDump.h | 21 + src/ssgAux/ssgaShapes.cxx | 860 + src/ssgAux/ssgaShapes.h | 222 + src/ssgAux/ssgaSky.cxx | 402 + src/ssgAux/ssgaSky.h | 397 + src/ssgAux/ssgaSkyDome.cxx | 504 + src/ssgAux/ssgaSphere.cxx | 125 + src/ssgAux/ssgaSphere.h | 33 + src/ssgAux/ssgaStars.cxx | 242 + src/ssgAux/ssgaTeapot.cxx | 342 + src/ssgAux/ssgaWaveSystem.cxx | 347 + src/ssgAux/ssgaWaveSystem.h | 131 + src/util/Makefile.am | 13 + src/util/ul.cxx | 544 + src/util/ul.dsp | 130 + src/util/ul.h | 851 + src/util/ulClock.cxx | 117 + src/util/ulError.cxx | 90 + src/util/ulLinkedList.cxx | 272 + src/util/ulList.cxx | 109 + src/util/ulLocal.h | 52 + src/util/ulRTTI.cxx | 109 + src/util/ulRTTI.h | 420 + tools/AUTHORS | 62 + tools/COPYING | 340 + tools/ChangeLog | 0 tools/INSTALL | 182 + tools/Makefile.am | 8 + tools/NEWS | 0 tools/README | 0 tools/autogen.sh | 19 + tools/configure.in | 161 + tools/doc/index.html | 65 + tools/src/Makefile.am | 10 + tools/src/af2rgb/Makefile.am | 8 + tools/src/af2rgb/af2rgb.cxx | 206 + tools/src/af2rgb/af2rgb.dsp | 102 + tools/src/afm2txf/README | 18 + tools/src/afm2txf/afm2txf.pl | 283 + tools/src/plibconvert.cxx | 99 + tools/src/txfdump/txfdump.pl | 93 + tools/tools.dsw | 29 + transferCvs2WebSite | 82 + 809 files changed, 211135 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 KNOWN_BUGS create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 NOTICE create mode 100644 README create mode 100644 README.GLUT create mode 100644 README.JOYSTICKS.FREEBSD create mode 100644 README.mac create mode 100644 README.win32 create mode 100644 TODO-1.6 create mode 100644 TODO-2.0 create mode 100644 TODO_AFTER135 create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 demos/README create mode 100755 demos/build_all_demos create mode 100644 demos/exposer/AUTHORS create mode 100644 demos/exposer/COPYING create mode 100644 demos/exposer/ChangeLog create mode 100644 demos/exposer/INSTALL create mode 100644 demos/exposer/Makefile.am create mode 100644 demos/exposer/NEWS create mode 100644 demos/exposer/README create mode 100755 demos/exposer/autogen.sh create mode 100644 demos/exposer/configure.in create mode 100644 demos/exposer/data/Makefile.am create mode 100644 demos/exposer/data/dog.ac create mode 100644 demos/exposer/data/dog.bones create mode 100644 demos/exposer/data/human.ac create mode 100644 demos/exposer/data/human.bones create mode 100644 demos/exposer/data/human_die.bones create mode 100644 demos/exposer/data/human_walk.bones create mode 100644 demos/exposer/data/teeshirt_human.rgb create mode 100644 demos/exposer/doc/Makefile.am create mode 100644 demos/exposer/doc/bone.png create mode 100644 demos/exposer/doc/exposer.png create mode 100644 demos/exposer/doc/exposer_logo.png create mode 100644 demos/exposer/doc/index.html create mode 100644 demos/exposer/doc/menu.png create mode 100644 demos/exposer/doc/timeline.png create mode 100644 demos/exposer/doc/translate.png create mode 100644 demos/exposer/doc/vcr.png create mode 100644 demos/exposer/doc/viewer.png create mode 100644 demos/exposer/exposer.dsw create mode 100644 demos/exposer/src/Makefile.am create mode 100644 demos/exposer/src/boneGUI.cxx create mode 100644 demos/exposer/src/boneGUI.h create mode 100644 demos/exposer/src/bones.cxx create mode 100644 demos/exposer/src/bones.h create mode 100644 demos/exposer/src/event.cxx create mode 100644 demos/exposer/src/event.h create mode 100644 demos/exposer/src/exposer.cxx create mode 100644 demos/exposer/src/exposer.dsp create mode 100644 demos/exposer/src/exposer.h create mode 100644 demos/exposer/src/floor.cxx create mode 100644 demos/exposer/src/floor.h create mode 100644 demos/exposer/src/load_save.cxx create mode 100644 demos/exposer/src/load_save.h create mode 100644 demos/exposer/src/merge_tweens.cxx create mode 100644 demos/exposer/src/merge_tweens.dsp create mode 100644 demos/exposer/src/merge_tweens.h create mode 100644 demos/exposer/src/model.cxx create mode 100644 demos/exposer/src/model.h create mode 100644 demos/exposer/src/timebox.cxx create mode 100644 demos/exposer/src/timebox.h create mode 100644 demos/exposer/src/vertices.cxx create mode 100644 demos/exposer/src/vertices.h create mode 100644 demos/p-guide/AUTHORS create mode 100644 demos/p-guide/COPYING create mode 100644 demos/p-guide/ChangeLog create mode 100644 demos/p-guide/INSTALL create mode 100644 demos/p-guide/Makefile.am create mode 100644 demos/p-guide/NEWS create mode 100644 demos/p-guide/README create mode 100644 demos/p-guide/TODO create mode 100755 demos/p-guide/autogen.sh create mode 100644 demos/p-guide/configure.in create mode 100644 demos/p-guide/doc/p-guide.html create mode 100644 demos/p-guide/pGuide.dsw create mode 100644 demos/p-guide/src/LoadSave.cxx create mode 100644 demos/p-guide/src/Makefile.am create mode 100644 demos/p-guide/src/PropertiesWindow.cxx create mode 100644 demos/p-guide/src/StatusWindow.cxx create mode 100644 demos/p-guide/src/WidgetList.h create mode 100644 demos/p-guide/src/WidgetWindow.cxx create mode 100644 demos/p-guide/src/WriteCode.cxx create mode 100644 demos/p-guide/src/example.xml create mode 100644 demos/p-guide/src/pGuide.cxx create mode 100644 demos/p-guide/src/pGuide.dsp create mode 100644 demos/simon/AUTHORS create mode 100644 demos/simon/COPYING create mode 100644 demos/simon/Makefile create mode 100644 demos/simon/README create mode 100644 demos/simon/astropong/astropong.psl create mode 100644 demos/simon/astropong/data/Penguin_beak.rgb create mode 100644 demos/simon/astropong/data/Penguin_body.rgb create mode 100644 demos/simon/astropong/data/Penguin_body2.rgb create mode 100644 demos/simon/astropong/data/Penguin_foot.rgba create mode 100644 demos/simon/astropong/data/Scoreboard.ac create mode 100644 demos/simon/astropong/data/Scoreboard.rgb create mode 100644 demos/simon/astropong/data/arena2.ac create mode 100644 demos/simon/astropong/data/ball3.ac create mode 100644 demos/simon/astropong/data/bluepaddle2.ac create mode 100644 demos/simon/astropong/data/embossed_herring.rgb create mode 100644 demos/simon/astropong/data/redpaddle2.ac create mode 100644 demos/simon/astropong/data/shadow2.ac create mode 100644 demos/simon/doc/index.html create mode 100644 demos/simon/src/Makefile create mode 100644 demos/simon/src/simon.cxx create mode 100644 demos/simon/src/simon.dsp create mode 100644 demos/simon/src/simon.dsw create mode 100644 demos/simon/src/simon.h create mode 100644 demos/simon/src/simon_api.cxx create mode 100644 demos/ttt3d/AUTHORS create mode 100644 demos/ttt3d/COPYING create mode 100644 demos/ttt3d/ChangeLog create mode 100644 demos/ttt3d/INSTALL create mode 100644 demos/ttt3d/Makefile.am create mode 100644 demos/ttt3d/NEWS create mode 100644 demos/ttt3d/README create mode 100644 demos/ttt3d/TTT3D.dsp create mode 100644 demos/ttt3d/TTT3D.dsw create mode 100755 demos/ttt3d/acinclude.m4 create mode 100755 demos/ttt3d/autogen.sh create mode 100644 demos/ttt3d/configure.in create mode 100644 demos/ttt3d/data/Makefile.am create mode 100644 demos/ttt3d/data/levels.dat create mode 100644 demos/ttt3d/doc/Makefile.am create mode 100644 demos/ttt3d/doc/index.html create mode 100644 demos/ttt3d/fonts/Makefile.am create mode 100644 demos/ttt3d/fonts/sorority.txf create mode 100644 demos/ttt3d/images/Makefile.am create mode 100644 demos/ttt3d/images/O.rgb create mode 100644 demos/ttt3d/images/X.rgb create mode 100644 demos/ttt3d/images/controls.rgb create mode 100644 demos/ttt3d/images/ground.rgb create mode 100644 demos/ttt3d/mods/Makefile.am create mode 100644 demos/ttt3d/mods/mfarmer.mod create mode 100644 demos/ttt3d/src/Makefile.am create mode 100644 demos/ttt3d/src/cell.cxx create mode 100644 demos/ttt3d/src/cell.h create mode 100644 demos/ttt3d/src/game.cxx create mode 100644 demos/ttt3d/src/game.h create mode 100644 demos/ttt3d/src/gfx.cxx create mode 100644 demos/ttt3d/src/gfx.h create mode 100644 demos/ttt3d/src/gui.cxx create mode 100644 demos/ttt3d/src/gui.h create mode 100644 demos/ttt3d/src/level.h create mode 100644 demos/ttt3d/src/material.cxx create mode 100644 demos/ttt3d/src/material.h create mode 100644 demos/ttt3d/src/p3d.h create mode 100644 demos/ttt3d/src/puzzle.cxx create mode 100644 demos/ttt3d/src/puzzle.h create mode 100644 demos/ttt3d/src/sound.cxx create mode 100644 demos/ttt3d/src/sound.h create mode 100644 demos/ttt3d/src/status.cxx create mode 100644 demos/ttt3d/src/status.h create mode 100644 demos/ttt3d/src/ttt3d.cxx create mode 100644 demos/ttt3d/wavs/Makefile.am create mode 100644 demos/ttt3d/wavs/ahooga.wav create mode 100644 demos/ttt3d/wavs/clap.wav create mode 100644 demos/ttt3d/wavs/frog.wav create mode 100644 demos/ttt3d/wavs/glasbk.wav create mode 100644 demos/ttt3d/wavs/pop.wav create mode 100644 demos/ttt3d/wavs/ugh.wav create mode 100644 demos/ttt3d/wavs/who_else.wav create mode 100644 demos/widgy/README create mode 100644 doc/FDS_logo.png create mode 100644 doc/FNT.png create mode 100644 doc/JS.png create mode 100644 doc/MinigolfManiacs.png create mode 100644 doc/NET.png create mode 100644 doc/PSL.png create mode 100644 doc/PUI.png create mode 100644 doc/PW.png create mode 100644 doc/SG.png create mode 100644 doc/SL.png create mode 100644 doc/SSG.png create mode 100644 doc/UL.png create mode 100644 doc/auxlibs/index.html create mode 100644 doc/contacts.html create mode 100644 doc/contacts.png create mode 100644 doc/download.html create mode 100644 doc/download.png create mode 100644 doc/examples.html create mode 100644 doc/examples.png create mode 100644 doc/fgfs.png create mode 100644 doc/fnt/index.html create mode 100644 doc/gallery.html create mode 100644 doc/gallery.png create mode 100644 doc/home.png create mode 100644 doc/index.html create mode 100644 doc/js/index.html create mode 100644 doc/license.html create mode 100644 doc/license.png create mode 100644 doc/majik3d.png create mode 100644 doc/marble.png create mode 100644 doc/mote.png create mode 100644 doc/net/index.html create mode 100644 doc/plib_logo.png create mode 100644 doc/ppe_logo_1.png create mode 100644 doc/psl/appl_guide.html create mode 100644 doc/psl/impl_guide.html create mode 100644 doc/psl/index.html create mode 100644 doc/psl/prog_guide.html create mode 100644 doc/puAux.png create mode 100644 doc/puAux/index.html create mode 100644 doc/pui/BasicPUI.html create mode 100644 doc/pui/FAQ.html create mode 100644 doc/pui/complex.png create mode 100644 doc/pui/index.html create mode 100644 doc/pui/labelplaces.png create mode 100644 doc/pui/legendplaces.png create mode 100644 doc/pui/pooh.png create mode 100644 doc/pui/puArrowButton.png create mode 100644 doc/pui/puBiSlider.png create mode 100644 doc/pui/puButton.png create mode 100644 doc/pui/puButtonBox.png create mode 100644 doc/pui/puComboBox.png create mode 100644 doc/pui/puDial.png create mode 100644 doc/pui/puDialogBox.png create mode 100644 doc/pui/puFileSelector.png create mode 100644 doc/pui/puFrame.png create mode 100644 doc/pui/puInput.png create mode 100644 doc/pui/puLargeInput.png create mode 100644 doc/pui/puListBox.png create mode 100644 doc/pui/puMenuBar.png create mode 100644 doc/pui/puOneShot.png create mode 100644 doc/pui/puPopupMenu.png create mode 100644 doc/pui/puSelectBox.png create mode 100644 doc/pui/puSlider.png create mode 100644 doc/pui/puText.png create mode 100644 doc/pui/puTriSlider.png create mode 100644 doc/pui/puVerticalMenu.png create mode 100644 doc/pui/simple.png create mode 100644 doc/pw/index.html create mode 100644 doc/requirements.html create mode 100644 doc/requirements.png create mode 100644 doc/sg/index.html create mode 100644 doc/sg/triangle_params.png create mode 100644 doc/sl/index.html create mode 100644 doc/sl/mixer.png create mode 100644 doc/sl/sl.html create mode 100644 doc/sl/sl.png create mode 100644 doc/sl/sm.html create mode 100644 doc/spacething-small.png create mode 100644 doc/ssg/LoaderWriter.html create mode 100644 doc/ssg/branches.html create mode 100644 doc/ssg/index.html create mode 100644 doc/ssg/non_class.html create mode 100644 doc/ssg/ssgBase.html create mode 100644 doc/ssg/ssgContext.html create mode 100644 doc/ssg/ssgEntity.html create mode 100644 doc/ssg/ssgLeaf.html create mode 100644 doc/ssg/state.html create mode 100644 doc/ssgAux.png create mode 100644 doc/ssgAux/index.html create mode 100644 doc/ssgAux/snapshot.png create mode 100644 doc/ssgAux/ssgaSky.png create mode 100644 doc/torcs_logo.png create mode 100644 doc/tux_aqfh.png create mode 100644 doc/tuxfleet_logo.png create mode 100644 doc/tuxkart_logo.png create mode 100644 doc/util/index.html create mode 100644 doc/valid-html40.png create mode 100644 doc/whats_inside.html create mode 100644 doc/whats_inside.png create mode 100644 examples/AUTHORS create mode 100644 examples/COPYING create mode 100644 examples/COPYING.DATA create mode 100644 examples/ChangeLog create mode 100644 examples/INSTALL create mode 100644 examples/Makefile.am create mode 100644 examples/NEWS create mode 100644 examples/README create mode 100755 examples/autogen.sh create mode 100644 examples/configure.in create mode 100644 examples/plib_examples.dsp create mode 100644 examples/plib_examples.dsw create mode 100644 examples/src/Makefile.am create mode 100644 examples/src/fnt/Makefile.am create mode 100644 examples/src/fnt/data/AvantGarde-Book.txf create mode 100644 examples/src/fnt/data/AvantGarde-BookOblique.txf create mode 100644 examples/src/fnt/data/AvantGarde-Demi.txf create mode 100644 examples/src/fnt/data/AvantGarde-DemiOblique.txf create mode 100644 examples/src/fnt/data/Bookman-Demi.txf create mode 100644 examples/src/fnt/data/Bookman-DemiItalic.txf create mode 100644 examples/src/fnt/data/Bookman-Light.txf create mode 100644 examples/src/fnt/data/Bookman-LightItalic.txf create mode 100644 examples/src/fnt/data/Courier-Bold.txf create mode 100644 examples/src/fnt/data/Courier-BoldOblique.txf create mode 100644 examples/src/fnt/data/Courier-Oblique.txf create mode 100644 examples/src/fnt/data/Courier.txf create mode 100644 examples/src/fnt/data/Helvetica-Bold.txf create mode 100644 examples/src/fnt/data/Helvetica-BoldOblique.txf create mode 100644 examples/src/fnt/data/Helvetica-Oblique.txf create mode 100644 examples/src/fnt/data/Helvetica.txf create mode 100644 examples/src/fnt/data/NewCenturySchlbk-Bold.txf create mode 100644 examples/src/fnt/data/NewCenturySchlbk-BoldItalic.txf create mode 100644 examples/src/fnt/data/NewCenturySchlbk-Italic.txf create mode 100644 examples/src/fnt/data/NewCenturySchlbk-Roman.txf create mode 100644 examples/src/fnt/data/Palatino-Bold.txf create mode 100644 examples/src/fnt/data/Palatino-BoldItalic.txf create mode 100644 examples/src/fnt/data/Palatino-Italic.txf create mode 100644 examples/src/fnt/data/Palatino-Roman.txf create mode 100644 examples/src/fnt/data/README create mode 100644 examples/src/fnt/data/Times-Bold.txf create mode 100644 examples/src/fnt/data/Times-BoldItalic.txf create mode 100644 examples/src/fnt/data/Times-Italic.txf create mode 100644 examples/src/fnt/data/Times-Roman.txf create mode 100644 examples/src/fnt/data/ZapfChancery-MediumItalic.txf create mode 100644 examples/src/fnt/data/old/README create mode 100644 examples/src/fnt/data/old/charter.txf create mode 100644 examples/src/fnt/data/old/courier-bold.txf create mode 100644 examples/src/fnt/data/old/courier_medium.txf create mode 100644 examples/src/fnt/data/old/curlfont.txf create mode 100644 examples/src/fnt/data/old/default.txf create mode 100644 examples/src/fnt/data/old/derniere.txf create mode 100644 examples/src/fnt/data/old/haeberli.txf create mode 100644 examples/src/fnt/data/old/helvetica_bold.txf create mode 100644 examples/src/fnt/data/old/helvetica_medium.txf create mode 100644 examples/src/fnt/data/old/junius.txf create mode 100644 examples/src/fnt/data/old/ledfixed.txf create mode 100644 examples/src/fnt/data/old/lucida.txf create mode 100644 examples/src/fnt/data/old/lucidabright_bold.txf create mode 100644 examples/src/fnt/data/old/schoolbook_bold.txf create mode 100644 examples/src/fnt/data/old/schoolbook_medium.txf create mode 100644 examples/src/fnt/data/old/sorority.txf create mode 100644 examples/src/fnt/data/old/symbol.txf create mode 100644 examples/src/fnt/data/old/times_bold.txf create mode 100644 examples/src/fnt/data/old/times_medium.txf create mode 100644 examples/src/fnt/data/old/typewriter.txf create mode 100644 examples/src/fnt/data/test_slideshow.txt create mode 100644 examples/src/fnt/fnt_test.cxx create mode 100644 examples/src/fnt/fnt_test.dsp create mode 100644 examples/src/fnt/hello_world.cxx create mode 100644 examples/src/fnt/slideshow.cxx create mode 100644 examples/src/fnt/slideshow.dsp create mode 100644 examples/src/js/Makefile.am create mode 100644 examples/src/js/js_demo.cxx create mode 100644 examples/src/js/js_demo.dsp create mode 100644 examples/src/net/Makefile.am create mode 100644 examples/src/net/client_server/Makefile.am create mode 100644 examples/src/net/client_server/README create mode 100644 examples/src/net/client_server/net_udp_client.cxx create mode 100644 examples/src/net/client_server/net_udp_client.dsp create mode 100644 examples/src/net/client_server/net_udp_server.cxx create mode 100644 examples/src/net/client_server/net_udp_server.dsp create mode 100644 examples/src/net/http_get/Makefile.am create mode 100644 examples/src/net/http_get/README create mode 100644 examples/src/net/http_get/http_get.cxx create mode 100644 examples/src/net/http_get/http_get.dsp create mode 100644 examples/src/net/net_echo/Makefile.am create mode 100644 examples/src/net/net_echo/README create mode 100644 examples/src/net/net_echo/net_echo.cxx create mode 100644 examples/src/net/net_echo/net_echo.dsp create mode 100644 examples/src/psl/Makefile.am create mode 100644 examples/src/psl/data/header.psh create mode 100644 examples/src/psl/data/test.psl create mode 100644 examples/src/psl/psl_demo.cxx create mode 100644 examples/src/psl/psl_demo.dsp create mode 100644 examples/src/pui/Makefile.am create mode 100644 examples/src/pui/PointPicker.cxx create mode 100644 examples/src/pui/PointPicker.dsp create mode 100644 examples/src/pui/complex.cxx create mode 100644 examples/src/pui/complex.dsp create mode 100644 examples/src/pui/fltk_demo/Makefile create mode 100644 examples/src/pui/fltk_demo/gui.fl create mode 100644 examples/src/pui/fltk_demo/main.cxx create mode 100644 examples/src/pui/fltk_demo/puf.cxx create mode 100644 examples/src/pui/fltk_demo/puf.h create mode 100644 examples/src/pui/sdl_example/Demo.cxx create mode 100644 examples/src/pui/sdl_example/Demo.h create mode 100644 examples/src/pui/sdl_example/EventLoopSDL.cxx create mode 100644 examples/src/pui/sdl_example/EventLoopSDL.h create mode 100644 examples/src/pui/sdl_example/Makefile create mode 100644 examples/src/pui/sdl_example/mainSDL.cxx create mode 100644 examples/src/pui/simple.cxx create mode 100644 examples/src/pui/simple.dsp create mode 100644 examples/src/pui/widget_list.cxx create mode 100644 examples/src/pui/widget_list.dsp create mode 100644 examples/src/pui/widget_test.cxx create mode 100644 examples/src/pui/widget_test.dep create mode 100644 examples/src/pui/widget_test.dsp create mode 100644 examples/src/pui/widget_test.mak create mode 100644 examples/src/pui/widget_test.plg create mode 100644 examples/src/pw/Makefile.am create mode 100644 examples/src/pw/pw_demo.cxx create mode 100644 examples/src/pw/pw_demo.dsp create mode 100644 examples/src/pw/pw_pui.cxx create mode 100644 examples/src/pw/pw_pui.dsp create mode 100644 examples/src/sg/Makefile.am create mode 100644 examples/src/sg/sg_quat_test.cxx create mode 100644 examples/src/sg/sg_quat_test.dsp create mode 100644 examples/src/sl/Makefile.am create mode 100644 examples/src/sl/README create mode 100644 examples/src/sl/cuckoo.au create mode 100644 examples/src/sl/example.cxx create mode 100644 examples/src/sl/example.dsp create mode 100644 examples/src/sl/mod_demo.cxx create mode 100644 examples/src/sl/mod_demo.dsp create mode 100644 examples/src/sl/playsound.cxx create mode 100644 examples/src/sl/scream.ub create mode 100644 examples/src/sl/tuxr.mod create mode 100644 examples/src/sl/wheeee.ub create mode 100644 examples/src/sl/zzap.wav create mode 100644 examples/src/ssg/Makefile.am create mode 100644 examples/src/ssg/README create mode 100644 examples/src/ssg/dynamics/Makefile.am create mode 100644 examples/src/ssg/dynamics/data/bw.rgb create mode 100644 examples/src/ssg/dynamics/data/mg.rgb create mode 100644 examples/src/ssg/dynamics/dynamics.cxx create mode 100644 examples/src/ssg/dynamics/dynamics.dsp create mode 100644 examples/src/ssg/load_save/Makefile.am create mode 100644 examples/src/ssg/load_save/data/Penguin_beak.rgb create mode 100644 examples/src/ssg/load_save/data/Penguin_body.rgb create mode 100644 examples/src/ssg/load_save/data/embossed_herring.bmp create mode 100644 examples/src/ssg/load_save/data/pedestal.ac create mode 100644 examples/src/ssg/load_save/data/pedestal.safe create mode 100644 examples/src/ssg/load_save/data/tuxedo.ac create mode 100644 examples/src/ssg/load_save/data/wood.rgb create mode 100644 examples/src/ssg/load_save/load.cxx create mode 100644 examples/src/ssg/load_save/load.dsp create mode 100644 examples/src/ssg/load_save/save.cxx create mode 100644 examples/src/ssg/load_save/save.dsp create mode 100644 examples/src/ssg/majik/Makefile.am create mode 100644 examples/src/ssg/majik/data/Penguin_beak.rgb create mode 100644 examples/src/ssg/majik/data/Penguin_body.rgb create mode 100644 examples/src/ssg/majik/data/bumpnoise.rgb create mode 100644 examples/src/ssg/majik/data/majik.rgb create mode 100644 examples/src/ssg/majik/data/tuxedo.ac create mode 100644 examples/src/ssg/majik/elevation_map.h create mode 100644 examples/src/ssg/majik/image_map.h create mode 100644 examples/src/ssg/majik/majik_demo.cxx create mode 100644 examples/src/ssg/majik/majik_demo.dsp create mode 100644 examples/src/ssg/shapes/Makefile.am create mode 100644 examples/src/ssg/shapes/shapes.cxx create mode 100644 examples/src/ssg/shrubs/Makefile.am create mode 100644 examples/src/ssg/shrubs/data/Courier-Bold.txf create mode 100644 examples/src/ssg/shrubs/data/deciduous-tree.rgb create mode 100644 examples/src/ssg/shrubs/shrubs.cxx create mode 100644 examples/src/ssg/sky/Makefile.am create mode 100644 examples/src/ssg/sky/README create mode 100644 examples/src/ssg/sky/data/candy.rgb create mode 100644 examples/src/ssg/sky/data/halo.rgba create mode 100644 examples/src/ssg/sky/data/moon.rgba create mode 100644 examples/src/ssg/sky/data/ocean.rgb create mode 100644 examples/src/ssg/sky/data/scattered.rgba create mode 100644 examples/src/ssg/sky/data/sorority.txf create mode 100644 examples/src/ssg/sky/sky.cxx create mode 100644 examples/src/ssg/sky/sky.dsp create mode 100644 examples/src/ssg/state_test/Makefile.am create mode 100644 examples/src/ssg/state_test/herring.inta create mode 100644 examples/src/ssg/state_test/stest.cxx create mode 100644 examples/src/ssg/state_test/stest.dsp create mode 100644 examples/src/ssg/tux/Makefile.am create mode 100644 examples/src/ssg/tux/WavingFlag.cxx create mode 100644 examples/src/ssg/tux/WavingFlag.dsp create mode 100644 examples/src/ssg/tux/WavingFlag.h create mode 100644 examples/src/ssg/tux/WavingFlagDemo.cxx create mode 100644 examples/src/ssg/tux/data/Penguin_beak.rgb create mode 100644 examples/src/ssg/tux/data/Penguin_body.rgb create mode 100644 examples/src/ssg/tux/data/embossed_herring.bmp create mode 100644 examples/src/ssg/tux/data/pedestal.ac create mode 100644 examples/src/ssg/tux/data/pedestal.safe create mode 100644 examples/src/ssg/tux/data/tuxedo.ac create mode 100644 examples/src/ssg/tux/data/wood.rgb create mode 100644 examples/src/ssg/tux/tux_example.cxx create mode 100644 examples/src/ssg/tux/tux_example.dsp create mode 100644 examples/src/ssg/tween_test/Makefile.am create mode 100644 examples/src/ssg/tween_test/tween_test.cxx create mode 100644 examples/src/ssg/tween_test/tween_test.dsp create mode 100644 examples/src/ssg/viewer/Makefile.am create mode 100644 examples/src/ssg/viewer/data/NOTES create mode 100644 examples/src/ssg/viewer/data/Pbody.rgb create mode 100644 examples/src/ssg/viewer/data/Penguin_beak.rgb create mode 100644 examples/src/ssg/viewer/data/Penguin_body.rgb create mode 100644 examples/src/ssg/viewer/data/animated.ASE create mode 100644 examples/src/ssg/viewer/data/ant_vr.wrl create mode 100644 examples/src/ssg/viewer/data/brown.bmp create mode 100644 examples/src/ssg/viewer/data/default.txf create mode 100644 examples/src/ssg/viewer/data/door.ase create mode 100644 examples/src/ssg/viewer/data/embossed_herring.bmp create mode 100644 examples/src/ssg/viewer/data/gray.bmp create mode 100644 examples/src/ssg/viewer/data/pedestal.ac create mode 100644 examples/src/ssg/viewer/data/plane.3ds create mode 100644 examples/src/ssg/viewer/data/plib.ac create mode 100644 examples/src/ssg/viewer/data/sorority.txf create mode 100644 examples/src/ssg/viewer/data/test.ac create mode 100644 examples/src/ssg/viewer/data/tuxedo.ac create mode 100644 examples/src/ssg/viewer/data/wood.rgb create mode 100644 examples/src/ssg/viewer/pview.cxx create mode 100755 examples/src/ssg/viewer/pview.dsp create mode 100644 examples/src/ssg/viewer/viewer.cxx create mode 100644 examples/src/ssg/viewer/viewer.dsp create mode 100644 examples/src/ssg/water/Makefile.am create mode 100644 examples/src/ssg/water/README create mode 100644 examples/src/ssg/water/data/droplet.rgb create mode 100644 examples/src/ssg/water/data/ocean.rgb create mode 100644 examples/src/ssg/water/data/pattern.rgb create mode 100644 examples/src/ssg/water/data/pavement.rgb create mode 100644 examples/src/ssg/water/data/sorority.txf create mode 100644 examples/src/ssg/water/water.cxx create mode 100644 examples/src/ssg/water/water.dsp create mode 100644 examples/src/util/Makefile.am create mode 100644 examples/src/util/test_dir.cxx create mode 100644 examples/src/util/test_dir.dsp create mode 100644 plib.dsp create mode 100644 plib.dsw create mode 100644 src/Makefile.am create mode 100644 src/fnt/Makefile.am create mode 100644 src/fnt/fnt.cxx create mode 100644 src/fnt/fnt.dsp create mode 100644 src/fnt/fnt.h create mode 100644 src/fnt/fntBitmap.cxx create mode 100644 src/fnt/fntLocal.h create mode 100644 src/fnt/fntTXF.cxx create mode 100644 src/js/Makefile.am create mode 100644 src/js/README create mode 100644 src/js/js.cxx create mode 100644 src/js/js.dsp create mode 100644 src/js/js.h create mode 100644 src/js/jsBSD.cxx create mode 100644 src/js/jsLinux.cxx create mode 100644 src/js/jsLinuxOld.cxx create mode 100644 src/js/jsMacOS.cxx create mode 100644 src/js/jsMacOSX.cxx create mode 100644 src/js/jsNone.cxx create mode 100644 src/js/jsWindows.cxx create mode 100644 src/net/Makefile.am create mode 100644 src/net/README create mode 100644 src/net/net.dsp create mode 100644 src/net/net.h create mode 100644 src/net/netBuffer.cxx create mode 100644 src/net/netBuffer.h create mode 100644 src/net/netChannel.cxx create mode 100644 src/net/netChannel.h create mode 100644 src/net/netChat.cxx create mode 100644 src/net/netChat.h create mode 100644 src/net/netMessage.cxx create mode 100644 src/net/netMessage.h create mode 100644 src/net/netMonitor.cxx create mode 100644 src/net/netMonitor.h create mode 100644 src/net/netSocket.cxx create mode 100644 src/net/netSocket.h create mode 100644 src/psl/Makefile.am create mode 100644 src/psl/psl.cxx create mode 100644 src/psl/psl.dsp create mode 100644 src/psl/psl.h create mode 100644 src/psl/pslCodeGen.cxx create mode 100644 src/psl/pslCompiler.cxx create mode 100644 src/psl/pslCompiler.h create mode 100644 src/psl/pslContext.cxx create mode 100644 src/psl/pslContext.h create mode 100644 src/psl/pslDump.cxx create mode 100644 src/psl/pslError.cxx create mode 100644 src/psl/pslExpression.cxx create mode 100644 src/psl/pslFileIO.cxx create mode 100644 src/psl/pslFileIO.h create mode 100644 src/psl/pslLocal.h create mode 100644 src/psl/pslOpcodes.h create mode 100644 src/psl/pslProgram.cxx create mode 100644 src/psl/pslSymbol.h create mode 100644 src/psl/pslSymbols.cxx create mode 100644 src/psl/pslToken.cxx create mode 100644 src/puAux/Makefile.am create mode 100644 src/puAux/puAux.cxx create mode 100644 src/puAux/puAux.dsp create mode 100644 src/puAux/puAux.h create mode 100644 src/puAux/puAuxBiSlider.cxx create mode 100644 src/puAux/puAuxBiSliderWithEnds.cxx create mode 100644 src/puAux/puAuxChooser.cxx create mode 100644 src/puAux/puAuxComboBox.cxx create mode 100644 src/puAux/puAuxCompass.cxx create mode 100644 src/puAux/puAuxFileSelector.cxx create mode 100644 src/puAux/puAuxLargeInput.cxx create mode 100644 src/puAux/puAuxList.cxx create mode 100644 src/puAux/puAuxLocal.h create mode 100644 src/puAux/puAuxScrollBar.cxx create mode 100644 src/puAux/puAuxSelectBox.cxx create mode 100644 src/puAux/puAuxSliderWithInput.cxx create mode 100644 src/puAux/puAuxSpinBox.cxx create mode 100644 src/puAux/puAuxTriSlider.cxx create mode 100644 src/puAux/puAuxVerticalMenu.cxx create mode 100644 src/pui/Makefile.am create mode 100644 src/pui/pu.cxx create mode 100644 src/pui/pu.h create mode 100644 src/pui/puArrowButton.cxx create mode 100644 src/pui/puBox.cxx create mode 100644 src/pui/puButton.cxx create mode 100644 src/pui/puButtonBox.cxx create mode 100644 src/pui/puDial.cxx create mode 100644 src/pui/puDialogBox.cxx create mode 100644 src/pui/puFLTK.h create mode 100644 src/pui/puFont.cxx create mode 100644 src/pui/puFrame.cxx create mode 100644 src/pui/puGLUT.h create mode 100644 src/pui/puGroup.cxx create mode 100644 src/pui/puInput.cxx create mode 100644 src/pui/puInputBase.cxx create mode 100644 src/pui/puInterface.cxx create mode 100644 src/pui/puListBox.cxx create mode 100644 src/pui/puLocal.h create mode 100644 src/pui/puMenuBar.cxx create mode 100644 src/pui/puNative.h create mode 100644 src/pui/puObject.cxx create mode 100644 src/pui/puOneShot.cxx create mode 100644 src/pui/puPW.h create mode 100644 src/pui/puPopup.cxx create mode 100644 src/pui/puPopupMenu.cxx create mode 100644 src/pui/puRange.cxx create mode 100644 src/pui/puSDL.h create mode 100644 src/pui/puSlider.cxx create mode 100644 src/pui/puText.cxx create mode 100644 src/pui/puValue.cxx create mode 100644 src/pui/pui.dsp create mode 100644 src/pw/Makefile.am create mode 100644 src/pw/pw.cxx create mode 100644 src/pw/pw.dsp create mode 100644 src/pw/pw.h create mode 100644 src/pw/pwMacOSX.cxx create mode 100644 src/pw/pwWindows.cxx create mode 100644 src/pw/pwX11.cxx create mode 100644 src/sg/Makefile.am create mode 100644 src/sg/sg.cxx create mode 100644 src/sg/sg.dsp create mode 100644 src/sg/sg.h create mode 100644 src/sg/sgIsect.cxx create mode 100644 src/sg/sgPerlinNoise.cxx create mode 100644 src/sg/sgd.cxx create mode 100755 src/sg/sgdIsect.cxx create mode 100644 src/sl/Makefile.am create mode 100644 src/sl/sl.dsp create mode 100644 src/sl/sl.h create mode 100644 src/sl/slDSP.cxx create mode 100644 src/sl/slEnvelope.cxx create mode 100644 src/sl/slMODPlayer.cxx create mode 100644 src/sl/slMODPrivate.h create mode 100644 src/sl/slMODdacio.cxx create mode 100644 src/sl/slMODfile.cxx create mode 100644 src/sl/slMODfile.h create mode 100644 src/sl/slMODinst.cxx create mode 100644 src/sl/slMODnote.cxx create mode 100644 src/sl/slPlayer.cxx create mode 100644 src/sl/slPortability.h create mode 100644 src/sl/slSample.cxx create mode 100644 src/sl/slSamplePlayer.cxx create mode 100644 src/sl/slScheduler.cxx create mode 100644 src/sl/sm.h create mode 100644 src/sl/smMixer.cxx create mode 100644 src/ssg/Makefile.am create mode 100644 src/ssg/pcx.h create mode 100644 src/ssg/ssg.cxx create mode 100644 src/ssg/ssg.dsp create mode 100644 src/ssg/ssg.h create mode 100644 src/ssg/ssg3ds.h create mode 100644 src/ssg/ssgAnimTransform.cxx create mode 100644 src/ssg/ssgAnimation.cxx create mode 100644 src/ssg/ssgAxisTransform.cxx create mode 100644 src/ssg/ssgBase.cxx create mode 100644 src/ssg/ssgBaseTransform.cxx create mode 100644 src/ssg/ssgBranch.cxx create mode 100644 src/ssg/ssgContext.cxx create mode 100644 src/ssg/ssgCutout.cxx create mode 100644 src/ssg/ssgDList.cxx create mode 100644 src/ssg/ssgEntity.cxx create mode 100644 src/ssg/ssgIO.cxx create mode 100644 src/ssg/ssgInvisible.cxx create mode 100644 src/ssg/ssgIsect.cxx create mode 100644 src/ssg/ssgKeyFlier.h create mode 100644 src/ssg/ssgLeaf.cxx create mode 100644 src/ssg/ssgList.cxx create mode 100644 src/ssg/ssgLoad.cxx create mode 100644 src/ssg/ssgLoad3ds.cxx create mode 100644 src/ssg/ssgLoadAC.cxx create mode 100644 src/ssg/ssgLoadASC.cxx create mode 100644 src/ssg/ssgLoadASE.cxx create mode 100644 src/ssg/ssgLoadATG.cxx create mode 100644 src/ssg/ssgLoadBMP.cxx create mode 100644 src/ssg/ssgLoadDOF.cxx create mode 100644 src/ssg/ssgLoadDXF.cxx create mode 100644 src/ssg/ssgLoadFLT.cxx create mode 100644 src/ssg/ssgLoadIV.cxx create mode 100644 src/ssg/ssgLoadM.cxx create mode 100644 src/ssg/ssgLoadMD2.cxx create mode 100644 src/ssg/ssgLoadMDL.cxx create mode 100644 src/ssg/ssgLoadMDL.h create mode 100644 src/ssg/ssgLoadMDL_BGLTexture.cxx create mode 100644 src/ssg/ssgLoadOBJ.cxx create mode 100644 src/ssg/ssgLoadOFF.cxx create mode 100644 src/ssg/ssgLoadPCX.cxx create mode 100644 src/ssg/ssgLoadPNG.cxx create mode 100644 src/ssg/ssgLoadSGI.cxx create mode 100644 src/ssg/ssgLoadSSG.cxx create mode 100644 src/ssg/ssgLoadStrip.cxx create mode 100644 src/ssg/ssgLoadTGA.cxx create mode 100644 src/ssg/ssgLoadTRI.cxx create mode 100644 src/ssg/ssgLoadTexture.cxx create mode 100644 src/ssg/ssgLoadVRML.h create mode 100644 src/ssg/ssgLoadVRML1.cxx create mode 100644 src/ssg/ssgLoadX.cxx create mode 100644 src/ssg/ssgLoadXPlaneObj.cxx create mode 100644 src/ssg/ssgLoaderWriterStuff.cxx create mode 100644 src/ssg/ssgLoaderWriterStuff.h create mode 100644 src/ssg/ssgLocal.h create mode 100644 src/ssg/ssgMSFSPalette.h create mode 100644 src/ssg/ssgOptimiser.cxx create mode 100644 src/ssg/ssgParser.cxx create mode 100644 src/ssg/ssgParser.h create mode 100644 src/ssg/ssgRangeSelector.cxx create mode 100644 src/ssg/ssgRoot.cxx create mode 100644 src/ssg/ssgSave3ds.cxx create mode 100644 src/ssg/ssgSaveAC.cxx create mode 100644 src/ssg/ssgSaveASC.cxx create mode 100644 src/ssg/ssgSaveASE.cxx create mode 100644 src/ssg/ssgSaveATG.cxx create mode 100644 src/ssg/ssgSaveDXF.cxx create mode 100644 src/ssg/ssgSaveFLT.cxx create mode 100644 src/ssg/ssgSaveIV.cxx create mode 100644 src/ssg/ssgSaveM.cxx create mode 100644 src/ssg/ssgSaveOBJ.cxx create mode 100644 src/ssg/ssgSaveOFF.cxx create mode 100644 src/ssg/ssgSaveQHI.cxx create mode 100644 src/ssg/ssgSaveTRI.cxx create mode 100644 src/ssg/ssgSaveVRML1.cxx create mode 100644 src/ssg/ssgSaveX.cxx create mode 100644 src/ssg/ssgSelector.cxx create mode 100644 src/ssg/ssgSimpleList.cxx create mode 100644 src/ssg/ssgSimpleState.cxx create mode 100644 src/ssg/ssgState.cxx create mode 100644 src/ssg/ssgStateSelector.cxx create mode 100644 src/ssg/ssgStateTables.cxx create mode 100644 src/ssg/ssgStatistics.cxx create mode 100644 src/ssg/ssgStats.cxx create mode 100644 src/ssg/ssgTexTrans.cxx create mode 100644 src/ssg/ssgTexture.cxx create mode 100644 src/ssg/ssgTransform.cxx create mode 100644 src/ssg/ssgTween.cxx create mode 100644 src/ssg/ssgTweenController.cxx create mode 100644 src/ssg/ssgVTable.cxx create mode 100644 src/ssg/ssgVertSplitter.cxx create mode 100644 src/ssg/ssgVertSplitter.h create mode 100644 src/ssg/ssgVtxArray.cxx create mode 100644 src/ssg/ssgVtxTable.cxx create mode 100644 src/ssg/ssgconf.h create mode 100644 src/ssgAux/Makefile.am create mode 100644 src/ssgAux/ssgAux.cxx create mode 100644 src/ssgAux/ssgAux.dsp create mode 100644 src/ssgAux/ssgAux.h create mode 100644 src/ssgAux/ssgaBillboards.cxx create mode 100644 src/ssgAux/ssgaBillboards.h create mode 100644 src/ssgAux/ssgaCelestialBody.cxx create mode 100644 src/ssgAux/ssgaCloudLayer.cxx create mode 100644 src/ssgAux/ssgaFire.cxx create mode 100644 src/ssgAux/ssgaFire.h create mode 100644 src/ssgAux/ssgaLensFlare.cxx create mode 100644 src/ssgAux/ssgaLensFlare.h create mode 100644 src/ssgAux/ssgaLensFlareTexture.cxx create mode 100644 src/ssgAux/ssgaParticleSystem.cxx create mode 100644 src/ssgAux/ssgaParticleSystem.h create mode 100644 src/ssgAux/ssgaPatch.cxx create mode 100644 src/ssgAux/ssgaScreenDump.cxx create mode 100644 src/ssgAux/ssgaScreenDump.h create mode 100644 src/ssgAux/ssgaShapes.cxx create mode 100644 src/ssgAux/ssgaShapes.h create mode 100644 src/ssgAux/ssgaSky.cxx create mode 100644 src/ssgAux/ssgaSky.h create mode 100644 src/ssgAux/ssgaSkyDome.cxx create mode 100644 src/ssgAux/ssgaSphere.cxx create mode 100644 src/ssgAux/ssgaSphere.h create mode 100644 src/ssgAux/ssgaStars.cxx create mode 100644 src/ssgAux/ssgaTeapot.cxx create mode 100644 src/ssgAux/ssgaWaveSystem.cxx create mode 100644 src/ssgAux/ssgaWaveSystem.h create mode 100644 src/util/Makefile.am create mode 100644 src/util/ul.cxx create mode 100644 src/util/ul.dsp create mode 100644 src/util/ul.h create mode 100644 src/util/ulClock.cxx create mode 100644 src/util/ulError.cxx create mode 100644 src/util/ulLinkedList.cxx create mode 100644 src/util/ulList.cxx create mode 100644 src/util/ulLocal.h create mode 100644 src/util/ulRTTI.cxx create mode 100644 src/util/ulRTTI.h create mode 100644 tools/AUTHORS create mode 100644 tools/COPYING create mode 100644 tools/ChangeLog create mode 100644 tools/INSTALL create mode 100644 tools/Makefile.am create mode 100644 tools/NEWS create mode 100644 tools/README create mode 100755 tools/autogen.sh create mode 100644 tools/configure.in create mode 100644 tools/doc/index.html create mode 100644 tools/src/Makefile.am create mode 100644 tools/src/af2rgb/Makefile.am create mode 100644 tools/src/af2rgb/af2rgb.cxx create mode 100644 tools/src/af2rgb/af2rgb.dsp create mode 100644 tools/src/afm2txf/README create mode 100755 tools/src/afm2txf/afm2txf.pl create mode 100644 tools/src/plibconvert.cxx create mode 100755 tools/src/txfdump/txfdump.pl create mode 100644 tools/tools.dsw create mode 100644 transferCvs2WebSite diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..be3ac26 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,66 @@ +PLIB was originally written by Steve Baker as a solo project - but +has subsequently been greatly enhanced and expanded by a 'cast of +thousands'. + +Most important of all - the 'official' PLIB developers: + + Alex Perry + Curtis Olson + Ben Woodhead + Dave McClurg + Gerard Decatrel + Gil Carter + Giancarlo Niccolai + James Jones + John Fay ("Mr. PUI") + Juergen Marquardt + Mark Vallevand + Marten Stromberg + Nathan Matias + Norman Vine + Per Liedman + Sam Stickland + Sebastian Ude + Steve Baker <--- Hmmm - must sort this list by SECOND name! + Will Lachance + Wolfram Kuss + +Then, the non-registered contributors: + + (Apologies to anyone I've left + out - please let me know who + you are and I'll add you in.) + + Andrew Ross + Christopher St.John + Christian Mayer + Darrell Walisser + Dave Heil + Erik Hofman + Kevin Thompson + Karl Kremer + Negative0 <---- Who? Some kind of 1's complement arithmetic fanatic? + Mark Danks + Michael Kurth + Sean Palmer + Simon Britnell + Themie Gouthas + Tony Knienieder + Trent Gamblin + +Special mention must go to: + + Daryll Strauss (GLIDE port to Linux) + David Bucciarelli (Mesa -> GLIDE interface) + Brian Paul (Mesa) + Curtis Olson (FlightGear guru and all-round "Mr Nice Guy", + who nagged me into writing most of this stuff!) + Vasily Lewis (Tireless maintainer and sysadmin at Woodsoup + where PLIB was first hosted) + Toru Egashira (Who wrote a program called 'NSPmod' which was + the original basis of the SL 'MOD' player - + although the original code has been almost + entirely rewritten in the process of merging + it with SL. + Everyone at OPENGL-GAMEDEV-L + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..018efbc --- /dev/null +++ b/COPYING @@ -0,0 +1,496 @@ + +SPECIAL NOTICE: + + As a special exception Steve Baker gives permission to link these + libraries with proprietary software and distribute the resulting + executable without including that proprietary code in any distribution + as the LGPL would normally dictate. + + This exception is ONLY granted in the case of an embedded system in + which there is no possibility of an end user re-linking or recompiling + against new versions of this library that may appear in the future. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..233558b --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1102 @@ + +PLIB Change Log. +================ + + +PLIB v1.8.5 (Oct 2006) +~~~~~~~~~~~~~~~~~~~~~~ + + + * removed several widgets from pui/, which were declared + obsolete since a long time. Most of them are now available + in puAux/ + + * several fixes and improvements to puAuxList + + * puInputText scrolling fixed. + + * Improved DirectX loader. + + * Check ownership before deleting in ssgSimpleList. + + * Fixed netSocket. + + * Fixed mem leak in ssgVertSplitter. + + * Fixed pw window sizes on win32. + + * Added pview ssg-example. + + * Fix clock delta in first call. + + * Made colour of listbox changable. + + * Fixed text with negative coordinates + + * Fixed misc bugs in puAuxLargeInput + + * Allow the user to activate a widget with custom mouse button. + + * Made material loading from wavefront models more robust. + + * Handle linux joysticks with a lot of axes. + + * Avoid specular anomalies in 3ds files. + + * Added a demo for ssgaShapes. + + * Fixed the diameter of ssgaSphere. + + * Fixed inability to add colour to the ssgaTeapot. + + * Remove scale dep in ssgaFire + + + +PLIB v1.8.4 (Jan 2005) +~~~~~~~~~~~~~~~~~ + + * Added a way to get SSG intersection tests to work with + backfacing polygons. + + * Added ssgaScreenDepthDump into ssgAux. + + * Several fixes for FreeBSD included. + + * Added exporter for OpenInventor. + + * Implemented im- and exporter for ASC files. + + * Optimisations. + + * Various (minor) improvements to ASE, MDL, PCX and X loading and ASE saving. + + * Implemented ssgAnimTransforms. + + * AC loader now reads "crease" lines and creates much smaller and + cleaner scene graphs. + + * Joystick code adapted to current OS versions (especially Linux). + + +PLIB v1.8.3 (April 8th 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * NET now allows a NULL argument to netSocket::accept + + * Added #include to all top level header files + in order to ensure that NULL is defined. + + * Added ssgaScreenDump into ssgAux.dsp + + * Cleaned up some warnings from MSVC. + + * Added Christos Dimitrakakis' fixes to SL to prevent + crackling in certain envelope conditions. + + * Added pwAutoRepeatKey to control keyboard auto-repeat. + + * Fixed a horrific bug in the sgDistToLineSegment routines. + + +PLIB v1.8.2 (March 26th 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * PW now runs on Mac and MacOSX. + + * Uppercase keystrokes now work in PW for Windows. + + * Version number in ul.h now set correctly. + + * Package now builds correctly under MSVC. + +PLIB v1.8.1 (March 21th 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * puAux library wouldn't build without GLUT being + installed...that's fixed. + + * the NET library examples needed some love and + attention. They are much more useful now! + +PLIB v1.8.0 (March 19th 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * puAux library added to provide a place for the + more obscure PUI widgets. + + * PW (PLIB Windowing) library added to provide a + built-in alternative to windowing libraries such + as GLUT and SDL. Applications that only need a + single window, mouse and keyboard (and no other + features of GLUT) can now have no dependencies + whatever outside of PLIB and OpenGL. + + * PLIB is now completely independent of GLUT. You + can even build it without GLUT being installed. + Modules to allow PUI to work with GLUT, freeglut, + SDL, FLTK and our own PW windowing library are + provided. + + * ssgAux now includes a relatively simple sky model + kindly donated by the SimGear project. Now you + can have sun, moon, stars and clouds with just + a handful of function calls. + + * PSL - a C-like interpreted scripting language + is now bundled into PLIB. It's a byte-code + interpreter - and it has some useful properties + for games and other interactive packages in + that it allows the application much finer control + over the running of the script(s) than traditional + scripting engines. + + * In the demos area, we added the 'simon' package + which provides a simple framework for PSL scripts + to be loaded - and they may in turn load 3D models, + move them around, etc. Astropong.psl (a simple 3D + 'Pong' game in PSL) is provided as a demo. + +And of course, many bugs have been slain and numerous +other smaller features have been added. + + +PLIB v1.6.0 (Sept 2nd 2002) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's been over a year since the last PLIB release +and we have been somewhat lax about maintaining a +list of changes. + +Some highlights are: + + * Significant improvements and cleanup throughout PLIB, + especially in PUI. + + * ssgAux library added - includes handy classes for + more sophisticated effects layered on top of SSG. + + + Standard shapes like cubes, spheres, + cylinders and teapots. + + + Water waves. + + + Fire. + + + Particle Systems + + + Lens Flare. + + * More example programs. + + * P-Guide GUI builder for PUI. + + * JS promoted to a full library. + +PLIB v1.5.1 (July 21st 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Added ssgTween and ssgTweenController + + * Added puFileSelector - henceforth puFilePicker is deprecated. + + * Added ulList + + * Added VRML loader (this one works!) + + * Numerous bug fixes in other loaders. + + * Added TopHat support to JS + + * Added support for >3 simultaneous sounds in SL. + (You have to explicitly enable that though - it's not + the default) + + * Fixed bugs in util's directory handling. + + * puInput and puListBox now cope better with text that + is too big for the widget + + * puListBox (and hence puFileSelector and puFilePicker) + works with both large and small font sizes. + + * puDial changed to be derived from puSlider - many + consequences of that change will be visible in the + application. (eg output range is 0..1 and not 0..360) + + * PUI widgets are now 'sticky' so that if you are + dragging a slider along a puSlider and your mouse + slips off the edge of it, it'll still have "focus". + + * Added sgLerpAnglesVec3 to linearly interpolate + angles allowing for the nasty wrap-around-360 + problem. + + * Added sgLineSegment3ToLine3, sgDistSquaredToLineVec3 + sgDistSquaredToLineSegmentVec3, sgDistToLineVec3 and + sgDistToLineSegmentVec3. Also sgLine3 and sgLineSegment3 + structures. + + * Added a 'Gallery' page to the documentation to show + potential developers the range of applications that use + PLIB - and also to promote things our developers have done. + To save download time and CVS space, the actual images + are not in the PLIB tarball. + +PLIB v1.4.2 (July 21st 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Changes in NET to allow compilation under RedHat 7.1 + + * Fixed a small bug in ssgaSphere + + * Fixes a small bug in puFilePicker + +PLIB v1.4.1 (June 25th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Converted MSVC project files to 'BINARY' in the CVS archive + because some versions of MSVC don't line UNIX-style line endings. + + * Many improvements and bug fixes to puFileSelector and puListBox. + + * Minor fix to NET to accomodate Solaris users. + + * Fixed puMenuBar so it highlights as you drag the mouse across it. + + * Fixed ulOpenDir so it returns NULL on error on non-MSVC systems + + * Changed colour of text on buttons in documentation to make them + easier to read by colour-blind users. + +PLIB v1.5.0 (June 20th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Just a 'place holder' - actually identical to 1.4.0. + +PLIB v1.4.0 (June 20th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Fixed example programs so they don't use the obsolete _SSG_TYPE + tags. + + * Removed multiplie tags on web pages. + + * Updated 'AUTHORS' file. + + * Added 'demos' to the CVS repository. + +PLIB v1.3.3 (June 18th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Various warnings cleaned up under MSVC. + + * Fixed function prototypes for the Quaternion functions in SG + + * Fixed bug in sgEulerToQuat where the angles were not being + converted from degrees to radians. + + * Changed PUI to make sliders and dial widgets grab a hold + of the mouse focus and not release it until the button is + released. This is a subtle but important behavioural change. + + * puDial's are now derived from puSlider's - one side-effect of + that is that they now return 0..1 rather than 0..360 and no + longer support the maximum and minimum range functions. + +PLIB v1.3.2 (June 13th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * This CHANGES file has regrettably not been well maintained, + hence there a numerous changes not listed here. + + * File loaders now require app to pass 'ssgLoaderOptions' + rather than other ad-hoc callback function hooks. + + * Added Active Widget Implementation (pui) + + * Added puFilePicker + + * Added Ref Counting to ssgState + + * Added puBiSlider and puTriSlider + + * Added some missing 'AUTHORS' entries. + + * puDial widget (was actually in 1.3.1 but got + missed in the 'CHANGES' file) + + * Fixed a problem in the configure.in that prevented + ssgAux from building. + + * Moved the 'name' and 'user_data' (with associated + set/get member functions) from ssgEntity down into + ssgBase so that these functions will work with ssgState + and ssgTexture objects. This is needed for PrettyPoly. + +PLIB v1.3.1 (Sept 15th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Changed License conditions to specifically permit + use of PLIB outside of LGPL for embedded applications. + + * There is now a task and bug tracking system for PLIB + on Sourceforge. + + * Configuration/Install tool changed to support CygWin + better than in the past. + + * PLIB examples' data files consolidated into a single + 'examples/data' directory. + + * PLIB's documentation is now under CVS. + + * Added 'SSGAUX' library - include + and link to /usr/lib/libplibssgaux.a + + * Aux library has 'ssga' classes to add high level + shapes such as cylinders, cubes and spheres (two + different kinds). + + * Lots of new example programs. + + New PUI 'complex' uses multiple windows. + + Cute 'waving flag' demo + + Working Quaternions versus Eulers demo + + Model viewer (this is actually a useful + program - as well as a demo). + + * More const-correctness throughout library. + + * OPTIONAL support for PNG image loading (you have + to compile PLIB specially to get this because it + introduces an additional dependancy). + + * PLIB now checks that a valid OpenGL rendering + context exists when the various ssgInit/puInit/etc + routines are called. + + * PUI hasi: + + better support for multiple windows, + + a new function to allow the deletion of an object + within one of it's own callbacks. + + the ability to operate in a separate OpenGL + coordinate space. + + puFilePicker and puListBox + + enhancements to puSlider + + * SL has a completely new audio driver for SGI's IRIX. + Unfortunately, we don't think it fixes the long-standing + timing problems for audio under IRIX. + + * SSG's ssgVtxArray and ssgIndexArray now use 'short's for + indices - which is more efficient on most OpenGL's. + + * Several bugs fixed in SSG - also some changes to make it + more suited to embedded use in game consoles and such. + + * Many improvements to SSG's post-processing tools that are + typically applied to scene graph fragments after loading + from disk. + + * SSG now permits you to disable MIPmapping for selected + textures. + + * SSG's print() functions now work for more classes and + produce more useful output. + + * Code for ssgVtxArray streamlined. + + * Added a 'Lookat' function for SSG's camera model. + + * ssgSelectors can now have more than 32 kids. + + * ssgContext had incorrect defaults for the camera + orientation. + + * Some loaders were not sharing ssgState's between + polygons - that's fixed. + + * 3DS loader has had numerous improvements...but the + limitations of this format are becoming evident. + + * All error/warning handling is now done through a + centralized handler that can be redirected (eg to + send errors to a popup window or something) + + * Some bugs fixed in ASE loader. It now uses + ssgVtxArray primitives. + + * Added a loader for the Quake 'MD2' format. It + seems to work amazingly well. + + * Added 'UL' library. Applications MUST link to + -lplibul (/usr/lib/libplibul.a) - and should + include and call ulInit() before + making UL calls. + + If you use automake/autoconf, then it's easy + to make applications that will compile and link + under both PLIB 1.2 and 1.3/1.4, just add this: + + AC_CHECK_LIB(plibul, ulInit,,,) + + libplibul currently contains: + + + Fast, portable clock/timer routines. + + Low level networking code. + + Some directory searching routines. + + * Documents and Examples are now under CVS. + + * Added Tools and Auxlibs folders for future + work. + + +PLIB v1.3.0 (July 1st 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Unstable version of 1.2.0, no significant code + changes. + +PLIB v1.2.0 (July 1st 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Stable version of 1.1.12, no significant code + changes. + +PLIB v1.1.12 (June 24th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed some compilation problems for Macintosh/Codewarrior + which treats 'xor' and 'or' as reserved words!! Yikes! + Thanks to Darrell Walisser for that piece of nasty news. + + * Fixed some problems in slMODinst.cxx that were causing + compilation warnings under windoze (thanks to Bram Stolk) + + * Changed SL to open audio devices with O_NONBLOCK - + thanks to Trent Gamblin for spotting this one. + + * Added configure.in check for libdl to cover bug in ld.so + under Linux. Application programs under Linux need to + link to libdl.so in order to work around this. + Ben Woodhead added this. + + * Added Borland project files, thanks to Dave Heil for + contributing these. + + * Swapped order of multiplication in sgMultMat4. Thanks to + Lucas Ackerman for spotting this one. + + * Added a unique ID field to every node in the scene graph. + This helps out some picking algorithms in PPE. + + * Added some more ifdefs to make PLIB Audio/JS components + to make them work under NetBSD. (They already work with + FreeBSD and OpenBSD). Many thanks to Rene Hexel! + + * Fixed a ref() problem with ssgVtxTable::clone - thanks + to Will Weisser for catching that one! + + * Added checks to ensure that a valid OpenGL rendering + context exists before ssgInit/puiInit/fnt-loaders are + called. + + * Added Dave McClurg's ASE loader and matching writer. + If you use the new 'ssgLoad()/ssgSave()' functions, + it'll "just work" - if you prefer you can call + ssgLoadASE/ssgSaveASE analogously to all the other + loaders. + + * Several fixes to the quaternion code in SG were + contributed by "Negative0" + (Who was that masked man?) + +PLIB v1.1.11 (Feb 9th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed a tiny problem with double-sided polygons. + +PLIB v1.1.10 (Feb 9th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added ssgLoad and ssgSave that look at the + filename extension and call the appropriate + lower level load/save routine. + + * Added a 'clone' function for all SSG classes. + + * PLIB now managed under CVS at SourceForge. + + http://sourceforge.net/project/?group_id=382 + + * Added a collection of line/plane, plane/plane + and line/line intersection routines to SG. + Thanks to Norman Vine for most of them. + + * Fixed a bug in SSG file loader that would + prevent recognition of obsolete SSG file + types. + +PLIB v1.1.9 (Jan 28th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed bug that caused the ref counts of the + various vertex arrays in an ssgVtxTable to + be incorrectly set to zero when that VtxTable + is loaded from an '.ssg' format file. + This tended to crash SSG with an assertion + failure whenever you deleted an object + loaded from a '.ssg' file. + + * Added the current texture map filename to + ssgSimpleState and ssgStateSelector - which + means that... + + * The SSG file format finally works correctly. + Note that any SSG files you might have created + before this release won't work anymore...but + then they didn't work correctly before. + +PLIB v1.1.8 (Jan 23rd 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added ssgContext. This makes it a lot easier + to write programs that need to render multiple + eyepoints in quick succession. + + * Added functionality to ssgVtxTable to enable + applications to construct a vertex table and + then add the vertex arrays instead of doing + it all in one step. + +PLIB v1.1.7 (Jan 20th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Ooops! 1.1.6 accidentally went out set up to + REQUIRE the 'freeglut' library instead of the + original GLUT. That's fixed now - it'll pick + up on whichever you have installed and use + freeglut if you have both. (This only really + selects which header PLIB will use - you can + still link your application against either + GLUT) + + freeglut is here: + http://freeglut.sourceforge.net + +PLIB v1.1.6 (Jan 19th 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Changed ssgSaveSSG and ssgSaveAC to return + TRUE on success, FALSE otherwise. Neither + function is really working well enough for + 'primetime' use. + + * Added an optimisation to sgInvertMat4 (thanks + to Norman Vine). + + * Changed all Makefile.am's to refer to top_srcdir + rather than top_builddir - I have no idea why that + matters - but Steve Lamont + says it matters - so I fixed it! + + * Fixed a bug in PUI's default colour scheme picker + that caused text on dark coloured backgrounds to + be black instead of white. + +PLIB v1.1.5 (Dec 20th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Optimised sgMakeNormal (thanks to Klaus Hartmann!) + * Added 'Orthographic' rendering modes to SSG...this + is a work in progress - so no documentation yet - sorry! + +PLIB v1.1.4 (Dec 3rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Moved pre- and post-draw callbacks from ssgLeaf + to all ssgEntities. + * Changed user-data - all userdata must be derived + from ssgBase so that it can be ref-counted and + automatically deleted when no longer referenced. + * Added some specialised highlighting code for + the PPE tool. + +PLIB v1.1.3 (Nov 28th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added some missing 'get' functions to SSG. + * Added a way to load the current view frustum + and modelview matrices into OpenGL. + * Removed dependancy of SSG on GLUT. + * Added ssgVtxTable - which will ultimately + replace ssgVTable. The main difference is + that ssgVtxTables don't support indexing and + add reference counted vertex data tables. + * ssgLoadAC changed to use the new ssgVtxTable. + * Added pre- and post-draw callbacks to ssgLeaf. + +PLIB v1.1.2 (Nov 8th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed double declaration of sgdTransposeNegateMat4 + that prevented SG from compiling under Windoze. + * Split examples and documentation off into a + separate tarball. + +PLIB v1.1.1 (Nov 6th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added routines to SG to interconvert sgd and sg + data types. + * Matrix inversion routines added to SG. + * Attempt to work around possible compiler + error in ssgLoad3Ds - this shows up in some + versions of CygWin under Windoze and exhibits + itself as an out-of-memory error when loading + 3DStudio models. + +PLIB v1.1.0 (Oct 29th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This is actually identical to 1.0.20 except + that it installs its headers in /usr/include/plib + and its libraries in /usr/lib. To avoid name-space + pollution in /usr/lib, the libraries are renamed + as follows: + + libssg.a --> libplibssg.a + libsg.a --> libplibsg.a + libfnt.a --> libplibfnt.a + + ...you get the idea I'm sure. + + * With this release, I'm going to the even-number-stable, + odd-number-experimental numbering scheme. Hence, + I'll be adding bug fixes only to the 1.0.xx series. + When 1.1.xx has been enhanced - and is again stable, + it'll become 1.2.0 and 1.3.0 will be the experimental + version. + +PLIB v1.0.20 (Oct 29th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed problem with memory leakage in SSG, the + ref count of branch nodes was including the + parent nodes as well as the children. Revised + instructions on deleting nodes added to manual. + + * Added sgDistanceSquaredVec2/3/4 to SG. (Christian + Mayer asked *so* nicely). + + * Added Per Liedman's latest 3D studio file loader. + This is a truly awesomely useful thing. Thanks Per! + + * This will be the last version to install in + /usr/local/plib. + +PLIB v1.0.19 (Oct 16th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added bunches of missing 'const' directives to SG and + parts of SSG. Thanks to Christian Mayer for this one. + + * Fixed a bug in FNT that should allow more than two + lines of text to be output with a single call to + puFont::drawString. Thanks to Sylvan Clebsch for this one. + + * Added a 'saturation' limit to js.h - intended for those + joysticks that don't generate a full range of output. + Thanks again to Sylvan Clebsch! + + * Keiji Kato provided a patch to SL for the Windoze + platform that fixes some kind of a slowdown problem + when playing sounds on WinNT-4 machines. + +PLIB v1.0.18 (Oct 3rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Removed GIF files from all the Web pages. Rassin-frassin + Unisys...grumble >:-( + + * Fixed a problem that caused too many joystick axes to be + reported by JS. Thanks to Paolo Leoncini - good catch! + + * Added new Quaternion interpolation routines and reduced + complexity of quaternion multiplication routines. Thanks + to Sylvan Clebsch. + + * ssgEntity (and hence most SSG classes) didn't make a copy + of strings passed to setName() - hence there was a tendancy + to leak name memory as objects were deleted. That's fixed + but be aware that 'name' is now copied. + + * As requested by Christian Mayer, sgAddScaleVec functions + have been added into SG. + + * Fixed up some compilation warnings in ssgStateSelector.cxx, + fnt.cxx and ssgSaveAC.cxx. (Thanks to Sylvan Clebsch for those) + + * Fixed a potential problem in ssgModelPath/ssgTexturePath that + would blow up if you ever passed a local string variable to + them. Be sure not to call either function before ssgInit() - OK! + +PLIB v1.0.17 (Aug 23rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added new ssgState derivitive "ssgStateSelector". + Check out SSG documents for details. + + * Stopped warning about unused variable 'ww' in FNT and SSG. + + * Fixed bug in puValue::setValue(char*) that prevented empty + strings from changing the current string state for the puValue. + (Thanks to Sylvan Clebsch for finding that one) + + * Removed the 'test' program for PUI (it was never meant to + be in the release). + + * Added error return to Font loaders (Thanks to Norman Vine). + +PLIB v1.0.16 (Aug 13th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed compiler warning in slMODfile.cxx + + * Fixed a bunch of compile errors in slScheduler.cxx due to + some default arguments that got left in place by mistake. + +PLIB v1.0.15 (Aug 12th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Minor fix to SL to make it compile under CygWin. + Thanks to Norman Vine for that one. + + * MacOS port of SL is now complete! Many thanks to + Darrell Walisser + + * Fixed a problem with const correctness when compiling with + gcc 2.95. + + * Moved a lot of SL functions from inline to non-inline... + they had grown over time from one liners into 10 liners. + + * In SM, changed 'not_working' to 'notWorking' to make it + look more like SL. + + * Fixed bug that caused SL to crash if you played MOD music + without a sound card (Thanks to Daryll Strauss for finding + that one). + + * Fixed bug in puButton constructor that makes menus come + out with irregular height. + + * Fixed recurrent crashing problem with the PUI 'complex' + demo program. + +PLIB v1.0.14 (Aug 4th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The use of PROXY textures seems to break both nVidia and G200 + Mesa ports for Linux - so I have temporarily commented out + that code. If you need it, find the code in src/ssg/ssgImageLoader.cxx + and src/fnt/fntTXF.cxx and re-instate the code that's ifdef'ed out + using #ifdef PROXY_TEXTURES_ARE_NOT_BROKEN + + * Significant fixes to SL to make it work with /dev/audio under + SOLARIS. Thanks to Steve Brown for his patch. + + * Somehow the FNT documentation became corrupted in 1.0.13, + it's fixed now though. + + * FNT and PUI now allow '\n' characters inside strings to + signify carriage-return + line-feed. Thanks to Shane Arnott + for this one. + + * Added parameters to FNT routines to allow the application + to pick which OpenGL texture filtering to apply. + + * Fixed some minor name-space pollution in PUI. + + * Fixed a bug in ssgBaseTransform that causes ssgGetLastTransform + to mess up on the first call. + + * Fixed a bug in measuring the bounding box of text containing + space characters in the FNT component. Thanks to Alexander A. + Babichev for this one. + + * Lots of minor syntax changes for MSVC, + +PLIB v1.0.13 (July 8th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Included a JS patch from Karl Heinz Kremer that + adds support for the latest Linux joystick driver. For those + who have the latest driver, this will increase the number of + joystick axes from 2 to 6.Beware though that the detection of + which kind of driver is present is currently done at compile + time which could be a problem for binary distributed code. + + * Added a simple terrain demo (examples/ssg/majik)- originally + put together to give the good people in the Majik group + something to play with. + Check out *their* good stuff at: http://majik.netti.fi + + * Improved state handling in SSG. Also added ssgForceBasicState(). + + * JS library should work with FreeBSD. (Thanks to Stephen Montgomery-Smith) + + * Texture loaders in SSG and FNT changed to use GL_PROXY texture + tests instead of being hard-coded to crappy 256x256 limits. + + * Since puiAlone is sadly outdated, I have ceased to distribute it. + If you need it, the last version is with plib-1.0.12 on the PLIB + homepage. + + * Changed definition of SG_PI to be a 'float', fixed the precision + of SGD_PI to be double. + + * Removed strncasecmp - because it's not universally implemented. + + * Changed an ifdef in SL to allow it to compile under FreeBSD. + + * Added an 'puArrowButton' call - just like a puButton - but + shaped like an arrow. + + * Added an 'ssgGetVersion' call. + + * Deleted ssgAnimation class (it was never completely + implemented anyway). + + * Added ssgTimedSelector (which takes over the functions + I'd planned to add to ssgAnimation). + + * I didn't rename the file ssgAnimation.cxx to ssgTimedSelector.cxx + because I couldn't stand to change all the MSVC project thingies. + + * Numerous fixes for the Mac: + + * Fixed a byte-swapping problem with the BMP + texture loader. + + * Changed some arrays from local to 'new/delete' because + the Mac CodeWarrior compiler can't cope with more than + 32Kb of local memory in a single function or class. + + * Provided conditional compilation for Mac file naming + conventions in a couple of places. It's not enough though. + +PLIB v1.0.12 (June 3rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added a set of MSVC project files. + + * More portability issues resolved. + + * Lots more actually. + + * Official Acronym "IBFITNR" (It'll Be Fixed In The Next Release) + now adopted. + +PLIB v1.0.11 (May 30th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed a problem with LoadAC creating 2-point tstrips. + + * Fixed an assert in SSG's deRef. + + * The tables inside an ssgVTable are now 'protected' rather + than 'private'. + + * Added new code to slScheduler to allow applications to + replay MOD files. + + * Reorganized slSamplePlayer to be derived from a new + abstract base class slPlayer. Added new private class + slMODPlayer for replaying MOD files. + + * Added a demo program for the new SL MOD player. + + * Added ssgRangeSelector class to allow level-of-detail + management. This isn't well tested yet - but it's + really simple code - so it should be OK. + +PLIB v1.0.10 (May 10th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Ooops! A fatal error in my strcasecmp code. + 1.0.9 and 1.0.10 should have *died* on most + machines! + +PLIB v1.0.9 (May 9th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Error in SGI version of slSample.cxx cleared up (Thanks Devrim!) + + * Made some changes for MSVC. + +PLIB v1.0.8 (May 3rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + * A couple of warnings eliminated from SL when compiled + under CygWin. + + * Added a contribution from Simon Britnell + to allow programs to search the SSG scene graph by name or + path. (Thanks Simon!) + + * Fixed a bug in sgIdentityQuat (Thanks Kevin!) + + * Following the first release of Tux-aqfh, I was snowed under with + little fixes. + + + strcasecmp should be stricmp on some machines...but alas, not + on others. The heck with it - I'll just write my own! + + + fntFont wasn't pushing/popping GL state correctly. + + + More minor hacks to make for clean compiles under IRIX. + + + puInterface's destructor was only deleting half of it's + child node. + + + Missing contructor functions for various SG classes. + + + SSG culling functions were not testing for empty + bounding spheres. + + + Minor compilation warnings for slSample.cxx + + + example/sl/example.cxx used M_PI - which is a no-no under + Windoze. + + +PLIB v1.0.7 (May 3rd 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Removed a bunch of redundant NOTICE, CHANGES + and REAME files. All that stuff is now maintained + in the top level directory. + + * SG used to be set up so you could compile it + in double precision with the drop of an option. + However, some applications need both float and + double support - so SG now has duplicate functions + for double-precision that start 'sgd'. + + * Changed the structure of the FNT library to move + font sizes from the fntFont/fntTexFont class into + the fntRenderer class. This corrects a poor design + decision because it was hard to create fonts in + multiple point sizes and slants without using + multiple identical texture maps (BAD!). RTFM. + + * Added a bunch of TXF fonts into examples/fnt/data + and changed the fnt_test program to cycle through + them whenever you press a key. + + * PUI can now use FNT fonts - which is good news + because now you can use TXF fonts for better + variety and speed compared to GLUT fonts. + This means that PUI programs now need to + link with -lpui -lfnt -lsg + + * The PUI 'complex' example program now uses + the sorority font. + + * I added a 'rendering callback' to some + PUI widgets as suggested by Eero Pajarre. The + idea is to allow you to do arbitary OpenGL + rendering onto a PUI widget...however, I'm + not convinced that this is a good idea. In + any case, you can put icons on widgets more + easily by defining custom fonts using FNT. + + +PLIB v1.0.6 (May 1st 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added a complete new component library called + FNT that allows texturemapped fonts to be + loaded and rendered nicely. + + * More small configure/compile problems fixed. + + * Added Quaternions to the SG library thanks + to a fine contribution from Kevin Thompson + + + * Fixed a bug in JS under Windoze that made + three axis sticks return garbage in the + third axis and swapped the order of the + third and fourth axes for >3 axis sticks. + + +PLIB v1.0.5 (April 21st 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed another IRIX compile problem in pu.h + + * Found that configure script was not enabling + full warning levels...as a result, fixed a + dozen minor warnings in SSG. + + * SSG's image loader now loads 8 bit and 24 bit + uncompressed BMP files. This seems to work + for RGB images - but I can't seem to build + BMP images with Alpha planes using any of + the tools I have. + + +PLIB v1.0.4 (April 20th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Restructured SSG texture image loader to permit + use of other file formats than SGI. + + * In the event that a texture doesn't load, SSG will + now insert a red and white chequerboard. + + * Another round of changes to configure.in should + solve the CygWin issues. + + * Added in some *ancient* fixes to PUI - that kind + people donated and I somehow forgot all about (oops!) + + +PLIB v1.0.3 (April 18th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Fixed to compile cleanly under IRIX 6.2 using the + official SGI C++ compiler. + + * More fixes for MSVC under Windoze. + + * OpenGL logo added to HTML documents following + request from opengl.org + + +PLIB v1.0.2 (April 14th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Yet more minor build tweaks - CygWin wasn't defining WIN32, + that should work now. + + * JS should now work with Windoze - and with sticks with + up to 6 axes. Under Linux, the presence of the joystick + driver (or at least it's header files) is now mandatory, + PLIB won't build without it. + + * The JS demo program has been fixed to display all of the + axes of all of the joysticks. + + * PUI now supports valuators - this is where the value of + a widget is set using the address of one of the application's + variables. Once the valuator is set, the PUI widget's display + will always the same as the valuator - and any changes to the + widget will also change the value stored at that address. + This saves you from creating a bunch of callbacks when buttons + and sliders have no side-effects apart from changing some + global variable. + + +PLIB v1.0.1 (April 11th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Some minor portability issues cleared up. + + * More minor build process errors corrected. + + +PLIB v1.0 (April 1st 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + * All the separate libraries that I support in the + public domain are now rolled into a single tarball + under the new name 'PLIB'. + + * Although this is the first release of PLIB. The sub- + libraries: SL, JS and PUI had previous releases - this + is the first Beta of SG and SSG. + + * Directory layouts for all of the component libraries + have been somewhat rationalized. + + * I have shifted from a completely open license to + LGPL on the advice of several OpenSource authors. + Any earlier releases of these libraries that you + may happen to have remain free of LGPL restrictions. + If any contributors to the earlier code have objections + to this arrangement, please let me know and I'll arrange + to remove their code from subsequent copies. + + * With help from Curtis Olson, we now have proper 'configure' + scripts to streamline the build process. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e048404 --- /dev/null +++ b/INSTALL @@ -0,0 +1,218 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +OpenGL Configure +================ + +Configure is not able to find the OpenGL headers and libraries. In this +case please add + +./configure --with-GL=PREFIX + +Examples + +./configure --with-GL=/usr/local + +This will look in the /usr/local/include for headers (GL/ is not needed) +and /usr/local/lib for the libraries. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Compiling for Windows with MSVC +=============================== + +You can easily compile plib with MSVC 6. First, you have to set some paths +in tools\options\directories. In "include" set the plib path itself (and, +if needed the parent). Then, in "library paths" add the plib path itself again +so that packages that use plib will find it. The building process will copy +the finished *.lib files and any header files that are visible to projects +using plib into the plib path. + +There is a workspace (*.dsw file) for plib, for examples, for tools +(including only af2rgb) and for each of the demos: exposer, p-guide +and ttt3d. Simply open one of them (starting with plib itself, of course) +in MSVC, choose what you want to compile (build/set active configuration) +and then compile (F7). if you want to compile or even create a project +that uses plib, please note that the plib workspaces have "multi threading" +enabled. Either enable this as well in your project or disable it for +plib. A missmatch leads to errors. + + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/KNOWN_BUGS b/KNOWN_BUGS new file mode 100644 index 0000000..b104b96 --- /dev/null +++ b/KNOWN_BUGS @@ -0,0 +1,25 @@ +Known bugs and limitations in PLIB version 1.8.x + +More in depth stuff can be found in TODO_AFTER135. + + * ssgLOS is really broken. + + * ssgStripify and ssgFlatten might well have bugs in them. + A known bug of ssgStripify is that : since it rearranges and + merges leaf nodes, ssgSelectors are messed up + + * Sometimes cr*p values appear in 3D files, for example .ac files. + The reason seems to be that sometimes the loader handles colours + differently than the writer. + + * Several, general smalish .MDL loader bugs are known; Look in TODO_AFTER135 + + * Scaling does not work (intentionally). Uniform scaling may be + enabled by uncommenting "radius *= sgLengthVec3(m[0])" in + sgSphere::orthoXform. It is currently disabled because that extra + calculation would degrade performance on non-scaled matrices. + Scaling really makes a mess of SSG bounding sphere tests + + * .ssg files should not be used for long term storage. It will probably + change in incompatible ways. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9ce5486 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = src + +EXTRA_DIST = KNOWN_BUGS NOTICE README README.GLUT \ + README.win32 README.mac TODO-1.6 TODO-2.0 \ + TODO_AFTER135 autogen.sh missing \ + plib.dsp plib.dsw transferCvs2WebSite + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..2f86bd4 --- /dev/null +++ b/NEWS @@ -0,0 +1,4 @@ +Fri Oct 13 16:54:04 CEST 2006 +Moved from SF CVS to SF SVN. +To grab a copy from svn, use this command: +$ svn co https://svn.sf.net/svnroot/plib/trunk plib diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..bfc5b0c --- /dev/null +++ b/NOTICE @@ -0,0 +1,23 @@ +NOTICE: The PLIB Portable Interactive Applications Library distribution +contains source code that is released as OpenSource under the GNU +Library Public License. These programs are thus freely distributable +without licensing fees. These programs are provided without guarantee +or warrantee expressed or implied. + +If you use PLIB in a commercial or shareware product, it would be nice +if you gave credit where it is due. If you make any neat modifications +or improvements to PLIB, I would greatly appreciate a copy of the +improved code or at least a source patch. + +Note that PLIB uses an addition to the LGPL: + + As a special exception Steve Baker gives permission to link these + libraries with proprietary software and distribute the resulting + executable without including that proprietary code in any distribution + as the LGPL would normally dictate. + + This exception is ONLY granted in the case of an embedded system in + which there is no possibility of an end user re-linking or recompiling + against new versions of this library that may appear in the future." + + diff --git a/README b/README new file mode 100644 index 0000000..a7e6411 --- /dev/null +++ b/README @@ -0,0 +1,182 @@ + +Portability Libraries. +====================== + by Steve Baker. + +All the documentation and installation instructions +are now online at: + + http://plib.sourceforge.net + +They are also downloadable - along with example +programs and demos from the same site. + + +SUPPORT: + +Please don't email me (Steve Baker) directly - I +get *WAY* too much mail as it is! Instead, please +sign up to the PLIB user's mailing list. Instructions +can be found here: + + http://plib.sourceforge.net + + +LICENSING: + +These libraries are offered as OpenSource freeware +under the provisions of the GNU Library Public +License (LGPL). Those terms and conditions are +detailed in the file 'LICENSE' in this directory. + +I have added a short preamble to LGPL that removes +no rights from ordinary users but offers special +dispensation to allow PLIB to be used under certain +commercial conditions (such as Games Consoles) where +the constraints of LGPL are impossible to meet. + +| As a special exception Steve Baker gives permission to link these +| libraries with proprietary software and distribute the resulting +| executable without including that proprietary code in any distribution +| as the LGPL would normally dictate. +| +| This exception is ONLY granted in the case of an embedded system in +| which there is no possibility of an end user re-linking or recompiling +| against new versions of this library that may appear in the future. + + +REQUIREMENTS: + +In addition to the libraries that I provide, you +will also need OpenGL (OpenGL 1.1 or later - +Mesa 3.0 or later) and GLUT (The OpenGL utilities +toolkit - version 3.7 or later) and a hardware 3D +graphics accellerator that supports OpenGL. + + +WHAT DO YOU GET? + +The following libraries are provided: + +JS -- A Joystick interface. +PUI -- A simple GUI built on top of OpenGL. +SG -- Some Standard Geometry functions (vector and + matrix math, spheres, boxes, frustra, etc) +SL -- A Games-oriented Sound Library. +FNT -- OpenGL texture-mapped font Library. +PW -- A minimalist windowing library. +PSL -- A C-like scripting language. +SSG -- A Simple Scene Graph API built on top of OpenGL. +NET -- Some Networking functions for games. +UTIL-- Utilities - things that your OS would normally + handle - but these functions are portable. + +SSGAUX -- Some optional additional high level features for SSG +PUIAUX -- Some optional additional high level features for PUI + + +DIRECTORY STRUCTURE: + +PLIB expects to be installed in one standard place: + + /usr/lib + +...with header files at: + + /usr/include/plib + + +PORTABILITY and DEPENDANCIES: + +Although all these libraries are designed to be easily +portable (and more importantly so that programs using +them can be UTTERLY portable), some of them have not +yet been ported to all operating systems. + +JS -- Currently Linux/Windows/BSD only. + +UL -- Should be portable to POSIX-compliant + OS's ... and Windows. + +SG -- Totally portable, no dependancies. + +PUI -- Requires OpenGL also FNT and SG. + PUI also requires some kind of windowing + library - which could be PW - or GLUT, + freeglut, SDL, FLTK - or others. + +PSL -- Requires UL. + +PW -- Requires UL. + +NET -- Should be portable to POSIX-compliant + OS's. + +FNT and SSG -- Require OpenGL, SG and UL. + +SSGAUX -- Requires SSG - and hence OpenGL, SG and UL. +PUIAUX -- Requires PUI - and hence FNT and SG. + +SL -- This has now been ported onto: + + Linux + FreeBSD + OpenBSD + M$ Windows (NT,95,98 - so far) + MacOS + SGI IRIX + Sun Solaris + Any operating system that supports OSS (The + Open Sound System). + + NOTE: SL has a subsidiary library 'SM' that + can be used to control the audio mixer - but + that is not portable beyond Linux. You might + get it to work under OSS-based sound systems + too. Don't use SM if you want to write portable + code. + + No other requirements. + + +AUX LIBRARIES AND TOOLS: + +The 'tools' directory contains a number of useful tools +and utility programs that are either built using PLIB or +which are specifically useful when writing PLIB programs. + + +COMPILING/LINKING WITH PLIB + +In your source code, add any combination of: + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +Add any of these to your link line: + + -lplibjs -lplibssgaux -lplibssg -lplibsl -lplibpu + -lplibpuaux -lplibpw -lplibpsl -lplibfnt -lplibnet + -lplibsg -lplibul + +Under UNIX/Linux, you'll also need: + + -lGLU -lGL -L/usr/X11/lib -lX11 -lXext -lXmu -lm + +...and perhaps a -l for whatever windowing library +you're using - unless it's the built-in PW library. + + Steve Baker. + + http://www.sjbaker.org + diff --git a/README.GLUT b/README.GLUT new file mode 100644 index 0000000..2d3d208 --- /dev/null +++ b/README.GLUT @@ -0,0 +1,3 @@ + +PUI no longer depends on GLUT or freeglut. + diff --git a/README.JOYSTICKS.FREEBSD b/README.JOYSTICKS.FREEBSD new file mode 100644 index 0000000..49ddfe5 --- /dev/null +++ b/README.JOYSTICKS.FREEBSD @@ -0,0 +1,96 @@ +FreeBSD joystick support +------------------------ + +FreeBSD supports two flavors of joysticks: analog and USB. By default, +non-root users do not have access to these devices on FreeBSD, and the +required devices may not even be created yet. Please browse the below +notes before writing to the plib mailing list. + +Contents of this file: +----------- + Analog joysticks + USB joysticks + Testing the joysticks + Compiling and linking + +Analog joysticks +---------------- + +Plib uses the joy(4) interface to analog joysticks. You will need to +make sure the device files exist: + ls -l /dev/joy* + +If they do not yet exist, create them (logged in as root): + cd /dev + sh MAKEDEV joy + +By default, they will have permissions that do not allow unprivileged +users access to them: + crw-r----- 1 root operator 51, 0 Aug 30 16:59 joy0 + crw-r----- 1 root operator 51, 1 Aug 30 16:59 joy1 + +You may wish to consider making the devices world read/writable: + chmod a+rw /dev/joy* + +Users of xdm(1) or any of its more modern successors may wish to look +into GiveConsole and TakeConsole or its equivalent. + +If you do not have a joystick driver compiled into you kernel, you can +load one dynamically with the command + kldload joy +When in doubt, check the FreeBSD handbook about making sure this driver +is loaded every time you reboot. + + +USB joysticks +------------- + +Plib uses the usbhid(3) interface to USB joysticks. Joysticks will show +up as generic USB Human Interface Devices (uhid). Because the USB design +allows an almost unlimited number of USB buses and USB devices, you should +make sure you have enough devices in /dev to deal with your devices. +When in doubt, create a few extra to make sure. If you have insufficient +USB or uhid devices, your joystick may not appear at all, or it may +disappear if you connect your USB devices in a different order. +Fortunately, device nodes are free. For example, you might see this: + ls -al /dev/usb* /dev/uhid* + crw-rw---- 1 root operator 108, 255 Apr 12 01:44 /dev/usb + crw-rw---- 1 root operator 108, 0 Apr 12 01:44 /dev/usb0 + crw-rw---- 1 root operator 122, 0 Apr 12 01:44 /dev/uhid0 + +This shows you have one usb interface (/dev/usb itself doesn't count), and +one uhid device. Make a few extra with + cd /dev + sh MAKEDEV usb0 usb1 usb2 usb3 uhid0 uhid1 uhid2 uhid3 +(you can leave out the devices that exist already). + +Next, you'll have to think about permissions. Both the /dev/usb0..3 +(but not /dev/usb) have to be world readable, as well as the uhid devices. +This command will take care of it: + chmod a+r /dev/usb?* /dev/uhid* +(yes, the "?*" after /dev/usb is intentional). + +Obviously, any permission change you make is at your own risk! XDM users +may use the GiveConsole/TakeConsole mechanism as an alternative. + +Plib will complain if /dev/uhid* or /dev/usb* exist but are +unreadable with your current privileges. + + +Testing the joysticks +--------------------- + +Users of FlightGear can quickly test their joysticks with the js_demo +program. + + +Compiling and linking +--------------------- + +Compile plib on FreeBSD with the same compiler options as used for the +other libraries you may use. In particular, if you use pthreads anywhere +in your application, you should compile Plib with + g++ -pthread + +Applications linking to recent Plib's -ljs should add -lusbhid to the +link command line in the Makefile. diff --git a/README.mac b/README.mac new file mode 100644 index 0000000..136b9c2 --- /dev/null +++ b/README.mac @@ -0,0 +1,19 @@ + +PLIB Project files for CodeWarrior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NOTE from Steve: + It seems that the CodeWarrior project files for + PLIB are considerably larger than the entire source + distribution (450Kb!) + + Hence, since Mac/CodeWarrior users are not exactly + commonplace, I'm not going to include the project + files into the distro and cause everyone else prolonged + download times. + + I suggest you contact Darrell Walisser + should you need to build PLIB in this setting. + + Sorry! + diff --git a/README.win32 b/README.win32 new file mode 100644 index 0000000..d189e19 --- /dev/null +++ b/README.win32 @@ -0,0 +1,52 @@ + +PLIB for Windoze. +~~~~~~~~~~~~~~~~~ + +Let me first say that I (Steve Baker) don't own a copy +of Windoze - so I have little or no ability to help +Windoze owners with PLIB problems. It's especially +important therefore that you direct problems and +queries to the PLIB mailing lists. Subscription +information is on the PLIB homepage: + + http://plib.sourceforge.net + + +CygWin: +~~~~~~~ +The simplest way to build PLIB for Windoze is to +use the CygWin compiler and toolset. Since that +environment is pretty close to UNIX/Linux, the + + ./configure + make + make install + +...mechanism works perfectly. + + +MingW32: +~~~~~~~~ +A couple of people have asked about doing a MingW32 port - but +as far as I can tell, the "configure/make/make install" approach +*should* work. + + +Microsoft Visual C++: +~~~~~~~~~~~~~~~~~~~~~ +All you need to do is open the workspace "plib.dsw" in MSVC++, +select the "plib" project (using Set Active Project in the Project +menu), and build it (using "Rebuild All" in the Build menu). + +In the examples folder, open the workspace "plib_examples.dsw". +Each example has a seperate project in the workspace. Select +the one you want and build it. Then you should be able to hit +the Go icon on the toolbar and try it out. + +For your own application, please note that the plib workspace +leaves all the necessary includes (*.h) and libraries (*.lib) +in the plib root directory (same as workspace). The debug +libraries are given a '_d' suffix. Where ever you put those +files, make sure #include works so your code is +more portable. + diff --git a/TODO-1.6 b/TODO-1.6 new file mode 100644 index 0000000..d2001dd --- /dev/null +++ b/TODO-1.6 @@ -0,0 +1,69 @@ +NEW FEATURES: + +Structure section + +* SSG needs RTTI to replace the rapidly vanishing SSG_TYPE bits resource. + +* I'd like to see the FGFS sky model moved into ssgAux so everyone can use + it. We got part of the way towards that - but it was never finished. + +* I'd like to have a ROAM engine built into ssgAux for terrain rendering. + (maybe not *literally* ROAM - but something very like it). + +SSG section + +* Support for bump maps, cubic environment maps, etc, etc on hardware + that supports it. + +* Support for multi-texture. + +* Import some kind of Skin & Bones animation into ssgAux (using the + ExPoser code - or something even better). + + +Loader section + +* MORE file readers and writer. +For example, a reader/loader for WWII Fighters would be nice. + +* No file writers for about 2/3rds of the file formats we support. + +* I want the PPE material editor's file format built into SSG so we can + load and save material lists. + +* Model formats + * VRML + * XML version of ssg + * WWII (I think thats the name) + +SG section + +* function to get the angle between two vectors + +* plot points on a vector (walking in a straight line) + +* Expand SG with more intersection and distance-measuring tricks. + + +PUI section + +* PUI should also be implemented with RTTI as well. + +* Movable widgets + +* PUI is still somewhat tied to GLUT...that should go. It'll need a + way to get the window dimensions - and at least one built-in font + before we can do that. + +SL section + +* Re-implement SL on top of OpenAL. + +* Write players for more modern MOD-like music formats. + +Examples section + +* More example programs - more complete demo games and tools. + +* More documentation for all the new features. + diff --git a/TODO-2.0 b/TODO-2.0 new file mode 100644 index 0000000..d2ee294 --- /dev/null +++ b/TODO-2.0 @@ -0,0 +1,28 @@ +[ Put in tasks here that probably won't get done before PLIB 2.0 or change + the API in a way that could break existing applications ] + +PUI section + +* Remove deprecated puFilePicker widget + +* Remove deprecated old label and legend places + +* Remove deprecated PUSTYLE_RADIO + +* Remove deprecated 'int puValue::getValue ( void )' function + (Application developers: Use 'puValue::getIntegerValue' instead) + +* Remove deprecated 'int puObject::getDefaultValue ( void )' function + (Application developers: Use 'puValue::getDefaultIntegerValue' instead) + +* Line up widget constructors (explicit size vs. max coordinates issue) + +* Change function parameters taking a 0/1 FALSE/TRUE value from 'int' + to 'bool' + +* In puDeactivateWidget (), invoke the active widget's down callback + (Can't be realized prior to 2.0 since it could break existing programs + relying on the old behaviour) + +* Implement puiAUX for complex widgets [ unclear ] + diff --git a/TODO_AFTER135 b/TODO_AFTER135 new file mode 100644 index 0000000..4ddea6a --- /dev/null +++ b/TODO_AFTER135 @@ -0,0 +1,138 @@ + ++============+ +| TO BE DONE | ++============+ + +A tasks and bugs are now being tracked by sourceforge. Please +goto: http://sourceforge.net/projects/plib/ + + * Have a look into known_bugs and fix them. + + * Lines in ASCII files are terminated... + + With '\n' under UNIX/Linux + With '\r' under MacOS + With BOTH under Windoze. + + Hence, UNIX/Linux does no conversions of either '\r' or '\n'. + and MAC swaps '\r' and '\n' on input. + and Windoze dumps '\r' if it follows a '\n'. + + This is a mess - so I'm changing all the ASCII I/O code + to allow either or both '\n' or '\r' and I'm reading the + ASCII files in BINARY mode. + + * Array deletion requires '[]' after 'delete' on Mac. + + * Some of the MSVC-project files for plib_examples seem to be + broken, for example some miss a "winmm.lib". + + * Check whether the new ssgStripify works with TuxKart + See Steves post from 11.12.2000 15:12 for a problem description + + + +The abbreviation NIV14 means not in Version 1.4. + + + + + + +Bugs: +===== + +- When creating fgfsTux, sometimes .ac, .dxf, + .ase and .obj saved zero objects, although + there were objects. For example, PLIB + created this .ac file: + ----------- snip ------------ + AC3Db + OBJECT world + kids 0 + OBJECT group + kids 0 + ----------- snip ------------ + This seems to happen after ssgFlatten. +- WK: Create a sphere in ppe. Save it as .ASE. See that values like + diffuse colour are cr*p. Try to load it. It crashes. This could be + one or - probably - two bugs. I don't think this is a ppe problem. + The loader complains that number of faces is -2. + The problem seems to be the writer. + + *new remark*: About the diffuse values: I think it has to do with + colour material, a loader (.ASE?) loading colours into a colour + list and writing colours from the ssgSimpleState where they are + undefined. I think the same bug is in .AC + +- WK: I have a crash in ssgFlatten if I load .ac files with + unused materials (not reproducable?) +- Loading and saving lines in .OBJ doesn't work. Maybe NIV14. +- Sam wrote: + Has anyone here debugged with plib under Windoze 2000 and MSVC++? + I get a whole bunch of + "Free Heap block modifed after it was free" + warnings with the ssgFlatten and ssgStripify. + I'll try and hunt this one down. + ... + I think it may be because I'm using MFC which (if I remember + correctly) enables a bunch more memory checks. Also I'm doing + #ifdef _WIN32 + #ifdef _DEBUG + #define new DEBUG_NEW + #endif + #endif +- Not all loaders use ssgLoaderOptions::begin. Don't all have to? + +- Search for todo, fix, fixme, kludge. + + + +Misc: +===== + +- Look whether scaling works. [see current discussion] + If not and if we can't/don't want to make it work, for example + because of performance issues we need a warning in the docs and + IMHO PLIB should "write out" a warning if an unallowed matrix is + sent to it. + Update: Scaling does not work (intentionally). Uniform scaling may be + enabled by uncommenting "radius *= sgLengthVec3(m[0])" in + sgSphere::orthoXform. It is currently disabled because that extra + calculation would degrade performance on non-scaled matrices. +- Reduce lint warnings. Lint is an error checking tool that gives warnings + where things *could be* wrong as opposed to the compiler that tells + you where they are wrong. + +- It would be great if we had one or even two working native .ssg + file formats. Currently (16.12.2001) the format work most of the time, but not all + entities are implemented. +- We should write the GetWrapU/GetWrapV function, since + loading /writing .ssg files can't work 100% without it. +- When saving, there is often a warning that "ref count doesn't + tally with parent count" +- Go through the mailing list for unfinished business. +- Someone, preferably a native english speaker with ssg-knowledge, + should go through the new ssg-doc and fix any mistakes. It should be + fairly complete by now. +- Add Per's new Formats to the doc? +- For the other parts of plib, people should bring the doc up to + date. +- There is a bug in the 3Dfx driver for Linux (tdfx_dri-4.0.1-1) that + causes the "complex" example program to crash in "fxSetupBlend". + This is fixed in more recent DRI snapshots (requires kernel 2.4.x). + + +Probably after 1.4.0: +===================== +- In fgfsTux, not a large object, you get a DList overflow + with the standard values for its size. Can we make its + size dynamic? +- Look at handling of normals. For example, loaders, + stripify etc should only recalculate missing normals. + Have *one* function that recalculates normals. + + + +It would be nice if people would volunteer for tasks. + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..0d119cd --- /dev/null +++ b/configure.in @@ -0,0 +1,426 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/js/js.h) + +# **WARNING**WARNING**WARNING**WARNING**WARNING**WARNING** +# +# When you make a new release, there are TWO places where you +# have to change the version number: +# 1) Right below this comment. +# 2) In 'ul.h' where PLIB_VERSION is created. +# +# **WARNING**WARNING**WARNING**WARNING**WARNING**WARNING** + +AM_INIT_AUTOMAKE(plib, 1.8.5) + +# This AC_PREFIX_DEFAULT will place the library files +# into /usr/lib - but will put the include files into +# /usr/include - when /usr/include/plib would be less +# polluting to the filename space. +# +# There is probably a better way to do this. + +AC_PREFIX_DEFAULT(/usr) + +if echo $includedir | egrep "plib$" > /dev/null; then + echo "includedir is" $includedir "libdir is" $libdir +else + includedir="${includedir}/plib" + echo "includedir changed to" $includedir "libdir is" $libdir +fi + + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl Command line arguments + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + +AC_ARG_ENABLE(fnt, +[ --enable-fnt build FNT library (OpenGL needed) default: yes],, +enable_fnt=yes) + +AC_ARG_ENABLE(js, +[ --enable-js build JS library default: yes],, +enable_js=yes) + +AC_ARG_ENABLE(pw, +[ --enable-pw build PW library default: yes],, +enable_pw=yes) + +AC_ARG_ENABLE(net, +[ --enable-net build NET library default: yes],, +enable_net=yes) + +AC_ARG_ENABLE(pui, +[ --enable-pui build PUI library (OpenGL needed) default: yes],, +enable_pui=yes) + +AC_ARG_ENABLE(sg, +[ --enable-sg build SG library default: yes],, +enable_sg=yes) + +AC_ARG_ENABLE(psl, +[ --enable-psl build PSL library default: yes],, +enable_psl=yes) + +AC_ARG_ENABLE(sl, +[ --enable-sl build SL library default: yes],, +enable_sl=yes) + +AC_ARG_ENABLE(ssg, +[ --enable-ssg build SSG library (OpenGL needed) default: yes],, +enable_ssg=yes) + +AC_ARG_ENABLE(puaux, +[ --enable-puaux build puAux library (OpenGL needed) default: yes],, +enable_puaux=yes) + +AC_ARG_ENABLE(ssgaux, +[ --enable-ssgaux build ssgAux library (OpenGL needed) default: yes],, +enable_ssgaux=yes) + +AC_ARG_ENABLE(ul, +[ --enable-ul build UTIL library default: yes],, +enable_ul=yes) + + +dnl Dependancy checking + +if test "x$enable_fnt" = "xyes"; then + need_opengl=yes + + if test "x$enable_sg" = "xno"; then + AC_MSG_WARN([cannot build FNT library without SG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build FNT library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_pw" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build PW library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_js" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build JS library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_net" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build NET library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_pui" = "xyes"; then + need_opengl=yes + + if test "x$enable_fnt" = "xno"; then + AC_MSG_WARN([cannot build PUI library without FNT library enabled]) + dependancy_problem=yes + fi + if test "x$enable_sg" = "xno"; then + AC_MSG_WARN([cannot build PUI library without SG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build PUI library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_sg" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build SG library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_psl" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build PSL library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_sl" = "xyes"; then + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build SL library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_ssg" = "xyes"; then + need_opengl=yes + + if test "x$enable_sg" = "xno"; then + AC_MSG_WARN([cannot build SSG library without SG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build SSG library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_puaux" = "xyes"; then + need_opengl=yes + + if test "x$enable_pui" = "xno"; then + AC_MSG_WARN([cannot build puAux library without PUI library enabled]) + dependancy_problem=yes + fi + if test "x$enable_sg" = "xno"; then + AC_MSG_WARN([cannot build puAux library without SG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build puAux library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$enable_ssgaux" = "xyes"; then + need_opengl=yes + + if test "x$enable_ssg" = "xno"; then + AC_MSG_WARN([cannot build ssgAux library without SSG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_sg" = "xno"; then + AC_MSG_WARN([cannot build ssgAux library without SG library enabled]) + dependancy_problem=yes + fi + if test "x$enable_ul" = "xno"; then + AC_MSG_WARN([cannot build ssgAux library without UL library enabled]) + dependancy_problem=yes + fi +fi + +if test "x$dependancy_problem" = "xyes"; then + AC_MSG_ERROR([dependancy problems detected]) +fi + + +AM_CONDITIONAL(BUILD_FNT, test "x$enable_fnt" = "xyes") +AM_CONDITIONAL(BUILD_JS, test "x$enable_js" = "xyes") +AM_CONDITIONAL(BUILD_PW, test "x$enable_pw" = "xyes") +AM_CONDITIONAL(BUILD_NET, test "x$enable_net" = "xyes") +AM_CONDITIONAL(BUILD_PUI, test "x$enable_pui" = "xyes") +AM_CONDITIONAL(BUILD_PUAUX, test "x$enable_puaux" = "xyes") +AM_CONDITIONAL(BUILD_SG, test "x$enable_sg" = "xyes") +AM_CONDITIONAL(BUILD_PSL, test "x$enable_psl" = "xyes") +AM_CONDITIONAL(BUILD_SL, test "x$enable_sl" = "xyes") +AM_CONDITIONAL(BUILD_SSG, test "x$enable_ssg" = "xyes") +AM_CONDITIONAL(BUILD_SSGAUX, test "x$enable_ssgaux" = "xyes") +AM_CONDITIONAL(BUILD_UL, test "x$enable_ul" = "xyes") + +if test "x$need_opengl" = "xyes" && test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + dnl Multimedia library + LIBS="$LIBS -lwinmm" + + if test "x$need_opengl" = "xyes"; then + dnl OpenGL libraries + LIBS="$LIBS -lopengl32" + fi + ;; +*-apple-darwin*) + dnl Mac OS X + + if test "x$need_opengl" = "xyes"; then + dnl OpenGL libraries + LIBS="$LIBS -framework OpenGL" + fi + + dnl Sound libraries + LIBS="$LIBS -framework Carbon" + ;; +*) + if test "x$need_opengl" = "xyes"; then + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + LIBS="$LIBS $X_PRE_LIBS $X_LIBS $X_EXTRA_LIBS -lm" + +dnl ========================================================= +dnl if test "x$x_includes" != "x"; then +dnl CPPFLAGS="$CPPFLAGS -I$x_includes" +dnl fi +dnl ========================================================= + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(pthread, pthread_create) + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList,, + AC_MSG_ERROR([could not find working GL library])) + fi + + fi + + AC_CHECK_LIB(dl, dlclose) + + dnl Check SGI audio library + AC_CHECK_LIB(audio, ALopenport) + ;; +esac + + +dnl Checks for header files. + +AC_HEADER_STDC + +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +if test "x$need_opengl" = "xyes"; then + dnl Check for OpenGl header files + + case "${host}" in + *-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADER(GL/gl.h,, + AC_MSG_ERROR([OpenGL header file not found])) + ;; + *-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADER(OpenGL/gl.h,, + AC_MSG_ERROR([OpenGL header file not found])) + ;; + *) + AC_CHECK_HEADER(GL/gl.h,, + AC_MSG_ERROR([OpenGL header file not found])) + ;; + esac +fi + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + if test "x$GCC" != xyes; then + # if the compiler is not GCC we reset WFLAG to null for safety + WFLAG="" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + +dnl FreeBSD requires the -pthread switch to enable pthreads. Look for this +dnl weirdness. +save_CXXFLAGS="$CXXFLAGS" +save_CFLAGS="$CFLAGS" +save_LIBS=$LIBS +CXXFLAGS="-pthread $CXXFLAGS" +CFLAGS="-pthread $CFLAGS" +AC_CHECK_LIB(c_r, pthread_exit) +if test "x$ac_cv_lib_c_r_pthread_exit" != "xyes"; then + CFLAGS=$save_CFLAGS + CXXFLAGS=$save_CXXFLAGS +fi +LIBS=$save_LIBS + +dnl Checks for typedefs, structures, and compiler characteristics. + +dnl check for socklen_t (in Unix98) +AC_MSG_CHECKING(for socklen_t) +AC_TRY_COMPILE([ +#include +#include +socklen_t apa; +],[],[ +AC_MSG_RESULT(yes)],[ +AC_TRY_COMPILE([ +#include +#include +int accept (int, struct sockaddr *, size_t *); +],[],[ +AC_MSG_RESULT(size_t) +AC_DEFINE(socklen_t,size_t)], [ +AC_MSG_RESULT(int) +AC_DEFINE(socklen_t,int)])]) + + +AC_OUTPUT( \ + Makefile \ + src/Makefile \ + src/js/Makefile \ + src/util/Makefile \ + src/sg/Makefile \ + src/sl/Makefile \ + src/psl/Makefile \ + src/ssg/Makefile \ + src/ssgAux/Makefile \ + src/fnt/Makefile \ + src/pui/Makefile \ + src/puAux/Makefile \ + src/pw/Makefile \ + src/net/Makefile ) + +AC_MSG_RESULT( +[PLIB configuration information: + + Building FNT library: $enable_fnt + Building JS library: $enable_js + Building NET library: $enable_net + Building PUI library: $enable_pui + Building puAux library: $enable_puaux + Building SG library: $enable_sg + Building PSL library: $enable_psl + Building SL library: $enable_sl + Building SSG library: $enable_ssg + Building ssgAux library: $enable_ssgaux + Building PW library: $enable_pw + Building UTIL library: $enable_ul +]) + diff --git a/demos/README b/demos/README new file mode 100644 index 0000000..f80384f --- /dev/null +++ b/demos/README @@ -0,0 +1,16 @@ + +PLIB Demonstration Programs and other Utilities. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The programs released under this directory are +fully complete programs written either as demonstrations +of PLIB or as useful utility programs. Each one +has it's own licensing rules (although all are OpenSourced +in some way) and each has it's own build mechanism and +documentation. + +You can build all of the demo's (on UNIX-like OS's) +by running the 'build_all_demos' script. + +Enjoy! + diff --git a/demos/build_all_demos b/demos/build_all_demos new file mode 100755 index 0000000..eef78b9 --- /dev/null +++ b/demos/build_all_demos @@ -0,0 +1,22 @@ + +echo "Building ExPoser..." +cd exposer +./autogen.sh +./configure +make +cd .. + +echo "Building P-Guide..." +cd p-guide +./autogen.sh +./configure +make +cd .. + +echo "Building 3D TicTacToe..." +cd ttt3d +./autogen.sh +./configure +make +cd .. + diff --git a/demos/exposer/AUTHORS b/demos/exposer/AUTHORS new file mode 100644 index 0000000..a199f12 --- /dev/null +++ b/demos/exposer/AUTHORS @@ -0,0 +1,2 @@ +Exposer was written by Steve Baker. + diff --git a/demos/exposer/COPYING b/demos/exposer/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/demos/exposer/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/demos/exposer/ChangeLog b/demos/exposer/ChangeLog new file mode 100644 index 0000000..7c9e971 --- /dev/null +++ b/demos/exposer/ChangeLog @@ -0,0 +1,16 @@ + +ExPoser Change Log. +=================== + +ExPoser v0.0.1 (July 21st 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Most bugs now removed. + + * Can load in 'scenery' + + * Better example models. + +ExPoser v0.0.0 (June 17th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * First release. + diff --git a/demos/exposer/INSTALL b/demos/exposer/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/demos/exposer/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/demos/exposer/Makefile.am b/demos/exposer/Makefile.am new file mode 100644 index 0000000..066b287 --- /dev/null +++ b/demos/exposer/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src data doc + +EXTRA_DIST = autogen.sh README exposer.dsw + diff --git a/demos/exposer/NEWS b/demos/exposer/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/demos/exposer/README b/demos/exposer/README new file mode 100644 index 0000000..3cc971e --- /dev/null +++ b/demos/exposer/README @@ -0,0 +1,44 @@ + +ExPoser 3D. +~~~~~~~~~~~ + by Steve Baker + + +INSTALLATION: + +Installation instructions are pretty basic, +and are covered in the documentation directory 'doc'. + + OpenGL (or Mesa) + GLUT (the GL Utility Toolkit) + PLIB (which can be obtained from one of: + http://plib.sourceforge.net + +Once that is done, it should be a simple matter of changing +to the directory where all this stuff is unpacked and type: + + ./configure + make + make install + +However, you might want to check the full documentation - +especially if you need to install Mesa/GLIDE/GLUT before +you can start. + +All documents are in HTML - so view them with your web browser. + +LICENSE: + +This software is released under the provisions of +the GNU General Public License (GPL) which can be +found in the file 'COPYING' in the current directory. + +DIRECTORY STRUCTURE: + +src -- Where the source code lives. + +data -- This contains all kinds of miscellaneous data + files to test with. + +doc -- Documentation - all in HTML + diff --git a/demos/exposer/autogen.sh b/demos/exposer/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/demos/exposer/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/demos/exposer/configure.in b/demos/exposer/configure.in new file mode 100644 index 0000000..67ee3ce --- /dev/null +++ b/demos/exposer/configure.in @@ -0,0 +1,167 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/exposer.cxx) +AM_INIT_AUTOMAKE(exposer, 0.2.0) + + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL + +plib_suffix="-lplibjs -lplibsl -lplibssgaux -lplibssg -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul" + +AC_LANG_CPLUSPLUS + + +dnl Command line arguments. + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + +if test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + LIBS="$LIBS -lwinmm -lglut32 -lglu32 -lopengl32 -luser32 -lgdi32" + ;; +*-apple-darwin*) + dnl Mac OS X + + LIBS="$LIBS -framework GLUT -lobjc -framework OpenGL -framework Carbon" + ;; +*) + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + LIBS="$LIBS $X_PRE_LIBS -lXi -lXmu -lXext -lX11 $X_EXTRA_LIBS -lm" + +dnl ========================================================= +dnl if test "x$x_includes" != "x"; then +dnl CPPFLAGS="$CPPFLAGS -I$x_includes" +dnl fi +dnl ========================================================= + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList,, + AC_MSG_ERROR([could not find working GL library])) + fi + + AC_CHECK_LIB(GLU, gluLookAt) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt,, + AC_MSG_ERROR([could not find working GLU library])) + fi + + dnl check for glut...or freeglut + AC_CHECK_LIB(freeglut, glutGetModifiers) + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xno" ; then + dnl if no freeglut, check for Real GLUT + AC_CHECK_LIB(glut, glutGetModifiers,, + AC_MSG_ERROR([could not find working GLUT library])) + fi + + AC_CHECK_LIB(dl, dlclose) + + dnl Check SGI audio library + AC_CHECK_LIB(audio, ALopenport, SND_LIBS="-laudio") + ;; +esac + +dnl Check PLIB library +AC_CHECK_LIB(plibul, ulInit, true, +AC_MSG_ERROR([there seems to be a problem with the PLIB libraries])) + +LIBS="$plib_suffix $LIBS" + + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADERS(OpenGL/gl.h OpenGL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GLUT/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*) + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + AC_CHECK_HEADER(GL/freeglut.h, AC_DEFINE(FREEGLUT_IS_PRESENT), + AC_MSG_ERROR([freeglut header file not found])) + else + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + fi + ;; +esac + +AC_CHECK_HEADER(plib/ul.h,, AC_MSG_ERROR([PLIB header files not found])) + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + + +AC_OUTPUT( Makefile \ + data/Makefile \ + doc/Makefile \ + src/Makefile \ + ) + diff --git a/demos/exposer/data/Makefile.am b/demos/exposer/data/Makefile.am new file mode 100644 index 0000000..b29ffd2 --- /dev/null +++ b/demos/exposer/data/Makefile.am @@ -0,0 +1,7 @@ + +pkgdatadir = $(datadir)/games/@PACKAGE@/data + +pkgdata_DATA = $(wildcard *.rgb *.ac *.bones) + +EXTRA_DIST = $(pkgdata_DATA) + diff --git a/demos/exposer/data/dog.ac b/demos/exposer/data/dog.ac new file mode 100644 index 0000000..545c68f --- /dev/null +++ b/demos/exposer/data/dog.ac @@ -0,0 +1,1539 @@ +AC3Db +MATERIAL "ac3dmat3" rgb 1 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat5" rgb 1 1 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat6" rgb 0 1 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat9" rgb 0 0 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 0 0 0 amb 0 0 0 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +MATERIAL "" rgb 1 1 1 amb 1 1 1 emis 1 1 1 spec 0.502 0.502 0.502 shi 0 trans 0 +MATERIAL "" rgb 0.561 0.404 0.251 amb 0.702 0.475 0.2 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +OBJECT world +kids 12 +OBJECT poly +name "nose" +loc -0.725253 0.226588 -0.122592 +numvert 6 +0.70885 0.640855 -1.10242 +0.63385 0.72076 -1.00242 +0.78385 0.72076 -1.00242 +0.70885 0.681685 -1.11637 +0.654175 0.72493 -1.06597 +0.763525 0.72493 -1.06597 +numsurf 5 +SURF 0x10 +mat 4 +refs 3 +0 0 0 +1 0 0 +2 0 0 +SURF 0x10 +mat 4 +refs 3 +5 0 0 +4 0 0 +3 0 0 +SURF 0x0 +mat 4 +refs 4 +0 0 0 +3 0 0 +4 0 0 +1 0 0 +SURF 0x0 +mat 4 +refs 4 +1 0 0 +4 0 0 +5 0 0 +2 0 0 +SURF 0x0 +mat 4 +refs 4 +2 0 0 +5 0 0 +3 0 0 +0 0 0 +kids 0 +OBJECT poly +name "body" +loc 0.586292 -0.0392416 -0.122191 +numvert 26 +-0.453097 0.85581 -0.403335 +-0.528095 0.98571 -0.403335 +-0.453097 0.965615 -0.203335 +-0.303097 0.70581 -0.203335 +-0.403097 1.05222 0.146665 +-0.203097 0.70581 0.146665 +-0.54331 0.86584 0.472749 +-0.353097 0.70581 0.446664 +-0.603095 0.762285 0.52058 +-0.678095 0.98571 -0.403335 +-0.753095 0.965615 -0.203335 +-0.803095 1.05222 0.146665 +-0.662885 0.86584 0.472749 +-0.603095 1.04208 0.870785 +-0.753095 0.85581 -0.403335 +-0.903095 0.70581 -0.203335 +-1.0031 0.70581 0.146665 +-0.853095 0.70581 0.446664 +-0.678095 0.725905 -0.403335 +-0.753095 0.446 -0.203335 +-0.803095 0.359398 0.146665 +-0.728095 0.489301 0.446664 +-0.528095 0.725905 -0.403335 +-0.453097 0.446 -0.203335 +-0.403097 0.359398 0.146665 +-0.478097 0.489301 0.446664 +numsurf 26 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +1 0 0 +2 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +2 0 0 +4 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +4 0 0 +6 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 3 +7 0 0 +6 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 4 +1 0 0 +9 0 0 +10 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +10 0 0 +11 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +11 0 0 +12 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 3 +6 0 0 +12 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 3 +12 0 0 +8 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +6 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 4 +9 0 0 +14 0 0 +15 0 0 +10 0 0 +SURF 0x10 +mat 6 +refs 4 +10 0 0 +15 0 0 +16 0 0 +11 0 0 +SURF 0x10 +mat 6 +refs 4 +11 0 0 +16 0 0 +17 0 0 +12 0 0 +SURF 0x10 +mat 6 +refs 3 +12 0 0 +17 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 4 +14 0 0 +18 0 0 +19 0 0 +15 0 0 +SURF 0x10 +mat 6 +refs 4 +15 0 0 +19 0 0 +20 0 0 +16 0 0 +SURF 0x10 +mat 6 +refs 4 +16 0 0 +20 0 0 +21 0 0 +17 0 0 +SURF 0x10 +mat 6 +refs 3 +17 0 0 +21 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 4 +18 0 0 +22 0 0 +23 0 0 +19 0 0 +SURF 0x10 +mat 6 +refs 4 +19 0 0 +23 0 0 +24 0 0 +20 0 0 +SURF 0x10 +mat 6 +refs 4 +20 0 0 +24 0 0 +25 0 0 +21 0 0 +SURF 0x10 +mat 6 +refs 3 +21 0 0 +25 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 4 +22 0 0 +0 0 0 +3 0 0 +23 0 0 +SURF 0x10 +mat 6 +refs 4 +23 0 0 +3 0 0 +5 0 0 +24 0 0 +SURF 0x10 +mat 6 +refs 4 +24 0 0 +5 0 0 +7 0 0 +25 0 0 +SURF 0x10 +mat 6 +refs 3 +25 0 0 +7 0 0 +8 0 0 +kids 0 +OBJECT poly +name "head" +loc -0.300768 0.330614 -0.122191 +numvert 7 +0.385963 0.759545 -0.605295 +0.408963 0.65246 -0.703335 +0.365838 0.699325 -0.64344 +0.283963 0.688305 -0.68395 +0.184782 0.75935 -0.60292 +0.198369 0.699325 -0.64344 +0.158963 0.65246 -0.703335 +numsurf 6 +SURF 0x10 +mat 5 +refs 3 +0 0 0 +1 0 0 +2 0 0 +SURF 0x10 +mat 4 +refs 3 +1 0 0 +3 0 0 +2 0 0 +SURF 0x10 +mat 5 +refs 3 +3 0 0 +0 0 0 +2 0 0 +SURF 0x10 +mat 5 +refs 3 +4 0 0 +3 0 0 +5 0 0 +SURF 0x10 +mat 4 +refs 3 +3 0 0 +6 0 0 +5 0 0 +SURF 0x10 +mat 5 +refs 3 +6 0 0 +4 0 0 +5 0 0 +kids 0 +OBJECT poly +name "head" +loc -0.400768 0.139108 -0.122191 +numvert 32 +0.533965 0.67746 -1.00333 +0.458963 0.80736 -1.00333 +0.508965 0.843965 -0.703335 +0.633965 0.67746 -0.803335 +0.485964 0.95105 -0.605295 +0.583965 0.67746 -0.603335 +0.453348 0.92959 -0.50328 +0.533965 0.67746 -0.503335 +0.458963 0.80736 -0.403335 +0.533965 0.67746 -0.403335 +0.308963 0.80736 -1.00333 +0.258963 0.843965 -0.703335 +0.315133 0.92218 -0.50144 +0.308963 0.80736 -0.403335 +0.233963 0.67746 -1.00333 +0.133963 0.67746 -0.803335 +0.183963 0.67746 -0.603335 +0.284783 0.950855 -0.60292 +0.233963 0.67746 -0.503335 +0.233963 0.67746 -0.403335 +0.303331 0.564205 -1.00138 +0.258963 0.460952 -0.803335 +0.283963 0.504255 -0.603335 +0.308963 0.547555 -0.503335 +0.308963 0.547555 -0.403335 +0.453331 0.564205 -1.00138 +0.508965 0.460952 -0.803335 +0.483963 0.504255 -0.603335 +0.458963 0.547555 -0.503335 +0.458963 0.547555 -0.403335 +0.383963 0.72746 -1.10333 +0.383963 0.879815 -0.68395 +numsurf 30 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +1 0 0 +2 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +2 0 0 +4 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +4 0 0 +6 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 4 +7 0 0 +6 0 0 +8 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 4 +1 0 0 +10 0 0 +11 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +12 0 0 +13 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 4 +10 0 0 +14 0 0 +15 0 0 +11 0 0 +SURF 0x10 +mat 6 +refs 4 +11 0 0 +15 0 0 +16 0 0 +17 0 0 +SURF 0x10 +mat 6 +refs 4 +17 0 0 +16 0 0 +18 0 0 +12 0 0 +SURF 0x10 +mat 6 +refs 4 +12 0 0 +18 0 0 +19 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 4 +14 0 0 +20 0 0 +21 0 0 +15 0 0 +SURF 0x10 +mat 6 +refs 4 +15 0 0 +21 0 0 +22 0 0 +16 0 0 +SURF 0x10 +mat 6 +refs 4 +16 0 0 +22 0 0 +23 0 0 +18 0 0 +SURF 0x10 +mat 6 +refs 4 +18 0 0 +23 0 0 +24 0 0 +19 0 0 +SURF 0x10 +mat 6 +refs 4 +20 0 0 +25 0 0 +26 0 0 +21 0 0 +SURF 0x10 +mat 6 +refs 4 +21 0 0 +26 0 0 +27 0 0 +22 0 0 +SURF 0x10 +mat 6 +refs 4 +22 0 0 +27 0 0 +28 0 0 +23 0 0 +SURF 0x10 +mat 6 +refs 4 +23 0 0 +28 0 0 +29 0 0 +24 0 0 +SURF 0x10 +mat 6 +refs 4 +25 0 0 +0 0 0 +3 0 0 +26 0 0 +SURF 0x10 +mat 6 +refs 4 +26 0 0 +3 0 0 +5 0 0 +27 0 0 +SURF 0x10 +mat 6 +refs 4 +27 0 0 +5 0 0 +7 0 0 +28 0 0 +SURF 0x10 +mat 6 +refs 4 +28 0 0 +7 0 0 +9 0 0 +29 0 0 +SURF 0x10 +mat 6 +refs 3 +30 0 0 +20 0 0 +14 0 0 +SURF 0x10 +mat 6 +refs 3 +30 0 0 +25 0 0 +20 0 0 +SURF 0x10 +mat 6 +refs 3 +30 0 0 +0 0 0 +25 0 0 +SURF 0x10 +mat 6 +refs 3 +30 0 0 +1 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 3 +30 0 0 +14 0 0 +10 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +17 0 0 +12 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 3 +2 0 0 +11 0 0 +31 0 0 +SURF 0x10 +mat 6 +refs 3 +17 0 0 +4 0 0 +31 0 0 +kids 0 +OBJECT poly +name "l_hind_leg" +loc 0.704382 -0.340368 0.250812 +numvert 16 +-1.02078 1.07199 0.104938 +-1.06371 0.935195 -0.193764 +-1.06657 0.62464 0.00429365 +-1.09876 0.391723 -0.0382313 +-1.10781 0.363491 -0.189256 +-1.15356 0.314084 -0.242758 +-1.01939 0.299968 0.108566 +-1.05766 0.645815 0.0681295 +-0.963235 1.03401 0.181203 +-0.95813 1.11164 0.0775365 +-0.9622 0.62464 -0.0236723 +-0.95559 0.391723 -0.0765935 +-1.02327 0.363491 -0.211908 +-1.01039 0.314084 -0.281121 +-0.95092 0.299968 0.0902175 +-0.956215 0.645815 0.0356551 +numsurf 29 +SURF 0x10 +mat 6 +refs 3 +0 0.784615 1 +1 0.153846 0.782609 +2 0.569231 0.4 +SURF 0x10 +mat 6 +refs 3 +3 0.461538 0.113043 +4 0.138462 0.0782608 +5 0 0.0173913 +SURF 0x10 +mat 6 +refs 3 +3 0.461538 0.113043 +5 0 0.0173913 +6 0.815385 0 +SURF 0x10 +mat 6 +refs 3 +2 0.569231 0.4 +3 0.461538 0.113043 +6 0.815385 0 +SURF 0x10 +mat 6 +refs 3 +0 0.784615 1 +2 0.569231 0.4 +6 0.815385 0 +SURF 0x10 +mat 6 +refs 3 +0 0.784615 1 +6 0.815385 0 +7 0.815385 0.426087 +SURF 0x10 +mat 6 +refs 3 +0 0.784615 1 +7 0.815385 0.426087 +8 1 0.904348 +SURF 0x10 +mat 6 +refs 3 +10 0 0 +1 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +13 0 0 +12 0 0 +11 0 0 +SURF 0x10 +mat 6 +refs 3 +14 0 0 +13 0 0 +11 0 0 +SURF 0x10 +mat 6 +refs 3 +14 0 0 +11 0 0 +10 0 0 +SURF 0x10 +mat 6 +refs 3 +14 0 0 +10 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +15 0 0 +14 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +15 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +0 0 0 +9 0 0 +1 0 0 +SURF 0x10 +mat 6 +refs 3 +1 0 0 +10 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +10 0 0 +9 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +11 0 0 +12 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +12 0 0 +13 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +13 0 0 +11 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +13 0 0 +14 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +14 0 0 +11 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +10 0 0 +11 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +14 0 0 +10 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +14 0 0 +9 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +14 0 0 +15 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 4 +7 0 0 +15 0 0 +9 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 3 +7 0 0 +15 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +9 0 0 +0 0 0 +kids 0 +OBJECT poly +name "r_hind_leg" +loc 0.676152 -0.340368 -0.454996 +numvert 16 +-0.349655 1.07199 0.782515 +-0.306731 0.935195 0.483811 +-0.303869 0.62464 0.68187 +-0.271681 0.391723 0.639345 +-0.262634 0.363491 0.48832 +-0.216879 0.314084 0.434817 +-0.351043 0.299968 0.78614 +-0.312778 0.645815 0.745705 +-0.407203 1.03401 0.85878 +-0.412307 1.11164 0.75511 +-0.408239 0.62464 0.653905 +-0.414851 0.391723 0.60098 +-0.347174 0.363491 0.465667 +-0.360048 0.314084 0.396455 +-0.41952 0.299968 0.767795 +-0.414223 0.645815 0.71323 +numsurf 29 +SURF 0x10 +mat 6 +refs 3 +2 0.569231 0.4 +1 0.153846 0.782609 +0 0.784615 1 +SURF 0x10 +mat 6 +refs 3 +5 0 0.0173913 +4 0.138462 0.0782608 +3 0.461538 0.113043 +SURF 0x10 +mat 6 +refs 3 +6 0.815385 0 +5 0 0.0173913 +3 0.461538 0.113043 +SURF 0x10 +mat 6 +refs 3 +6 0.815385 0 +3 0.461538 0.113043 +2 0.569231 0.4 +SURF 0x10 +mat 6 +refs 3 +6 0.815385 0 +2 0.569231 0.4 +0 0.784615 1 +SURF 0x10 +mat 6 +refs 3 +7 0.815385 0.426087 +6 0.815385 0 +0 0.784615 1 +SURF 0x10 +mat 6 +refs 3 +8 1 0.904348 +7 0.815385 0.426087 +0 0.784615 1 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +1 0 0 +10 0 0 +SURF 0x10 +mat 6 +refs 3 +11 0 0 +12 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 3 +11 0 0 +13 0 0 +14 0 0 +SURF 0x10 +mat 6 +refs 3 +10 0 0 +11 0 0 +14 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +10 0 0 +14 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +14 0 0 +15 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +15 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 3 +1 0 0 +9 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 3 +2 0 0 +10 0 0 +1 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +9 0 0 +10 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +12 0 0 +11 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +13 0 0 +12 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +11 0 0 +13 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +14 0 0 +13 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +11 0 0 +14 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +11 0 0 +10 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +10 0 0 +14 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +9 0 0 +14 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +7 0 0 +15 0 0 +14 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +9 0 0 +15 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +15 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 3 +0 0 0 +9 0 0 +8 0 0 +kids 0 +OBJECT poly +name "l_front_leg" +loc 0.140728 -0.396832 0.230312 +numvert 15 +-0.44984 1.03401 -0.534705 +-0.481804 0.66699 -0.65257 +-0.510035 0.44113 -0.546695 +-0.510035 0.398781 -0.701975 +-0.601015 0.363491 -0.737265 +-0.601015 0.363491 -0.454942 +-0.510035 0.66699 -0.52552 +-0.488201 1.00577 -0.391536 +-0.393375 1.03401 -0.534705 +-0.453571 0.44113 -0.546695 +-0.453571 0.398781 -0.701975 +-0.453571 0.363491 -0.737265 +-0.453571 0.363491 -0.454942 +-0.453571 0.66699 -0.52552 +-0.431737 1.00577 -0.391536 +numsurf 25 +SURF 0x10 +mat 6 +refs 3 +0 0.571429 1 +1 0.244898 0.44086 +2 0.55102 0.11828 +SURF 0x10 +mat 6 +refs 3 +2 0.55102 0.11828 +3 0.102041 0.0752688 +4 0 0.0215054 +SURF 0x10 +mat 6 +refs 3 +2 0.55102 0.11828 +4 0 0.0215054 +5 0.816326 0 +SURF 0x10 +mat 6 +refs 3 +2 0.55102 0.11828 +5 0.816326 0 +6 0.612245 0.44086 +SURF 0x10 +mat 6 +refs 3 +0 0.571429 1 +2 0.55102 0.11828 +6 0.612245 0.44086 +SURF 0x10 +mat 6 +refs 3 +0 0.571429 1 +6 0.612245 0.44086 +7 1 0.956989 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +1 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 3 +11 0 0 +10 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +12 0 0 +11 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +13 0 0 +12 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +13 0 0 +9 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 3 +14 0 0 +13 0 0 +8 0 0 +SURF 0x10 +mat 6 +refs 3 +0 0 0 +8 0 0 +1 0 0 +SURF 0x10 +mat 6 +refs 3 +1 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +9 0 0 +8 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +9 0 0 +10 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +10 0 0 +11 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +11 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +11 0 0 +12 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +12 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +12 0 0 +13 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +13 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +13 0 0 +8 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +13 0 0 +14 0 0 +7 0 0 +SURF 0x10 +mat 6 +refs 4 +7 0 0 +14 0 0 +8 0 0 +0 0 0 +kids 0 +OBJECT poly +name "r_front_leg" +loc 0.13367 -0.396832 -0.426089 +numvert 15 +0.118907 1.03401 0.114638 +0.150872 0.66699 -0.00322455 +0.179104 0.44113 0.102646 +0.179104 0.398781 -0.052631 +0.270085 0.363491 -0.0879215 +0.270085 0.363491 0.194402 +0.179104 0.66699 0.123821 +0.15727 1.00577 0.257807 +0.062443 1.03401 0.114638 +0.12264 0.44113 0.102646 +0.12264 0.398781 -0.052631 +0.12264 0.363491 -0.0879215 +0.12264 0.363491 0.194402 +0.12264 0.66699 0.123821 +0.100805 1.00577 0.257807 +numsurf 25 +SURF 0x10 +mat 6 +refs 3 +2 0.55102 0.11828 +1 0.244898 0.44086 +0 0.571429 1 +SURF 0x10 +mat 6 +refs 3 +4 0 0.0215054 +3 0.102041 0.0752688 +2 0.55102 0.11828 +SURF 0x10 +mat 6 +refs 3 +5 0.816326 0 +4 0 0.0215054 +2 0.55102 0.11828 +SURF 0x10 +mat 6 +refs 3 +6 0.612245 0.44086 +5 0.816326 0 +2 0.55102 0.11828 +SURF 0x10 +mat 6 +refs 3 +6 0.612245 0.44086 +2 0.55102 0.11828 +0 0.571429 1 +SURF 0x10 +mat 6 +refs 3 +7 1 0.956989 +6 0.612245 0.44086 +0 0.571429 1 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +1 0 0 +9 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +10 0 0 +11 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +11 0 0 +12 0 0 +SURF 0x10 +mat 6 +refs 3 +9 0 0 +12 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +9 0 0 +13 0 0 +SURF 0x10 +mat 6 +refs 3 +8 0 0 +13 0 0 +14 0 0 +SURF 0x10 +mat 6 +refs 3 +1 0 0 +8 0 0 +0 0 0 +SURF 0x10 +mat 6 +refs 3 +2 0 0 +9 0 0 +1 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +8 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +3 0 0 +10 0 0 +9 0 0 +2 0 0 +SURF 0x10 +mat 6 +refs 4 +4 0 0 +11 0 0 +10 0 0 +3 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +9 0 0 +11 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +5 0 0 +12 0 0 +11 0 0 +4 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +9 0 0 +12 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +6 0 0 +13 0 0 +12 0 0 +5 0 0 +SURF 0x10 +mat 6 +refs 4 +2 0 0 +9 0 0 +13 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +8 0 0 +13 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +7 0 0 +14 0 0 +13 0 0 +6 0 0 +SURF 0x10 +mat 6 +refs 4 +0 0 0 +8 0 0 +14 0 0 +7 0 0 +kids 0 +OBJECT poly +name "right_ear" +loc 0.229869 1.07982 -0.695912 +numvert 5 +-0.144673 0.0103428 -0.0315753 +0.0925925 -0.098678 -0.076712 +0.155331 -0.130047 -0.0273054 +0.0714185 -0.098678 0.0503335 +-0.17729 -0.0111147 0.070438 +numsurf 6 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +1 0.764706 0 +2 1 0.35 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +2 1 0.35 +3 0.705882 0.9 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +3 0.705882 0.9 +4 0 1 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +3 0.705882 0.9 +2 1 0.35 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +2 1 0.35 +1 0.764706 0 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +1 0.764706 0 +0 0.117647 0.25 +kids 0 +OBJECT poly +name "left_ear" +loc -0.261382 1.07611 -0.693597 +numvert 5 +0.145397 0.013855 -0.0315158 +-0.09105 -0.0949725 -0.0762386 +-0.175747 -0.113794 -0.0205581 +-0.069876 -0.0949725 0.050807 +0.175747 -0.0148195 0.0699645 +numsurf 6 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +3 0.705882 0.9 +2 1 0.35 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +2 1 0.35 +1 0.764706 0 +SURF 0x10 +mat 6 +refs 3 +4 0 1 +1 0.764706 0 +0 0.117647 0.25 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +1 0.764706 0 +2 1 0.35 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +2 1 0.35 +3 0.705882 0.9 +SURF 0x10 +mat 6 +refs 3 +0 0.117647 0.25 +3 0.705882 0.9 +4 0 1 +kids 0 +OBJECT poly +name "tongue" +loc -0.0252535 0.611636 -1.12259 +numvert 4 +-0.072184 0.0916755 -0.000977755 +-0.025 -0.1 -0.0999999 +0.025 -0.1 -0.0999999 +0.077816 0.0916755 -0.000977755 +numsurf 1 +SURF 0x30 +mat 0 +refs 4 +3 1 1 +2 0.666667 0 +1 0.333333 0 +0 0 1 +kids 0 +OBJECT poly +name "line" +loc -0.0252535 0.484011 -0.247592 +numvert 29 +0.0250001 0.277625 -0.875 +0.0250001 0.027625 -0.975 +0.0250001 0.0776249 -0.075 +0.0250001 0.327625 -0.075 +0.0250001 0.277625 -0.875 +0.025 0.427625 -0.375 +0.0250001 0.327625 0.575 +0.0250001 0.477625 0.975 +-0.425 0.527625 -0.475 +0.425 0.527625 -0.475 +-0.275 0.127625 0.0250001 +0.0250001 0.327625 0.075 +0.275 0.127625 0.0250001 +-0.275 0.127625 0.425 +0.325 0.127625 0.425 +-0.305 -0.224875 -0.1575 +-0.338 -0.467125 -0.0565 +-0.338 -0.527625 -0.2985 +0.275 0.127625 0.0250001 +0.3 -0.224875 -0.1575 +0.3275 -0.467125 -0.0565 +0.3275 -0.527625 -0.2985 +-0.305 -0.169875 0.5575 +-0.338 -0.467125 0.5485 +-0.338 -0.527625 0.246 +0.325 0.127625 0.425 +0.355 -0.169875 0.5575 +0.388 -0.467125 0.5485 +0.388 -0.527625 0.246 +numsurf 10 +SURF 0x22 +mat 3 +refs 2 +0 1 1 +1 0 0 +SURF 0x22 +mat 0 +refs 2 +2 0 0 +3 0 1 +SURF 0x22 +mat 2 +refs 5 +4 0 0.333333 +5 0.342105 0.333333 +3 0.447368 0 +6 0.789474 0 +7 1 1 +SURF 0x22 +mat 3 +refs 3 +8 0 0.5 +5 0.529412 0 +9 1 1 +SURF 0x22 +mat 1 +refs 3 +10 0 1 +11 0.545455 0 +12 1 1 +SURF 0x22 +mat 1 +refs 3 +13 0 1 +6 0.5 0 +14 1 1 +SURF 0x22 +mat 1 +refs 4 +10 1 1 +15 0.4 0.461538 +16 0.8 0.0769231 +17 0 0 +SURF 0x22 +mat 1 +refs 4 +18 1 1 +19 0.4 0.461538 +20 0.8 0.0769231 +21 0 0 +SURF 0x22 +mat 1 +refs 4 +13 1 1 +22 1 0.466667 +23 0.833333 0.0666667 +24 0 0 +SURF 0x22 +mat 1 +refs 4 +25 1 1 +26 1 0.466667 +27 0.833333 0.0666667 +28 0 0 +kids 0 diff --git a/demos/exposer/data/dog.bones b/demos/exposer/data/dog.bones new file mode 100644 index 0000000..720e86a --- /dev/null +++ b/demos/exposer/data/dog.bones @@ -0,0 +1,150 @@ +NUMBONES=24 NUMEVENTS=5 MAXTIME=3.392857 Z_OFFSET=0 SPEED=0 +BONE "Tongue" HP. +BONE "WholeDog" H.. +BONE "Nose" ... +BONE "Head" HPR +BONE "Body" H.. +BONE "Tail" HP. +BONE "LeftEar" ..R +BONE "RightEar" ..R +BONE "FrLfShoulder" ... +BONE "FrRtShoulder" ... +BONE "BkLfHip" ... +BONE "BkRtHip" ... +BONE "FrLfLeg" .P. +BONE "FrLfKnee" .P. +BONE "FrLfFoot" HP. +BONE "FrRtLeg" .P. +BONE "FrRtKnee" .P. +BONE "FrRtFoot" HP. +BONE "BkLfLeg" .P. +BONE "BkLfKnee" .P. +BONE "BkLfFoot" .P. +BONE "BkRtLeg" HPR +BONE "BkRtKnee" .P. +BONE "BkRtFoot" .P. +EVENT 0.000000 24 (0,0,0) + (0,0,0) + (0,0,0) + (-8,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,14,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) +EVENT 0.502870 24 (0,0,0) + (0,0,0) + (0,0,0) + (-8,0,0) + (11,-23,0) + (0,0,0) + (-28,-10,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,14,0) + (0,-24,0) + (21,-18,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) +EVENT 1.005740 24 (0,0,0) + (0,0,0) + (0,0,0) + (-8,0,0) + (0,-30,0) + (0,0,0) + (-52,-10,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,14,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) +EVENT 2.005421 24 (0,0,0) + (0,0,0) + (0,0,0) + (-8,0,0) + (51,0,0) + (0,0,0) + (51,-10,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,14,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) +EVENT 3.000000 24 (0,0,0) + (0,0,0) + (0,0,0) + (-8,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,14,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) diff --git a/demos/exposer/data/human.ac b/demos/exposer/data/human.ac new file mode 100644 index 0000000..b53f360 --- /dev/null +++ b/demos/exposer/data/human.ac @@ -0,0 +1,1556 @@ +AC3Db +MATERIAL "ac3dmat1" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat3" rgb 1 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat5" rgb 1 1 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +OBJECT world +kids 3 +OBJECT poly +name "backOfHead" +loc 0.0024697 1.87326 -0.000320375 +texture "teeshirt_human.rgb" +texrep -1 1 +texoff 0.09 0 +numvert 17 +0.0285838 -0.119611 0.00298071 +0.0463477 -0.119611 -0.0971166 +0.0752035 -0.0501754 -0.112326 +0.0464781 -0.0501754 0.0530327 +-0.0464782 -0.0501754 0.0530327 +-0.0289011 -0.119611 0.00298071 +-0.046665 -0.119611 -0.0971166 +-0.0752035 -0.0501754 -0.112326 +0.0752035 0.0299871 -0.112326 +0.0464781 0.0299871 0.0530327 +-0.0464782 0.0299871 0.0530327 +-0.0752035 0.0299871 -0.112326 +0.0511259 0.0948387 -0.0948688 +0.0287249 0.0948387 0.00732839 +-0.0287252 0.0948387 0.00732839 +-0.051126 0.0948387 -0.0948688 +-1.00816e-07 0.119611 -0.0666221 +numsurf 21 +SURF 0x10 +mat 0 +refs 3 +0 0.506483 0.879793 +1 0.516778 0.879793 +2 0.533502 0.914684 +SURF 0x10 +mat 0 +refs 3 +2 0.533502 0.914684 +3 0.516854 0.914684 +0 0.506483 0.879793 +SURF 0x10 +mat 0 +refs 3 +0 0.506483 0.879793 +3 0.516854 0.914684 +4 0.46298 0.914684 +SURF 0x10 +mat 0 +refs 3 +4 0.46298 0.914684 +5 0.473167 0.879793 +0 0.506483 0.879793 +SURF 0x10 +mat 0 +refs 3 +6 0.462872 0.879793 +5 0.473167 0.879793 +4 0.46298 0.914684 +SURF 0x10 +mat 0 +refs 3 +4 0.46298 0.914684 +7 0.446332 0.914684 +6 0.462872 0.879793 +SURF 0x10 +mat 0 +refs 3 +2 0.533502 0.914684 +8 0.533502 0.954965 +9 0.516854 0.954965 +SURF 0x10 +mat 0 +refs 3 +9 0.516854 0.954965 +3 0.516854 0.914684 +2 0.533502 0.914684 +SURF 0x10 +mat 0 +refs 3 +4 0.46298 0.914684 +3 0.516854 0.914684 +9 0.516854 0.954965 +SURF 0x10 +mat 0 +refs 3 +9 0.516854 0.954965 +10 0.46298 0.954965 +4 0.46298 0.914684 +SURF 0x10 +mat 0 +refs 3 +4 0.46298 0.914684 +10 0.46298 0.954965 +11 0.446332 0.954965 +SURF 0x10 +mat 0 +refs 3 +11 0.446332 0.954965 +7 0.446332 0.914684 +4 0.46298 0.914684 +SURF 0x10 +mat 0 +refs 3 +9 0.516854 0.954965 +8 0.533502 0.954965 +12 0.519547 0.987552 +SURF 0x10 +mat 0 +refs 3 +12 0.519547 0.987552 +13 0.506565 0.987552 +9 0.516854 0.954965 +SURF 0x10 +mat 0 +refs 3 +9 0.516854 0.954965 +13 0.506565 0.987552 +14 0.473269 0.987552 +SURF 0x10 +mat 0 +refs 3 +14 0.473269 0.987552 +10 0.46298 0.954965 +9 0.516854 0.954965 +SURF 0x10 +mat 0 +refs 3 +11 0.446332 0.954965 +10 0.46298 0.954965 +14 0.473269 0.987552 +SURF 0x10 +mat 0 +refs 3 +14 0.473269 0.987552 +15 0.460286 0.987552 +11 0.446332 0.954965 +SURF 0x10 +mat 0 +refs 3 +16 0.489917 1 +13 0.506565 0.987552 +12 0.519547 0.987552 +SURF 0x10 +mat 0 +refs 3 +16 0.489917 1 +14 0.473269 0.987552 +13 0.506565 0.987552 +SURF 0x10 +mat 0 +refs 3 +16 0.489917 1 +15 0.460286 0.987552 +14 0.473269 0.987552 +kids 0 +OBJECT poly +name "cylinder" +loc 0.0198678 0.997829 -0.0567626 +texture "teeshirt_human.rgb" +texrep -1 1 +numvert 137 +0.0289497 0.755816 -0.0406743 +0.0164419 0.71337 0.0566146 +0.0227837 0.710298 -0.0910033 +0.0111858 0.755816 0.0594229 +-0.0383992 0.71337 0.0582384 +-0.0462992 0.755816 0.0594229 +-0.0455715 0.710298 -0.0931686 +-0.064063 0.755816 -0.0406743 +-0.0103786 0.701213 -0.114082 +-0.0175567 0.755816 -0.102538 +-0.0173982 0.845283 -0.15173 +0.0578054 0.825252 -0.0558837 +-0.0926016 0.825252 -0.0558838 +-0.0173982 0.930159 -0.119973 +0.0578054 0.905414 -0.0558837 +-0.0926016 0.905414 -0.0558838 +-0.0173982 0.970266 -0.101588 +0.0337278 0.970266 -0.0384265 +-0.0685241 0.970266 -0.0384265 +-0.0173982 0.995038 -0.0101799 +0.721375 0.599141 -0.0752391 +0.721375 0.678966 -0.0752391 +0.850837 0.701735 -0.0100079 +0.862726 0.596113 -0.0100079 +0.850837 0.701735 0.0523306 +0.862726 0.596113 0.0523306 +0.721375 0.678966 0.00307237 +0.721375 0.599141 0.00307237 +0.428545 0.65929 -0.0775772 +0.428545 0.65929 0.000229806 +0.436648 0.623394 -0.116481 +0.721375 0.638611 -0.0758877 +0.424493 0.5875 -0.0775772 +0.424493 0.5875 0.000229806 +0.436648 0.623394 0.0391333 +0.721375 0.638611 0.00288412 +0.212256 0.704764 -0.0854696 +0.212256 0.704764 0.0133027 +0.199367 0.640677 -0.142779 +0.201946 0.566164 -0.0853052 +0.201946 0.566164 0.0131383 +0.211269 0.676988 0.0792123 +0.187895 -0.420728 0.016965 +0.182152 0.00920123 -0.0531171 +0.18185 0.008591 0.0661881 +0.187895 -0.420728 -0.0714491 +0.195435 0.0043382 -0.12528 +0.131938 -0.431623 -0.115656 +-0.00667014 -0.0552303 -0.153251 +0.0759816 -0.442518 -0.0714491 +-0.0111928 -0.0415102 0.0924745 +0.0759816 -0.442518 0.016965 +0.147871 -0.00582391 0.12347 +0.131938 -0.431623 0.0611721 +0.209488 -0.909576 0.0195732 +0.209488 -0.922156 -0.0674977 +0.174265 -0.922345 -0.0952882 +0.141767 -0.917753 -0.0908897 +0.141767 -0.898883 0.0397166 +0.174265 -0.901966 0.0457666 +-0.00923719 0.701911 0.0839428 +-0.00820317 0.234595 -0.103266 +-0.00949695 0.65369 -0.167939 +0.0831915 0.239069 -0.103388 +0.142893 0.239069 -0.0877754 +0.135309 0.239069 0.0171247 +0.135035 0.239069 -0.0459313 +0.112774 0.239069 0.0506644 +0.083244 0.239069 0.0757644 +0.185319 0.630283 0.0957721 +-0.00949695 0.65369 0.0957721 +-0.00820281 0.234595 0.0753487 +0.0984038 0.0481983 -0.142285 +0.12876 0.0427306 0.160877 +-0.721375 0.599141 0.0324727 +-0.721375 0.678966 0.0324727 +-0.850837 0.701735 -0.0327586 +-0.862726 0.596113 -0.0327586 +-0.850837 0.701735 -0.0950972 +-0.862726 0.596113 -0.0950972 +-0.721375 0.678966 -0.0458388 +-0.721375 0.599141 -0.0458388 +-0.221465 -0.993297 0.0392883 +-0.205063 -0.89973 0.0457203 +-0.221465 -0.951063 -0.234999 +-0.221465 -0.994626 -0.240268 +-0.127518 -0.944565 -0.234999 +-0.127518 -0.988128 -0.240268 +-0.126265 -0.895398 0.0457203 +-0.127518 -0.986799 0.0392883 +-0.428545 0.65929 0.0348106 +-0.428545 0.65929 -0.0429963 +-0.436648 0.623394 0.0737138 +-0.721375 0.638611 0.0331213 +-0.424493 0.5875 0.0348106 +-0.424493 0.5875 -0.0429963 +-0.436648 0.623394 -0.0818998 +-0.721375 0.638611 -0.0456505 +-0.212256 0.704764 0.0427032 +-0.212256 0.704764 -0.0560693 +-0.199367 0.640677 0.100013 +-0.201946 0.566164 0.0425387 +-0.201946 0.566164 -0.0559048 +-0.211269 0.676988 -0.121978 +-0.188669 -0.415884 -0.0597315 +-0.182926 0.0140458 0.0770654 +-0.182624 0.0134358 -0.0422398 +-0.188669 -0.415884 0.0286826 +-0.196209 0.00918311 0.149229 +-0.132712 -0.426778 0.0728899 +-0.0767553 -0.437673 0.0286826 +-0.0767553 -0.437673 -0.0597315 +-0.148645 -0.000979066 -0.0995216 +-0.132712 -0.426778 -0.103939 +-0.210262 -0.915718 -0.0569848 +-0.210262 -0.903138 0.030086 +-0.175039 -0.895527 0.0562793 +-0.142541 -0.892445 0.0502294 +-0.142541 -0.911315 -0.080377 +-0.175039 -0.915907 -0.0847754 +-0.0831915 0.239069 0.0987987 +-0.142893 0.239069 0.0831867 +-0.135309 0.239069 -0.0217138 +-0.135035 0.239069 0.0413422 +-0.112774 0.239069 -0.0552535 +-0.083244 0.239069 -0.0803539 +-0.185319 0.630283 -0.138538 +-0.14526 0.0412554 0.166288 +-0.113906 0.0548714 -0.139786 +0.143549 -0.993709 0.0451747 +0.141884 -0.900248 0.0302683 +0.143549 -0.951475 -0.229112 +0.143549 -0.995038 -0.234381 +0.227687 -0.992871 -0.234381 +0.227687 -0.949309 -0.229112 +0.211284 -0.897099 0.0520636 +0.227687 -0.991543 0.0451747 +numsurf 187 +SURF 0x10 +mat 0 +refs 3 +0 0.516778 0.879793 +1 0.509529 0.858464 +2 0.513205 0.85692 +SURF 0x10 +mat 0 +refs 3 +0 0.516778 0.879793 +3 0.506483 0.879793 +1 0.509529 0.858464 +SURF 0x10 +mat 0 +refs 3 +3 0.506483 0.879793 +4 0.477745 0.858464 +1 0.509529 0.858464 +SURF 0x10 +mat 0 +refs 3 +3 0.506483 0.879793 +5 0.473167 0.879793 +4 0.477745 0.858464 +SURF 0x10 +mat 0 +refs 3 +5 0.473167 0.879793 +6 0.473589 0.85692 +4 0.477745 0.858464 +SURF 0x10 +mat 0 +refs 3 +5 0.473167 0.879793 +7 0.462872 0.879793 +6 0.473589 0.85692 +SURF 0x10 +mat 0 +refs 3 +7 0.462872 0.879793 +8 0.493985 0.852355 +6 0.473589 0.85692 +SURF 0x10 +mat 0 +refs 3 +7 0.462872 0.879793 +9 0.489825 0.879793 +8 0.493985 0.852355 +SURF 0x10 +mat 0 +refs 3 +9 0.489825 0.879793 +2 0.513205 0.85692 +8 0.493985 0.852355 +SURF 0x10 +mat 0 +refs 3 +9 0.489825 0.879793 +0 0.516778 0.879793 +2 0.513205 0.85692 +SURF 0x10 +mat 0 +refs 3 +9 0.489825 0.879793 +10 0.489917 0.924749 +11 0.533502 0.914684 +SURF 0x10 +mat 0 +refs 3 +11 0.533502 0.914684 +0 0.516778 0.879793 +9 0.489825 0.879793 +SURF 0x10 +mat 0 +refs 3 +7 0.462872 0.879793 +12 0.446332 0.914684 +10 0.489917 0.924749 +SURF 0x10 +mat 0 +refs 3 +10 0.489917 0.924749 +9 0.489825 0.879793 +7 0.462872 0.879793 +SURF 0x10 +mat 0 +refs 3 +11 0.533502 0.914684 +10 0.489917 0.924749 +13 0.489917 0.967399 +SURF 0x10 +mat 0 +refs 3 +13 0.489917 0.967399 +14 0.533502 0.954965 +11 0.533502 0.914684 +SURF 0x10 +mat 0 +refs 3 +10 0.489917 0.924749 +12 0.446332 0.914684 +15 0.446332 0.954965 +SURF 0x10 +mat 0 +refs 3 +15 0.446332 0.954965 +13 0.489917 0.967399 +10 0.489917 0.924749 +SURF 0x10 +mat 0 +refs 3 +13 0.489917 0.967399 +16 0.489917 0.987552 +17 0.519547 0.987552 +SURF 0x10 +mat 0 +refs 3 +17 0.519547 0.987552 +14 0.533502 0.954965 +13 0.489917 0.967399 +SURF 0x10 +mat 0 +refs 3 +15 0.446332 0.954965 +18 0.460286 0.987552 +16 0.489917 0.987552 +SURF 0x10 +mat 0 +refs 3 +16 0.489917 0.987552 +13 0.489917 0.967399 +15 0.446332 0.954965 +SURF 0x10 +mat 0 +refs 3 +19 0.489917 1 +17 0.519547 0.987552 +16 0.489917 0.987552 +SURF 0x10 +mat 0 +refs 3 +19 0.489917 1 +16 0.489917 0.987552 +18 0.460286 0.987552 +SURF 0x10 +mat 0 +refs 4 +20 0.918079 0.801065 +21 0.918079 0.841176 +22 0.99311 0.852617 +23 1 0.799543 +SURF 0x10 +mat 0 +refs 4 +24 0.99311 0.852617 +25 1 0.799543 +23 1 0.799543 +22 0.99311 0.852617 +SURF 0x10 +mat 0 +refs 4 +25 1 0.799543 +24 0.99311 0.852617 +26 0.918079 0.841176 +27 0.918079 0.801065 +SURF 0x10 +mat 0 +refs 4 +27 0.918079 0.801065 +26 0.918079 0.841176 +21 0.918079 0.841176 +20 0.918079 0.801065 +SURF 0x10 +mat 0 +refs 4 +21 0.918079 0.841176 +26 0.918079 0.841176 +24 0.99311 0.852617 +22 0.99311 0.852617 +SURF 0x10 +mat 0 +refs 4 +27 0.918079 0.801065 +20 0.918079 0.801065 +23 1 0.799543 +25 1 0.799543 +SURF 0x10 +mat 0 +refs 3 +26 0.918079 0.841176 +28 0.748367 0.831289 +29 0.748367 0.831289 +SURF 0x10 +mat 0 +refs 3 +26 0.918079 0.841176 +21 0.918079 0.841176 +28 0.748367 0.831289 +SURF 0x10 +mat 0 +refs 3 +21 0.918079 0.841176 +30 0.753063 0.813252 +28 0.748367 0.831289 +SURF 0x10 +mat 0 +refs 3 +21 0.918079 0.841176 +31 0.918079 0.820898 +30 0.753063 0.813252 +SURF 0x10 +mat 0 +refs 3 +31 0.918079 0.820898 +32 0.746018 0.795215 +30 0.753063 0.813252 +SURF 0x10 +mat 0 +refs 3 +31 0.918079 0.820898 +20 0.918079 0.801065 +32 0.746018 0.795215 +SURF 0x10 +mat 0 +refs 3 +20 0.918079 0.801065 +33 0.746018 0.795215 +32 0.746018 0.795215 +SURF 0x10 +mat 0 +refs 3 +20 0.918079 0.801065 +27 0.918079 0.801065 +33 0.746018 0.795215 +SURF 0x10 +mat 0 +refs 3 +27 0.918079 0.801065 +34 0.753063 0.813252 +33 0.746018 0.795215 +SURF 0x10 +mat 0 +refs 3 +27 0.918079 0.801065 +35 0.918079 0.820898 +34 0.753063 0.813252 +SURF 0x10 +mat 0 +refs 3 +35 0.918079 0.820898 +29 0.748367 0.831289 +34 0.753063 0.813252 +SURF 0x10 +mat 0 +refs 3 +35 0.918079 0.820898 +26 0.918079 0.841176 +29 0.748367 0.831289 +SURF 0x10 +mat 0 +refs 3 +29 0.748367 0.831289 +36 0.623015 0.85414 +37 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 3 +29 0.748367 0.831289 +28 0.748367 0.831289 +36 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 3 +28 0.748367 0.831289 +38 0.615545 0.821936 +36 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 3 +28 0.748367 0.831289 +30 0.753063 0.813252 +38 0.615545 0.821936 +SURF 0x10 +mat 0 +refs 3 +30 0.753063 0.813252 +39 0.61704 0.784494 +38 0.615545 0.821936 +SURF 0x10 +mat 0 +refs 3 +30 0.753063 0.813252 +32 0.746018 0.795215 +39 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 3 +32 0.746018 0.795215 +40 0.61704 0.784494 +39 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 3 +32 0.746018 0.795215 +33 0.746018 0.795215 +40 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 3 +33 0.746018 0.795215 +41 0.622443 0.840182 +40 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 3 +33 0.746018 0.795215 +34 0.753063 0.813252 +41 0.622443 0.840182 +SURF 0x10 +mat 0 +refs 3 +34 0.753063 0.813252 +37 0.623015 0.85414 +41 0.622443 0.840182 +SURF 0x10 +mat 0 +refs 3 +34 0.753063 0.813252 +29 0.748367 0.831289 +37 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 3 +42 0.608896 0.288587 +43 0.605568 0.504624 +44 0.605393 0.504317 +SURF 0x10 +mat 0 +refs 3 +42 0.608896 0.288587 +45 0.608896 0.288587 +43 0.605568 0.504624 +SURF 0x10 +mat 0 +refs 3 +45 0.608896 0.288587 +46 0.613266 0.50218 +43 0.605568 0.504624 +SURF 0x10 +mat 0 +refs 3 +45 0.608896 0.288587 +47 0.576466 0.283112 +46 0.613266 0.50218 +SURF 0x10 +mat 0 +refs 3 +47 0.576466 0.283112 +48 0.496134 0.472247 +46 0.613266 0.50218 +SURF 0x10 +mat 0 +refs 3 +47 0.576466 0.283112 +49 0.544036 0.277638 +48 0.496134 0.472247 +SURF 0x10 +mat 0 +refs 3 +49 0.544036 0.277638 +50 0.493513 0.479141 +48 0.496134 0.472247 +SURF 0x10 +mat 0 +refs 3 +49 0.544036 0.277638 +51 0.544036 0.277638 +50 0.493513 0.479141 +SURF 0x10 +mat 0 +refs 3 +51 0.544036 0.277638 +52 0.5857 0.497074 +50 0.493513 0.479141 +SURF 0x10 +mat 0 +refs 3 +51 0.544036 0.277638 +53 0.576466 0.283112 +52 0.5857 0.497074 +SURF 0x10 +mat 0 +refs 3 +53 0.576466 0.283112 +44 0.605393 0.504317 +52 0.5857 0.497074 +SURF 0x10 +mat 0 +refs 3 +53 0.576466 0.283112 +42 0.608896 0.288587 +44 0.605393 0.504317 +SURF 0x10 +mat 0 +refs 3 +54 0.621411 0.0429441 +45 0.608896 0.288587 +42 0.608896 0.288587 +SURF 0x10 +mat 0 +refs 3 +54 0.621411 0.0429441 +55 0.621411 0.0366228 +45 0.608896 0.288587 +SURF 0x10 +mat 0 +refs 3 +55 0.621411 0.0366228 +47 0.576466 0.283112 +45 0.608896 0.288587 +SURF 0x10 +mat 0 +refs 3 +55 0.621411 0.0366228 +56 0.600997 0.0365278 +47 0.576466 0.283112 +SURF 0x10 +mat 0 +refs 3 +56 0.600997 0.0365278 +49 0.544036 0.277638 +47 0.576466 0.283112 +SURF 0x10 +mat 0 +refs 3 +56 0.600997 0.0365278 +57 0.582162 0.0388352 +49 0.544036 0.277638 +SURF 0x10 +mat 0 +refs 3 +57 0.582162 0.0388352 +51 0.544036 0.277638 +49 0.544036 0.277638 +SURF 0x10 +mat 0 +refs 3 +57 0.582162 0.0388352 +58 0.582162 0.0483173 +51 0.544036 0.277638 +SURF 0x10 +mat 0 +refs 3 +58 0.582162 0.0483173 +53 0.576466 0.283112 +51 0.544036 0.277638 +SURF 0x10 +mat 0 +refs 3 +58 0.582162 0.0483173 +59 0.600997 0.0467681 +53 0.576466 0.283112 +SURF 0x10 +mat 0 +refs 3 +59 0.600997 0.0467681 +42 0.608896 0.288587 +53 0.576466 0.283112 +SURF 0x10 +mat 0 +refs 3 +59 0.600997 0.0467681 +54 0.621411 0.0429441 +42 0.608896 0.288587 +SURF 0x10 +mat 0 +refs 4 +2 0.513205 0.85692 +1 0.509529 0.858464 +37 0.623015 0.85414 +36 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 4 +60 0.494647 0.852706 +41 0.622443 0.840182 +37 0.623015 0.85414 +1 0.509529 0.858464 +SURF 0x10 +mat 0 +refs 4 +38 0.615545 0.821936 +8 0.493985 0.852355 +2 0.513205 0.85692 +36 0.623015 0.85414 +SURF 0x10 +mat 0 +refs 4 +61 0.495246 0.617883 +62 0.494496 0.828475 +38 0.615545 0.821936 +63 0.548214 0.620131 +SURF 0x10 +mat 0 +refs 3 +8 0.493985 0.852355 +38 0.615545 0.821936 +62 0.494496 0.828475 +SURF 0x10 +mat 0 +refs 3 +38 0.615545 0.821936 +64 0.582815 0.620131 +63 0.548214 0.620131 +SURF 0x10 +mat 0 +refs 4 +40 0.61704 0.784494 +65 0.578419 0.620131 +66 0.578261 0.620131 +39 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 4 +41 0.622443 0.840182 +67 0.565359 0.620131 +65 0.578419 0.620131 +40 0.61704 0.784494 +SURF 0x10 +mat 0 +refs 4 +64 0.582815 0.620131 +38 0.615545 0.821936 +39 0.61704 0.784494 +66 0.578261 0.620131 +SURF 0x10 +mat 0 +refs 4 +68 0.548245 0.620131 +69 0.607403 0.816713 +70 0.494496 0.828475 +71 0.495246 0.617883 +SURF 0x10 +mat 0 +refs 4 +67 0.565359 0.620131 +41 0.622443 0.840182 +69 0.607403 0.816713 +68 0.548245 0.620131 +SURF 0x10 +mat 0 +refs 4 +41 0.622443 0.840182 +60 0.494647 0.852706 +70 0.494496 0.828475 +69 0.607403 0.816713 +SURF 0x10 +mat 0 +refs 4 +48 0.496134 0.472247 +61 0.495246 0.617883 +63 0.548214 0.620131 +72 0.557031 0.524219 +SURF 0x10 +mat 0 +refs 4 +64 0.582815 0.620131 +46 0.613266 0.50218 +72 0.557031 0.524219 +63 0.548214 0.620131 +SURF 0x10 +mat 0 +refs 3 +46 0.613266 0.50218 +48 0.496134 0.472247 +72 0.557031 0.524219 +SURF 0x10 +mat 0 +refs 4 +65 0.578419 0.620131 +44 0.605393 0.504317 +43 0.605568 0.504624 +66 0.578261 0.620131 +SURF 0x10 +mat 0 +refs 4 +67 0.565359 0.620131 +52 0.5857 0.497074 +44 0.605393 0.504317 +65 0.578419 0.620131 +SURF 0x10 +mat 0 +refs 4 +46 0.613266 0.50218 +64 0.582815 0.620131 +66 0.578261 0.620131 +43 0.605568 0.504624 +SURF 0x10 +mat 0 +refs 4 +73 0.574624 0.521472 +68 0.548245 0.620131 +71 0.495246 0.617883 +50 0.493513 0.479141 +SURF 0x10 +mat 0 +refs 4 +52 0.5857 0.497074 +67 0.565359 0.620131 +68 0.548245 0.620131 +73 0.574624 0.521472 +SURF 0x10 +mat 0 +refs 3 +50 0.493513 0.479141 +52 0.5857 0.497074 +73 0.574624 0.521472 +SURF 0x10 +mat 0 +refs 4 +74 0.0819211 0.801065 +75 0.0819211 0.841176 +76 0.00689036 0.852617 +77 0 0.799543 +SURF 0x10 +mat 0 +refs 4 +78 0.00689036 0.852617 +79 0 0.799543 +77 0 0.799543 +76 0.00689036 0.852617 +SURF 0x10 +mat 0 +refs 4 +79 0 0.799543 +78 0.00689036 0.852617 +80 0.0819211 0.841176 +81 0.0819211 0.801065 +SURF 0x10 +mat 0 +refs 4 +81 0.0819211 0.801065 +80 0.0819211 0.841176 +75 0.0819211 0.841176 +74 0.0819211 0.801065 +SURF 0x10 +mat 0 +refs 4 +75 0.0819211 0.841176 +80 0.0819211 0.841176 +78 0.00689036 0.852617 +76 0.00689036 0.852617 +SURF 0x10 +mat 0 +refs 4 +81 0.0819211 0.801065 +74 0.0819211 0.801065 +77 0 0.799543 +79 0 0.799543 +SURF 0x10 +mat 0 +refs 4 +82 0.371648 0.000874867 +83 0.381154 0.0478917 +84 0.371648 0.0220972 +85 0.371648 0.000207051 +SURF 0x10 +mat 0 +refs 4 +86 0.426096 0.0253624 +87 0.426096 0.00347224 +85 0.371648 0.000207051 +84 0.371648 0.0220972 +SURF 0x10 +mat 0 +refs 4 +87 0.426096 0.00347224 +86 0.426096 0.0253624 +88 0.426822 0.0500685 +89 0.426096 0.00414006 +SURF 0x10 +mat 0 +refs 4 +89 0.426096 0.00414006 +88 0.426822 0.0500685 +83 0.381154 0.0478917 +82 0.371648 0.000874867 +SURF 0x10 +mat 0 +refs 4 +83 0.381154 0.0478917 +88 0.426822 0.0500685 +86 0.426096 0.0253624 +84 0.371648 0.0220972 +SURF 0x10 +mat 0 +refs 4 +89 0.426096 0.00414006 +82 0.371648 0.000874867 +85 0.371648 0.000207051 +87 0.426096 0.00347224 +SURF 0x10 +mat 0 +refs 3 +80 0.0819211 0.841176 +90 0.251633 0.831289 +91 0.251633 0.831289 +SURF 0x10 +mat 0 +refs 3 +80 0.0819211 0.841176 +75 0.0819211 0.841176 +90 0.251633 0.831289 +SURF 0x10 +mat 0 +refs 3 +75 0.0819211 0.841176 +92 0.246937 0.813252 +90 0.251633 0.831289 +SURF 0x10 +mat 0 +refs 3 +75 0.0819211 0.841176 +93 0.0819211 0.820898 +92 0.246937 0.813252 +SURF 0x10 +mat 0 +refs 3 +93 0.0819211 0.820898 +94 0.253982 0.795215 +92 0.246937 0.813252 +SURF 0x10 +mat 0 +refs 3 +93 0.0819211 0.820898 +74 0.0819211 0.801065 +94 0.253982 0.795215 +SURF 0x10 +mat 0 +refs 3 +74 0.0819211 0.801065 +95 0.253982 0.795215 +94 0.253982 0.795215 +SURF 0x10 +mat 0 +refs 3 +74 0.0819211 0.801065 +81 0.0819211 0.801065 +95 0.253982 0.795215 +SURF 0x10 +mat 0 +refs 3 +81 0.0819211 0.801065 +96 0.246937 0.813252 +95 0.253982 0.795215 +SURF 0x10 +mat 0 +refs 3 +81 0.0819211 0.801065 +97 0.0819211 0.820898 +96 0.246937 0.813252 +SURF 0x10 +mat 0 +refs 3 +97 0.0819211 0.820898 +91 0.251633 0.831289 +96 0.246937 0.813252 +SURF 0x10 +mat 0 +refs 3 +97 0.0819211 0.820898 +80 0.0819211 0.841176 +91 0.251633 0.831289 +SURF 0x10 +mat 0 +refs 3 +91 0.251633 0.831289 +98 0.376985 0.85414 +99 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 3 +91 0.251633 0.831289 +90 0.251633 0.831289 +98 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 3 +90 0.251633 0.831289 +100 0.384455 0.821936 +98 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 3 +90 0.251633 0.831289 +92 0.246937 0.813252 +100 0.384455 0.821936 +SURF 0x10 +mat 0 +refs 3 +92 0.246937 0.813252 +101 0.382961 0.784494 +100 0.384455 0.821936 +SURF 0x10 +mat 0 +refs 3 +92 0.246937 0.813252 +94 0.253982 0.795215 +101 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 3 +94 0.253982 0.795215 +102 0.382961 0.784494 +101 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 3 +94 0.253982 0.795215 +95 0.253982 0.795215 +102 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 3 +95 0.253982 0.795215 +103 0.377557 0.840182 +102 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 3 +95 0.253982 0.795215 +96 0.246937 0.813252 +103 0.377557 0.840182 +SURF 0x10 +mat 0 +refs 3 +96 0.246937 0.813252 +99 0.376985 0.85414 +103 0.377557 0.840182 +SURF 0x10 +mat 0 +refs 3 +96 0.246937 0.813252 +91 0.251633 0.831289 +99 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 3 +104 0.390655 0.291021 +105 0.393984 0.507058 +106 0.394159 0.506751 +SURF 0x10 +mat 0 +refs 3 +104 0.390655 0.291021 +107 0.390655 0.291021 +105 0.393984 0.507058 +SURF 0x10 +mat 0 +refs 3 +107 0.390655 0.291021 +108 0.386285 0.504614 +105 0.393984 0.507058 +SURF 0x10 +mat 0 +refs 3 +107 0.390655 0.291021 +109 0.423086 0.285547 +108 0.386285 0.504614 +SURF 0x10 +mat 0 +refs 3 +109 0.423086 0.285547 +50 0.493513 0.479141 +108 0.386285 0.504614 +SURF 0x10 +mat 0 +refs 3 +109 0.423086 0.285547 +110 0.455516 0.280072 +50 0.493513 0.479141 +SURF 0x10 +mat 0 +refs 3 +110 0.455516 0.280072 +48 0.496134 0.472247 +50 0.493513 0.479141 +SURF 0x10 +mat 0 +refs 3 +110 0.455516 0.280072 +111 0.455516 0.280072 +48 0.496134 0.472247 +SURF 0x10 +mat 0 +refs 3 +111 0.455516 0.280072 +112 0.413852 0.499508 +48 0.496134 0.472247 +SURF 0x10 +mat 0 +refs 3 +111 0.455516 0.280072 +113 0.423086 0.285547 +112 0.413852 0.499508 +SURF 0x10 +mat 0 +refs 3 +113 0.423086 0.285547 +106 0.394159 0.506751 +112 0.413852 0.499508 +SURF 0x10 +mat 0 +refs 3 +113 0.423086 0.285547 +104 0.390655 0.291021 +106 0.394159 0.506751 +SURF 0x10 +mat 0 +refs 3 +114 0.378141 0.0398578 +107 0.390655 0.291021 +104 0.390655 0.291021 +SURF 0x10 +mat 0 +refs 3 +114 0.378141 0.0398578 +115 0.378141 0.0461792 +107 0.390655 0.291021 +SURF 0x10 +mat 0 +refs 3 +115 0.378141 0.0461792 +109 0.423086 0.285547 +107 0.390655 0.291021 +SURF 0x10 +mat 0 +refs 3 +115 0.378141 0.0461792 +116 0.398555 0.0500036 +109 0.423086 0.285547 +SURF 0x10 +mat 0 +refs 3 +116 0.398555 0.0500036 +110 0.455516 0.280072 +109 0.423086 0.285547 +SURF 0x10 +mat 0 +refs 3 +116 0.398555 0.0500036 +117 0.417389 0.0515523 +110 0.455516 0.280072 +SURF 0x10 +mat 0 +refs 3 +117 0.417389 0.0515523 +111 0.455516 0.280072 +110 0.455516 0.280072 +SURF 0x10 +mat 0 +refs 3 +117 0.417389 0.0515523 +118 0.417389 0.0420703 +111 0.455516 0.280072 +SURF 0x10 +mat 0 +refs 3 +118 0.417389 0.0420703 +113 0.423086 0.285547 +111 0.455516 0.280072 +SURF 0x10 +mat 0 +refs 3 +118 0.417389 0.0420703 +119 0.398555 0.0397628 +113 0.423086 0.285547 +SURF 0x10 +mat 0 +refs 3 +119 0.398555 0.0397628 +104 0.390655 0.291021 +113 0.423086 0.285547 +SURF 0x10 +mat 0 +refs 3 +119 0.398555 0.0397628 +114 0.378141 0.0398578 +104 0.390655 0.291021 +SURF 0x10 +mat 0 +refs 4 +4 0.477745 0.858464 +6 0.473589 0.85692 +99 0.376985 0.85414 +98 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 4 +8 0.493985 0.852355 +103 0.377557 0.840182 +99 0.376985 0.85414 +6 0.473589 0.85692 +SURF 0x10 +mat 0 +refs 4 +100 0.384455 0.821936 +60 0.494647 0.852706 +4 0.477745 0.858464 +98 0.376985 0.85414 +SURF 0x10 +mat 0 +refs 4 +71 0.495246 0.617883 +70 0.494496 0.828475 +100 0.384455 0.821936 +120 0.451786 0.620131 +SURF 0x10 +mat 0 +refs 3 +60 0.494647 0.852706 +100 0.384455 0.821936 +70 0.494496 0.828475 +SURF 0x10 +mat 0 +refs 3 +100 0.384455 0.821936 +121 0.417185 0.620131 +120 0.451786 0.620131 +SURF 0x10 +mat 0 +refs 4 +102 0.382961 0.784494 +122 0.421581 0.620131 +123 0.421739 0.620131 +101 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 4 +103 0.377557 0.840182 +124 0.434641 0.620131 +122 0.421581 0.620131 +102 0.382961 0.784494 +SURF 0x10 +mat 0 +refs 4 +121 0.417185 0.620131 +100 0.384455 0.821936 +101 0.382961 0.784494 +123 0.421739 0.620131 +SURF 0x10 +mat 0 +refs 4 +125 0.451755 0.620131 +126 0.392597 0.816713 +62 0.494496 0.828475 +61 0.495246 0.617883 +SURF 0x10 +mat 0 +refs 4 +124 0.434641 0.620131 +103 0.377557 0.840182 +126 0.392597 0.816713 +125 0.451755 0.620131 +SURF 0x10 +mat 0 +refs 4 +103 0.377557 0.840182 +8 0.493985 0.852355 +62 0.494496 0.828475 +126 0.392597 0.816713 +SURF 0x10 +mat 0 +refs 4 +50 0.493513 0.479141 +71 0.495246 0.617883 +120 0.451786 0.620131 +127 0.415813 0.520731 +SURF 0x10 +mat 0 +refs 4 +121 0.417185 0.620131 +108 0.386285 0.504614 +127 0.415813 0.520731 +120 0.451786 0.620131 +SURF 0x10 +mat 0 +refs 3 +108 0.386285 0.504614 +50 0.493513 0.479141 +127 0.415813 0.520731 +SURF 0x10 +mat 0 +refs 4 +122 0.421581 0.620131 +106 0.394159 0.506751 +105 0.393984 0.507058 +123 0.421739 0.620131 +SURF 0x10 +mat 0 +refs 4 +124 0.434641 0.620131 +112 0.413852 0.499508 +106 0.394159 0.506751 +122 0.421581 0.620131 +SURF 0x10 +mat 0 +refs 4 +108 0.386285 0.504614 +121 0.417185 0.620131 +123 0.421739 0.620131 +105 0.393984 0.507058 +SURF 0x10 +mat 0 +refs 4 +128 0.433985 0.527573 +125 0.451755 0.620131 +61 0.495246 0.617883 +48 0.496134 0.472247 +SURF 0x10 +mat 0 +refs 4 +112 0.413852 0.499508 +124 0.434641 0.620131 +125 0.451755 0.620131 +128 0.433985 0.527573 +SURF 0x10 +mat 0 +refs 3 +48 0.496134 0.472247 +112 0.413852 0.499508 +128 0.433985 0.527573 +SURF 0x10 +mat 0 +refs 4 +129 0.583195 0.000667816 +130 0.58223 0.0476314 +131 0.583195 0.0218901 +132 0.583195 0 +SURF 0x10 +mat 0 +refs 4 +133 0.631958 0.00108893 +134 0.631958 0.0229785 +135 0.622451 0.0492137 +136 0.631958 0.00175623 +SURF 0x10 +mat 0 +refs 4 +136 0.631958 0.00175623 +135 0.622451 0.0492137 +130 0.58223 0.0476314 +129 0.583195 0.000667816 +SURF 0x10 +mat 0 +refs 4 +136 0.631958 0.00175623 +129 0.583195 0.000667816 +132 0.583195 0 +133 0.631958 0.00108893 +SURF 0x10 +mat 0 +refs 4 +134 0.631958 0.0229785 +133 0.631958 0.00108893 +132 0.583195 0 +131 0.583195 0.0218901 +SURF 0x10 +mat 0 +refs 4 +130 0.58223 0.0476314 +135 0.622451 0.0492137 +134 0.631958 0.0229785 +131 0.583195 0.0218901 +SURF 0x10 +mat 0 +refs 3 +1 0.509529 0.858464 +4 0.477745 0.858464 +60 0.494647 0.852706 +kids 0 +OBJECT poly +name "bones" +loc 0.0111648 0.99798 -0.104503 +texture "teeshirt_human.rgb" +texrep -1 1 +numvert 19 +-0.00128026 0.22656 0.0769265 +-0.000640289 0.645104 0.0567337 +0.195822 0.645104 0.0116575 +0.424459 0.631817 0.0116575 +0.721283 0.649533 0.0116575 +0.853658 0.653962 0.0116575 +-0.195822 0.645104 0.0116575 +-0.42446 0.631817 0.0116575 +-0.721284 0.649533 0.0116575 +-0.853658 0.653962 0.0116575 +-0.00274499 0.735167 0.0377699 +-0.00274499 0.978763 0.0377699 +-0.00836541 0.00157213 0.0811241 +-0.154718 -0.404162 0.0760983 +-0.17926 -0.924128 0.0313503 +-0.166858 -0.966894 -0.195497 +0.1631 -0.405691 0.0760983 +0.187642 -0.925658 0.0313499 +0.183449 -0.968423 -0.167834 +numsurf 7 +SURF 0x32 +mat 1 +refs 2 +0 0.49925 0.613698 +1 0.499625 0.828646 +SURF 0x32 +mat 2 +refs 5 +1 0.499625 0.828646 +2 0.614696 0.828646 +3 0.748612 0.821822 +4 0.922466 0.83092 +5 1 0.833195 +SURF 0x32 +mat 2 +refs 5 +1 0.499625 0.828646 +6 0.385304 0.828646 +7 0.251388 0.821822 +8 0.0775334 0.83092 +9 0 0.833195 +SURF 0x32 +mat 2 +refs 3 +1 0.499625 0.828646 +10 0.498392 0.874898 +11 0.498392 1 +SURF 0x32 +mat 2 +refs 2 +0 0.49925 0.613698 +12 0.4951 0.498152 +SURF 0x32 +mat 2 +refs 4 +12 0.4951 0.498152 +13 0.409379 0.289783 +14 0.395005 0.0227482 +15 0.402269 0.000785255 +SURF 0x32 +mat 2 +refs 4 +12 0.4951 0.498152 +16 0.59553 0.288998 +17 0.609905 0.0219625 +18 0.607449 0 +kids 0 diff --git a/demos/exposer/data/human.bones b/demos/exposer/data/human.bones new file mode 100644 index 0000000..aced27c --- /dev/null +++ b/demos/exposer/data/human.bones @@ -0,0 +1,38 @@ +NUMBONES=18 NUMEVENTS=1 MAXTIME=10.000000 Z_OFFSET=-0.002791 SPEED=0.000000 +BONE "Human" HPR +BONE "RtShoulder" HPR +BONE "RtArm" HPR +BONE "RtElbow" HPR +BONE "RtWrist" HPR +BONE "LfShoulder" HPR +BONE "LfArm" HPR +BONE "LfElbow" HPR +BONE "LfWrist" HPR +BONE "Neck" HPR +BONE "Head" HPR +BONE "Waist" HPR +BONE "RtHip" .P. +BONE "RtKnee" .P. +BONE "RtAnkle" .P. +BONE "LfHip" .P. +BONE "LfKnee" .P. +BONE "LfAnkle" .P. +EVENT 0.000000 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) diff --git a/demos/exposer/data/human_die.bones b/demos/exposer/data/human_die.bones new file mode 100644 index 0000000..71837b3 --- /dev/null +++ b/demos/exposer/data/human_die.bones @@ -0,0 +1,133 @@ +NUMBONES=18 NUMEVENTS=6 MAXTIME=1.474031 Z_OFFSET=-0.002791 SPEED=0.000000 +BONE "Human" HPR +BONE "RtShoulder" ... +BONE "RtArm" HPR +BONE "RtElbow" HPR +BONE "RtWrist" HPR +BONE "LfShoulder" ... +BONE "LfArm" HPR +BONE "LfElbow" HPR +BONE "LfWrist" HPR +BONE "Neck" HPR +BONE "Head" HPR +BONE "Waist" HPR +BONE "RtHip" HP. +BONE "RtKnee" .P. +BONE "RtAnkle" .P. +BONE "LfHip" .P. +BONE "LfKnee" .P. +BONE "LfAnkle" .P. +EVENT 0.000000 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,0,71) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,-64) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) +EVENT 0.150392 18 (0.000000,-0.462963,0.000000) + (0,-11,0) + (0,0,0) + (0,38,68) + (31,0,-4) + (0,0,0) + (0,0,0) + (0,32,-52) + (0,-2,-57) + (0,0,0) + (0,0,0) + (0,-34,0) + (0,30,0) + (0,18,0) + (0,0,0) + (0,-21,0) + (0,15,0) + (0,0,0) + (0,-17,0) +EVENT 0.299477 18 (0.000000,-0.648148,-0.462963) + (0,41,0) + (0,0,0) + (0,22,28) + (75,0,-4) + (87,0,-3) + (0,0,0) + (0,71,-32) + (0,22,-15) + (0,0,0) + (0,0,0) + (0,20,0) + (0,10,0) + (0,0,0) + (0,0,0) + (0,-21,0) + (0,4,0) + (0,0,0) + (0,-17,0) +EVENT 0.451177 18 (0.000000,-0.787037,-1.157408) + (0,90,0) + (0,0,0) + (40,-43,22) + (135,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,11,0) + (0,-19,0) + (0,-45,0) + (0,4,0) + (0,0,0) + (0,-17,0) +EVENT 0.681740 18 (0.000000,-0.787037,-1.157408) + (0,90,0) + (0,0,0) + (15,-59,22) + (19,-21,0) + (0,0,0) + (0,0,0) + (0,-42,0) + (0,0,0) + (0,-41,0) + (0,0,0) + (30,-20,0) + (0,0,0) + (0,16,0) + (0,-19,0) + (0,-45,0) + (0,8,0) + (0,-4,0) + (0,-17,0) +EVENT 0.994971 18 (0.000000,-0.787037,-1.157408) + (0,90,0) + (0,0,0) + (15,-59,22) + (19,-21,0) + (0,0,0) + (0,0,0) + (0,-42,0) + (0,0,0) + (0,-41,0) + (0,0,0) + (61,-20,0) + (0,0,0) + (25,0,0) + (0,0,0) + (0,-21,0) + (0,0,0) + (0,0,0) + (0,-12,0) diff --git a/demos/exposer/data/human_walk.bones b/demos/exposer/data/human_walk.bones new file mode 100644 index 0000000..16ec55b --- /dev/null +++ b/demos/exposer/data/human_walk.bones @@ -0,0 +1,171 @@ +NUMBONES=18 NUMEVENTS=8 MAXTIME=1.402956 Z_OFFSET=-0.002791 SPEED=1.400000 +BONE "Human" HPR +BONE "RtShoulder" ... +BONE "RtArm" HPR +BONE "RtElbow" HPR +BONE "RtWrist" HPR +BONE "LfShoulder" ... +BONE "LfArm" HPR +BONE "LfElbow" HPR +BONE "LfWrist" HPR +BONE "Neck" HPR +BONE "Head" HPR +BONE "Waist" HPR +BONE "RtHip" .P. +BONE "RtKnee" .P. +BONE "RtAnkle" .P. +BONE "LfHip" .P. +BONE "LfKnee" .P. +BONE "LfAnkle" .P. +EVENT 0.000000 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,-24,69) + (19,0,0) + (18,0,0) + (0,0,0) + (0,14,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,-28,0) + (0,0,0) + (0,10,0) + (0,26,0) + (0,-20,0) + (0,0,0) +EVENT 0.195412 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,-11,69) + (19,0,0) + (18,0,0) + (0,0,0) + (0,-11,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,5,0) + (0,-57,0) + (0,-21,0) + (0,11,0) + (0,0,0) + (0,0,0) +EVENT 0.400845 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,9,69) + (19,0,0) + (18,0,0) + (0,0,0) + (0,-30,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,29,0) + (0,-60,0) + (0,-4,0) + (0,-4,0) + (0,-9,0) + (0,14,0) +EVENT 0.598762 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,18,69) + (34,-5,0) + (18,0,0) + (0,0,0) + (0,-45,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,22,0) + (0,0,0) + (0,0,0) + (0,-24,0) + (0,-9,0) + (0,5,0) +EVENT 0.801689 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,9,69) + (23,-5,0) + (21,0,0) + (0,0,0) + (0,-33,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,10,0) + (0,-9,0) + (0,-45,0) + (0,-16,0) +EVENT 1.004617 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,-8,69) + (23,-5,0) + (21,0,0) + (0,0,0) + (0,-23,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,-16,0) + (0,0,0) + (0,25,0) + (0,38,0) + (0,-53,0) + (0,18,0) +EVENT 1.200028 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,-12,69) + (0,5,0) + (15,0,0) + (0,0,0) + (0,0,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,-23,0) + (0,0,0) + (0,9,0) + (0,20,0) + (0,0,0) + (0,-8,0) +EVENT 1.402956 18 (0.000000,0.000000,0.000000) + (0,0,0) + (0,0,0) + (0,-24,69) + (19,0,0) + (18,0,0) + (0,0,0) + (0,15,-73) + (-21,0,-4) + (0,0,0) + (0,0,0) + (0,0,0) + (0,0,0) + (0,-28,0) + (0,0,0) + (0,10,0) + (0,26,0) + (0,-19,0) + (0,0,0) diff --git a/demos/exposer/data/teeshirt_human.rgb b/demos/exposer/data/teeshirt_human.rgb new file mode 100644 index 0000000000000000000000000000000000000000..998ea4f2e8d4b03adb4f89750cb5554551fadea8 GIT binary patch literal 61392 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 literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/Makefile.am b/demos/exposer/doc/Makefile.am new file mode 100644 index 0000000..b307f82 --- /dev/null +++ b/demos/exposer/doc/Makefile.am @@ -0,0 +1,7 @@ + +pkgdatadir = $(datadir)/games/@PACKAGE@/doc + +pkgdata_DATA = $(wildcard *.*) + +EXTRA_DIST = $(pkgdata_DATA) + diff --git a/demos/exposer/doc/bone.png b/demos/exposer/doc/bone.png new file mode 100644 index 0000000000000000000000000000000000000000..127d7d472c56ec68840dde4825cca6b680243c1f GIT binary patch literal 3210 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 Y-FhC9?50bhe=Zm_)O1vfl}&>G4>5Hl3jhEB literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/exposer.png b/demos/exposer/doc/exposer.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc1f2689e61d2bebfcc9ff93a3c6986fa7cb706 GIT binary patch literal 20543 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⪻lG5Oji|6=;nC)LYD9E=%J z5p?Rg$LlYlrSC`pyl>w-0Mds;7rnGk1%95{ir%S!Sq5d|l=D`^ zeE7b!hE&=CQ|IRjN5 zP^zrvEp%Z9J@Z^wtbhL+y0h6%XRWQNp;i=T^J47V7F%+UWL14pBdnM_)+E#_g-rHv zI1;@Es`vQ|xWxQzEmic=J$vHv{GR7$cCU=l2S)Eu*2!0&U#kJmY*VY&c=P@8b;p(| zgHzgZOkx=h_(JjoF+9WXzBB9&4CI%`(fxLg074QR`2j~P<%)pIGxgU5l0UUO!y2tV zZsvBSXgmj}G2sy}zzjsp5!+}A1;e^aD#nq&Ljs5a9BsikVoWNAXf%lp6ALf|&J{oRHvZP%w~m!X<;%MU;&><0s%}Clf!9+aMQswgI-fm=GRnzWtqEZD|xL|TNfR_ z3R$cQwt8rV4fw%ihpX&25LhqDQT9LhctS^hVEEg?I z^%;0$641gIHX3;8X$M~R84AW4PN2j5In}06*l9{d=8ko&(VHGiV`;t3G0MP;%Z;(M zHQM=4UhV5sLFUyAcysvLLW9SRx>PZRE^*Bw?7%RUeTI` z)zuE=<|}3X#CtEPwE3RzKbW^srA_n)0ONH+lby8!?CZ=^Pb znx_rF^uYKmp?4z4)TF%7{;MT1fu5N&9zDG-SY6MhBp(vk80?|@ z6ECx5VZTrxSDfLQP8Yb8)P-pARD=jS`Y}sAex1#u-Y(8qF|aO7$}|!1_F^4^hW-{7Bi?| z3I@{xY8Y6U3WhGy_{f4q7A_`PxX4Pzj8rnPFxb)gi-N&}4U-)+(ldwqCW(h5Uq{yA zFEEPgZ6=|8gJYh&ad)luuoRq|%+n8Ju*V`KO^RztJ=txuT}kB`^I{7; zQ+T|*Uw{5smoZlB_l{cbakw4Mz)Q6*JDry`hDDEcyJHLP{kU+|yT8BNm!&e%-2jqw zaABJ3Zs1UEUaUKou72yurb~OXc`W+r0uz_(j@OOYXypFI=hOxhH6$MpQH1^U*00Tq zuRgNJ2q_^-4RyZyjkn(DVv+j8niPjpNJ+NWd#JJWe(mn0Q=w2u=6vC)+)_gpOIcH9 zca_g5eTH%Q8MQBq)jw*^oHs-M5XC<4!6(vHZf%|4PjNaI#slx_*irSH$T?R{>i5oB zUgavkG6OL)nBTr$t4v7@2J^R#e(kl-fv9r~kPMwWvj+W*;ZyWOtN>^wWMWf@CJ2`0aP{Tq&e zR6vW?5B$G0V*fezGf6!?J)K9tRwR^Uk8#%|h6lpUt_Q|P^n2}RIpp!M?qgLZ5G~&1 z%~eJ&IOHUJeNstFXV01H&7&xOjpofOS8CnhfCJ&=hl>_3)?U0iaTB;dMU?#hdLRG; zoz|j@ep3@U??vJ-oKK;;O(xch=PS|;s!c6b&4HjixUTb|Ru!@t6 zi#7hBsIEp&b$S|iNja%19vOWx4k1O8+%vO%XKJlB6qM(OoLZqUZsl{05&I`kB?Tbh z4RKTdM0=m@sLwpp+SB85P3!4=X5L>OdG;TdEtbO_OOxt58()d<=;&yxxCUN2V9o&C zFq^req63(!XGc2(;bMWfy|KO%Kz--T_E!V~ff(zy&gkguboJCvm9}HudlGU| z6*FK%$kTvdU_3(1mSJu%voHaMtX5VsVZa0f#vDKrLC_c=#dH9HSS6%D;Bh9Hjlee8 zxiQ)Qxc>r)IzR+~2>?{s2mo8_0FYmGI)MG*S%3~PL}?+*x)To*0j rmi~`M!H@CL%Pkx?3;1b@AMgDWIR`5(n#wHh00000NkvXXu0mjf1@)<0 literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/index.html b/demos/exposer/doc/index.html new file mode 100644 index 0000000..4233d66 --- /dev/null +++ b/demos/exposer/doc/index.html @@ -0,0 +1,451 @@ + + + + + Exposer Home Page + + + + + + + + + +
+Exposer Logo +
+
+
+

+ EXPOSER: + A + TOOL FOR + ANIMATING + PLIB + CRITTERS. +

+
+
+
    + The foot bone's connected to the leg bone,
    + The leg bone's connected to the knee bone,
    + The knee bone's connected to the thigh bone,
    + The thigh bone's connected to the back bone,
    + The back bone's connected to the neck bone,
    + The neck bone's connected to the head bone,
    + Oh, hear the word of the Lord!
    +
    +-- Unknown (but incompetent) anatomist. +
+
+ +Sourceforge Logo +
+
+ +

Introduction.

+Exposer is a tool to allow you to perform fairly complex animations +of complex creatures with relative ease using the + PLIB Scene Graph API. Naturally, +Exposer is written using PLIB and generates output in several forms +that PLIB programs will find useful. +

+Exposer is totally unrelated to the Windows program 'poser'. The +name 'exposer' relates to the fact that this program used to be +called 'poser' until I heard of the existance of a Windows program +of the same name. This is therefore the ex-Poser program. +

+Exposer has a complex (but very powerful) GUI - you WILL need to +read this manual in order to take advantage of it. +

Licensing.

+Exposer is OpenSource software, issued under the terms of the +GNU Public License (GPL). +

Installing and Running Exposer

+Download ExPoser from the PLIB site: + +http://plib.sf.net/dist/exposer-0.0.1.tar.gz +

+Next, unpack the tarball containing the Exposer sources: +

+
+   tar xzvf exposer-0.0.1.tar.gz
+
+
+Then run the usual: +
+
+   ./configure ; make
+
+
+(And you can optionally run 'make install' if you wish to fully +install the program rather than running it out of the installation +directory). +

+Then to run the program (without installing it) you could type +something like: +

+
+  src/exposer
+
+
+The program will prompt you to enter a 3D model file - the +ones in data with the extension '.ac' will do for starters. +

+Exposer can read any format that the PLIB library supports +that can contain both line and polygon objects. + +

So What Does It Do.

+Well, right now, not a lot. The idea is that you can take an +arbitary polygonal model (typically of a person or animal of +some kind) and with minimal additional modelling effort be able +to animate it in your game or other interactive application. +

+The technique that exposer uses is 'skin and bones' animation +where the polygons of the model form the creatures 'skin' and +you model a set of line primitives inside the creature to make +a crude skeleton (the 'bones') much like the armature that a +sculptor would use. +

+Exposer therefore separates out the polygons and the lines in the +model you load (because your game will only be using the polygons) +and uses the lines to form a hierarchical structure of matrix +transformations. Exposer then assigns each vertex in the +skin to one of the bones (possibly more than one in the future) +according to distance - and then moves each vertex in accordance +with the matrix of the bone to which it's attached. +

+Exposer can optionally display each bone as a coloured cuboid and +provides a Heading/Pitch/Roll dial for each of them - but typically, +you'll only want to view the 'skin' - and that's the default. +By playing +with the coloured dials, you can move the bones in any way you like and +the skin will try to follow that movement as smoothly as possible. +

+One important issue is how the lines are assembled into a bone +heirarchy. Firstly, exactly ONE of the lines in the original +model must be painted RED. This bone is special because it's +the root of the bone hierarchy. Typically, this will be a +bone in the spine of a human or other mammal - but you might +choose it to be something else. You can "Make the Tail Wag +the Dog" by making the tailbone be the red one! +

+Once the program has found that red 'root' bone, it looks +for other lines that have one or other of their vertices +touching the root bone. Other lines can connect to the +root somewhere other than at the ends of that red line, +but your non-root bones MUST connect to it at their ends. +

+The program allows a 1% tolerance on this positioning so +bones don't have to be EXACTLY touching - but they do have +to be VERY close. +

+So, now we have the root bone and some other bones connected +to it - and now the program proceeds to expand out the +skeleton by treating all of the newly connected bones as +'root' and proceeding recursively from there. +

+In a sense, it's like reading the lines of the song at +the top of this page backwards: +

+   The back bone's...the RED bone,
+   The thigh bone's connected to the back bone,
+   The knee bone's connected to the thigh bone,
+   The leg bone's connected to the knee bone,
+   The foot bone's connected to the leg bone
+
+...although if it were me, I'd want a pelvis-bone and for +the sake of graphics, there wouldn't need to be a knee bone. +

+So, that's more or less it. + +

Controls.

+Having loaded your first model, you'll see something like this: +
+
ExPoser screenshot
+
+The rather nice shade of green is my favorite colour. If +you prefer shock-pink...bad luck - that's what source code +is for! The 3D model sits in the middle of the screen with +the GUI widgets floating in front of it. +

+There is an optional black and white chequerboard 'floor'. +

+Let's go through the GUI controls piece by piece: + +

The Menu Bar.

+In the top-left, there is a pretty standard menu bar, +
+
Menu Bar
+
+The 'File' menu allows you to replace the model you are +working on with another, it also allows you to load +and save 'bone' files - which contain everything you +create in Exposer. Generally, these Bone files have +the same filename as the 3D model but with the extension +".bones". Exposer will fill that filename into the +file selector box - so you probably only have to hit "OK" +to load and save. +

+The 'View' menu lets you zoom into and out of the 'timeline' +which we'll come to later. The 'Time' menu has other +'timeline' operations. + +

The Bone Dials

+To the left of the window are the bone dials - each coloured +row of GUI widgets represents one of the bones in your model. +
+
Bone Dials
+
+The button marked 'X' resets all of the angles of the bone +to zero - which will always be how your model was posed when +you created it. The 'H', 'P' and 'R' buttons let you freeze +and hide any of the three dials that you don't want to use +for that particular bone. This is useful for joints like +your elbow that can only flex in one degree of freedom. +

+The three dials represent the heading, pitch and roll +angles of the joint at the end of that bone nearest the +top of the bone connection hierarchy. +

+The rectangular area above the 'X/H/P/R' buttons can +be used to enter a short text string to identify the +bone. These strings are saved when you write out the +bone data and your application program might want to +read them to help identify what's what. +

+If you have more bones than will conveniently fit on the +screen, the scroll-bar to the left of the bone widgets +will allow you to scroll them up and down. + +

The Body Translators

+Just below the meny bar, there are three sliders with +text display/entry areas that allow you to bodily +translate your model. +
+
Body Translators
+
+You should generally use this +for things like adding 'bounce' to a character's walk +sequence - it's not advisable to try to position your +character within it's world using this feature. + +

View Control

+In the bottom-left corner of the screen are some +view controls. These can be used anytime to change +the view you have of the model - but they don't change +any actual data in the model or it's bones. +
+
View Controls
+
+You can also use the 'Zoom' slider to move the model +closer or further away, and there are buttons marked +'Skin', 'Bones' and 'Floor' that let you toggle the display of +each of those things - so you can see just the skin, +just the bones or both (Or neither if you are +particularly perverse!) - with or without the floor +plane. The initial default is to show just the skin. +

+The 'Pan' and 'Tilt' dials allow you to move your +eyepoint around the model to view it from all sides +as it animates. + +

VCR Controls

+In the bottom-right corner of the screen, there +are some VCR-like controls to play the animation, +pause it, play in reverse and to play at triple-speed +in forward or reverse. +
+
VCR Controls
+
+You cannot adjust any joint angles in the model when +the animation is playing - so be sure to press the +downward arrow (STOP!) when you want to do editing. +

+There is a control to make the ground translate +along at a specified speed (in OpenGL-units-per-second). +This is useful if (for example) you want to animate +a running character that's you'll be moving at two +meters per second in your game. You need to move +the legs so that the feet move along at the same +speed as the ground or else it'll look like +'moonwalking'. Set the VCR's ground speed to match +the speed in the game and you'll be able to see if +the animation is going to work. + +

The TimeLine

+This is a large rectangle taking up most of the bottom +edge of the screen: +
+
TimeLine
+
+It has a number of functions, it starts up as a view +of a 10 second animation. Seconds are indicated by +the long tickmarks, tenth seconds by the short ones. +You can zoom into or out of the view or reset to seeing +the entire timeline using the 'View' menu in the menubar. +

+If you are zoomed into the timeline, a slider will appear +to allow you to scroll it left and right. +

+Magenta lines that extend across the full height of +the timeline indicate "events" - each event is a point +in time at which you have set the bone joint angles. +You can add an event by selecting 'Add an Event' in +the 'Time' menu and then clicking somewhere onto the +timeline to position it. One they are positioned, you +can click and drag events up and down the timeline to +change when they happen. +

+To change an event, click on it and it'll flash to +indicate that you have it selected. Then you can +adjust it using the bone dials. A common problem +is to try to edit a position without selecting it +first - make sure that the event is flashing on +the timeline. +

+Various operations on the 'selected' event are +available from the 'Time' menu. +

+You can also select a 'region' by left-clicking +on one end and right-clicking on the other. The +current region is highlighted in yellow. There +are operations that operate on the current region +in the 'Time' menu. +

+Wherever you click the left button on the timeline, +the model will move to that position - and the bone +dials will follow. You can drag the mouse up and +down the timeline to replay the animation at whatever +speed you move the mouse. However, to get a 'realistic' +playback speed, use the single arrows on the VCR +menu. +

+Remember though that you can only EDIT the position +of the model on a selected event. + +

Menu Operations on the Timeline.

+The 'Time' menu allows you to add an event, +delete the selected event, delete ALL the events +in the 'region' that you have highlighted, +or delete the section of time in the 'region' - +compressing up the rest of the timeline to fit. +Finally, you can add more time to the timeline +so that you can create animations that are longer +than the ten seconds initially provided. + +

The Exposer ".bones" file format.

+This is a very simple ASCII text file - however +the parser for it is VERY crude at this stage. +

+The first line of the file is: +

+
+  NUMBONES=17 NUMEVENTS=7 MAXTIME=10.0000 Z_OFFSET=1.3 SPEED=0.3
+
+
+So there are 17 bones, 7 events - and the +duration of the animation is ten seconds. +

+The Z_OFFSET is advice to the application about the +height of the origin of the model above the lowest +point on the model in it's default pose. +

+SPEED is the speed set on the VCR 'Ground Speed' +control - which the application can use to tell +at what speed the character should move forwards +in order to make the animation look right. +

+Then follows the bones - one per line: +

+
+  BONE "bone-name" HPR
+
+
+The 'bone-name' is the string you entered beside the +bone dials. The 'HPR' part sets which axes are frozen. +(in this case, none of them...the frozen axis letters +are replaced by dots). +

+After all the bones come the events. For each event +there is a header line: +

+
+  EVENT 3.0000 17 (0.000,0.000,0.000)
+
+
+The first number is the time (in seconds) at which the +event occurred, the second number is the number of bones +that are affected by this event. Currently this must be +the same as 'NUMBONES'. The coordinate in parentheses is +the body-translation of the model from the origin at this +moment in time. +

+After the event header, there is one line per bone +containing the three joint angles in degrees for +that bone at that time: +

+
+  (90,45,37)
+
+
+That's it! However, as I said - the parser is 'brittle' +and very intolerant of errors - edit the file at your +own peril! + +

Support Programs.

+ +In the ExPoser directory, you'll also find a short +program called 'merge_bones' - this takes a number +of '.bones' files (listed in order on the command line) +and concatenates them end-to-end - placing the results +in the file 'out.bones'. +

+On stdout, it lists the header from each of the component +files. It is (of course) necessary that the '.bones' +files originated from the same 3D model - merge_bones +checks that things are consistant and warns of errors. + + +

Some Advice

+Bones don't necessarily have to be placed where the +bones would be in an anatomically correct version of +the critter. In the example 'dog.ac' file, there is +a tongue-bone and two ear-bones - and there are no ribs, +spinal vertibrae, etc. +

+You'll also probably need to hop back and forth between +your modeller and exposer to position the bones how you +want them. You'll find on occasions that the algorithm +that causes exposer to attach a skin vertex to the nearest +bone will sometimes result in part of a character's stomach +being attached to their hand or something. If that happens +then you might try adding some lateral bones +in the stomach to ensure that there is always one attached +to the spine that's closer to the skin than the hand is. +

+Artists should be encouraged to model the character in +a position with their legs wide apart and their arms +fully outstretched to minimise this kind of problem. +

+It is normal to model the character facing down the +positive Y axis - facing away from the camera. +

+Using the axis freeze buttons to isolate only those +axes through which a real joint can rotate is very +useful. A human elbow joint can really only +rotate through one axis and turning off the other two is +helpful. +

+You can also use Exposer for things other than animals. +You could model plants bending in a heavy wind - or +even flexible things like flags or clothing. + +


+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/demos/exposer/doc/menu.png b/demos/exposer/doc/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..ef104ee07ecfad1e0c51b6b61a802616b9163cb0 GIT binary patch literal 329 zcmV-P0k-~$P)Z|YP&)|}C`Aa-Q~x2Dmv38f0wC%D98(7=?nT3zcY&&JyQ-%yY{5v> zXHBE5%QQj+@X*|pF;{@s>F!#Ujml$YpT2131ikbM3Y{H1pII}~FLjVCRPG~2$0LK$ z(bGVXk=QF~szeGgIL|_;O(dQ%W)YW1yPtLTAI)&7dZ)#<07d*Yak5dNomxmAOL{^$ zCgZ#cqYyIDLI{o4Qsj)lTqRmGrBREaw`FW(z4e9HtHJ;wwnKqz1lFe=`o68l+PrsM b$sbr=dINW;053V!00000NkvXXu0mjfsfmUO literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/timeline.png b/demos/exposer/doc/timeline.png new file mode 100644 index 0000000000000000000000000000000000000000..9ecf7cc6cdf3ee1fe65794ce0ae0d4070eda3d47 GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^O+cK+!py+H*z`Z;43NW;?&#~tz_78O`%fY(kS`kG z6XI&zV0`Axng0y`fdVlzHLqP;3}i{q)|m&?$L#6i7*cWTZA2hnlL1d^$A9Ix1O2j% z!aBEkU;W?nUp41?;|2j9E|v}b6U_RbAHEVK)xD_yl%#mr36Zo3sf!CbescP#)y?o( ze28Dl<}lltufmIGE?jTdJhRs*RQP>P@=ZUd13@j9uIz|OQF-Os;TphaeSOz9ky!rD zr2kwM+JCNbEv@L#2>W5;ZQ%5v`*`3R?sBdA$32B+|30j%j_-65StM|D7nf9LRC0z4 z!+B4+*(NTo8z()g5BT45W&cg3?uSC(RlU2txCEEIeLumL@sR=_1l_kfu!Fhd@JU${ zxvqbKFW8GdY`OB3(Sju`t5Bdl)~ZME&i3Ob^ZuUw^P_^X$$;1P+7WgUVDK<_y85}S Ib4q9e08%)WJOBUy literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/translate.png b/demos/exposer/doc/translate.png new file mode 100644 index 0000000000000000000000000000000000000000..3940beb53cda4502e0b679791e13637b59726697 GIT binary patch literal 2474 zcmV;b303xqP)K0rIC@Qk&&m8lBkoDsg#tem6fZOmaLYRte2Oqn3%4Zn68DTEsKBYI z!Ktajs;a`PtHZ0So{Nj1jg6y@j-`;0rjnAVl$5QPm#>+bwx6H3prE;v9Z3UrpT|a$+5A^va-yx zvj6}9u$!CBw6xB(wa>P;(6_hIxVXx)veLP^)4ICUySvrAyw<(F*S@~kzrWeQz}msV z+rq-!!^7Rg#NNfl-^Rw^$Hz=eOtZ7IXJ=<3}bii(PijEswmi;j+t zjg5_vkdTj$kCKv-k&%&+(LA=}k()~W)nSlWY z@>Qv2^ZW|)`po>&N)S^qhP}bt9U1H8rEvVZ5w2nyoABr%X{GSXG?H>1CeMX&2NXg7ADyvhy)J#rmXXo&e-e9uW*qdS$)y;&c zZ-9^*%@&)>;aqoneEvA*W4Dx=Rar|kYFOWVTpsD|Y*; z!|7Vz2woYupr^NAJvcNnI*v@_tMFa&Rj13og*=-+pVz;&v%R;sdmtF^bMXm@De0Lx zdHF?(vg*1<6?!?Lc{MpTJ)7LLxRPvwCU%Z@rg`Cli!jjTE%K8==Z6@s|+e~4H zRstt0XYLCopp7(C#$EyEO0K}m4&h;~2QRFAR2*zZPhY=UMmxa|47Pb`CtwO-2!a<@ z2q$M~cw|h*jTqZI?A~Xu*n)=a9t1D^)aj89=bJwq=5!9{`zYB_3VlXJ+Ex5-m+mDc zr>13G%gWCA`})m-e~L?P-7c?qTJx;og{rl+t-W1_UAFNA&&|&-EG#ZA z;;A`<4H*nZqtOI&X@Nl_I|>G2mFyl7Mdmp}gl-fR5~0$^71czjQKbT*j?S*G?w+3B zJ{bZDPJ@i_GR&j_Bur+!XtKKzXn+K0I9)E7(G9m7)|{AP2?Ih;h)@eB)X56<_p1j6 z2Zx4-Wr&jy!N3;B<~E`d8&qNwwv^ZJhwkN4J97MTVnUHBlQPKY!#{uq;Abg^ep4eiSAp zbXm-Wj^L@%wGrXwfwe*1T#i9Wv{l&Z^z9*{=BWmO0WOcleN;#*;I;MZL*tM~H(m^H5eED!@dc91fZV=#F-|LJ%27@Jc0!n98K3XD}>T4o6lH zAiK?yDZ|Lxcrp?!No*h~1rWs>#X&7`P@#>&SUb05FxysL2t{kuY6ueqQkp&>RJq;Z zU1UVMn8gq>2FUVRvJxOG1G1;en!2X?hQ=2nWL+#7$wYmS!PI~#-Y6D|X9{Z+#`DWY z%Zg)T6QO90a`8+ol!rz*`VR-mabbvz*r48I2pLmcTcWrHWOrDyXJKURqGXWx21vpr zsSgmv8x`}3!Wf3s>Go|Q6s=Jn9?D0dXb`5Ukid$rUg=CGPWE~t?wTNa;pOH&VJ0d3Phe`p?w68|Li4yE*&mCd!~n o=|)O7Qo8vXR=-qINp;%wKLSk88)0FxR{#J207*qoM6N<$g4PA&!2kdN literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/vcr.png b/demos/exposer/doc/vcr.png new file mode 100644 index 0000000000000000000000000000000000000000..ed330ad61ac3988119b4ade5ea555df667154370 GIT binary patch literal 2251 zcmV;+2sHPJP)< zlarH_l$4c~m6n#4mzS5An3&AW%(A9JW?5PP=;)h>h@6Ruor;Q|i;JL)jG>KDQDsJWw~z^SRhs;a`PtGcA5y``nU zr>DTEsKTnM!>g;rtgObZt-hwF!Ktamt*ytduE?*i$*{1so}R|8uFA2o%d)b}v$Myq zugI{l&9t=6wYAByvCp=)%CfT1x3|%_xYD_~#jLE;y1LZ6yVbnB*1f&gzP{MMzuCaR z+QGrw!ou9c!`;Nh-o?e=#>U{s$KlAx;>pS5%F5)+%jL|>rr~;2000JxNklotN;R% z8-mDI<^vFf%88baS7C+I-axJ73)Uf9? z=n*sv9v_262o+UmLK=&_5U8w`-1sO|69eKvK?<>FWv$Y3?ZLC2*Wt*}NK8ne zmvZ$7Bp*Inw~yFEB^-$k$Hph8=N6VSmwd<9{9%=(4IrC?ET0%1+72f%&PVv8eg|mZ zH8&qTETt?H6ar5vPdKm%vF6yAx% z`vL9eFC&sM3>Xkt(;)yC(IGwz9HyemG;xOFgbM@-gKj2cg?=6K7)0pN9#(!hc=+hq z#*^nSHn&`DYwvjU_{poceW8Avg;e6kz>m|qvYK${WwTs_II0Hqh)n3im4QO##3|If ze!IP+>t6TM-q)dUeiynx37TU$9dpmX;2*e2ncs%xmqVT!m$GJ(lJFQdgEO z8yA=Ck~8mB7MFTiwsCCzVvuxTqNuFD@%XRJ`i+}5Z`rzS`;Nk$yNXFVFok7b>{mk4 zfhmZJ{YptXFa=SgAC#1q69a+)`4_F&fVzkQL4zrXG7;feNBQh=+7k4#5TjgX)imjvV{x=W`d%G+h$EbT*=V)MC0( zuc#uDp=m(+s@bR_TW<5PdihEtRcxh-cTsVGe_L%t^#lS`RMilPr?P?ywF^-3T_~(6#@L=^X4Xbj z?6;q!165%gNViE$ac+LmMpW$gIY|eqtXQpz3rp|+P8208*VMJG*;-p`|D#I{Iq{FO z(+1Bn6)W;bIz9B zt$Ew>w-@Yi7Z&aG?DFEVdzDz>za&Jb`oTrk@@bx5>~GEDoI2?h`)E9?!)D?pUXq;j zO8hjQ)nPL+>m|uaue6NDW5Yr`3zn9^E<3Wuf|bpTQ^5%U)5G07>fwQmSXde>KS#y0 zqETNw%l>Nd8OPF*@rTGNDYs;t3O)gtPdqB2ULZ*05quhE5?RwIk5yz1%5ajvTHYXK z74^%nzW(OhgNF_uIeP4PL*t2)r%r$O{h6kwmzSN!F z-ky3I&+4#>+74K(kT4+yk1^(P63_nAGs!u1(%ZX_#tK8c1# Z{{fcgc6;LgP|pAW002ovPDHLkV1n@Vgb)A# literal 0 HcmV?d00001 diff --git a/demos/exposer/doc/viewer.png b/demos/exposer/doc/viewer.png new file mode 100644 index 0000000000000000000000000000000000000000..0e1a2e4d0c847adaddef2fd5b309a2bdcc91685d GIT binary patch literal 2764 zcmV;-3N!VIP)pc00004XF*Lt006O% z3;baP0001`P)t-sGk`O)rb0|iOtZ7IXJ=<?l!%CkmX?-^ii(VkjE;_u z|NsAxkdTs+l9Q8@m6es3mzS8BnC1fU`Tzh4(n&-?RCwC$S_yaRxDF=ry@j?EC_7!g-H!8y@YUMy=ZqMw3A}%oVDwj{ZXr5oqJ-e1CXlp)K_vhCG3-)SWy$cSqG0 zFE{Aepob?sZTjK1J>qGLd9z3FKJy37^U~lYh+gIoTh25n|5PYGm#d9tt=(<)>w!P& zOsJCMLaAD71@?*YWQR0OND{Pg&gsz(==AIp<0<_GQ1rkufIGeAkk}4g z4&9JvZuk8Thg7V#c{E#5iQDy2FC@t2<#5CL)YB(BL|)@+yxqT0&>0M;`&aTO%i(l) zAh0pdwI_PY{C&;)xWn_#-n;kVtMxK}Z=-jPh4^JAp6rNR-pAT1>EeplR*&xPxma#; z-M3FHPj*DH5uWkd6-M`Y8PKisM4uQ>c0|*kpYdWf&gaBBz@IQVT@$t=0-`hC{o?TQ z`bKWRfc1&-bnQG8yw;?_ipdRK*%8iKe_|dAYX3Tp7(+LOvB6kpWtc3Z7}zg`al-yaeEs@&!7AKA8A zw^a{PX$c*Vc~ffj=&IaNr!$e$U^W;mmqa3s^KxR9t4)z6JF%0XsRpGbbd<{mAs{rRQp+)^7C!pG+lt|M*mVwoxTfC7XMY zi2y^l4+1+3%qnfT=9uO*fEMe>YbawGMjbij$vi%>oT(bN_69`wS#E&AA4&W|a&D?L zW-D#O^CKUr0Fa<+SRa_PTTvg{yHu5 z)BeRi(EEZB-Ul8o@`B!9h*Q0bF!;tW*ZV6_&Mf){9huMS_-X%mGvC${IyCe#x%(*g z(ah}Fy=RWXB6hN;TuzF2^~3;Ew$(t=^9YEkP3%j+W5z zXz~r8Zjo-|6=SmZ9H|hCS+ceSO`No&C3F-ugu6w$Mnh|3ViYc2qih;} zNViD0p_VbHDn=ciC1`e{KuhRoYM6M7^yyY2xt%d*Dtf$(X-)-b2_0SIhjY3`x{VV> z-l>WihcOLVxuYd?1R5jUBK`0pN%fn^J5{j^Ii@imX@9f_Ia*O{#jQA%c6+p&!ody& zvlX}InC4J`7H2p}O*9@wL8C9>pzJ5ax23YJIuf?m;}d>2HCz;VXDY^gq#ad^6FPDa z7$0dsL@H}Nj;K35GOz(rIU?>6_JTVsB;Ix6wntj5VjJs_fVsp`$gUx+F);{xXvk|iuC-L)<7O4R3sH0e7mY7*G69mx%UJZ>w!X*;LP7a4;a0bb;m#NWs z)|j^2W*mvzA8E0QeXOHg#Tiyt0RveLB-ZL8waY1j7P2kFNreC<#BX+Q0Y^PA7g_ii@C``Gx9+mp zY);xeN*X9?psaz9I!}cH$`2?Jl%AhaB|s*RFENtMBcGR)8%a0PZhSnE5(D5+LKcT}`EL=Szhl!;Goi7s|Cd(HEKSBmYJQE?F|nI~u2E6;Lh_ zN&3SX=lk#QMOYGisW8NHN&JAKL-a+aPmtnEj*l!K<{gdGl9C1CW>}IQ7&q#?!{sm z8n+roFs_5Od?_ycccmg{bxyseZmwtC)N1Hm1{A7;04U&GvtWwva=D9wFw*wA?x^1} zSnqu9i2);N+ekE*QR$pZ}S#cN_cA^W;$4iV3|R|psy`ZR4f7QXb$Bn&vzn};w@XP9Fr zCddDoan`?_@}+?QUy2LI1j{;-w}6)r8L$j_#j>C9!3Hoa1UX^E#ty5Aa%kXIFJQR0 z)bDGz_R9e;0>;WA=0zJ@jZ+j!;~3wA4i|hEAR@jr7jE}rZ~`AM0TyiW#R)LtYop2g zozlGrjI+&P`z-@F#Jt3Ef+v>ud|c}~!}z9k!FReR5_~Bx+>NsrxRzlUVc*Wz S5xJQF0000 + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/demos/exposer/src/Makefile.am b/demos/exposer/src/Makefile.am new file mode 100644 index 0000000..83e9ec2 --- /dev/null +++ b/demos/exposer/src/Makefile.am @@ -0,0 +1,22 @@ +# src/ + +bindir=$(prefix)/games + +bin_PROGRAMS = exposer merge_tweens + +exposer_SOURCES = \ + exposer.cxx exposer.h \ + floor.cxx floor.h \ + vertices.cxx vertices.h \ + boneGUI.cxx boneGUI.h \ + load_save.cxx load_save.h \ + timebox.cxx timebox.h \ + bones.cxx bones.h \ + event.cxx event.h \ + model.cxx model.h + +merge_tweens_SOURCES = \ + merge_tweens.cxx merge_tweens.h + +EXTRA_DIST = exposer.dsp + diff --git a/demos/exposer/src/boneGUI.cxx b/demos/exposer/src/boneGUI.cxx new file mode 100644 index 0000000..ae784f6 --- /dev/null +++ b/demos/exposer/src/boneGUI.cxx @@ -0,0 +1,305 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: boneGUI.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "exposer.h" + +static puSlider *XtranslateSlider ; +static puSlider *YtranslateSlider ; +static puSlider *ZtranslateSlider ; +static puInput *XtranslateInput ; +static puInput *YtranslateInput ; +static puInput *ZtranslateInput ; + + +static void jointHeadingCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + float a ; ob -> getValue ( & a ) ; + + bone->setAngle ( 0, a * 360.0f - 180.0f ) ; + setShowAngle ( a * 360.0f - 180.0f ) ; +} + + +static void jointPitchCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + float a ; ob -> getValue ( & a ) ; + + bone->setAngle ( 1, a * 360.0f - 180.0f ) ; + setShowAngle ( a * 360.0f - 180.0f ) ; +} + + +static void jointRollCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + float a ; ob -> getValue ( & a ) ; + + bone->setAngle ( 2, a * 360.0f - 180.0f ) ; + setShowAngle ( a * 360.0f - 180.0f ) ; +} + + +void hide_headingCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + + if ( ob -> getValue () ) + bone -> sh -> hide () ; + else + bone -> sh -> reveal () ; +} + + +void hide_pitchCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + + if ( ob -> getValue () ) + bone -> sp -> hide () ; + else + bone -> sp -> reveal () ; +} + +void hide_rollCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + + if ( ob -> getValue () ) + bone -> sr -> hide () ; + else + bone -> sr -> reveal () ; +} + + +static void resetCB ( puObject *ob ) +{ + Bone *bone = (Bone *) (ob->getUserData()) ; + + bone -> setAngles ( 0, 0, 0 ) ; +} + + +void syncTranslators ( sgVec3 trans ) +{ + XtranslateSlider -> setValue ( trans [ 0 ] / 5.0f + 0.5f ) ; + YtranslateSlider -> setValue ( trans [ 1 ] / 5.0f + 0.5f ) ; + ZtranslateSlider -> setValue ( trans [ 2 ] / 5.0f + 0.5f ) ; + + if ( ! XtranslateInput -> isAcceptingInput () ) + XtranslateInput -> setValue ( trans [ 0 ] ) ; + if ( ! YtranslateInput -> isAcceptingInput () ) + YtranslateInput -> setValue ( trans [ 1 ] ) ; + if ( ! ZtranslateInput -> isAcceptingInput () ) + ZtranslateInput -> setValue ( trans [ 2 ] ) ; + +} + + +static void currTranslateTxtXCB ( puObject *sl ) +{ + float v = sl -> getFloatValue () ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 0 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + + +static void currTranslateTxtYCB ( puObject *sl ) +{ + float v = sl -> getFloatValue () ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 1 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + + +static void currTranslateTxtZCB ( puObject *sl ) +{ + float v = sl -> getFloatValue () ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 2 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + + + +static void currTranslateXCB ( puObject *sl ) +{ + float v = (((puSlider *)sl) -> getFloatValue () - 0.5f ) * 5.0f ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 0 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + + +static void currTranslateYCB ( puObject *sl ) +{ + float v = (((puSlider *)sl) -> getFloatValue () - 0.5f ) * 5.0f ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 1 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + + +static void currTranslateZCB ( puObject *sl ) +{ + float v = (((puSlider *)sl) -> getFloatValue () - 0.5f ) * 5.0f ; + + if ( eventList->getCurrentEvent() == NULL ) return ; + + sgVec3 xyz ; + + eventList->getCurrentEvent() -> getTranslate ( xyz ) ; + xyz [ 2 ] = v ; + eventList->getCurrentEvent() -> setTranslate ( xyz ) ; + + syncTranslators ( xyz ) ; +} + + +void initBoneGUI () +{ + puText *message ; + + ZtranslateInput = new puInput ( 5, 485, 80, 505 ) ; + ZtranslateInput -> setCallback ( currTranslateTxtZCB ) ; + + ZtranslateSlider = new puSlider ( 80, 485, 120, FALSE ) ; + ZtranslateSlider -> setCBMode ( PUSLIDER_DELTA ) ; + ZtranslateSlider -> setDelta ( 0.01f ) ; + ZtranslateSlider -> setCallback ( currTranslateZCB ) ; + message = new puText ( 205,485 ) ; message->setLabel ( "Z" ) ; + + YtranslateInput = new puInput ( 5, 505, 80, 525 ) ; + YtranslateInput -> setCallback ( currTranslateTxtYCB ) ; + + YtranslateSlider = new puSlider ( 80, 505, 120, FALSE ) ; + YtranslateSlider -> setCBMode ( PUSLIDER_DELTA ) ; + YtranslateSlider -> setDelta ( 0.01f ) ; + YtranslateSlider -> setCallback ( currTranslateYCB ) ; + message = new puText ( 205,505 ) ; message->setLabel ( "Y" ) ; + + XtranslateInput = new puInput ( 5, 525, 80, 545 ) ; + XtranslateInput -> setCallback ( currTranslateTxtXCB ) ; + + XtranslateSlider = new puSlider ( 80, 525, 120, FALSE ) ; + XtranslateSlider -> setCBMode ( PUSLIDER_DELTA ) ; + XtranslateSlider -> setDelta ( 0.01f ) ; + XtranslateSlider -> setCallback ( currTranslateXCB ) ; + message = new puText ( 205,525 ) ; message->setLabel ( "X" ) ; +} + + +void Bone::createJoint () +{ + widget = new puGroup ( 0, 0 ) ; + rs = new puOneShot ( 0, 0, "x" ) ; + hb = new puButton (20, 0, "H" ) ; + pb = new puButton (40, 0, "P" ) ; + rb = new puButton (60, 0, "R" ) ; + sh = new puDial ( 80, 0, 40 ) ; + sp = new puDial ( 120, 0, 40 ) ; + sr = new puDial ( 160, 0, 40 ) ; + na = new puInput ( 0,20,80,40 ) ; + + na->setUserData ( this ) ; + na->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + rs->setUserData ( this ) ; + rs->setCallback ( resetCB ) ; + rs->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + hb->setUserData ( this ) ; + hb->setCallback ( hide_headingCB ) ; + hb->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + pb->setUserData ( this ) ; + pb->setCallback ( hide_pitchCB ) ; + pb->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + rb->setUserData ( this ) ; + rb->setCallback ( hide_rollCB ) ; + rb->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + sh->setUserData ( this ) ; + sh->setValue ( 0.5f ) ; + sh->setCallback ( jointHeadingCB ) ; + sh->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + sp->setUserData ( this ) ; + sp->setValue ( 0.5f ) ; + sp->setCallback ( jointPitchCB ) ; + sp->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + sr->setUserData ( this ) ; + sr->setValue ( 0.5f ) ; + sr->setCallback ( jointRollCB ) ; + sr->setColourScheme ( colour[0], colour[1], colour[2], 0.5f ) ; + + widget -> close () ; + widget -> hide () ; +} + diff --git a/demos/exposer/src/boneGUI.h b/demos/exposer/src/boneGUI.h new file mode 100644 index 0000000..2763718 --- /dev/null +++ b/demos/exposer/src/boneGUI.h @@ -0,0 +1,32 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: boneGUI.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +void hide_headingCB ( puObject *ob ) ; +void hide_pitchCB ( puObject *ob ) ; +void hide_rollCB ( puObject *ob ) ; + +void syncTranslators ( sgVec3 trans ) ; +void initBoneGUI () ; + + diff --git a/demos/exposer/src/bones.cxx b/demos/exposer/src/bones.cxx new file mode 100644 index 0000000..dc56724 --- /dev/null +++ b/demos/exposer/src/bones.cxx @@ -0,0 +1,278 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: bones.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "exposer.h" + +static sgVec3 curr_translate = { 0.0f, 0.0f, 0.0f } ; + +static ssgSimpleState *boneState = NULL ; +static Bone *bone = NULL ; + +ssgSimpleState *getBoneState () { return boneState ; } +float *getCurrTranslate () { return curr_translate ; } +Bone *getBone ( int i ) { return & ( bone [ i ] ) ; } + + +void init_bones () +{ + if ( bone == NULL ) + bone = new Bone [ 1000 ] ; + + sgZeroVec3 ( curr_translate ) ; +} + + +Bone::Bone () +{ + parent = -1 ; +} + + +void Bone::read ( FILE *fd ) +{ + char name [ PUSTRING_MAX ] ; + char shb, spb, srb ; + + fscanf ( fd, "BONE \"%s %c%c%c\n", + name, & shb, & spb, & srb ) ; + + if ( name[strlen(name)-1] == '\"' ) + name[strlen(name)-1] = '\0' ; + + na -> setValue ( name ) ; + hb -> setValue ( (shb == 'H') ? 0 : 1 ) ; + pb -> setValue ( (spb == 'P') ? 0 : 1 ) ; + rb -> setValue ( (srb == 'R') ? 0 : 1 ) ; + + hide_headingCB ( hb ) ; + hide_pitchCB ( pb ) ; + hide_rollCB ( rb ) ; +} + + +void Bone::write ( FILE *fd ) +{ + fprintf ( fd, "BONE \"%s\" %c%c%c\n", + na->getStringValue(), + hb->getValue() ? '.' : 'H', + pb->getValue() ? '.' : 'P', + rb->getValue() ? '.' : 'R' ) ; +} + + +void Bone::setAngles ( float h, float p, float r ) +{ + sgVec3 hpr ; + sgSetVec3 ( hpr, h, p, r ) ; + setAngles ( hpr ) ; +} + + +void Bone::setAngle ( int which, float a ) +{ + getXForm() -> hpr [ which ] = a ; + setAngles ( getXForm() -> hpr ) ; +} + + +void Bone::setAngles ( sgVec3 src ) +{ + sgCopyVec3 ( getXForm() -> hpr, src ) ; +} + + +float *Bone::getDialAngles () +{ + static sgVec3 dst ; + dst[0] = sh -> getFloatValue () * 360.0f - 180.0f ; + dst[1] = sp -> getFloatValue () * 360.0f - 180.0f ; + dst[2] = sr -> getFloatValue () * 360.0f - 180.0f ; + return dst ; +} + + +sgCoord *Bone::getXForm ( Event *prev, Event *next, float lerptime ) +{ + static sgCoord c ; + + sgCoord *coord0 = prev->getBoneCoord ( id ) ; + sgCoord *coord1 = next->getBoneCoord ( id ) ; + + sgCopyVec3 ( c.xyz, xlate ) ; + + sgLerpAnglesVec3 ( c.hpr, coord0->hpr, coord1->hpr, lerptime ) ; + + sh -> setValue ( (c.hpr[0] + 180.0f) / 360.0f ) ; + sp -> setValue ( (c.hpr[1] + 180.0f) / 360.0f ) ; + sr -> setValue ( (c.hpr[2] + 180.0f) / 360.0f ) ; + + return & c ; +} + + +sgCoord *Bone::getXForm () +{ + if ( eventList->getCurrentEvent() != NULL ) + { + sgCoord *coord = eventList->getCurrentEvent()->getBoneCoord ( id ) ; + sgCopyVec3 ( coord->xyz, xlate ) ; + return coord ; + } + + static sgCoord coord ; + sgZeroCoord ( & coord ) ; + sgCopyVec3 ( coord.xyz, xlate ) ; + return &coord ; +} + + +void Bone::computeTransform ( Event *prev, Event *next, float t ) +{ + effector -> setTransform ( getXForm( prev, next, t ) ) ; +} + + +void Bone::transform ( sgVec3 dst, sgVec3 src ) +{ + sgXformPnt3 ( dst, src, netMatrix ) ; + sgAddVec3 ( dst, curr_translate ) ; +} + + +void Bone::swapEnds() +{ + sgVec3 tmp ; + + /* Swap vertices so that vx0 is always the root. */ + + sgCopyVec3 ( tmp, vx[0] ) ; + sgCopyVec3 ( vx[0], vx[1] ) ; + sgCopyVec3 ( vx[1], tmp ) ; + + sgCopyVec3 ( tmp, orig_vx[0] ) ; + sgCopyVec3 ( orig_vx[0], orig_vx[1] ) ; + sgCopyVec3 ( orig_vx[1], tmp ) ; +} + + +void Bone::init ( ssgLeaf *l, sgMat4 newmat, short vv[2], int ident ) +{ + parent = -1 ; + sgCopyVec3 ( vx [ 0 ], l -> getVertex ( vv[0] ) ) ; + sgCopyVec3 ( vx [ 1 ], l -> getVertex ( vv[1] ) ) ; + sgXformPnt3 ( vx [ 0 ], newmat ) ; + sgXformPnt3 ( vx [ 1 ], newmat ) ; + + sgCopyVec3 ( orig_vx [ 0 ], vx [ 0 ] ) ; + sgCopyVec3 ( orig_vx [ 1 ], vx [ 1 ] ) ; + + id = ident ; + if ( sgEqualVec3 ( vx [ 0 ], vx [ 1 ] ) ) + fprintf ( stderr, "exposer: Zero length bone found.\n" ) ; +} + + +void Bone::print ( FILE *fd, int which ) +{ + fprintf ( fd, "Bone %d: vx (%f,%f,%f) -> (%f,%f,%f) Parent = %d\n", + which, + vx[0][0],vx[0][1],vx[0][2], + vx[1][0],vx[1][1],vx[1][2], + parent ) ; +} + + +#define NUM_COLOURS 13 + +static sgVec4 colourTable [] = +{ + { 1, 0, 0, 0.3f }, { 0, 1, 0, 0.3f }, { 0, 0, 1, 0.3f }, + { 1, 1, 0, 0.3f }, { 1, 0, 1, 0.3f }, { 0, 1, 1, 0.3f }, + + { 0.5f, 0.5f, 0.5f, 0.3f }, + + { 1, 0.5f, 0.5f, 0.3f }, { 0.5f, 1 , 0.5f, 0.3f }, { 0.5f, 0.5f, 1, 0.3f }, + { 1, 1 , 0.5f, 0.3f }, { 1 , 0.5f, 1 , 0.3f }, { 0.5f, 1, 1, 0.3f } +} ; + + +ssgBranch *Bone::generateGeometry ( int root ) +{ + static int nextColIndex = 0 ; + + ssgTransform *b = new ssgTransform ; + + b -> setUserData ( this ) ; + effector = b ; + sgZeroVec3 ( getXForm()->hpr ) ; + sgCopyVec3 ( xlate, vx[0] ) ; + sgZeroVec3 ( vx[0] ) ; + sgSubVec3 ( vx[1], xlate ) ; + + sgMat4 mat ; + sgMakeCoordMat4 ( mat, getXForm() ) ; + b -> setTransform ( mat ) ; + + offsetChildBones ( root, xlate ) ; + + sgCopyVec4 ( colour, colourTable[nextColIndex] ) ; + + createJoint () ; + + ssgaCube *shape = new ssgaCube () ; + sgVec3 org ; sgCopyVec3 ( org, vx[1] ) ; + sgVec3 siz ; sgCopyVec3 ( siz, vx[1] ) ; + + sgScaleVec3 ( org, 0.5 ) ; + + siz[0] = (float)fabs ( siz[0] ) ; if ( siz[0] <= 0.1f ) siz [ 0 ] = 0.1f ; + siz[1] = (float)fabs ( siz[1] ) ; if ( siz[1] <= 0.1f ) siz [ 1 ] = 0.1f ; + siz[2] = (float)fabs ( siz[2] ) ; if ( siz[2] <= 0.1f ) siz [ 2 ] = 0.1f ; + + if ( boneState == NULL ) + { + boneState = new ssgSimpleState () ; + boneState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + boneState -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + boneState -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + boneState -> enable ( GL_BLEND ) ; + } + + shape -> setCenter ( org ) ; + shape -> setSize ( siz ) ; + shape -> setColour ( colourTable[nextColIndex] ) ; + shape -> setKidState ( boneState ) ; + + nextColIndex = ( nextColIndex + 1 ) % NUM_COLOURS ; + + b -> addKid ( shape ) ; + + for ( int i = 0 ; i < getNumBones() ; i++ ) + if ( i != root && getBone(i)->parent == root ) + b -> addKid ( getBone(i)->generateGeometry ( i ) ) ; + + return b ; +} + + diff --git a/demos/exposer/src/bones.h b/demos/exposer/src/bones.h new file mode 100644 index 0000000..f3b294f --- /dev/null +++ b/demos/exposer/src/bones.h @@ -0,0 +1,84 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: bones.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +#define ROOT_BONE 9999 + +class Bone : public ssgBase +{ +public: + int id ; + int parent ; + ssgTransform *effector ; + + sgVec3 xlate ; + sgMat4 netMatrix ; + + sgVec3 vx [ 2 ] ; /* Relative to joint */ + sgVec3 orig_vx [ 2 ] ; /* Absolute */ + + puGroup *widget ; + puOneShot *rs ; + puButton *hb ; + puButton *pb ; + puButton *rb ; + puDial *sh ; + puDial *sp ; + puDial *sr ; + puInput *na ; + sgVec4 colour ; + Bone () ; + + void read ( FILE *fd ) ; + void write ( FILE *fd ) ; + void createJoint () ; + + void computeTransform ( Event *prev, Event *next, float tim ) ; + + void setAngle ( int which, float a ) ; + void setAngles ( float h, float p, float r ) ; + void setAngles ( sgVec3 a ) ; + + float *getXYZ () { return xlate ; } + + float *getDialAngles () ; + + sgCoord *getXForm ( Event *prev, Event *next, float tim ) ; + sgCoord *getXForm () ; + + void transform ( sgVec3 dst, sgVec3 src ) ; + void swapEnds() ; + void init ( ssgLeaf *l, sgMat4 newmat, short vv[2], int id ) ; + void print ( FILE *fd, int which ) ; + ssgBranch *generateGeometry ( int root ) ; +} ; + + +void init_bones () ; +void setShowAngle ( float a ) ; + +float *getCurrTranslate () ; +Bone *getBone ( int i ) ; + +ssgSimpleState *getBoneState () ; + diff --git a/demos/exposer/src/event.cxx b/demos/exposer/src/event.cxx new file mode 100644 index 0000000..6be7b1e --- /dev/null +++ b/demos/exposer/src/event.cxx @@ -0,0 +1,300 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: event.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "exposer.h" + + +void EventList::read ( int nevents, FILE *fd ) +{ + for ( int i = 0 ; i < nevents ; i++ ) + { + Event *e = new Event ( getNumBones(), (float) i ) ; + e -> read ( fd ) ; + addEvent ( e ) ; + } +} + + +void EventList::write ( FILE *fd ) +{ + for ( int i = 0 ; i < getNumEvents () ; i++ ) + getEvent ( i ) -> write ( fd ) ; +} + + +void EventList::moveEvent ( Event *e, float new_time ) +{ + removeEvent ( e ) ; + e -> setTime ( new_time ) ; + addEvent ( e ) ; +} + + +void EventList::newEvent ( float t ) +{ + if ( getNumBones () <= 0 ) + return ; + + setCurrentEvent ( new Event ( getNumBones (), t ) ) ; + addEvent ( getCurrentEvent () ) ; +} + + +void EventList::deleteEvent ( Event *e ) +{ + if ( e == NULL ) + return ; + + if ( e == curr_event ) + curr_event = NULL ; + + removeEvent ( e ) ; + delete e ; +} + + +void EventList::compressEventsBetween ( float t1, float t2 ) +{ + deleteEventsBetween ( t1, t2 ) ; + + for ( int i = 0 ; i < getNumEvents() ; i++ ) + { + Event *ev = getEvent ( i ) ; + + float t = ev -> getTime () ; + + if ( t > t1 || t > t2 ) + ev -> setTime ( t - (float)fabs(t1-t2) ) ; + } +} + + +int EventList::getNumEventsBetween ( float t1, float t2 ) +{ + int nevents = 0 ; + + for ( int i = 0 ; i < getNumEvents() ; i++ ) + { + float t = getEvent ( i ) -> getTime () ; + + if ( ( t1 < t2 && t >= t1 && t <= t2 ) || + ( t2 < t1 && t >= t2 && t <= t1 ) ) + nevents++ ; + } + + return nevents ; +} + + +int EventList::getEventsBetween ( float t1, float t2, Event ***elist ) +{ + int nevents = getNumEventsBetween ( t1, t2 ) ; + + if ( nevents == 0 ) + { + elist = NULL ; + return 0 ; + } + + /* Make a list of the events */ + + *elist = new Event* [ nevents ] ; + + nevents = 0 ; + + for ( int i = 0 ; i < getNumEvents() ; i++ ) + { + Event *ev = getEvent ( i ) ; + float t = ev -> getTime () ; + + if ( ( t1 < t2 && t >= t1 && t <= t2 ) || + ( t2 < t1 && t >= t2 && t <= t1 ) ) + (*elist) [ nevents++ ] = ev ; + } + + return nevents ; +} + + +void EventList::deleteEventsBetween ( float t1, float t2 ) +{ + int found_one = FALSE ; + + do + { + found_one = FALSE ; + + for ( int i = 0 ; i < getNumEvents() && ! found_one ; i++ ) + { + Event *ev = getEvent ( i ) ; + + float t = ev -> getTime () ; + + if ( ( t1 < t2 && t >= t1 && t <= t2 ) || + ( t2 < t1 && t >= t2 && t <= t1 ) ) + { + deleteEvent ( ev ) ; + found_one = TRUE ; + } + } + + } while ( found_one ) ; +} + + +void EventList::reverseEventsBetween ( float t1, float t2 ) +{ + /* Make a list of the events */ + + Event **elist ; + int nevents = getEventsBetween ( t1, t2, &elist ) ; + + if ( nevents == 0 ) + return ; + + /* Reverse their order */ + + for ( int i = 0 ; i < nevents ; i++ ) + { + Event *ev = elist [ i ] ; + + removeEvent ( ev ) ; + + float t = ev -> getTime () ; + + if ( t1 > t2 ) + t = t1 - ( t - t2 ) ; + else + t = t2 - ( t - t1 ) ; + + ev -> setTime ( t ) ; + + addEvent ( ev ) ; + } + + delete [] elist ; +} + + +void EventList::addEvent ( Event *e ) +{ + /* + Keep list in time-increasing order. + */ + + for ( int i = 0 ; i < getNumEvents() ; i++ ) + if ( e -> getTime() < getEvent(i)->getTime() ) + { + addEntityBefore ( i, e ) ; + return ; + } + + addEntity ( e ) ; +} + + + +Event *EventList::findNearestEvent ( float t, float tolerance ) +{ + int nearest = -1 ; + float min = FLT_MAX ; + + for ( int i = 0 ; i < getNumEvents() ; i++ ) + { + float t2 = getEvent(i)->getTime() ; + float diff = (float) fabs ( t - t2 ) ; + + if ( diff < min ) + { + min = diff ; + nearest = i ; + } + } + + return ( nearest >= 0 && min < tolerance ) ? getEvent ( nearest ) : NULL ; +} + + +void EventList::deleteAll () +{ + while ( getNumEvents() > 0 ) + deleteEvent ( getEvent ( 0 ) ) ; +} + + +EventList::EventList () +{ + curr_event = NULL ; +} + + + +Event::Event ( int n, float t ) +{ + time = t ; + nbones = n ; + bone_angles = new sgCoord [ n ] ; + + sgCopyVec3 ( translate, getCurrTranslate() ) ; + + for ( int i = 0 ; i < n ; i++ ) + { + sgCopyVec3 ( bone_angles [ i ] . hpr, getBone(i)->getDialAngles() ) ; + sgCopyVec3 ( bone_angles [ i ] . xyz, getBone(i)->getXYZ() ) ; + } +} + + +void Event::read ( FILE *fd ) +{ + fscanf ( fd, "EVENT %f %d (%f,%f,%f)\n", &time, &nbones, + &translate[0], &translate[1], &translate[2] ) ; + + for ( int i = 0 ; i < nbones ; i++ ) + { + int ssh, ssp, ssr ; + + fscanf ( fd, " (%d,%d,%d)\n", & ssh, & ssp, & ssr ) ; + + sgSetVec3 ( bone_angles [ i ] . hpr, (float)ssh, (float)ssp, (float)ssr ) ; + sgCopyVec3 ( bone_angles [ i ] . xyz, getBone(i)->getXYZ() ) ; + } +} + + +void Event::write ( FILE *fd ) +{ + fprintf ( fd, "EVENT %f %d (%f,%f,%f)\n", time, nbones, + translate[0], translate[1], translate[2] ) ; + + for ( int i = 0 ; i < nbones ; i++ ) + { + fprintf ( fd, " (%d,%d,%d)\n", + (int) bone_angles [ i ] . hpr[0], + (int) bone_angles [ i ] . hpr[1], + (int) bone_angles [ i ] . hpr[2] ) ; + } +} + + diff --git a/demos/exposer/src/event.h b/demos/exposer/src/event.h new file mode 100644 index 0000000..c444c64 --- /dev/null +++ b/demos/exposer/src/event.h @@ -0,0 +1,102 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: event.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +class Event +{ + float time ; + int nbones ; + sgCoord *bone_angles ; + sgVec3 translate ; + +public: + + Event ( int n, float t ) ; + ~Event () { delete [] bone_angles ; } + + void read ( FILE *fd ) ; + void write ( FILE *fd ) ; + + void setTranslate ( sgVec3 tra ) { sgCopyVec3 ( translate, tra ) ; } + void getTranslate ( sgVec3 tra ) { sgCopyVec3 ( tra, translate ) ; } + + void setBoneAngles ( int n, sgVec3 angles ) + { + if ( n < nbones ) + sgCopyVec3 ( bone_angles[n].hpr, angles ) ; + } + + void getBoneAngles ( int n, sgVec3 angles ) + { + if ( n < nbones ) + sgCopyVec3 ( angles, bone_angles[n].hpr ) ; + else + sgZeroVec3 ( angles ) ; + } + + sgCoord *getBoneCoord ( int n ) { return & ( bone_angles [ n ] ) ; } + + void setTime ( float t ) { time = t ; } + float getTime () { return time ; } +} ; + + + + + +class EventList : public ulList +{ + Event *curr_event ; + +public: + + EventList () ; + + int getNumEvents () { return getNumEntities () ; } + + void addEvent ( Event *e ) ; + void newEvent ( float t ) ; + Event *findNearestEvent ( float t, float tolerance ) ; + + void moveEvent ( Event *e, float new_time ) ; + void removeEvent ( Event *e ) { removeEntity ( e ) ; } + void setCurrentEvent ( Event *ev ) { curr_event = ev ; } + Event *getEvent ( int i ) { return (Event *) getEntity ( i ) ; } + void deleteEvent ( Event *e ) ; + void deleteAll () ; + + Event *getCurrentEvent () { return curr_event ; } + void deleteCurrentEvent() { deleteEvent ( curr_event ) ; } + + int getNumEventsBetween ( float t1, float t2 ) ; + int getEventsBetween ( float t1, float t2, Event ***elist ) ; + void reverseEventsBetween ( float t1, float t2 ) ; + void deleteEventsBetween ( float t1, float t2 ) ; + void compressEventsBetween( float t1, float t2 ) ; + + void write ( FILE *fd ) ; + void read ( int nevents, FILE *fd ) ; +} ; + + + diff --git a/demos/exposer/src/exposer.cxx b/demos/exposer/src/exposer.cxx new file mode 100644 index 0000000..7707666 --- /dev/null +++ b/demos/exposer/src/exposer.cxx @@ -0,0 +1,480 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: exposer.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "exposer.h" + +static int curr_button = 0 ; +static int scroll_controllers = 0 ; + +EventList * eventList = NULL ; +TimeBox * timebox = NULL ; +Floor * ground = NULL ; +ssgRoot * skinScene = NULL ; +ssgRoot *tweenScene = NULL ; +ssgRoot * boneScene = NULL ; +ssgRoot *sceneScene = NULL ; + +static puText *message ; +static puButton *hideBones ; +static puButton *hideSkin ; +static puButton *hideGround ; +static puButton *hideScene ; +static puSlider *scroller ; +static puMenuBar *menuBar ; + +static puSlider *rangeSlider ; +static puDial * panSlider ; +static puDial * tiltSlider ; + +static puInput *show_angle ; + +static void save_60th_CB ( puObject * ) { setTweenInterval ( 1.0f/60.0f ) ; } +static void save_30th_CB ( puObject * ) { setTweenInterval ( 1.0f/30.0f ) ; } +static void save_20th_CB ( puObject * ) { setTweenInterval ( 1.0f/20.0f ) ; } +static void save_15th_CB ( puObject * ) { setTweenInterval ( 1.0f/15.0f ) ; } +static void save_10th_CB ( puObject * ) { setTweenInterval ( 1.0f/10.0f ) ; } +static void save_8th_CB ( puObject * ) { setTweenInterval ( 1.0f/ 8.0f ) ; } +static void save_4th_CB ( puObject * ) { setTweenInterval ( 1.0f/ 4.0f ) ; } +static void save_2th_CB ( puObject * ) { setTweenInterval ( 1.0f/ 2.0f ) ; } +static void save_1th_CB ( puObject * ) { setTweenInterval ( 1.0f ) ; } + +void setShowAngle ( float a ) +{ + show_angle -> setValue ( a ) ; + show_angle -> rejectInput () ; +} + + +static void update_eye_motion () +{ + float r ; rangeSlider -> getValue ( & r ) ; r *= 100.0f ; r += 2.0f ; + float h ; panSlider -> getValue ( & h ) ; h *= 360.0f ; h += 180.0f ; + float p ; tiltSlider -> getValue ( & p ) ; p *= 360.0f ; p += 180.0f ; + + sgMat4 mat ; + sgVec3 vec ; + + sgMakeCoordMat4 ( mat, 0, 0, 0, h, p, 0 ) ; + sgSetVec3 ( vec, 0, r * 20, 0 ) ; + sgXformPnt3 ( vec, mat ) ; + sgNegateVec3 ( vec ) ; + sgCopyVec3 ( mat[3], vec ) ; + ssgSetCamera ( mat ) ; + transformModel ( boneScene, timebox->getCursorTime () ) ; +} + + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; + ssgSetFOV ( 60.0f, 60.0f * (float) h / (float) w ) ; +} + + + +static void specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; +} + + +static void keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; +} + + +static void passmotionfn ( int x, int y ) +{ + puMouse ( x, y ) ; +} + + +static void motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + timebox->updateEventQueue ( curr_button, x, y, FALSE ) ; +} + + +static void mousefn ( int button, int updown, int x, int y ) +{ + puMouse ( button, updown, x, y ) ; + + curr_button = button ; + timebox->updateEventQueue ( curr_button, x, y, updown == PU_DOWN ) ; +} + + +static void scrollerCB ( puObject *ob ) +{ + scroll_controllers = (int)(((float)getNumBones()) * ob -> getFloatValue ()) ; +} + + + +static void exitCB ( puObject *ob ) +{ + if ( ob -> getValue () ) + exit ( 1 ) ; +} + + + +/* + CALLBACK FUNCTIONS FOR GUI. +*/ + +static void deleteEventCB ( puObject * ) { timebox -> deleteEvent () ; } +static void addNewEventCB ( puObject * ) { timebox -> addNewEvent () ; } +static void reverseRegionCB ( puObject * ) { timebox -> reverseRegion () ; } +// static void deleteAllCB ( puObject * ) { timebox -> deleteAll () ; } +static void deleteRegionCB ( puObject * ) { timebox -> deleteRegion () ; } +static void deleteRegionAndCompressCB ( puObject * ) + { timebox -> deleteRegionAndCompress () ; } + +static void zoom_nrm_CB ( puObject * ) { timebox -> setZoom ( 1.0f ) ; } +static void zoom_in_CB ( puObject * ) { timebox -> setZoom ( + timebox -> getZoom () * 1.5f ) ; } +static void zoom_out_CB ( puObject * ) { float scale = timebox->getZoom() / 1.5f ; + timebox -> setZoom ( ( scale <= 1.0f ) ? 1.0f : scale ) ; } + +static void add_1_CB ( puObject * ) {timebox->setMaxTime(timebox->getMaxTime()+1.0f);} +static void add_2_CB ( puObject * ) {timebox->setMaxTime(timebox->getMaxTime()+2.0f);} +static void add_5_CB ( puObject * ) {timebox->setMaxTime(timebox->getMaxTime()+5.0f);} + +static void opaqueBones () +{ + if ( getBoneState() != NULL ) + getBoneState() -> disable ( GL_BLEND ) ; +} + + +static void blendBones () +{ + if ( getBoneState() != NULL ) + getBoneState() -> enable ( GL_BLEND ) ; +} + + +/* GLUT callbacks */ + +static void redraw () +{ + int i ; + + timebox->updateVCR () ; + + update_eye_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + glEnable ( GL_DEPTH_TEST ) ; + + if ( ! hideScene -> getValue () && sceneScene != NULL ) + ssgCullAndDraw ( sceneScene ) ; + + if ( ! hideGround -> getValue () ) + ground -> draw () ; + + if ( ! hideSkin -> getValue () ) + { + ssgCullAndDraw ( skinScene ) ; + blendBones () ; + } + else + opaqueBones () ; + + if ( ! hideBones -> getValue () ) + { + glClear ( GL_DEPTH_BUFFER_BIT ) ; + ssgCullAndDraw ( boneScene ) ; + } + + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glEnable ( GL_BLEND ) ; + glDisable ( GL_DEPTH_TEST ) ; + + for ( i = 0 ; i < getNumBones() ; i++ ) + getBone ( i ) -> widget -> hide () ; + + for ( i = 0 ; i < 10 && i+scroll_controllers < getNumBones() ; i++ ) + { + getBone ( i + scroll_controllers ) -> widget -> setPosition ( 24, 70+i*40); + + if ( eventList -> getCurrentEvent() == NULL ) + { + getBone ( i + scroll_controllers ) -> widget -> reveal () ; + getBone ( i + scroll_controllers ) -> widget -> greyOut () ; + } + else + { + getBone ( i + scroll_controllers ) -> widget -> reveal () ; + getBone ( i + scroll_controllers ) -> widget -> activate () ; + } + } + + puDisplay () ; + + timebox -> draw () ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + +/* Menu bar entries: */ + +static char *file_submenu [] = { "Exit", + "------------", + "Save Tweened Model As...", + "Save Bones As...", + "Load Bones", + "Load Scenery", + "Load Model", + NULL } ; + +static puCallback file_submenu_cb [] = { exitCB, + NULL, + twsaveCB, + bnsaveCB, + bnloadCB, + scloadCB, + loadCB, + NULL } ; + +static char *view_submenu [] = { "Zoom Timeline In", + "Zoom Timeline Out", + "Zoom Timeline 1:1", + NULL } ; + +static puCallback view_submenu_cb [] = { zoom_in_CB, + zoom_out_CB, + zoom_nrm_CB, + NULL } ; + +static char *pref_submenu [] = { "1/60th second", + "1/30th second", + "1/20th second", + "1/15th second", + "1/10th second", + "eighth second", + "quarter second", + "half second", + "second", + "Save a tween bank every...", + NULL } ; + +static puCallback pref_submenu_cb [] = { save_60th_CB, + save_30th_CB, + save_20th_CB, + save_15th_CB, + save_10th_CB, + save_8th_CB, + save_4th_CB, + save_2th_CB, + save_1th_CB, + NULL, + NULL } ; + +static char *time_submenu [] = { "Add 5 seconds", + "Add 2 seconds", + "Add 1 second", + "------------", + "Reverse region", + "Delete region & compress", + "Delete region", + "Delete selected event", + "Add new event", + NULL } ; + +static puCallback time_submenu_cb [] = { add_5_CB, + add_2_CB, + add_1_CB, + NULL, + reverseRegionCB, + deleteRegionAndCompressCB, + deleteRegionCB, + deleteEventCB, + addNewEventCB, + NULL } ; + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + + fake_argv[0] = "ExPoser" ; + fake_argv[1] = "ExPoser - Skin and Bones animation for PLIB." ; + fake_argv[2] = NULL ; + + /* Initialise GLUT */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 800, 600 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( passmotionfn ) ; + + /* Initialise SSG & PUI */ + + ssgInit () ; + puInit () ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultFonts ( PUFONT_HELVETICA_10, PUFONT_HELVETICA_10 ) ; + + /* Some basic OpenGL setup */ + + glClearColor ( 0.2f, 0.5f, 0.2f, 1.0f ) ; + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 0.1f, 700.0f ) ; + + /* Set up the Sun. */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, 0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + + /* GUI setup. */ + + timebox = new TimeBox ( eventList ) ; + ground = new Floor () ; + + menuBar = new puMenuBar () ; + { + menuBar -> add_submenu ( "File", file_submenu, file_submenu_cb ) ; + menuBar -> add_submenu ( "View", view_submenu, view_submenu_cb ) ; + menuBar -> add_submenu ( "Time", time_submenu, time_submenu_cb ) ; + menuBar -> add_submenu ( "SavePrefs", pref_submenu, pref_submenu_cb ) ; + } + menuBar -> close () ; + + show_angle = new puInput ( 5, 560, 80, 580 ) ; + show_angle -> setValue ( "" ) ; + show_angle -> rejectInput () ; + message = new puText ( 80, 560 ) ; + message -> setLabel ( "Angle" ) ; + + scroller = new puSlider ( 5, 70, 400, TRUE ) ; + scroller -> setCBMode ( PUSLIDER_DELTA ) ; + scroller -> setDelta ( 0.01f ) ; + scroller -> setCallback ( scrollerCB ) ; + + panSlider = new puDial ( 0, 0, 40 ) ; + panSlider -> setCBMode ( PUSLIDER_DELTA ) ; + panSlider -> setDelta ( 0.01f ) ; + panSlider -> setValue ( 0.5f ) ; + message = new puText ( 0, 40 ) ; + message -> setColour ( PUCOL_LABEL, 0.7f,0.65f,0.26f,1 ) ; + message -> setLabel ( "Pan" ) ; + + tiltSlider = new puDial ( 40, 0, 40 ) ; + tiltSlider -> setCBMode ( PUSLIDER_DELTA ) ; + tiltSlider -> setDelta ( 0.01f ) ; + tiltSlider -> setValue ( 0.5f ) ; + message = new puText ( 40, 40 ) ; + message -> setColour ( PUCOL_LABEL, 0.7f,0.65f,0.26f,1 ) ; + message -> setLabel ( "Tilt" ) ; + + hideBones = new puButton ( 80, 0, "Bones" ) ; + hideBones -> setValue ( TRUE ) ; + hideSkin = new puButton ( 119, 0, "Skin" ) ; + hideSkin -> setValue ( FALSE ) ; + hideGround = new puButton ( 149, 0, "Floor" ) ; + hideGround -> setValue ( FALSE ) ; + hideScene = new puButton ( 183, 0, "Scene" ) ; + hideScene -> setValue ( FALSE ) ; + + rangeSlider = new puSlider ( 80, 20, 141, FALSE ) ; + rangeSlider -> setCBMode ( PUSLIDER_DELTA ) ; + rangeSlider -> setDelta ( 0.01f ) ; + message = new puText ( 80, 40 ) ; + message -> setColour ( PUCOL_LABEL, 0.7f,0.65f,0.26f,1 ) ; + message -> setLabel ( "Zoom" ) ; +} + + +static void init_database () +{ + skinScene = new ssgRoot ; + boneScene = new ssgRoot ; +} + + +static void help () +{ + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, "exposer: Interactive Usage -\n" ) ; + fprintf ( stderr, " exposer\n\n" ) ; + fprintf ( stderr, " ...or...\n\n" ) ; + fprintf ( stderr, "exposer: Batch Tweenfile generation Usage -\n" ) ; + fprintf ( stderr, " exposer -f framerate modelfile bonefile tweenfile\n" ); + fprintf ( stderr, "\n" ) ; +} + + +int main ( int argc, char **argv ) +{ + eventList = new EventList ; + + init_graphics () ; + init_database () ; + init_bones () ; + initBoneGUI () ; + + /* Interactive? */ + + if ( argc <= 1 ) + { + loadCB ( NULL ) ; + + glutPostRedisplay () ; + glutMainLoop () ; + return 0 ; + } + + if ( argc != 6 || strcmp ( argv [ 1 ], "-f" ) != 0 || + atof ( argv [ 2 ] ) > 100.0f || atof ( argv[2] ) < 0.1f ) + { + help () ; + return 1 ; + } + + setTweenInterval ( 1.0f / (float)atof ( argv [ 2 ] ) ) ; + loadFile ( argv [ 3 ], FALSE ) ; + loadBoneFile ( argv [ 4 ], FALSE ) ; + saveTweenFile ( argv [ 5 ], FALSE ) ; + return 0 ; +} + + + diff --git a/demos/exposer/src/exposer.dsp b/demos/exposer/src/exposer.dsp new file mode 100644 index 0000000..70cfbf0 --- /dev/null +++ b/demos/exposer/src/exposer.dsp @@ -0,0 +1,156 @@ +# Microsoft Developer Studio Project File - Name="exposer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=exposer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "exposer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "exposer.mak" CFG="exposer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "exposer - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "exposer - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "exposer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg.lib ssg.lib ssgAux.lib fnt.lib pui.lib puAux.lib ul.lib opengl32.lib glu32.lib glut32.lib Winmm.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\.." + +!ELSEIF "$(CFG)" == "exposer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg_d.lib ssg_d.lib ssgAux_d.lib fnt_d.lib pui_d.lib puAux_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib Winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\.." + +!ENDIF + +# Begin Target + +# Name "exposer - Win32 Release" +# Name "exposer - Win32 Debug" +# Begin Source File + +SOURCE=.\boneGUI.cxx +# End Source File +# Begin Source File + +SOURCE=.\boneGUI.h +# End Source File +# Begin Source File + +SOURCE=.\bones.cxx +# End Source File +# Begin Source File + +SOURCE=.\bones.h +# End Source File +# Begin Source File + +SOURCE=.\event.cxx +# End Source File +# Begin Source File + +SOURCE=.\event.h +# End Source File +# Begin Source File + +SOURCE=.\exposer.cxx +# End Source File +# Begin Source File + +SOURCE=.\exposer.h +# End Source File +# Begin Source File + +SOURCE=.\floor.cxx +# End Source File +# Begin Source File + +SOURCE=.\floor.h +# End Source File +# Begin Source File + +SOURCE=.\load_save.cxx +# End Source File +# Begin Source File + +SOURCE=.\load_save.h +# End Source File +# Begin Source File + +SOURCE=.\model.cxx +# End Source File +# Begin Source File + +SOURCE=.\model.h +# End Source File +# Begin Source File + +SOURCE=.\timebox.cxx +# End Source File +# Begin Source File + +SOURCE=.\timebox.h +# End Source File +# Begin Source File + +SOURCE=.\vertices.cxx +# End Source File +# Begin Source File + +SOURCE=.\vertices.h +# End Source File +# End Target +# End Project diff --git a/demos/exposer/src/exposer.h b/demos/exposer/src/exposer.h new file mode 100644 index 0000000..e5bcf6c --- /dev/null +++ b/demos/exposer/src/exposer.h @@ -0,0 +1,65 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: exposer.h 1528 2002-09-01 07:10:09Z ude $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include + +#include "event.h" +#include "model.h" +#include "bones.h" +#include "boneGUI.h" +#include "vertices.h" +#include "timebox.h" +#include "floor.h" +#include "load_save.h" + +extern EventList * eventList ; +extern TimeBox * timebox ; +extern Floor * ground ; +extern ssgRoot * skinScene ; +extern ssgRoot *tweenScene ; +extern ssgRoot * boneScene ; +extern ssgRoot *sceneScene ; + diff --git a/demos/exposer/src/floor.cxx b/demos/exposer/src/floor.cxx new file mode 100644 index 0000000..a5d75b8 --- /dev/null +++ b/demos/exposer/src/floor.cxx @@ -0,0 +1,108 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: floor.cxx 1425 2002-08-17 23:58:38Z ude $ +*/ + + +#include "exposer.h" + +static unsigned char Texture0 [] = +{ + 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0 +} ; + +static unsigned char Texture1 [] = +{ + 0, 0, 255, 255, + 0, 0, 255, 255, + 255, 255, 0, 0, + 255, 255, 0, 0 +} ; + +static unsigned char Texture2 [] = +{ + 0, 255, + 255, 0 +} ; + +static unsigned char Texture3 [] = +{ + 127 +} ; + + +void Floor::draw () +{ + glMatrixMode ( GL_PROJECTION) ; _ssgCurrentContext->loadProjectionMatrix() ; + glMatrixMode ( GL_MODELVIEW ) ; _ssgCurrentContext->loadModelviewMatrix () ; + + glDisable ( GL_LIGHTING ) ; + glEnable ( GL_TEXTURE_2D ) ; + glEnable ( GL_CULL_FACE ) ; + glBindTexture ( GL_TEXTURE_2D, texhandle ) ; + glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + + glBegin ( GL_QUADS ) ; + glTexCoord2f ( -30.0f, timebox->getGroundPosition() - 30.0f ) ; + glVertex3f ( -30.0f, -30.0f, z_coord ) ; + glTexCoord2f ( 30.0f, timebox->getGroundPosition() - 30.0f ) ; + glVertex3f ( 30.0f, -30.0f, z_coord ) ; + glTexCoord2f ( 30.0f, timebox->getGroundPosition() + 30.0f ) ; + glVertex3f ( 30.0f, 30.0f, z_coord ) ; + glTexCoord2f ( -30.0f, timebox->getGroundPosition() + 30.0f ) ; + glVertex3f ( -30.0f, 30.0f, z_coord ) ; + glEnd () ; + + glDisable ( GL_TEXTURE_2D ) ; + glEnable ( GL_LIGHTING ) ; +} + + +Floor::Floor () +{ + glGenTextures ( 1, & texhandle ) ; + + glBindTexture ( GL_TEXTURE_2D, texhandle ) ; + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ; + glTexImage2D ( GL_TEXTURE_2D, 0, 1, 8, 8, + FALSE, GL_LUMINANCE, GL_UNSIGNED_BYTE, Texture0 ) ; + glTexImage2D ( GL_TEXTURE_2D, 1, 1, 4, 4, + FALSE, GL_LUMINANCE, GL_UNSIGNED_BYTE, Texture1 ) ; + glTexImage2D ( GL_TEXTURE_2D, 2, 1, 2, 2, + FALSE, GL_LUMINANCE, GL_UNSIGNED_BYTE, Texture2 ) ; + glTexImage2D ( GL_TEXTURE_2D, 3, 1, 1, 1, + FALSE, GL_LUMINANCE, GL_UNSIGNED_BYTE, Texture3 ) ; + glBindTexture ( GL_TEXTURE_2D, 0 ) ; +} + + diff --git a/demos/exposer/src/floor.h b/demos/exposer/src/floor.h new file mode 100644 index 0000000..9de867f --- /dev/null +++ b/demos/exposer/src/floor.h @@ -0,0 +1,39 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: floor.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +class Floor +{ + unsigned int texhandle ; + float z_coord ; + +public: + + void setZcoord ( float z ) { z_coord = z ; } + float getZcoord () { return z_coord ; } + + void draw () ; + Floor () ; +} ; + + diff --git a/demos/exposer/src/load_save.cxx b/demos/exposer/src/load_save.cxx new file mode 100644 index 0000000..7898fe0 --- /dev/null +++ b/demos/exposer/src/load_save.cxx @@ -0,0 +1,621 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: load_save.cxx 2078 2006-04-13 16:22:38Z bram $ +*/ + +#include + +#include "exposer.h" + +#define FILE_SELECTOR_ARGS (640-320)/2,(480-270)/2,320,270,1 + + +static float tweeninterval = 1.0f/8.0f ; + +void setTweenInterval ( float t ) { tweeninterval = t ; } +float getTweenInterval () { return tweeninterval ; } + + +static puaFileSelector *file_selector = NULL ; + + +static char lastModelFilePath [ PUSTRING_MAX ] ; +static char lastModelFileName [ PUSTRING_MAX ] ; + +static void dismissDialogCB ( puObject * ) ; + +static puButton *dialog_button = NULL ; + + +static void initDialog () +{ + if ( dialog_button != NULL ) + return ; + + dialog_button = new puButton ( 250, 240, "" ) ; + dialog_button -> setSize ( 400, 40 ) ; + dialog_button -> setLegendFont ( PUFONT_TIMES_ROMAN_24 ) ; + dialog_button -> setCallback ( dismissDialogCB ) ; + dialog_button -> setColorScheme ( 1, 1, 0, 1 ) ; + dialog_button -> hide () ; +} + + +static void dismissDialogCB ( puObject * ) +{ + initDialog () ; + dialog_button -> hide () ; +} + + +static void dialog ( const char *msg, float r, float g, float b ) +{ + initDialog () ; + dialog_button -> setLegend ( msg ) ; + dialog_button -> setColorScheme ( r, g, b, 1 ) ; + dialog_button -> reveal () ; +} + + +static void twsavepickfn ( puObject * ) +{ + char path [ PUSTRING_MAX ] ; + + file_selector -> getValue ( path ) ; + + if ( path [ 0 ] == '\0' ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + + dialog ( "FAILED TO SAVE TWEENED MODEL!", 1, 0, 0 ) ; + return ; + } + + saveTweenFile ( path, TRUE ) ; +} + + +void saveTweenFile ( char *path, int interactive ) +{ + char orig_path [ PUSTRING_MAX ] ; + char fname [ PUSTRING_MAX ] ; + char *p = NULL ; + int i ; + + strcpy ( orig_path, path ) ; + + for ( i = strlen(path) ; i >= 0 ; i-- ) + if ( path[i] == '/' || path[i] == '\\' ) + { + p = & ( path[i+1] ) ; + path[i] = '\0' ; + break ; + } + + if ( p == NULL ) + { + ssgModelPath ( "." ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( "." ) ; + strcpy ( fname, path ) ; + } + else + { + ssgModelPath ( path ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( path ) ; + strcpy ( fname, p ) ; + } + + /* SAVE THE TWEENED MODEL */ + + if ( orig_path[0] == '\0' ) + { + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + dialog ( "FAILED TO SAVE TWEENED MODEL!", 1, 0, 0 ) ; + return ; + } + else + { + perror ( "saveTween:" ) ; + exit ( 1 ) ; + } + } + + tweenScene = (ssgRoot *) makeTweenCopy ( skinScene ) ; + + for ( i = 0 ; i <= (int)( timebox->getMaxTime() / tweeninterval ) ; i++ ) + { + if ( i != 0 ) + addTweenBank ( tweenScene ) ; + + transformModel ( boneScene, (float) i * tweeninterval ) ; + makeTweenCopy ( tweenScene, skinScene ) ; + } + + if ( ! ssgSave ( orig_path, tweenScene ) ) + { + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + dialog ( "FAILED TO SAVE TWEENED MODEL!", 1, 0, 0 ) ; + return ; + } + else + { + perror ( "saveTween:" ) ; + exit ( 1 ) ; + } + } + + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + dialog ( "TWEENED MODEL WAS SAVED OK.", 1, 1, 0 ) ; + } +} + + +static void bnsavepickfn ( puObject * ) +{ + char path [ PUSTRING_MAX ] ; + char fname [ PUSTRING_MAX ] ; + + file_selector -> getValue ( path ) ; + + if ( path [ 0 ] == '\0' ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + + dialog ( "FAILED TO SAVE BONES!", 1, 0, 0 ) ; + return ; + } + + char *p = NULL ; + int i ; + + for ( i = strlen(path) ; i >= 0 ; i-- ) + if ( path[i] == '/' || path[i] == '\\' ) + { + p = & ( path[i+1] ) ; + path[i] = '\0' ; + break ; + } + + if ( p == NULL ) + { + ssgModelPath ( "." ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( "." ) ; + strcpy ( fname, path ) ; + } + else + { + ssgModelPath ( path ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( path ) ; + strcpy ( fname, p ) ; + } + + /* SAVE THE BONES */ + + if ( file_selector->getStringValue()[0] == '\0' ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + dialog ( "FAILED TO SAVE BONES!", 1, 0, 0 ) ; + return ; + } + + FILE *fd = fopen ( file_selector->getStringValue(), "wa" ) ; + + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + + if ( fd == NULL ) + { + dialog ( "FAILED TO SAVE BONES!", 1, 0, 0 ) ; + return ; + } + + fprintf ( fd, "NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + getNumBones(), eventList->getNumEvents(), timebox->getMaxTime (), + -ground->getZcoord(), timebox->getGroundSpeed() ) ; + + for ( i = 0 ; i < getNumBones () ; i++ ) + getBone ( i ) -> write ( fd ) ; + + eventList -> write ( fd ) ; + + fclose ( fd ) ; + dialog ( "BONES WERE SAVED OK.", 1, 1, 0 ) ; +} + + +static void bnpickfn ( puObject * ) +{ + char path [ PUSTRING_MAX ] ; + + file_selector -> getValue ( path ) ; + + if ( path [ 0 ] == '\0' ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + return ; + } + + loadBoneFile ( path, TRUE ) ; +} + +void loadBoneFile ( char *path, int interactive ) +{ + char orig_path [ PUSTRING_MAX ] ; + char fname [ PUSTRING_MAX ] ; + char *p = NULL ; + int i ; + + strcpy ( orig_path, path ) ; + + for ( i = strlen(path) ; i >= 0 ; i-- ) + if ( path[i] == '/' || path[i] == '\\' ) + { + p = & ( path[i+1] ) ; + path[i] = '\0' ; + break ; + } + + if ( p == NULL ) + { + ssgModelPath ( "." ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( "." ) ; + strcpy ( fname, path ) ; + } + else + { + ssgModelPath ( path ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( path ) ; + strcpy ( fname, p ) ; + } + + /* LOAD THE BONES */ + + if ( orig_path[0] == '\0' ) + { + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + return ; + } + else + { + perror ( "loadBones:" ) ; + exit ( 1 ) ; + } + } + + FILE *fd = fopen ( orig_path, "ra" ) ; + + if ( fd == NULL ) + { + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + return ; + } + else + { + perror ( "loadBones:" ) ; + exit ( 1 ) ; + } + } + + timebox->deleteAll () ; + + int numbones, numevents ; + float floor_z_coord, maxtime, new_ground_speed ; + + fscanf ( fd, + "NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + &numbones, &numevents, + &maxtime, &floor_z_coord, &new_ground_speed ) ; + + + /* Don't use the floor_z_coord from the file. */ + /* ground -> setZcoord ( floor_z_coord ) ; */ + + timebox->setMaxTime ( maxtime ) ; + timebox->setGroundSpeed ( new_ground_speed ) ; + + if ( numbones != getNumBones () ) + { + fprintf ( stderr, + "Number of bones in model (%d) doesn't agree with number in bones file (%d)!\n", getNumBones (), numbones ) ; + exit ( 1 ) ; + } + + for ( i = 0 ; i < getNumBones () ; i++ ) + getBone ( i ) -> read ( fd ) ; + + eventList -> read ( numevents, fd ) ; + + fclose ( fd ) ; + + if ( interactive ) + { + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + } +} + + + +static void scpickfn ( puObject * ) +{ + char path [ PUSTRING_MAX ] ; + char fname [ PUSTRING_MAX ] ; + + file_selector -> getValue ( path ) ; + + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + + if ( path [ 0 ] == '\0' ) + return ; + + if ( strlen ( path ) >= 6 && strcmp(&path[strlen(path)-6], ".bones" ) == 0 ) + { + fprintf ( stderr, "I think you tried to load a BONES file as 3D model.\n"); + fprintf ( stderr, "Try again!\n"); + scloadCB ( NULL ) ; + return ; + } + + char *p = NULL ; + + for ( int i = strlen(path) ; i >= 0 ; i-- ) + if ( path[i] == '/' || path[i] == '\\' ) + { + p = & ( path[i+1] ) ; + path[i] = '\0' ; + break ; + } + + if ( p == NULL ) + { + ssgModelPath ( "." ) ; + + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( "." ) ; + strcpy ( fname, path ) ; + } + else + { + ssgModelPath ( path ) ; + + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( path ) ; + + strcpy ( fname, p ) ; + } + + delete sceneScene ; + sceneScene = new ssgRoot ; + sceneScene -> addKid ( ssgLoad ( fname, NULL ) ) ; +} + + +static void pickfn ( puObject * ) +{ + char path [ PUSTRING_MAX ] ; + + file_selector -> getValue ( path ) ; + + puDeleteObject ( file_selector ) ; + file_selector = NULL ; + + if ( path [ 0 ] == '\0' ) + return ; + + if ( strlen ( path ) >= 6 && strcmp(&path[strlen(path)-6], ".bones" ) == 0 ) + { + fprintf ( stderr, "I think you tried to load a BONES file as 3D model.\n"); + fprintf ( stderr, "Try again!\n"); + loadCB ( NULL ) ; + return ; + } + + loadFile ( path, TRUE ) ; +} + +void loadFile ( char *path, int interactive ) +{ + char fname [ PUSTRING_MAX ] ; + char *p = NULL ; + + for ( int i = strlen(path) ; i >= 0 ; i-- ) + if ( path[i] == '/' || path[i] == '\\' ) + { + p = & ( path[i+1] ) ; + path[i] = '\0' ; + break ; + } + + if ( p == NULL ) + { + ssgModelPath ( "." ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( "." ) ; + strcpy ( fname, path ) ; + } + else + { + ssgModelPath ( path ) ; + if ( getenv ( "EXPOSER_TEXTURE_PATH" ) != NULL ) + ssgTexturePath ( getenv ( "EXPOSER_TEXTURE_PATH" ) ) ; + else + ssgTexturePath ( path ) ; + strcpy ( fname, p ) ; + } + + strcpy ( lastModelFilePath, path ) ; + strcpy ( lastModelFileName, fname ) ; + + skinScene -> addKid ( ssgLoad ( fname, NULL ) ) ; + ssgFlatten ( skinScene -> getKid ( 0 ) ) ; + ssgStripify ( skinScene -> getKid ( 0 ) ) ; + boneScene -> addKid ( extractBones ( skinScene ) ) ; + + extractVertices ( skinScene ) ; + timebox->deleteAll () ; + + eventList -> newEvent ( 0.0f ) ; + + ground -> setZcoord ( getLowestVertexZ () ) ; +} + + +void twsaveCB ( puObject * ) +{ + if ( file_selector == NULL ) + { + file_selector = new puaFileSelector ( FILE_SELECTOR_ARGS, + lastModelFilePath, "Save Tweened Model As..." ) ; + file_selector -> setCallback ( twsavepickfn ) ; + + char guess_fname [ PUSTRING_MAX ] ; + strcpy ( guess_fname, lastModelFileName ) ; + + for ( int i = strlen ( guess_fname ) ; i >= 0 ; i-- ) + if ( guess_fname [ i ] == '.' ) + { + guess_fname[i] = '\0' ; + break ; + } + + strcat ( guess_fname, "_tweened.ssg" ) ; + file_selector -> setInitialValue ( guess_fname ) ; + } +} + + +void bnsaveCB ( puObject * ) +{ + if ( file_selector == NULL ) + { + file_selector = new puaFileSelector ( FILE_SELECTOR_ARGS, + lastModelFilePath, "Save Bones As..." ) ; + file_selector -> setCallback ( bnsavepickfn ) ; + + char guess_fname [ PUSTRING_MAX ] ; + strcpy ( guess_fname, lastModelFileName ) ; + + for ( int i = strlen ( guess_fname ) ; i >= 0 ; i-- ) + if ( guess_fname [ i ] == '.' ) + { + guess_fname[i] = '\0' ; + break ; + } + + strcat ( guess_fname, ".bones" ) ; + file_selector -> setInitialValue ( guess_fname ) ; + } +} + + +void bnloadCB ( puObject * ) +{ + if ( file_selector == NULL ) + { + file_selector = new puaFileSelector ( FILE_SELECTOR_ARGS, + lastModelFilePath, "Load Bones from..." ) ; + file_selector -> setCallback ( bnpickfn ) ; + + /* Guess an initial filename from the model filename */ + + char guess_fname [ PUSTRING_MAX ] ; + strcpy ( guess_fname, lastModelFileName ) ; + + for ( int i = strlen ( guess_fname ) ; i >= 0 ; i-- ) + if ( guess_fname [ i ] == '.' ) + { + guess_fname[i] = '\0' ; + break ; + } + + strcat ( guess_fname, ".bones" ) ; + file_selector -> setInitialValue ( guess_fname ) ; + } +} + + + +void scloadCB ( puObject * ) +{ + if ( file_selector == NULL ) + { + file_selector = new puaFileSelector ( FILE_SELECTOR_ARGS, + "", "Load Scenery from..." ) ; + file_selector -> setCallback ( scpickfn ) ; + } +} + + +void loadCB ( puObject * ) +{ + if ( file_selector == NULL ) + { + file_selector = new puaFileSelector ( FILE_SELECTOR_ARGS, + "", "Load Model from..." ) ; + file_selector -> setCallback ( pickfn ) ; + } +} + + diff --git a/demos/exposer/src/load_save.h b/demos/exposer/src/load_save.h new file mode 100644 index 0000000..56384b0 --- /dev/null +++ b/demos/exposer/src/load_save.h @@ -0,0 +1,36 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: load_save.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +void loadCB ( puObject * ) ; +void scloadCB ( puObject * ) ; +void bnloadCB ( puObject * ) ; +void bnsaveCB ( puObject * ) ; +void twsaveCB ( puObject * ) ; + +void loadFile ( char *fname, int interactive ) ; +void loadBoneFile ( char *fname, int interactive ) ; +void saveTweenFile ( char *fname, int interactive ) ; + +void setTweenInterval ( float t ) ; + diff --git a/demos/exposer/src/merge_tweens.cxx b/demos/exposer/src/merge_tweens.cxx new file mode 100644 index 0000000..d0026ef --- /dev/null +++ b/demos/exposer/src/merge_tweens.cxx @@ -0,0 +1,179 @@ +/* + merge_tweens - Merges multiple '.bones' files written from ExPoser + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: merge_tweens.cxx 1425 2002-08-17 23:58:38Z ude $ +*/ + + +#include "merge_tweens.h" + +static void help () +{ + fprintf ( stderr, "\n\n" ) ; + fprintf ( stderr, "merge_tweens: Usage -\n\n" ) ; + fprintf ( stderr, " merge_tweens file.bones ...\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, "Merges multiple '.bones' files written from ExPoser\n" ) ; + fprintf ( stderr, "into a single animation sequence (stored in 'out.bones').\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, "Bones files must be compatible (same number of joints\n" ); + fprintf ( stderr, "in same order, etc).\n" ) ; + fprintf ( stderr, "\n" ) ; +} + + +static int numBones = 0 ; +static int totalEvents = 0 ; +static float totalTime = 0.0f ; +static float floor_z = 0.0f ; +static float ground_speed = 0.0f ; + +int main ( int argc, char **argv ) +{ + if ( argc < 2 ) + { + help () ; + exit ( 1 ) ; + } + + /* + First Pass - add up times - do error checking. + */ + + totalEvents = 0 ; + totalTime = 0.0f ; + + int i ; + + for ( i = 1 ; i < argc ; i++ ) + { + FILE *fd = fopen ( argv[i], "ra" ) ; + + int bones, numevents ; + float new_floor_z, maxtime, new_ground_speed ; + + fscanf ( fd, "NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + &bones, &numevents, &maxtime, &new_floor_z, &new_ground_speed ) ; + + printf ( + "%2d) %s: NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + i, argv[i], bones, numevents, maxtime, new_floor_z, new_ground_speed ) ; + + totalEvents += numevents ; + totalTime += maxtime ; + + if ( i == 1 ) + { + numBones = bones ; + floor_z = new_floor_z ; + ground_speed = new_ground_speed ; + } + else + { + if ( numBones != bones ) + { + fprintf ( stderr, + "merge_bones: FATAL - Number of bones must be the same across files.\n" ) ; + exit ( 1 ) ; + } + + if ( floor_z != new_floor_z ) + { + fprintf ( stderr, + "merge_bones: WARNING - Floor Z is not the same across files.\n" ) ; + } + + if ( ground_speed != new_ground_speed ) + { + fprintf ( stderr, + "merge_bones: WARNING - Ground speed is not the same across files.\n"); + } + } + + fclose ( fd ) ; + } + + /* + Pass TWO : Write out composite file. + */ + + FILE *out_fd = fopen ( "out.bones", "wa" ) ; + + float nextTime = 0.0f ; + + for ( i = 1 ; i < argc ; i++ ) + { + FILE *fd = fopen ( argv[i], "ra" ) ; + + int bones, numevents ; + float new_floor_z, maxtime, new_ground_speed ; + + fscanf ( fd, "NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + &bones, &numevents, &maxtime, &new_floor_z, &new_ground_speed ) ; + + if ( i == 1 ) + { + fprintf ( out_fd, + "NUMBONES=%d NUMEVENTS=%d MAXTIME=%f Z_OFFSET=%f SPEED=%f\n", + numBones, totalEvents, totalTime, floor_z, ground_speed ) ; + + for ( int j = 0 ; j < numBones ; j++ ) + { + char s [ 1000 ] ; + fgets ( s, 1000, fd ) ; + fprintf ( out_fd, "%s", s ) ; + } + } + else + { + for ( int j = 0 ; j < numBones ; j++ ) + { + char s [ 1000 ] ; + fgets ( s, 1000, fd ) ; + } + } + + for ( int j = 0 ; j < numevents ; j++ ) + { + float t, x, y, z ; + int nb ; + + fscanf ( fd, "EVENT %f %d (%f,%f,%f)\n", &t, &nb, &x, &y, &z ) ; + fprintf ( out_fd, "EVENT %f %d (%f,%f,%f)\n", t+nextTime, nb, x, y, z ) ; + + for ( int k = 0 ; k < nb ; k++ ) + { + int xx, yy, zz ; + fscanf ( fd, " (%d,%d,%d)\n", &xx, &yy, &zz ) ; + fprintf ( out_fd, " (%d,%d,%d)\n", xx, yy, zz ) ; + } + } + + nextTime += maxtime ; + fclose ( fd ) ; + } + + fclose ( out_fd ) ; + + return 0 ; +} + + + diff --git a/demos/exposer/src/merge_tweens.dsp b/demos/exposer/src/merge_tweens.dsp new file mode 100644 index 0000000..fb0f641 --- /dev/null +++ b/demos/exposer/src/merge_tweens.dsp @@ -0,0 +1,92 @@ +# Microsoft Developer Studio Project File - Name="merge_tweens" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=merge_tweens - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "merge_tweens.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "merge_tweens.mak" CFG="exposer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "merge_tweens - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "merge_tweens - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "merge_tweens - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "merge_tweens - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\.." + +!ENDIF + +# Begin Target + +# Name "merge_tweens - Win32 Release" +# Name "merge_tweens - Win32 Debug" +# Begin Source File + +SOURCE=.\merge_tweens.cxx +# End Source File +# Begin Source File + +SOURCE=.\merge_tweens.h +# End Source File +# Begin Source File + +# End Project diff --git a/demos/exposer/src/merge_tweens.h b/demos/exposer/src/merge_tweens.h new file mode 100644 index 0000000..310170e --- /dev/null +++ b/demos/exposer/src/merge_tweens.h @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include + diff --git a/demos/exposer/src/model.cxx b/demos/exposer/src/model.cxx new file mode 100644 index 0000000..1b292da --- /dev/null +++ b/demos/exposer/src/model.cxx @@ -0,0 +1,465 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: model.cxx 1425 2002-08-17 23:58:38Z ude $ +*/ + + +#include "exposer.h" + +static int rootBone = -1 ; +static int nextBone = 0 ; + +int getNumBones () { return nextBone ; } + +static void findChildBones ( int root ) +{ + float *v0 = getBone ( root ) -> vx[0] ; + float *v1 = getBone ( root ) -> vx[1] ; + + /* + Compute parametric equation. + */ + + sgVec3 vec ; + float len ; + int i ; + + sgSubVec3 ( vec, v1, v0 ) ; + len = sgLengthVec3 ( vec ) ; + sgScaleVec3 ( vec, 1.0f/len ) ; + + for ( i = 0 ; i < getNumBones() ; i++ ) + { + /******************************* + If this bone already has a parent + then we are done. + ********************************/ + + if ( getBone(i)->parent >= 0 ) + continue ; + + /******************************* + Test vertex zero of the daughter + bone against the parent bone. + ********************************/ + + float *vi0 = getBone(i)-> vx[0] ; + float *vi1 = getBone(i)-> vx[1] ; + sgVec3 vec_i ; + float len_i ; + + sgSubVec3 ( vec_i, vi0, v0 ) ; + len_i = sgLengthVec3 ( vec_i ) ; + + if ( len_i <= 0.01 ) /* Close to vertex 0 of parent? */ + { + getBone(i)-> parent = root ; + continue ; + } + + sgScaleVec3 ( vec_i, 1.0f/len_i ) ; + + /* Somewhere between vertex 0 and vertex 1 of parent? */ + + if ( sgDistanceSquaredVec3 ( vec, vec_i ) <= 0.001 && len_i <= len * 1.01 ) + { + getBone(i)-> parent = root ; + continue ; + } + + /******************************* + Test vertex one of the daughter + bone against the parent bone. + ********************************/ + + sgSubVec3 ( vec_i, vi1, v0 ) ; + len_i = sgLengthVec3 ( vec_i ) ; + + if ( len_i <= 0.01 ) /* Close to vertex 0 of parent? */ + { + getBone(i)->parent = root ; + getBone(i)->swapEnds () ; + continue ; + } + + sgScaleVec3 ( vec_i, 1.0f/len_i ) ; + + /* Somewhere between vertex 0 and vertex 1 of parent? */ + + if ( sgDistanceSquaredVec3 ( vec, vec_i ) <= 0.001 && len_i <= len * 1.01 ) + { + getBone(i)->parent = root ; + getBone(i)->swapEnds () ; + continue ; + } + } + + /**************************************************** + Recursively search any new child nodes we found. + ****************************************************/ + + for ( i = 0 ; i < getNumBones() ; i++ ) + if ( i != root && getBone(i)->parent == root ) /* If Parented by this root node */ + findChildBones ( i ) ; /* Recurse downwards. */ +} + + +void addTweenBank ( ssgBranch *root ) +{ + if ( root == NULL ) + return ; + + for ( int i = 0 ; i < root -> getNumKids () ; i++ ) + { + ssgEntity *e = root -> getKid ( i ) ; + + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + addTweenBank ( (ssgBranch *)e ) ; + else + if ( e -> isAKindOf ( ssgTypeTween() ) ) + { + ssgTween *tween = (ssgTween *) e ; + + ssgVertexArray *v1 = new ssgVertexArray ; + + for ( int ii = 0 ; ii < tween -> getNumVertices () ; ii++ ) + v1 -> add ( tween -> getVertex ( ii ) ) ; + + tween -> newBank ( v1, NULL, NULL, NULL ) ; + } + } +} + + +void makeTweenCopy ( ssgBranch *dst, ssgBranch *src ) +{ + for ( int i = 0 ; i < src -> getNumKids () ; i++ ) + { + ssgEntity *s = src -> getKid ( i ) ; + ssgEntity *d = dst -> getKid ( i ) ; + + if ( s -> isAKindOf ( ssgTypeBranch() ) ) + makeTweenCopy ( (ssgBranch *)d, (ssgBranch *)s ) ; + else + { + ssgLeaf *ls = (ssgLeaf *) s ; + ssgLeaf *ld = (ssgLeaf *) d ; + + for ( int ii = 0 ; ii < ls -> getNumVertices () ; ii++ ) + sgCopyVec3 ( ld->getVertex(ii), ls->getVertex(ii) ) ; + } + } +} + + +static void tweenify ( ssgBranch *root ) +{ + for ( int i = 0 ; i < root -> getNumKids () ; i++ ) + { + ssgEntity *e = root -> getKid ( i ) ; + + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + tweenify ( (ssgBranch *)e ) ; + else + { + ssgLeaf *l = (ssgLeaf *) e ; + + ssgVertexArray *v0 = new ssgVertexArray ; + ssgNormalArray *n0 = new ssgNormalArray ; + ssgTexCoordArray *t0 = new ssgTexCoordArray ; + ssgColourArray *c0 = new ssgColourArray ; + + for ( int ii = 0 ; ii < l -> getNumVertices () ; ii++ ) + { + v0 -> add ( l -> getVertex ( ii ) ) ; + n0 -> add ( l -> getNormal ( ii ) ) ; + t0 -> add ( l -> getTexCoord ( ii ) ) ; + c0 -> add ( l -> getColour ( ii ) ) ; + } + + ssgTween *tween = new ssgTween ( l -> getPrimitiveType () ) ; + + tween -> setState ( l -> getState () ) ; + tween -> newBank ( v0, n0, t0, c0 ) ; + + root -> replaceKid ( l, tween ) ; + } + } +} + + + +ssgEntity *makeTweenCopy ( ssgEntity *root ) +{ + if ( root == NULL ) + return NULL ; + + ssgEntity *res = (ssgEntity *) ( root -> clone ( SSG_CLONE_RECURSIVE | + SSG_CLONE_GEOMETRY ) ) ; + tweenify ( (ssgBranch *) res ) ; + + return res ; +} + + +static void walkBones ( ssgBranch *root, sgMat4 mat ) +{ + if ( root == NULL ) + return ; + + sgMat4 newmat ; + + if ( root -> isAKindOf ( ssgTypeTransform() ) ) + { + sgMat4 tmp ; + + ((ssgTransform *)root)-> getTransform ( tmp ) ; + sgMultMat4 ( newmat, mat, tmp ) ; + } + else + sgCopyMat4 ( newmat, mat ) ; + + for ( int i = 0 ; i < root -> getNumKids () ; i++ ) + { + ssgEntity *e = root -> getKid ( i ) ; + + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + walkBones ( (ssgBranch *)e, newmat ) ; + else + { + ssgLeaf *l = (ssgLeaf *) e ; + + if ( l -> getPrimitiveType () == GL_LINES || + l -> getPrimitiveType () == GL_LINE_LOOP || + l -> getPrimitiveType () == GL_LINE_STRIP ) + { + for ( int ll = 0 ; ll < l->getNumLines() ; ll++ ) + { + short vv [ 2 ] ; + + l -> getLine ( ll, & vv[0], & vv[1] ) ; + + getBone ( getNumBones() ) -> init ( l, newmat, vv, + getNumBones() ) ; + + /* + Is this line red? + If so, it's the 'root' line. + */ + + float *col = l -> getColour ( vv[0] ) ; + + if ( l -> getState () != NULL && + l -> getState () -> isAKindOf ( ssgTypeSimpleState () ) ) + { + ssgSimpleState *ss = (ssgSimpleState *)( l -> getState () ) ; + + if ( ss -> getColourMaterial () != GL_DIFFUSE && + ss -> getColourMaterial () != GL_AMBIENT_AND_DIFFUSE ) + { + col = ss -> getMaterial ( GL_DIFFUSE ) ; + } + } + + if ( col [ 0 ] > 0.75 && col [ 1 ] < 0.25 && col [ 2 ] < 0.25 ) + { + if ( rootBone != -1 ) + rootBone = -2 ; + else + rootBone = getNumBones() ; + } + + nextBone++ ; + } + + root -> removeKid ( e ) ; + i-- ; + } + } + } +} + + + +void offsetChildBones ( int root, sgVec3 v ) +{ + for ( int i = 0 ; i < getNumBones() ; i++ ) + if ( getBone(i)->parent == root ) + { + sgSubVec3 ( getBone(i)->vx[0], v ) ; + sgSubVec3 ( getBone(i)->vx[1], v ) ; + offsetChildBones ( i, v ) ; + } +} + + +ssgBranch *extractBones ( ssgBranch *root ) +{ + sgMat4 mat ; + + nextBone = 0 ; + rootBone = -1 ; + + sgMakeIdentMat4 ( mat ) ; + walkBones ( root, mat ) ; + + if ( rootBone == -1 ) + { + fprintf ( stderr, "exposer: ERROR - No 'root' bone found?!?\n" ) ; + fprintf ( stderr, "exposer: (Exactly one 'bone' line should be RED).\n" ) ; + exit ( 1 ) ; + } + + if ( rootBone == -2 ) + { + fprintf ( stderr, "exposer: ERROR - Multiple 'root' bones found?!?\n" ) ; + fprintf ( stderr, "exposer: (Only one 'bone' line should be RED).\n" ) ; + exit ( 1 ) ; + } + + fprintf ( stderr, "exposer: %d bones found, rootBone is number %d.\n", + getNumBones(), rootBone ) ; + + getBone(rootBone)->parent = ROOT_BONE ; + + findChildBones ( rootBone ) ; + + int disconnected = 0 ; + + for ( int i = 0 ; i < getNumBones() ; i++ ) + if ( getBone(i)->parent < 0 ) + disconnected++ ; + + if ( disconnected > 0 ) + { + fprintf ( stderr, + "exposer: ERROR - %d bones are not connected to the 'root' bone.\n", + disconnected ) ; + fprintf ( stderr, + "exposer: (The root bone line should be RED. All other bones\n" ) ; + fprintf ( stderr, + "exposer: should either touch the root bone - or touch some\n" ) ; + fprintf ( stderr, + "exposer: other bone that is connected to the root bone so that\n"); + fprintf ( stderr, + "exposer: all the bones form one continuously connected\n" ) ; + fprintf ( stderr, + "exposer: skeleton.)\n" ) ; + exit ( 1 ) ; + } + + + ssgBranch *res = getBone ( rootBone ) -> generateGeometry ( rootBone ) ; + + return res ; +} + + +static void walkTransforms ( ssgBranch *root, sgMat4 mat ) +{ + if ( root == NULL ) + return ; + + sgMat4 newmat ; + + if ( root -> isAKindOf ( ssgTypeTransform() ) ) + { + sgMat4 tmp ; + + ((ssgTransform *)root)-> getTransform ( tmp ) ; + sgMultMat4 ( newmat, mat, tmp ) ; + + Bone *bo = (Bone *)(((ssgTransform *)root)-> getUserData ()) ; + sgCopyMat4 ( bo -> netMatrix, newmat ) ; + } + else + sgCopyMat4 ( newmat, mat ) ; + + for ( int i = 0 ; i < root -> getNumKids () ; i++ ) + { + ssgEntity *e = root -> getKid ( i ) ; + + if ( e -> isAKindOf ( ssgTypeBranch () ) ) + walkTransforms ( (ssgBranch *) e, newmat ) ; + } +} + + +void transformModel ( ssgBranch *boneRoot, float tim ) +{ + if ( eventList->getNumEvents () < 1 ) + return ; + + Event *prev = eventList->getEvent ( 0 ) ; + Event *next = prev ; + + if ( eventList->getCurrentEvent() != NULL ) + { + prev = next = eventList->getCurrentEvent() ; + tim = eventList->getCurrentEvent() -> getTime () ; + } + else + { + for ( int i = 1 ; i < eventList->getNumEvents () ; i++ ) + { + Event *ev = eventList->getEvent ( i ) ; + float t = ev -> getTime () ; + + if ( t < tim ) + next = prev = ev ; + else + { + next = ev ; + break ; + } + } + } + + float lerptime = 0.0f ; + + if ( next->getTime() - prev->getTime() >= 0.01 ) + { + lerptime = tim - prev->getTime() ; + lerptime /= (next->getTime() - prev->getTime()) ; + } + + sgVec3 ptra ; prev->getTranslate(ptra) ; + sgVec3 ntra ; next->getTranslate(ntra) ; + + sgLerpVec3 ( getCurrTranslate(), ptra, ntra, lerptime ) ; + + syncTranslators ( getCurrTranslate() ) ; + + int i ; + + for ( i = 0 ; i < getNumBones () ; i++ ) + getBone ( i ) -> computeTransform ( prev, next, lerptime ) ; + + sgMat4 mat ; + + sgMakeIdentMat4 ( mat ) ; + + walkTransforms ( (ssgBranch *) boneRoot, mat ) ; + + transformVertices () ; +} + + diff --git a/demos/exposer/src/model.h b/demos/exposer/src/model.h new file mode 100644 index 0000000..323d620 --- /dev/null +++ b/demos/exposer/src/model.h @@ -0,0 +1,36 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: model.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +ssgBranch *extractBones ( ssgBranch *root ) ; +ssgEntity *makeTweenCopy ( ssgEntity *root ) ; +void extractVertices ( ssgBranch *root ) ; +void transformModel ( ssgBranch *boneRoot, float tim ) ; +void makeTweenCopy ( ssgBranch *dest, ssgBranch *src ) ; +void addTweenBank ( ssgBranch *root ) ; +void offsetChildBones ( int root, sgVec3 v ) ; + +float getLowestVertexZ () ; + +int getNumBones () ; + diff --git a/demos/exposer/src/timebox.cxx b/demos/exposer/src/timebox.cxx new file mode 100644 index 0000000..e8c8b9a --- /dev/null +++ b/demos/exposer/src/timebox.cxx @@ -0,0 +1,386 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: timebox.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "exposer.h" + +#define MODE_ADD 0 +#define MODE_SELECT 1 + +#define TIMEBOX_LEFT 230.0f +#define TIMEBOX_RIGHT 790.0f +#define TIMEBOX_TOP 100.0f +#define TIMEBOX_BOTTOM 10.0f +#define TIMEBOX_WIDTH (TIMEBOX_RIGHT-TIMEBOX_LEFT) +#define TIMEBOX_INITIAL_DURATION 10.0f /* Seconds */ + +/* GUI callback functions. */ + +static void vcr_fastReverse ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setReplaySpeed (-3.0f); +} + +static void vcr_reverse ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setReplaySpeed (-1.0f); +} + +static void vcr_stop ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setReplaySpeed ( 0.0f); +} + +static void vcr_play ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setReplaySpeed ( 1.0f); +} + +static void vcr_fastPlay ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setReplaySpeed ( 3.0f); +} + +static void vcr_groundSpeed ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + timebox -> setGroundSpeed ( ob -> getFloatValue () ) ; +} + + +static void timescrollerCB ( puObject *ob ) +{ + TimeBox *timebox = (TimeBox *) ob -> getUserData () ; + + timebox -> setScroll ( ob -> getFloatValue () * + ( timebox -> getMaxTime() - + TIMEBOX_WIDTH / timebox -> second () ) ) ; +} + + +float TimeBox::second () +{ + return scale * TIMEBOX_WIDTH / maxtime ; +} + + +void TimeBox::deleteRegionAndCompress () +{ + eventList -> compressEventsBetween ( lcursor, rcursor ) ; + + maxtime -= (float)fabs(lcursor-rcursor) ; + + if ( lcursor > rcursor ) + lcursor = rcursor ; + else + rcursor = lcursor ; +} + + +void TimeBox::addNewEvent () +{ + if ( getNumBones() <= 0 ) + return ; + + eventList->setCurrentEvent ( NULL ) ; + event_mode = MODE_ADD ; +} + + +void TimeBox::updateVCR () +{ + static ulClock timer ; + + timer.update () ; + + if ( replay_speed != 0.0f ) + { + float delta_t = replay_speed * (float)timer.getDeltaTime () ; + + lcursor += delta_t ; + + if ( lcursor < 0.0f ) + lcursor = maxtime ; + + if ( lcursor > maxtime ) + lcursor = 0.0f ; + } + + ground_position = lcursor * ground_speed ; +} + + +void TimeBox::draw () +{ + int w = puGetWindowWidth () ; + int h = puGetWindowHeight () ; + + glPushAttrib ( GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; + + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + + glViewport ( 0, 0, w, h ) ; + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + gluOrtho2D ( 0, w, 0, h ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; + + glColor4f ( 0.8f, 0.8f, 0.8f, 1.0f ) ; + glBegin ( GL_LINE_LOOP ) ; + glVertex2f ( TIMEBOX_LEFT , TIMEBOX_BOTTOM ) ; + glVertex2f ( TIMEBOX_RIGHT, TIMEBOX_BOTTOM ) ; + glVertex2f ( TIMEBOX_RIGHT, TIMEBOX_TOP ) ; + glVertex2f ( TIMEBOX_LEFT , TIMEBOX_TOP ) ; + glEnd () ; + + int nseconds = (int)floor(TIMEBOX_WIDTH / second () ) ; + int ntenthseconds = (int)floor(TIMEBOX_WIDTH*10.0f / second () ) ; + int tenthsecondoffset = (int)( (start*10.0f - + floor(start*10.0f)) + * second () / 10.0f ) ; + int secondoffset = (int)( (start - + floor(start)) + * second () ) ; + int i ; + + glBegin ( GL_LINES ) ; + + for ( i = 0 ; i < ntenthseconds ; i++ ) + { + float x = TIMEBOX_LEFT + tenthsecondoffset + + (float) i * second () / 10.0f ; + + glVertex2f ( x, TIMEBOX_BOTTOM ) ; + glVertex2f ( x, TIMEBOX_TOP/3.0f ) ; + } + + for ( i = 0 ; i < nseconds ; i++ ) + { + float x = TIMEBOX_LEFT + secondoffset + + (float) i * second () ; + + glVertex2f ( x, TIMEBOX_BOTTOM ) ; + glVertex2f ( x, TIMEBOX_TOP/2.0f ) ; + } + + glEnd () ; + + float tlcursor = ( lcursor - start ) * second () ; + float trcursor = ( rcursor - start ) * second () ; + + tlcursor = (tlcursor < 0.0f ) ? 0.0f : + (tlcursor > TIMEBOX_WIDTH) ? TIMEBOX_WIDTH : tlcursor ; + + trcursor = (trcursor < 0.0f ) ? 0.0f : + (trcursor > TIMEBOX_WIDTH) ? TIMEBOX_WIDTH : trcursor ; + + if ( tlcursor != trcursor ) + { + glColor4f ( 1.0f, 1.0f, 0.0f, 0.2f ) ; + glBegin ( GL_QUADS ) ; + glVertex2f ( TIMEBOX_LEFT + tlcursor, TIMEBOX_BOTTOM ) ; + glVertex2f ( TIMEBOX_LEFT + tlcursor, TIMEBOX_TOP ) ; + glVertex2f ( TIMEBOX_LEFT + trcursor, TIMEBOX_TOP ) ; + glVertex2f ( TIMEBOX_LEFT + trcursor, TIMEBOX_BOTTOM ) ; + glEnd () ; + } + + glColor4f ( 0.0f, 0.0f, 0.0f, 1.0f ) ; + + glBegin ( GL_LINES ) ; + glVertex2f ( TIMEBOX_LEFT + trcursor, TIMEBOX_BOTTOM ) ; + glVertex2f ( TIMEBOX_LEFT + trcursor, TIMEBOX_TOP ) ; + glEnd () ; + + glColor4f ( 1.0f, 0.0f, 0.0f, 1.0f ) ; + + glBegin ( GL_LINES ) ; + glVertex2f ( TIMEBOX_LEFT + tlcursor, TIMEBOX_BOTTOM ) ; + glVertex2f ( TIMEBOX_LEFT + tlcursor, TIMEBOX_TOP ) ; + glEnd () ; + + float t ; + + glColor4f ( 1.0f, 0.0f, 1.0f, 1.0f ) ; + + glBegin ( GL_LINES ) ; + + for ( i = 0 ; i < eventList->getNumEvents() ; i++ ) + { + t = eventList->getEvent(i) -> getTime () ; + + t = ( t - start ) * second () ; + + if ( t >= 0.0f && t <= TIMEBOX_WIDTH ) + { + glVertex2f ( TIMEBOX_LEFT + t, TIMEBOX_TOP/3.0f ) ; + glVertex2f ( TIMEBOX_LEFT + t, TIMEBOX_TOP ) ; + } + } + glEnd () ; + + if ( eventList->getCurrentEvent() != NULL ) + { + t = eventList->getCurrentEvent() -> getTime () ; + + t = ( t - start ) * second () ; + + if ( t >= 0.0f && t <= TIMEBOX_WIDTH ) + { + static ulClock flasher ; + flasher.update () ; + + if ( (int)(flasher.getAbsTime()*3.0f) & 1 ) + glColor4f ( 1.0f, 0.0f, 1.0f, 1.0f ) ; + else + glColor4f ( 0.0f, 1.0f, 0.0f, 1.0f ) ; + glBegin ( GL_LINES ) ; + glVertex2f ( TIMEBOX_LEFT + t, TIMEBOX_TOP/3.0f ) ; + glVertex2f ( TIMEBOX_LEFT + t, TIMEBOX_TOP ) ; + glEnd () ; + } + } + + glMatrixMode ( GL_PROJECTION ) ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + glPopAttrib () ; +} + + + + +void TimeBox::updateEventQueue ( int button, int x, int y, int new_click ) +{ + y = puGetWindowHeight () - y ; + + /* + Allow user to click a little outside the box so + we can make it easier to get the ends exactly. + */ + + if ( x > TIMEBOX_LEFT - 20 && x < TIMEBOX_RIGHT + 20 && + y > TIMEBOX_BOTTOM && y < TIMEBOX_TOP ) + { + float c = (float)(x - TIMEBOX_LEFT) ; + + if ( c < 0.0f ) c = 0.0f ; + if ( c > TIMEBOX_WIDTH ) c = TIMEBOX_WIDTH ; + + c /= second () ; + c += start ; + + if ( button == PU_RIGHT_BUTTON ) + rcursor = c ; + else + if ( button == PU_LEFT_BUTTON ) + { + rcursor = lcursor = c ; + + if ( new_click ) + { + if ( event_mode == MODE_ADD ) + eventList -> newEvent ( lcursor ) ; + else + eventList -> setCurrentEvent ( + eventList -> findNearestEvent ( lcursor, 10.0f/second () ) ) ; + + event_mode = MODE_SELECT ; + } + else + if ( eventList->getCurrentEvent() != NULL ) + eventList->moveEvent ( eventList->getCurrentEvent(), lcursor ) ; + } + } +} + + +void TimeBox::init () +{ + event_mode = MODE_SELECT ; + + maxtime = TIMEBOX_INITIAL_DURATION ; + scale = 1.0f ; + start = 0.0f ; + + lcursor = 0.0f ; + rcursor = 0.0f ; + + replay_speed = 0.0f ; + ground_speed = 0.0f ; + ground_position = 0.0f ; + + timescroller = new puSlider ( (int)TIMEBOX_LEFT, (int)TIMEBOX_TOP+5, + 570-(int)TIMEBOX_LEFT, FALSE ) ; + timescroller -> setCBMode ( PUSLIDER_DELTA ) ; + timescroller -> setDelta ( 0.01f ) ; + timescroller -> setCallback ( timescrollerCB ) ; + timescroller -> setUserData ( this ) ; + timescroller -> hide () ; + + puText *message ; + puOneShot *oneshot ; + puGroup *vcr = new puGroup ( 579, (int)TIMEBOX_TOP + 5 ) ; + + oneshot = new puArrowButton ( 0, 0, 30, 30, PUARROW_FASTLEFT ) ; + oneshot -> setCallback ( vcr_fastReverse ) ; + oneshot -> setUserData ( this ) ; + oneshot = new puArrowButton ( 30, 0, 60, 30, PUARROW_LEFT ) ; + oneshot -> setCallback ( vcr_reverse ) ; + oneshot -> setUserData ( this ) ; + oneshot = new puArrowButton ( 60, 0, 90, 30, PUARROW_DOWN ) ; + oneshot -> setCallback ( vcr_stop ) ; + oneshot -> setUserData ( this ) ; + oneshot = new puArrowButton ( 90, 0,120, 30, PUARROW_RIGHT ) ; + oneshot -> setCallback ( vcr_play ) ; + oneshot -> setUserData ( this ) ; + oneshot = new puArrowButton (120, 0,150, 30, PUARROW_FASTRIGHT ) ; + oneshot -> setCallback ( vcr_fastPlay ) ; + oneshot -> setUserData ( this ) ; + + ground_speed_input = new puInput ( 150, 0, 200, 20 ) ; + ground_speed_input -> setCallback ( vcr_groundSpeed ) ; + ground_speed_input -> setValue ( "0.0" ) ; + ground_speed_input -> setUserData ( this ) ; + + message = new puText ( 150, 20 ) ; + message -> setColour ( PUCOL_LABEL, 0.7f,0.65f,0.26f,1 ) ; + message -> setLabel ( "GrndSpeed" ) ; + + vcr -> close () ; +} + + diff --git a/demos/exposer/src/timebox.h b/demos/exposer/src/timebox.h new file mode 100644 index 0000000..2234874 --- /dev/null +++ b/demos/exposer/src/timebox.h @@ -0,0 +1,117 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: timebox.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +class TimeBox +{ + int event_mode ; + + float maxtime ; + float scale ; + float start ; + float lcursor ; + float rcursor ; + + EventList *eventList ; + puSlider *timescroller ; + puInput *ground_speed_input ; + + float replay_speed ; + float ground_speed ; + float ground_position ; + + void init () ; + +public: + + void setReplaySpeed ( float spd ) + { + replay_speed = spd ; + eventList->setCurrentEvent ( NULL ) ; + } + + void draw () ; + + void updateEventQueue ( int button, int x, int y, int new_click ) ; + void updateVCR () ; + + TimeBox ( EventList *el ) { eventList = el ; init () ; } + + float second () ; + + float getCursorTime () { return lcursor ; } + + void setMaxTime ( float t ) { maxtime = t ; } + float getMaxTime () { return maxtime ; } + + + void setGroundSpeed ( float t ) + { + ground_speed = t ; + ground_speed_input -> setValue ( ground_speed ) ; + } + float getGroundSpeed () { return ground_speed ; } + + void setGroundPosition ( float t ) { ground_position = t ; } + float getGroundPosition () { return ground_position ; } + + void setScroll ( float z ) { start = z ; } + float getScroll () { return start ; } + + void setZoom ( float z ) + { + scale = z ; + + if ( scale == 1.0f ) + timescroller -> hide () ; + else + timescroller -> reveal () ; + } + float getZoom () { return scale ; } + + void reverseRegion () + { + eventList -> reverseEventsBetween ( lcursor, rcursor ) ; + } + + void deleteAll () + { + eventList -> deleteAll () ; + lcursor = rcursor = 0.0f ; + } + + void deleteRegion () + { + eventList -> deleteEventsBetween ( lcursor, rcursor ) ; + } + + void deleteEvent () + { + eventList -> deleteCurrentEvent () ; + } + + void deleteRegionAndCompress () ; + void addNewEvent () ; +} ; + + diff --git a/demos/exposer/src/vertices.cxx b/demos/exposer/src/vertices.cxx new file mode 100644 index 0000000..0d879d9 --- /dev/null +++ b/demos/exposer/src/vertices.cxx @@ -0,0 +1,189 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: vertices.cxx 1425 2002-08-17 23:58:38Z ude $ +*/ + + +#include "exposer.h" + + +class Vertex +{ +public: + + int boneID ; + sgVec3 rel_vx ; + float *vx ; + + int getBoneID () { return boneID ; } +} ; + + +static int nextVertex = 0 ; + +#define MAX_VERTICES (64*1024) + +static Vertex vertex [ MAX_VERTICES ] ; + + + +static int getNumVertices () { return nextVertex ; } + + +static void addVertex ( sgVec3 v ) +{ + vertex [ nextVertex ] . vx = v ; + sgCopyVec3 ( vertex [ nextVertex ] . rel_vx, v ) ; + + nextVertex++ ; +} + + +static void initVertices () +{ + nextVertex = 0 ; +} + + +static void walkVertices ( ssgBranch *root, sgMat4 mat ) +{ + if ( root == NULL ) + return ; + + sgMat4 newmat ; + + if ( root -> isAKindOf ( ssgTypeTransform() ) ) + { + sgMat4 tmp ; + + ((ssgTransform *)root)-> getTransform ( tmp ) ; + sgMultMat4 ( newmat, mat, tmp ) ; + + sgMakeIdentMat4 ( tmp ) ; + ((ssgTransform *)root)-> setTransform ( tmp ) ; + } + else + sgCopyMat4 ( newmat, mat ) ; + + for ( int i = 0 ; i < root -> getNumKids () ; i++ ) + { + ssgEntity *e = root -> getKid ( i ) ; + + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + walkVertices ( (ssgBranch *)e, newmat ) ; + else + { + ssgLeaf *l = (ssgLeaf *) e ; + + for ( int ll = 0 ; ll < l->getNumVertices() ; ll++ ) + { + float *v = l -> getVertex ( ll ) ; + int vv = -1 ; + + for ( int j = 0 ; j < getNumVertices() ; j++ ) + if ( v == vertex [ j ] . vx ) + { + vv = j ; + break ; + } + + if ( vv < 0 ) + { + sgXformPnt3 ( v, mat ) ; + addVertex ( v ) ; + } + } + } + } +} + + +void extractVertices ( ssgBranch *root ) +{ + sgMat4 mat ; + + initVertices () ; + + /* + Walk the model - making a list of all of the + unique vertices - flattening the model's transforms + as we go. + */ + + sgMakeIdentMat4 ( mat ) ; + walkVertices ( root, mat ) ; + + /* + Now find the nearest bone to each vertex and + compute it's position relative to the root of + that bone. + */ + + for ( int i = 0 ; i < getNumVertices() ; i++ ) + { + float min = FLT_MAX ; + + for ( int j = 0 ; j < getNumBones() ; j++ ) + { + sgLineSegment3 ls ; + sgCopyVec3 ( ls.a, getBone(j)->orig_vx[0] ) ; + sgCopyVec3 ( ls.b, getBone(j)->orig_vx[1] ) ; + + float d = sgDistSquaredToLineSegmentVec3 ( ls, vertex[i].vx ) ; + + if ( d < min ) + { + min = d ; + vertex[i].boneID = j ; + } + } + + sgSubVec3 ( vertex[i].rel_vx, + vertex[i].vx, + getBone(vertex[i].boneID)->orig_vx[0] ) ; + } +} + + +float getLowestVertexZ () +{ + float lowest = FLT_MAX ; + + for ( int i = 0 ; i < getNumVertices() ; i++ ) + { + float v = vertex[i].vx [ 2 ] ; + + if ( v < lowest ) + lowest = v ; + } + + return lowest ; +} + + +void transformVertices () +{ + for ( int i = 0 ; i < getNumVertices () ; i++ ) + getBone ( vertex[i].getBoneID() ) -> transform ( vertex[i].vx, + vertex[i].rel_vx ) ; +} + + diff --git a/demos/exposer/src/vertices.h b/demos/exposer/src/vertices.h new file mode 100644 index 0000000..05d65b6 --- /dev/null +++ b/demos/exposer/src/vertices.h @@ -0,0 +1,28 @@ +/* + This file is part of ExPoser - A Tool for Animating PLIB Critters. + Copyright (C) 2001 Steve Baker + + ExPoser is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ExPoser is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ExPoser; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: vertices.h 1425 2002-08-17 23:58:38Z ude $ +*/ + + +void extractVertices ( ssgBranch *root ) ; +float getLowestVertexZ () ; +void transformVertices () ; + diff --git a/demos/p-guide/AUTHORS b/demos/p-guide/AUTHORS new file mode 100644 index 0000000..dee669c --- /dev/null +++ b/demos/p-guide/AUTHORS @@ -0,0 +1,3 @@ +P-GUIDE was written by John F. Fay. +Significant improvements were made by James 'J.C.' Jones + diff --git a/demos/p-guide/COPYING b/demos/p-guide/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/demos/p-guide/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/demos/p-guide/ChangeLog b/demos/p-guide/ChangeLog new file mode 100644 index 0000000..6857348 --- /dev/null +++ b/demos/p-guide/ChangeLog @@ -0,0 +1 @@ +July, 2002 -- P-Guide functionality increased dramatically -- James 'J.C.' Jones diff --git a/demos/p-guide/INSTALL b/demos/p-guide/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/demos/p-guide/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/demos/p-guide/Makefile.am b/demos/p-guide/Makefile.am new file mode 100644 index 0000000..62fbcb9 --- /dev/null +++ b/demos/p-guide/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src + +EXTRA_DIRS = autogen.sh README TODO pGuide.dsw + diff --git a/demos/p-guide/NEWS b/demos/p-guide/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/demos/p-guide/README b/demos/p-guide/README new file mode 100644 index 0000000..11f436b --- /dev/null +++ b/demos/p-guide/README @@ -0,0 +1,34 @@ + +PUI-based Graphical User Interface Designer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + by John F. Fay + +REQUIREMENTS: + + OpenGL (or Mesa) + GLUT (the GL Utility Toolkit) + PLIB + + +INSTALLATION: + +Installation instructions are pretty basic: + + ./configure + make + make install + + +LICENSE: + +This software is released under the provisions of +the GNU General Public License (GPL) which can be +found in the file 'COPYING' in the current directory. + + +DIRECTORY STRUCTURE: + +src -- Where the source code lives. + +doc -- Documentation - in HTML + diff --git a/demos/p-guide/TODO b/demos/p-guide/TODO new file mode 100644 index 0000000..c11eb61 --- /dev/null +++ b/demos/p-guide/TODO @@ -0,0 +1,30 @@ +Planned Bug Fixes or Upgrades to P-GUIDE: + +Bug Fixes: + +1. When the user has selected CTRL-widget, have the button value set back to zero when the selected_widget_type (or whatever it is) gets deselected or changed (eg when he presses on another widget button in the Widget window or presses another widget anywhere else), not just when he presses Escape. + +2. Make the Active widget's black line go all the way around it. + +3. puFileSelector needs options + +4. puScrollBar needs to be added + +5. Menus (Vert and normal) need a method to populate submenus. + +Upgrades: + +1. Improve layer support. + +2. Make widgets added to window the real widgets, rather than Frames. + +3. Multiple Window support + +4. Makefile building? + +5. Font viewing / selecting (a font browser and such) + +6. Copy / Cut / Paste + +7. Widget class changes + diff --git a/demos/p-guide/autogen.sh b/demos/p-guide/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/demos/p-guide/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/demos/p-guide/configure.in b/demos/p-guide/configure.in new file mode 100644 index 0000000..4b065f2 --- /dev/null +++ b/demos/p-guide/configure.in @@ -0,0 +1,162 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/pGuide.cxx) +AM_INIT_AUTOMAKE(pguide, 0.0.1) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL + +plib_suffix="-lplibpu -lplibfnt -lplibsg -lplibul" + +AC_LANG_CPLUSPLUS + + +dnl Command line arguments. + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + +if test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + dnl OpenGL libraries + LIBS="$LIBS -lwinmm -lglut32 -lglu32 -lopengl32 -luser32 -lgdi32" + ;; +*-apple-darwin*) + dnl Mac OS X + + LIBS="$LIBS -framework GLUT -lobjc -framework OpenGL" + ;; +*) + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + LIBS="$LIBS $X_PRE_LIBS -lXi -lXmu -lXext -lX11 $X_EXTRA_LIBS -lm" + +dnl ========================================================= +dnl if test "x$x_includes" != "x"; then +dnl CPPFLAGS="$CPPFLAGS -I$x_includes" +dnl fi +dnl ========================================================= + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList,, + AC_MSG_ERROR([could not find working GL library])) + fi + + AC_CHECK_LIB(GLU, gluLookAt) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt,, + AC_MSG_ERROR([could not find working GLU library])) + fi + + dnl check for glut...or freeglut + AC_CHECK_LIB(freeglut, glutGetModifiers) + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xno" ; then + dnl if no freeglut, check for Real GLUT + AC_CHECK_LIB(glut, glutGetModifiers,, + AC_MSG_ERROR([could not find working GLUT library])) + fi + + AC_CHECK_LIB(dl, dlclose) + ;; +esac + +dnl Check PLIB library +AC_CHECK_LIB(plibul, ulInit, true, +AC_MSG_ERROR([there seems to be a problem with the PLIB libraries])) + +LIBS="$plib_suffix $LIBS" + + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADERS(OpenGL/gl.h OpenGL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GLUT/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*) + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + AC_CHECK_HEADER(GL/freeglut.h, AC_DEFINE(FREEGLUT_IS_PRESENT), + AC_MSG_ERROR([freeglut header file not found])) + else + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + fi + ;; +esac + +AC_CHECK_HEADER(plib/ul.h,, AC_MSG_ERROR([PLIB header files not found])) + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + + +AC_OUTPUT( Makefile \ + src/Makefile \ + ) + diff --git a/demos/p-guide/doc/p-guide.html b/demos/p-guide/doc/p-guide.html new file mode 100644 index 0000000..07d0bfe --- /dev/null +++ b/demos/p-guide/doc/p-guide.html @@ -0,0 +1,146 @@ + + + + + + + PUI-Based GUI Designer (P-GUIDE) Documentation + + + +

+P-GUIDE Documentation

+ +

+PUI-Based Graphical User Interface Designer

+P-GUIDE comes up in three OpenGL windows.  The first window is the +Main window in which the user arranges the widgets of his graphical user +interface.  The second window is the Widget window which contains +one button for each PUI widget class.  The third window is the Status +window which allows the user to see and control various widget and window +parameters in the main window. +
The basic concept of P-GUIDE is that the user will click on a button +in the Widget window and then click in the Main window to create a widget +for his GUI.  He will then use the Status window to modify the settings +of that widget.  The Main window reflects the changes he makes in +real time.  When the user has created as many widgets as he likes, +he clicks on the "Write Window Code" button in the Status window and P-GUIDE +will write a file of C++ code which implements the GUI using OpenGL, GLUT, +and PUI. +
P-GUIDE is offered to the PLIB community under a GNU Public License +(GPL).  Users are free to modify the source code as they see fit but +must offer their changes back to the community. +

+GUI Window Control

+The user may size, position, color, and name his window by using the input +box widgets at the top of the Status window.  All the changes except +the window position are reflected immediately in the Main window. +
The user may also change the size of his window by clicking on an edge +of the window and dragging on it.  Any widgets which the user has +already defined and placed in the window will remain stationary while the +window is being resized; this means that if the user drags the left-hand +edge of the window to change its width, the x-coordinates of the widgets +will change.  (The user is cautioned against moving his mouse off +one side of the window, dragging it around the outside of the window, and +clicking on the opposite side of the window from the outside.  Due +to a limitation in the windowing system this will reposition the widgets +in the window incorrectly.) +

+Placing Widgets

+To place a widget in the GUI, the user clicks on that widget's button in +the Widget window and then clicks in the Main window.  The new widget +will be placed in the Main window with its lower left-hand corner at the +mouse position.  The widget will have an initial size of 90 pixels +wide by 20 pixels high. +
If the user wants to place more than one widget of the same type in +his GUI, he can press the Control key when he clicks on that widget's button +in the Widget window.  He will then click in the Main window once +for each widget of the given type that he wants to create.  Pressing +the Escape key or clicking on another widget's button in the Widget window +will deactivate the button. +
A widget which has just been placed or has just been clicked on by +the mouse is called the Active widget.  It is surrounded by a black +line. +

+Widget Settings

+The user can control the following settings of his widget from controls +in the Status window: +
    +
  • +Name (the name of the variable which is the pointer to the widget in the +program)
  • + +
  • +Position
  • + +
  • +Size
  • + +
  • +Legend text and position
  • + +
  • +Label text and position
  • + +
  • +Callbacks
  • +
+ +The user may also modify the size and position of his widgets directly +in the Main window.  To change the size, he clicks inside the widget +within five pixels of the edge and then drags the mouse to extend the widget.  +To change the position he clicks inside the widget away from the edges +and drags the mouse. +
The user may also move the Active widget one pixel at a time by pressing +the four arrow keys.  Holding the Control key while pressing the arrow +keys will move the Active widget ten pixels at a time. +

+Widget Visibility

+There are obviously times when the user will want to draw more than one +widget in the same place in the GUI and only show them one at a time.  +To allow this capability the Status window has a button box which controls +whether the widget in the Main window is visible or not.  To hide +a widget the user simply clicks on the "Visible" button box.  If he +then clicks on the "Visible" button box again the Active widget will reappear.  +If the user selects another Active widget, there is no simple and direct +way to make the single hidden widget reappear in the main window. +
To allow the user to make widgets reappear, the Status window has a +one-shot button labelled "Reveal All Widgets".  This makes all the +widgets in the Main window visible again.  The user can then select +the widgets he wishes to hide again and make them invisible. +
When the program writes the C++ code to create the GUI, the widgets +which are not visible in the Main window are hidden by default. +

+Widget Layers

+Many times the order in which widgets are drawn is of vital importance.  +For example, the user may want to fill a window with a frame widget to +provide a pleasing background for the actual widgets of the GUI.  +In order to allow this the program assigns each widget in the Main window +a layer number.  These layer numbers start at zero and increase.  +When the Main window is being drawn, the widgets in lower-numbered layers +are drawn first and the widgets with larger layer numbers are drawn after +(and therefore on top of) them.  When the C++ code to create the GUI +is being written, the widgets in the lower-numbered layers are created +first. +

+Program Controls

+Basic program control is provided by the three buttons at the bottom of +the Status window.  Clicking on the "Write Window Code" button will +cause a file selector widget to pop up for the user to enter a file name +into which the code is to be written.  An entire file is written which +contains the necessary include, window data, a default font definition, +widget variable definitions, GLUT window callbacks, widget callbacks for +each widget, and a complete main program that creates and manages the GLUT +window, initializes PUI, creates the widgets, and calls the GLUT main loop.  +(At the moment the widget definition calls are somewhat lacking and the +user will need to modify them before he can compile the code.) +
If the user clicks on the "Clear" button, all the widgets in the Main +window will be erased.  If the user has changed the window at all +since the last time he wrote code or cleared the Main window, the program +will pop up a box asking if he is sure. +
If the user clicks on the "Quit" button, the program will stop.  +Again, if there have been changes since the last code writing or clearing, +the program will ask if he is sure. + + + diff --git a/demos/p-guide/pGuide.dsw b/demos/p-guide/pGuide.dsw new file mode 100644 index 0000000..b798382 --- /dev/null +++ b/demos/p-guide/pGuide.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "pGuide"=.\src\pGuide.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/demos/p-guide/src/LoadSave.cxx b/demos/p-guide/src/LoadSave.cxx new file mode 100644 index 0000000..6e88ecb --- /dev/null +++ b/demos/p-guide/src/LoadSave.cxx @@ -0,0 +1,756 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: LoadSave.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include + +#include "WidgetList.h" + +#define TAG_SAVEFILE 0x00000001 +#define TAG_WINDOW 0x00000002 +#define TAG_OBJECT 0x00000004 +#define TAG_WIDGET 0x00000008 +#define TAG_SIZE 0x00000010 +#define TAG_POSITION 0x00000020 +#define TAG_COLOR 0x00000040 +#define TAG_LEGEND 0x00000080 +#define TAG_LABEL 0x00000100 +#define TAG_DATA 0x00000120 + +#define CALLBACK_UP 0x00000001 +#define CALLBACK_ACTIVE 0x00000002 +#define CALLBACK_DOWN 0x00000004 + +#ifdef WIN32 + #define PATH_SEPARATOR '\\' +#else + #define PATH_SEPARATOR '/' +#endif + +// From the Main Window: + +extern WidgetList *widgets ; +extern int max_layer ; + +extern int main_window_width ; +extern int main_window_height ; + +extern char main_window_name [ PUSTRING_MAX ] ; + +extern int main_window_x ; +extern int main_window_y ; + +extern int widget_number ; + +extern bool main_window_changed ; +extern bool currently_loading ; /* Var for the Reshape function to not do its thing */ +extern char pguide_current_directory [ PUSTRING_MAX ] ; + +extern bool autolock ; + + +extern float main_window_color_r, main_window_color_g, + main_window_color_b, main_window_color_a ; + +// From the Status Window + +extern void setStatusWidgets ( WidgetList *wid ); +extern int status_window ; +extern int main_window ; +extern puaFileSelector *file_selector ; + +// Duplication checking +static void chk_dupname ( WidgetList *new_wid ) ; +static puInput *dup_newname = (puInput *)NULL ; +static puDialogBox *dup_dialog = (puDialogBox *)NULL ; +static WidgetList *nextwid ; + +// Now our turn + +void saveProject ( puObject *ob ) { + char* filename ; + file_selector -> getValue ( &filename ) ; + if (filename[0] == '\0') + { + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + return ; + } + + /* Save the new current directory */ + strcpy(pguide_current_directory, filename) ; + int i = strlen(pguide_current_directory); + while (pguide_current_directory[i] != PATH_SEPARATOR) { + if (i>0) i-- ; + else break ; + } + pguide_current_directory[i+1] = '\0' ; + + /* If they didn't give an extension, then tack ".xml" onto the end. */ + if(!strstr(filename, ".")) + sprintf(filename, "%s.xml", filename); + + FILE *out = fopen ( filename, "wt" ) ; + if ( !out ) + printf ( "ERROR opening file <%s> for writing\n", filename ) ; + + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + + if ( !out ) return ; + + /* Begin writing the XML */ + + const char *autolock_text; + + if ( !autolock ) + autolock_text="FALSE"; + else + autolock_text="TRUE"; + + char projectname [] = "Test Project"; + + fprintf ( out, "\n\n\n\n" ) ; + fprintf ( out, "VERSION\n") ; + fprintf ( out, "%s\n", projectname) ; + fprintf ( out, "%s\n", autolock_text) ; + fprintf ( out, "\n\t%s\n", main_window_name) ; + fprintf ( out, "\t\n\t\t%d\n", main_window_width) ; + fprintf ( out, "\t\t%d\n\t\n", main_window_height) ; + fprintf ( out, "\t\n\t\t%d\n", main_window_x) ; + fprintf ( out, "\t\t%d\n\t\n", main_window_y) ; + fprintf ( out, "\t\n\t\t%f\n\t\t%f\n\t\t%f\n", main_window_color_r, main_window_color_g, main_window_color_b) ; + fprintf ( out, "\t\t%f\n\t\n\n\n", main_window_color_a) ; + fprintf ( out, "\n") ; + /* Now begin spitting out the actual widget data. */ + + WidgetList *wid = widgets ; + const char *place_name [] = { "PUPLACE_TOP_LEFT", "PUPLACE_TOP_CENTERED", "PUPLACE_TOP_RIGHT", + "PUPLACE_CENTERED_LEFT", "PUPLACE_CENTERED_RIGHT", + "PUPLACE_BOTTOM_LEFT", "PUPLACE_BOTTOM_CENTERED", "PUPLACE_BOTTOM_RIGHT", + "PUPLACE_CENTERED_CENTERED", + "PUPLACE_ABOVE_LEFT", "PUPLACE_ABOVE_RIGHT", + "PUPLACE_BELOW_LEFT", "PUPLACE_BELOW_RIGHT", + "PUPLACE_UPPER_LEFT", "PUPLACE_UPPER_RIGHT", + "PUPLACE_LOWER_LEFT", "PUPLACE_LOWER_RIGHT" } ; + + + while ( wid ) + { + puObject *obj = wid->obj ; + int x, y, w, h ; + const char *hidden ; + const char *locked ; + const char *bool_one ; + const char *bool_two ; + const char *bool_three ; + obj->getPosition ( &x, &y ) ; + obj->getSize ( &w, &h ) ; + + fprintf ( out, "\t\n\t\t%s\n", wid->object_name) ; + if (wid->legend_text != NULL) + fprintf ( out, "\t\t\n\t\t\t%s\n\t\t\t%s\n\t\t\n", wid->legend_text, place_name [ obj->getLegendPlace () ] ) ; + + if (wid->label_text != NULL) + fprintf ( out, "\t\t\n", wid->label_text, place_name [ obj->getLabelPlace () ] ) ; + + fprintf ( out, "\t\t%s\n", wid->object_type_name) ; + + if ( !wid->visible ) + hidden="FALSE"; + else + hidden="TRUE"; + + if ( !wid->locked ) + locked="FALSE"; + else + locked="TRUE"; + + if ( wid->callbacks & CALLBACK_UP ) // Up-callback defined + fprintf ( out, "\t\tUp\n") ; + if ( wid->callbacks & CALLBACK_ACTIVE ) // Active callback defined + fprintf ( out, "\t\tActive\n") ; + if ( wid->callbacks & CALLBACK_DOWN ) // Down-callback defined + fprintf ( out, "\t\tDown\n") ; + if ( wid->callbacks == 0 ) + fprintf ( out, "\t\t\n") ; + + fprintf ( out, "\t\t%d\n", wid->layer) ; + + fprintf ( out, "\t\t%s\n", hidden) ; + fprintf ( out, "\t\t%s\n", locked) ; + fprintf ( out, "\t\t\n\t\t\t%d\n\t\t\t%d\n\t\t\n", w, h) ; + fprintf ( out, "\t\t\n\t\t\t%d\n\t\t\t%d\n\t\t\n", x, y) ; + + fprintf ( out, "\t\t\n\t\t\t%d\n\t\t\t%d\n", wid->intval1, wid->intval2) ; + if ( wid->boolval1 ) + bool_one = "TRUE"; + else + bool_one = "FALSE"; + + if ( wid->boolval2 ) + bool_two = "TRUE"; + else + bool_two = "FALSE"; + + if ( wid->boolval3 ) + bool_three = "TRUE"; + else + bool_three = "FALSE"; + + fprintf ( out, "\t\t\t%s\n\t\t\t%s\n\t\t\t%s\n", bool_one, bool_two, bool_three) ; + + fprintf ( out, "\t\t\t%f\n\t\t\t%f\n", wid->floatval1, wid->floatval2) ; + fprintf ( out, "\t\t\t%f\n\t\t\t%f\n", wid->floatval3, wid->floatval4) ; + fprintf ( out, "\t\t\t%f\n\t\t\t%f\n", wid->floatval5, wid->floatval6) ; + + if (wid->allowed) + fprintf ( out, "\t\t\t%s\n", wid->allowed) ; + + /* Walk through the list of items, and make a seperate entry for each line in the string */ + if (wid->items) + { + char *temp_items = wid->items ; + char *cr = strchr(temp_items, '\n'); + while ( cr != '\0' ) + { + *cr = '\0'; + fprintf ( out, "\t\t\t%s\n", temp_items); + *cr = '\n'; + temp_items = cr + 1; + cr = strchr(temp_items, '\n'); + } + if (wid->object_type == PUCLASS_ARROW) + { + fprintf ( out, "\t\t\t%s\n", temp_items); + } + } + + fprintf ( out, "\t\t\n\t\n") ; + wid = wid->next ; + } + + fprintf ( out, "\n\n") ; /* XML output done */ + fclose (out) ; +} + +/*static void gotoNextLine( char* buffer ) +{ + while (buffer != "\n") + buffer++ ; +}*/ + +void loadProject ( puObject *ob ) { + extern puInput *window_name ; + extern puInput *window_size_x ; + extern puInput *window_size_y ; + extern puInput *window_position_x ; + extern puInput *window_position_y ; + + extern puaSpinBox *window_color_r ; + extern puaSpinBox *window_color_g ; + extern puaSpinBox *window_color_b ; + extern puaSpinBox *window_color_a ; + + char* filename ; + file_selector -> getValue ( &filename ) ; + if (filename[0] == '\0') + { + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + return ; + } + + /* Save the new current directory */ + strcpy(pguide_current_directory, filename) ; + int i = strlen(pguide_current_directory); + while (pguide_current_directory[i] != PATH_SEPARATOR) { + if (i>0) i-- ; + else break ; + } + pguide_current_directory[i+1] = '\0' ; + + FILE *in = fopen ( filename, "rt" ) ; + if ( !in ) + printf ( "ERROR opening file <%s> for reading\n", filename ) ; + + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + + if ( !in ) return ; + + char rawbuffer[2048]; + // Verify file is XML + + float tver = 0.0f ; + char encversion[PUSTRING_MAX]; + fgets( rawbuffer, sizeof(rawbuffer), in) ; + sscanf(rawbuffer,"", &tver, encversion) ; + if (!tver) { + printf ("File does not contain proper XML headings.\n"); + return ; + } + + // Verify file is P-Guide + char tag[PUSTRING_MAX] ; + char tagvalue[PUSTRING_MAX] ; + fgets( rawbuffer, sizeof(rawbuffer), in) ; + + if (!sscanf(rawbuffer,"", tag)) { + printf ("File, though XML, does not contain P-Guide information (or has damaged header information).\n"); + return ; + } + + // Start reading XML + main_window_changed = true ; + currently_loading = true ; + int groupid = 0 ; + puObject *new_obj = NULL; + WidgetList *new_wid = NULL ; + /* Dandy little variables */ + int wid_val_y = 0, wid_val_x = 0, val_object_width = 20, val_object_height = 20, val_label_place = 4, val_legend_place = 8; + int intval1 = 0, intval2 = 0 ; + bool boolval1 = false, boolval2 = false, boolval3 = false ; + float floatval1 = 0.0f, floatval2 = 0.0f, floatval3 = 0.0f, floatval4 = 0.0f, floatval5 = 0.0f, floatval6 = 0.0f; + + while(fgets( rawbuffer, sizeof(rawbuffer), in)) + { + /**************************************************************/ + /* Key for Groups in our XML file located at top of this file */ + /**************************************************************/ + + *tag = '\0'; + *tagvalue = '\0'; + char *buffer = rawbuffer; + + /* From here on we walk, line by line, through the XML and parse any tags we come to. */ + while (*buffer == '\t' || *buffer == ' ') /* Ignore the tabs and spaces */ + buffer++ ; + sscanf(buffer,"<%[^>]>", tag); /* Get the tag */ + + while (*buffer !='>') buffer++; + + sscanf(buffer,">%[a-zA-Z_!@#$^&*0123456789-+/\\{}()=~`,. ]setValue(tagvalue) ; + strcpy(main_window_name, tagvalue); + } + if (groupid & TAG_SIZE) + { + if ( strstr(tag,"width") ) + window_size_x->setValue(tagvalue) ; + if ( strstr(tag,"height") ) + window_size_y->setValue(tagvalue) ; + } + if (groupid & TAG_POSITION) + { + if ( strstr(tag,"x") ) + window_position_x->setValue(tagvalue) ; + if ( strstr(tag,"y") ) + window_position_y->setValue(tagvalue) ; + } + if (groupid & TAG_COLOR) + { + if ( strstr(tag,"r") ) + window_color_r->setValue(tagvalue) ; + if ( strstr(tag,"g") ) + window_color_g->setValue(tagvalue) ; + if ( strstr(tag,"b") ) + window_color_b->setValue(tagvalue) ; + if ( strstr(tag,"a") ) + window_color_a->setValue(tagvalue) ; + } + } + if (groupid & TAG_OBJECT) + { + if ( strstr(tag,"/widget") ) + { + extern WidgetList *widgets ; + /* Create a new frame in the viewing window (for manipulation). */ + new_obj = new puFrame ( 0, 0, val_object_width, val_object_height ) ; + new_obj->setWindow( main_window ) ; + /* Set the representative widget's position */ + new_obj->setPosition ( wid_val_x, wid_val_y ) ; + new_obj->setLegend ( new_wid->object_type_name ) ; + new_obj->setLegendPlace ( val_legend_place ) ; + if (new_wid->label_text) + { + new_obj->setLabel ( new_wid->label_text ) ; + new_obj->setLabelPlace ( val_label_place ) ; /**/ + } + + new_wid->boolval1 = boolval1; + new_wid->boolval2 = boolval2; + new_wid->boolval3 = boolval3; + new_wid->intval1 = intval1; + new_wid->intval2 = intval2; + new_wid->floatval1 = floatval1; + new_wid->floatval2 = floatval2; + new_wid->floatval3 = floatval3; + new_wid->floatval4 = floatval4; + new_wid->floatval5 = floatval5; + new_wid->floatval6 = floatval6; + + intval1 = intval2 = 0 ; + boolval1 = boolval2 = boolval3 = false ; + floatval1 = floatval2 = floatval3 = floatval4 = floatval5 = floatval6 = 0.0f; + new_wid->obj = new_obj ; + new_wid->next = widgets ; + widgets = new_wid ; + + } else if ( strstr(tag,"widget") ) { + /* Add the new object to the list of widgets (for the builder itself) */ + new_wid = new WidgetList ; + + /* Set those dandy variables */ + wid_val_y = 0; wid_val_x = 0; + val_object_width = 20; val_object_height = 20; + val_label_place = 4; val_legend_place = 8; + new_wid->callbacks = 0; + new_wid->allowed = (char *)NULL ; + new_wid->items = (char *)NULL ; + new_wid->locked = false; + new_wid->legend_text = NULL ; + new_wid->label_text = NULL ; + } + + + if (groupid & TAG_WIDGET) + { + if ( strstr(tag,"name") ) + { + strcpy(new_wid->object_name, tagvalue) ; + /* As names are copied, look for defaults (widget0..9999), */ + /* and if it's higher than the current widget_number, set */ + /* widget_number to it. */ + int tmp_wid_num = 0; + sscanf(tagvalue, "widget%d", &tmp_wid_num) ; + if (tmp_wid_num >= widget_number) widget_number = tmp_wid_num + 1; + } + if ( strstr(tag,"type") ) + { + new_wid->object_type = 0 ; + new_wid->object_type_name = new char [strlen(tagvalue)+1] ; + strcpy(new_wid->object_type_name, tagvalue) ; + /* BEWARE: Popup[menu], BiSlider[WithEnds], and Button[box] want to be each other! Don't let them! */ + + if ( strstr(new_wid->object_type_name, "puaList")) new_wid->object_type |= PUCLASS_LIST ; + if ( strstr(new_wid->object_type_name, "puaChooser")) new_wid->object_type |= PUCLASS_CHOOSER ; + if ( strstr(new_wid->object_type_name, "puaSliderWithInput")) new_wid->object_type |= PUCLASS_SLIDERWITHINPUT ; + if ( strstr(new_wid->object_type_name, "puaBiSliderWithEnds")) new_wid->object_type |= PUCLASS_BISLIDERWITHENDS ; + else if ( strstr(new_wid->object_type_name, "puaBiSlider")) new_wid->object_type |= PUCLASS_BISLIDER ; + if ( strstr(new_wid->object_type_name, "puaScrollBar")) new_wid->object_type |= PUCLASS_SCROLLBAR ; + if ( strstr(new_wid->object_type_name, "puaSpinBox")) new_wid->object_type |= PUCLASS_SPINBOX ; /**/ + if ( strstr(new_wid->object_type_name, "puaSelectBox")) new_wid->object_type |= PUCLASS_SELECTBOX ; + if ( strstr(new_wid->object_type_name, "puaComboBox")) new_wid->object_type |= PUCLASS_COMBOBOX ; + if ( strstr(new_wid->object_type_name, "puaLargeInput")) new_wid->object_type |= PUCLASS_LARGEINPUT ; + if ( strstr(new_wid->object_type_name, "puaVerticalMenu")) new_wid->object_type |= PUCLASS_VERTMENU ; + if ( strstr(new_wid->object_type_name, "puaTriSlider")) new_wid->object_type |= PUCLASS_TRISLIDER ; + if ( strstr(new_wid->object_type_name, "puaFileSelector")) new_wid->object_type |= PUCLASS_FILESELECTOR ; + if ( strstr(new_wid->object_type_name, "puDial")) new_wid->object_type |= PUCLASS_DIAL ; + if ( strstr(new_wid->object_type_name, "puListBox")) new_wid->object_type |= PUCLASS_LISTBOX ; + if ( strstr(new_wid->object_type_name, "puArrowButton")) new_wid->object_type |= PUCLASS_ARROW ; + if ( strstr(new_wid->object_type_name, "puDialogBox")) new_wid->object_type |= PUCLASS_DIALOGBOX ; /**/ + if ( strstr(new_wid->object_type_name, "puSlider")) new_wid->object_type |= PUCLASS_SLIDER ; + if ( strstr(new_wid->object_type_name, "puButtonBox")) new_wid->object_type |= PUCLASS_BUTTONBOX ; + else if ( strstr(new_wid->object_type_name, "puButton")) new_wid->object_type |= PUCLASS_BUTTON ; /* See above for why the elses' */ + if ( strstr(new_wid->object_type_name, "puInput")) new_wid->object_type |= PUCLASS_INPUT ; + if ( strstr(new_wid->object_type_name, "puMenuBar")) new_wid->object_type |= PUCLASS_MENUBAR ; + if ( strstr(new_wid->object_type_name, "puPopupMenu")) new_wid->object_type |= PUCLASS_POPUPMENU ; /**/ + else if ( strstr(new_wid->object_type_name, "puPopup")) new_wid->object_type |= PUCLASS_POPUP ; /**/ + if ( strstr(new_wid->object_type_name, "puOneShot")) new_wid->object_type |= PUCLASS_ONESHOT ; + if ( strstr(new_wid->object_type_name, "puText")) new_wid->object_type |= PUCLASS_TEXT ; + if ( strstr(new_wid->object_type_name, "puFrame")) new_wid->object_type |= PUCLASS_FRAME ; + } + if ( strstr(tag,"callbacks") ) + { + /* Update callback's bit set */ + if (strstr(tagvalue, "Up")) new_wid->callbacks |= CALLBACK_UP ; + if (strstr(tagvalue, "Down")) new_wid->callbacks |= CALLBACK_DOWN ; + if (strstr(tagvalue, "Active")) new_wid->callbacks |= CALLBACK_ACTIVE ; + } + if ( strstr(tag,"layer") ) + { + new_wid->layer = atoi(tagvalue) ; + if ( max_layer <= new_wid->layer ) max_layer = new_wid->layer + 1 ; + } + if ( strstr(tag,"visible") ) + { + if (strstr(tagvalue, "TRUE")) + new_wid->visible = true ; + else + new_wid->visible = false ; + } + if ( strstr(tag,"locked") ) + { + if (strstr(tagvalue, "TRUE")) + new_wid->locked = true ; + else + new_wid->locked = false ; + } + if (groupid & TAG_LEGEND) + { + if ( strstr(tag,"text") ) + { + delete new_wid->legend_text; + new_wid->legend_text = new char [strlen(tagvalue)+1] ; + strcpy(new_wid->legend_text, tagvalue) ; + } + if ( strstr(tag,"pos") ) + { + if (strstr(tagvalue, "PUPLACE_TOP_LEFT")) val_legend_place = PUPLACE_TOP_LEFT ; + if (strstr(tagvalue, "PUPLACE_TOP_CENTERED")) val_legend_place = PUPLACE_TOP_CENTERED ; + if (strstr(tagvalue, "PUPLACE_TOP_RIGHT")) val_legend_place = PUPLACE_TOP_RIGHT ; + if (strstr(tagvalue, "PUPLACE_CENTERED_LEFT")) val_legend_place = PUPLACE_CENTERED_LEFT ; + if (strstr(tagvalue, "PUPLACE_CENTERED_RIGHT")) val_legend_place = PUPLACE_CENTERED_RIGHT ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_LEFT")) val_legend_place = PUPLACE_BOTTOM_LEFT ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_CENTERED")) val_legend_place = PUPLACE_BOTTOM_CENTERED ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_RIGHT")) val_legend_place = PUPLACE_BOTTOM_RIGHT ; + if (strstr(tagvalue, "PUPLACE_CENTERED_CENTERED")) val_legend_place = PUPLACE_CENTERED_CENTERED ; + } + } + if (groupid & TAG_LABEL) + { + if ( strstr(tag,"text") ) + { + delete new_wid->label_text; + new_wid->label_text = new char [strlen(tagvalue)+1] ; + strcpy(new_wid->label_text, tagvalue) ; + } + if ( strstr(tag,"pos") ) + { + if (strstr(tagvalue, "PUPLACE_TOP_LEFT")) val_label_place = PUPLACE_TOP_LEFT ; + if (strstr(tagvalue, "PUPLACE_TOP_CENTERED")) val_label_place = PUPLACE_TOP_CENTERED ; + if (strstr(tagvalue, "PUPLACE_TOP_RIGHT")) val_label_place = PUPLACE_TOP_RIGHT ; + if (strstr(tagvalue, "PUPLACE_CENTERED_LEFT")) val_label_place = PUPLACE_CENTERED_LEFT ; + if (strstr(tagvalue, "PUPLACE_CENTERED_RIGHT")) val_label_place = PUPLACE_CENTERED_RIGHT ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_LEFT")) val_label_place = PUPLACE_BOTTOM_LEFT ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_CENTERED")) val_label_place = PUPLACE_BOTTOM_CENTERED ; + if (strstr(tagvalue, "PUPLACE_BOTTOM_RIGHT")) val_label_place = PUPLACE_BOTTOM_RIGHT ; + if (strstr(tagvalue, "PUPLACE_ABOVE_LEFT")) val_label_place = PUPLACE_ABOVE_LEFT ; + if (strstr(tagvalue, "PUPLACE_ABOVE_RIGHT")) val_label_place = PUPLACE_ABOVE_RIGHT ; + if (strstr(tagvalue, "PUPLACE_BELOW_LEFT")) val_label_place = PUPLACE_BELOW_LEFT ; + if (strstr(tagvalue, "PUPLACE_BELOW_RIGHT")) val_label_place = PUPLACE_BELOW_RIGHT ; + if (strstr(tagvalue, "PUPLACE_UPPER_LEFT")) val_label_place = PUPLACE_UPPER_LEFT ; + if (strstr(tagvalue, "PUPLACE_UPPER_RIGHT")) val_label_place = PUPLACE_UPPER_RIGHT ; + if (strstr(tagvalue, "PUPLACE_LOWER_LEFT")) val_label_place = PUPLACE_LOWER_LEFT ; + if (strstr(tagvalue, "PUPLACE_LOWER_RIGHT")) val_label_place = PUPLACE_LOWER_RIGHT ; + } + } + if (groupid & TAG_SIZE) + { + if ( strstr(tag,"width") ) + val_object_width = atoi(tagvalue) ; + if ( strstr(tag,"height") ) + val_object_height = atoi(tagvalue) ; + } + if (groupid & TAG_POSITION) + { + if ( strstr(tag,"x") ) + wid_val_x = atoi(tagvalue) ; + if ( strstr(tag,"y") ) + wid_val_y = atoi(tagvalue) ; + } + if (groupid & TAG_DATA) + { + if ( strstr(tag,"int1") ) + intval1 = atoi (tagvalue) ; + if ( strstr(tag,"int2") ) + intval2 = atoi (tagvalue) ; + if ( strstr(tag,"bool1") ) + { + if (strstr(tagvalue, "TRUE")) + boolval1 = true ; + else + boolval1 = false ; + } + if ( strstr(tag,"bool2") ) + { + if (strstr(tagvalue, "TRUE")) + boolval2 = true ; + else + boolval2 = false ; + } + if ( strstr(tag,"bool3") ) + { + if (strstr(tagvalue, "TRUE")) + boolval3 = true ; + else + boolval3 = false ; + } + if ( strstr(tag,"float1") ) + floatval1 = (float)atof (tagvalue) ; + if ( strstr(tag,"float2") ) + floatval2 = (float)atof (tagvalue) ; + if ( strstr(tag,"float3") ) + floatval3 = (float)atof (tagvalue) ; + if ( strstr(tag,"float4") ) + floatval4 = (float)atof (tagvalue) ; + if ( strstr(tag,"float5") ) + floatval5 = (float)atof (tagvalue) ; + if ( strstr(tag,"float6") ) + floatval6 = (float)atof (tagvalue) ; + + if ( strstr(tag,"allowed") ) + { + new_wid->allowed = new char [strlen(tagvalue)+1] ; + strcpy(new_wid->allowed, tagvalue) ; + } + + if ( strstr(tag,"item") ) + { + strcat(tagvalue, "\n"); + if (!new_wid->items) + { + new_wid->items = new char [1024] ; + strcpy(new_wid->items, tagvalue) ; + } + else + strcat(new_wid->items, tagvalue) ; + } + } + } + } + } + + if (strstr(tag, "/savefile")) + { + static int mouse_x ; + static int mouse_y ; + setStatusWidgets( new_wid ); + glutSetWindow ( main_window ) ; + glutSetWindowTitle ( window_name->getStringValue() ) ; + /* Fake out the Reshape parts that determine whether to move widgets around or not. */ + mouse_x = 999 ; + mouse_y = 999 ; + glutReshapeWindow ( window_size_x->getValue(), window_size_y->getValue() ) ; + glutSetWindow ( status_window ) ; + } + + buffer[0] = '\0'; + } + fclose (in); + WidgetList *wid = widgets; + /* Check name for duplication */ + nextwid = wid; /* Note: Yes, this is inelegant, but if we loop here, we end up making many copies of the same dialog box in the same memory space. This is a Bad Thing. */ + chk_dupname(nextwid); + /* Done checking name for duplication */ +} + +static void chk_dupname_ok_cb ( puObject *ob ) +{ + /* The user's new name from the dialog has already been set to the object_name*/ + /* Close dialogbox and update main window ...*/ + puDeleteObject ( dup_dialog ) ; + dup_dialog = (puDialogBox *)NULL ; + dup_newname = (puInput *)NULL ; + /*but force a recheck afterwards to ensure it's an okay substitution. */ + nextwid = nextwid->next ; + chk_dupname(nextwid); +} + +static void chk_dupname ( WidgetList *new_wid ) +{ + if (!new_wid) + return; + WidgetList *wid = widgets ; + while ( wid ) + { + if ( (strcmp(new_wid->object_name,wid->object_name) == 0) && (new_wid != wid) ) + { + /* Popup a dialog telling user something's bad! */ + dup_dialog = new puDialogBox ( 20, 20 ) ; + new puFrame ( 0, 0, 460, 120 ) ; + puText *text = new puText ( 80, 85 ) ; + text->setLabel ( "ERROR: Name already used." ) ; + text->setLabelFont(PUFONT_TIMES_ROMAN_24); + puText *directions = new puText ( 90, 65 ) ; + directions->setLabel ( "Please type in a new, unique name to continue." ) ; + directions->setLabelFont(PUFONT_HELVETICA_12); + dup_newname = new puInput (20,40,440,60) ; + dup_newname->setValuator(new_wid->object_name); + dup_newname->setValidData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_012345679"); + puOneShot *ok = new puOneShot ( 200, 10, "Accept" ) ; + ok->setCallback ( chk_dupname_ok_cb ) ; + dup_dialog->close () ; + dup_dialog->reveal () ; + break ; + } + wid = wid->next ; + } +} + + diff --git a/demos/p-guide/src/Makefile.am b/demos/p-guide/src/Makefile.am new file mode 100644 index 0000000..ecc73d2 --- /dev/null +++ b/demos/p-guide/src/Makefile.am @@ -0,0 +1,9 @@ +bin_PROGRAMS = pguide + +pguide_SOURCES = pGuide.cxx StatusWindow.cxx WidgetList.h \ + WidgetWindow.cxx WriteCode.cxx LoadSave.cxx PropertiesWindow.cxx + +pguide_LDADD = -lplibpuaux + +EXTRA_DIST = pGuide.dsp + diff --git a/demos/p-guide/src/PropertiesWindow.cxx b/demos/p-guide/src/PropertiesWindow.cxx new file mode 100644 index 0000000..ac4a5e3 --- /dev/null +++ b/demos/p-guide/src/PropertiesWindow.cxx @@ -0,0 +1,610 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: PropertiesWindow.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + +// Status Window + +#include + +#include "WidgetList.h" + +// From the Main Window: + +extern int max_layer ; + +extern WidgetList *active_widget ; +extern puObject *active_object ; + +extern char pguide_current_directory [ PUSTRING_MAX ] ; + +extern bool main_window_changed ; + +// Status window parameters +int properties_window = 0 ; // Window handle +extern int main_window ; + +// Status window widgets +puGroup *properties_group ; + +// Function to set the widgets from the active object + + +// GLUT Status Window Callbacks + +static void properties_window_specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + + glutPostRedisplay () ; +} + +static void properties_window_keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; + + glutPostRedisplay () ; +} + +static void properties_window_motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + + glutPostRedisplay () ; +} + +static void properties_window_mousefn ( int button, int updown, int x, int y ) +{ + puMouse ( button, updown, x, y ) ; + + glutPostRedisplay () ; +} + +static void properties_window_displayfn ( void ) +{ + glutSetWindow ( properties_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + puDisplay () ; + + /* Update GLUT */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +// PUI Callback Functions: + +static void cb_properties_close (puObject *obj) +{ + /* Save the changes ... Needed? */ + if ( puActiveWidget() ) + puActiveWidget() -> invokeDownCallback () ; /* Just make sure the last widget saves ... */ + /* Delete the widgets */ + puDeleteObject( properties_group ); + /* Destroy the window */ + glutDestroyWindow( properties_window ); + properties_window = 0; + glutSetWindow( main_window ); +} + +// Data setting standard callbacks + +static void cb_items (puObject *obj) +{ + /*Set the Items*/ + if (active_widget) + { + delete active_widget->items; + active_widget->items = new char [strlen(obj->getStringValue())+1] ; + strcpy(active_widget->items, obj->getStringValue()); + } + +} + +static void cb_allowed (puObject *obj) +{ + /*Set the Allowed string*/ + if (active_widget) + { + delete active_widget->allowed; + active_widget->allowed = new char [strlen(obj->getStringValue())+1] ; + strcpy(active_widget->allowed, obj->getStringValue()); + } +} +static void cb_int1 (puObject *obj) +{ + /*Set the first integer storage value */ + if (active_widget) + active_widget->intval1 = obj->getIntegerValue(); +} +static void cb_int2 (puObject *obj) +{ + /*Set the second integer storage value */ + if (active_widget) + active_widget->intval2 = obj->getIntegerValue(); +} +static void cb_bool1 (puObject *obj) +{ + /*Set the first boolean storage value */ + if (active_widget) + { + if (obj->getIntegerValue() == 1) + active_widget->boolval1 = true ; + else + active_widget->boolval1 = false ; + } +} +static void cb_bool2 (puObject *obj) +{ + /*Set the second boolean storage value */ + if (active_widget) + { + if (obj->getIntegerValue() == 1) + active_widget->boolval2 = true ; + else + active_widget->boolval2 = false ; + } +} +static void cb_bool3 (puObject *obj) +{ + /*Set the second boolean storage value */ + if (active_widget) + { + if (obj->getIntegerValue() == 1) + active_widget->boolval3 = true ; + else + active_widget->boolval3 = false ; + } +} +static void cb_float1 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval1 = obj->getFloatValue(); +} +static void cb_float2 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval2 = obj->getFloatValue(); +} +static void cb_float3 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval3 = obj->getFloatValue(); +} +static void cb_float4 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval4 = obj->getFloatValue(); +} +static void cb_float5 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval5 = obj->getFloatValue(); +} +static void cb_float6 (puObject *obj) +{ + /*Set the first floating storage value */ + if (active_widget) + active_widget->floatval6 = obj->getFloatValue(); +} + +// Function to define the window + +int define_properties_window () +{ + properties_window = glutCreateWindow ( "Properties" ) ; + + glutPositionWindow ( 400, 200 ) ; + glutReshapeWindow ( 500, 250 ) ; + glutDisplayFunc ( properties_window_displayfn ) ; + glutKeyboardFunc ( properties_window_keyfn ) ; + glutSpecialFunc ( properties_window_specialfn ) ; + glutMotionFunc ( properties_window_motionfn ) ; + glutMouseFunc ( properties_window_mousefn ) ; + + // Set up the widgets + + properties_group = new puGroup ( 0, 0 ) ; + new puFrame ( 0, 0, 500, 250 ) ; + + puText *properties_instructions = new puText ( 250, 230 ); + properties_instructions->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_instructions->setLabel("Here are the extended options for your"); + + puText *properties_typelabel = new puText ( 250, 215 ); + properties_typelabel->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_typelabel->setLabel(active_widget->object_type_name); + + puOneShot *properties_close = new puOneShot ( 370, 10, 490, 30 ) ; + properties_close->setLegend("Accept"); + properties_close->setCallback(cb_properties_close); + + /* puStyle stuff here*/ + + /* Now customize the display based on the widget currently selected */ + if (active_widget->object_type == PUCLASS_FRAME) + { + puText *properties_nooptions = new puText (25, 100 ); + properties_nooptions->setLabelPlace(PUPLACE_CENTERED_RIGHT); + properties_nooptions->setLabel("There are no options for a puFrame."); + } if (active_widget->object_type == PUCLASS_TEXT) + { + /* Add in font properties? */ + puText *properties_nooptions = new puText (25, 100 ); + properties_nooptions->setLabelPlace(PUPLACE_CENTERED_RIGHT); + properties_nooptions->setLabel("There are no options for puText."); + } if (active_widget->object_type == PUCLASS_BUTTON) + { + puText *properties_nooptions = new puText (25, 100 ); + properties_nooptions->setLabelPlace(PUPLACE_CENTERED_RIGHT); + properties_nooptions->setLabel("There are no options for puButton."); + } if (active_widget->object_type == PUCLASS_ONESHOT) + { + puText *properties_nooptions = new puText (25, 100 ); + properties_nooptions->setLabelPlace(PUPLACE_CENTERED_RIGHT); + properties_nooptions->setLabel("There are no options for puOneShot."); + } if ( (active_widget->object_type == PUCLASS_POPUPMENU) || + (active_widget->object_type == PUCLASS_MENUBAR) || + (active_widget->object_type == PUCLASS_VERTMENU) || + (active_widget->object_type == PUCLASS_LISTBOX) || + (active_widget->object_type == PUCLASS_COMBOBOX) || + (active_widget->object_type == PUCLASS_SELECTBOX) || + (active_widget->object_type == PUCLASS_BUTTONBOX) || + (active_widget->object_type == PUCLASS_LIST) ) + { + /* List */ + puaLargeInput *properties_list_items = new puaLargeInput(10,10,300,195,0,5); + properties_list_items->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_list_items->setLabel("Enter items, each on a new line."); + properties_list_items->setValidData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_012345679 :;.,\n"); + properties_list_items->setCallback(cb_items); + properties_list_items->setDownCallback(cb_items); + properties_list_items->setValue(active_widget->items); + + puText *properties_list_note = new puText(400,190); + properties_list_note->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_list_note->setLabel("Each line will become"); + + puText *properties_list_note2 = new puText(400,175); + properties_list_note2->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_list_note2->setLabel("a selection option."); + + } if (active_widget->object_type == PUCLASS_POPUPMENU) + { + /* Allow a list of menu items, and remember to point out these are hidden when created, and must be reveal()ed. */ + /* NOT CURRENTLY IMPLEMENTED */ + puText *properties_popupmenu_warning = new puText(400,150); + properties_popupmenu_warning->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_popupmenu_warning->setLabel("WARNING: Popup Menus are hidden by \ + default when created. You must use \ + reveal() to make them appear."); + + } if (active_widget->object_type == PUCLASS_MENUBAR) + { + /* Allow a list of menu items */ + + } if (active_widget->object_type == PUCLASS_VERTMENU) + { + /* Allow a list of menu items */ + + } if (active_widget->object_type == PUCLASS_LISTBOX) + { + /* Allow a list of items */ + + } if (active_widget->object_type == PUCLASS_COMBOBOX) + { + /* List and a setCurrentItem, and if editable */ + puaSpinBox *properties_combobox_currentitem = new puaSpinBox(320,135,480,155); + properties_combobox_currentitem->setLabel("Initial Selection:"); + properties_combobox_currentitem->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_combobox_currentitem->setMinValue(0.0f); + properties_combobox_currentitem->setMaxValue(30.0f); + properties_combobox_currentitem->setCallback(cb_int1); + properties_combobox_currentitem->setValue(active_widget->intval1); + + static char *properties_combobox_editable_callback_entries [] = { "No", "Yes", NULL } ; + puButtonBox *properties_combobox_editable = new puButtonBox(320, 50, 480, 120, properties_combobox_editable_callback_entries, TRUE); + properties_combobox_editable->setLabel("Editable?"); + properties_combobox_editable->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_combobox_editable->setCallback(cb_bool1); + properties_combobox_editable->setValue(active_widget->boolval1); + + } if (active_widget->object_type == PUCLASS_SELECTBOX) + { + /* List and a setCurrentItem */ + puaSpinBox *properties_selectbox_currentitem = new puaSpinBox(320,135,480,155); + properties_selectbox_currentitem->setLabel("Initial Selection:"); + properties_selectbox_currentitem->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_selectbox_currentitem->setMinValue(0.0f); + properties_selectbox_currentitem->setMaxValue(30.0f); + properties_selectbox_currentitem->setCallback(cb_int1); + properties_selectbox_currentitem->setValue(active_widget->intval1); + + } if (active_widget->object_type == PUCLASS_BUTTONBOX) + { + /* Allow a list of menu items, and if multiple can be selected at once */ + puText *properties_buttonbox_label = new puText(400,135); + properties_buttonbox_label->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_buttonbox_label->setLabel("Allow multiple"); + + static char *properties_buttonbox_multiselect_callback_entries [] = { "No", "Yes", NULL } ; + puButtonBox *properties_buttonbox_multiselect = new puButtonBox(320, 50, 480, 120, properties_buttonbox_multiselect_callback_entries, TRUE); + properties_buttonbox_multiselect->setLabel("selections?"); + properties_buttonbox_multiselect->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_buttonbox_multiselect->setCallback(cb_bool1); + properties_buttonbox_multiselect->setValue(active_widget->boolval1); + + } if ( (active_widget->object_type == PUCLASS_SLIDER ) || + (active_widget->object_type == PUCLASS_BISLIDER ) || + (active_widget->object_type == PUCLASS_TRISLIDER ) || + (active_widget->object_type == PUCLASS_DIAL ) || + (active_widget->object_type == PUCLASS_SPINBOX ) || + (active_widget->object_type == PUCLASS_SCROLLBAR ) || + (active_widget->object_type == PUCLASS_SLIDERWITHINPUT ) || + (active_widget->object_type == PUCLASS_BISLIDERWITHENDS )) + { + /* int minx, int miny, int sz, {TRUE|FALSE}, int width */ + puaSpinBox *properties_range_setmaxvalue = new puaSpinBox(30,150,110,170); + properties_range_setmaxvalue->setLabel("Maximum Value:"); + properties_range_setmaxvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_range_setmaxvalue->setMinValue(-5000.0f); + properties_range_setmaxvalue->setMaxValue(5000.0f); + properties_range_setmaxvalue->setCallback(cb_float1); + properties_range_setmaxvalue->setValue(active_widget->floatval1); + + puaSpinBox *properties_range_setminvalue = new puaSpinBox(230,150,310,170); + properties_range_setminvalue->setLabel("Minimum Value:"); + properties_range_setminvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_range_setminvalue->setMinValue(-5000.0f); + properties_range_setminvalue->setMaxValue(5000.0f); + properties_range_setminvalue->setCallback(cb_float2); + properties_range_setminvalue->setValue(active_widget->floatval2); + + puaSpinBox *properties_range_setstepvalue = new puaSpinBox(400,150,480,170); + properties_range_setstepvalue->setLabel("Step Size:"); + properties_range_setstepvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_range_setstepvalue->setMinValue(0.0f); + properties_range_setstepvalue->setMaxValue(5000.0f); + properties_range_setstepvalue->setCallback(cb_float3); + properties_range_setstepvalue->setValue(active_widget->floatval3); + + static char *properties_range_cbmode_callback_entries [] = { "On Click", "Always", NULL } ; + puButtonBox *properties_range_cbmode = new puButtonBox(20, 80, 170, 130, properties_range_cbmode_callback_entries, TRUE); + properties_range_cbmode->setLabel("Callback Mode:"); + properties_range_cbmode->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_range_cbmode->setCallback(cb_bool1); + properties_range_cbmode->setValue(active_widget->boolval1); + + } if ( (active_widget->object_type == PUCLASS_SLIDER ) || + (active_widget->object_type == PUCLASS_BISLIDER ) || + (active_widget->object_type == PUCLASS_TRISLIDER ) || + (active_widget->object_type == PUCLASS_SCROLLBAR ) ) + { + static char *properties_range_vertical_callback_entries [] = { "Horizontal", "Vertical", NULL } ; + puButtonBox *properties_range_vertical = new puButtonBox(20, 10, 170, 60, properties_range_vertical_callback_entries, TRUE); + properties_range_vertical->setLabel("Orientation:"); + properties_range_vertical->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_range_vertical->setValue(active_widget->boolval2); + properties_range_vertical->setCallback(cb_bool2); + + } if (active_widget->object_type == PUCLASS_SLIDER) + { + puaSpinBox *properties_slider_value = new puaSpinBox(230,110,310,130); + properties_slider_value->setLabel("Starting Value:"); + properties_slider_value->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_slider_value->setMinValue(-5000.0f); + properties_slider_value->setMaxValue(5000.0f); + properties_slider_value->setValue(active_widget->floatval4); + properties_slider_value->setStepSize(0.1f); + properties_slider_value->setCallback(cb_float4); + + } if ((active_widget->object_type == PUCLASS_BISLIDER) || + (active_widget->object_type == PUCLASS_BISLIDERWITHENDS)) + { + puaSpinBox *properties_bislider_topvalue = new puaSpinBox(230,110,310,130); + properties_bislider_topvalue->setLabel("Top Slider Value:"); + properties_bislider_topvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_bislider_topvalue->setMinValue(-5000.0f); + properties_bislider_topvalue->setMaxValue(5000.0f); + properties_bislider_topvalue->setValue(active_widget->floatval4); + properties_bislider_topvalue->setStepSize(0.1f); + properties_bislider_topvalue->setCallback(cb_float4); + + puaSpinBox *properties_bislider_botvalue = new puaSpinBox(230,40,310,60); + properties_bislider_botvalue->setLabel("Bottom Slider Value:"); + properties_bislider_botvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_bislider_botvalue->setMinValue(-5000.0f); + properties_bislider_botvalue->setMaxValue(5000.0f); + properties_bislider_botvalue->setValue(active_widget->floatval5); + properties_bislider_botvalue->setStepSize(0.1f); + properties_bislider_botvalue->setCallback(cb_float5); + + } if (active_widget->object_type == PUCLASS_TRISLIDER) + { + puaSpinBox *properties_trislider_topvalue = new puaSpinBox(230,110,310,130); + properties_trislider_topvalue->setLabel("Top Slider Value:"); + properties_trislider_topvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_trislider_topvalue->setMinValue(-5000.0f); + properties_trislider_topvalue->setMaxValue(5000.0f); + properties_trislider_topvalue->setValue(active_widget->floatval4); + properties_trislider_topvalue->setStepSize(0.1f); + properties_trislider_topvalue->setCallback(cb_float4); + + puaSpinBox *properties_trislider_centvalue = new puaSpinBox(230,60,310,80); + properties_trislider_centvalue->setLabel("Center Slider Value:"); + properties_trislider_centvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_trislider_centvalue->setMinValue(-5000.0f); + properties_trislider_centvalue->setMaxValue(5000.0f); + properties_trislider_centvalue->setValue(active_widget->floatval6); + properties_trislider_centvalue->setStepSize(0.1f); + properties_trislider_centvalue->setCallback(cb_float6); + + puaSpinBox *properties_trislider_botvalue = new puaSpinBox(230,10,310,30); + properties_trislider_botvalue->setLabel("Bottom Slider Value:"); + properties_trislider_botvalue->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_trislider_botvalue->setMinValue(-5000.0f); + properties_trislider_botvalue->setMaxValue(5000.0f); + properties_trislider_botvalue->setValue(active_widget->floatval5); + properties_trislider_botvalue->setStepSize(0.1f); + properties_trislider_botvalue->setCallback(cb_float5); + + static char *properties_trislider_lock_callback_entries [] = { "No", "Yes", NULL } ; + puButtonBox *properties_trislider_lock = new puButtonBox(380, 80, 480, 130, properties_trislider_lock_callback_entries, TRUE); + properties_trislider_lock->setLabel("Lock End Sliders:"); + properties_trislider_lock->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_trislider_lock->setValue(active_widget->boolval3); + properties_trislider_lock->setCallback(cb_bool3); + + } if (active_widget->object_type == PUCLASS_DIAL) + { + /* Wrap Mode */ + static char *properties_dial_wrap_callback_entries [] = { "No", "Yes", NULL } ; + puButtonBox *properties_dial_wrap = new puButtonBox(20, 10, 170, 60, properties_dial_wrap_callback_entries, TRUE); + properties_dial_wrap->setLabel("Allow Dial Wrapping:"); + properties_dial_wrap->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_dial_wrap->setValue(active_widget->boolval2); + properties_dial_wrap->setCallback(cb_bool2); + + } if (active_widget->object_type == PUCLASS_SPINBOX) + { + /* Arrow position and height */ + static char *properties_spinbox_arrow_callback_entries [] = { "Left", "Right", NULL } ; + puButtonBox *properties_spinbox_arrow = new puButtonBox(20, 10, 170, 60, properties_spinbox_arrow_callback_entries, TRUE); + properties_spinbox_arrow->setLabel("Arrow Position"); + properties_spinbox_arrow->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_spinbox_arrow->setValue(active_widget->boolval2); + properties_spinbox_arrow->setCallback(cb_bool2); + + puaSpinBox *properties_spinbox_arrowheight = new puaSpinBox(230,110,310,130); + properties_spinbox_arrowheight->setLabel("Arrow Height:"); + properties_spinbox_arrowheight->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_spinbox_arrowheight->setMinValue(0.0f); + properties_spinbox_arrowheight->setMaxValue(20.0f); + properties_spinbox_arrowheight->setValue(active_widget->floatval4); + properties_spinbox_arrowheight->setStepSize(0.05f); + properties_spinbox_arrowheight->setCallback(cb_float4); + + puText *properties_spinbox_arrowextra = new puText ( 310, 90 ); + properties_spinbox_arrowextra->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_spinbox_arrowextra->setLabel("(as porportion of spinbox height)"); + + } if (active_widget->object_type == PUCLASS_DIALOGBOX) + { + /* Not yet implemented as a class */ + + } if (active_widget->object_type == PUCLASS_ARROW) + { + static char *properties_arrow_arrowtype_options [] = { "Up", "Down", "Left", "Right", + "Double-Up", "Double-Down", "Double-Left", + "Double-Right", NULL } ; + + puaComboBox *properties_arrow_arrowtype = new puaComboBox ( 120,10,310,30, properties_arrow_arrowtype_options, FALSE ) ; + properties_arrow_arrowtype->setLabel ( "Arrow Type:" ) ; + properties_arrow_arrowtype->setLabelPlace ( PUPLACE_LOWER_LEFT ) ; + properties_arrow_arrowtype->setCallback(cb_items); + + int num = 0; + + if (active_widget->items) + { + if (strstr(active_widget->items,"Double-Up")) + num = 4; + else if (strstr(active_widget->items,"Double-Down")) + num = 5; + else if (strstr(active_widget->items,"Double-Left")) + num = 6; + else if (strstr(active_widget->items,"Double-Right")) + num = 7; + else if (strstr(active_widget->items,"Up")) + num = 0; + else if (strstr(active_widget->items,"Down")) + num = 1; + else if (strstr(active_widget->items,"Left")) + num = 2; + else if (strstr(active_widget->items,"Right")) + num = 3; + } + + properties_arrow_arrowtype->setCurrentItem(num); + + } if ( (active_widget->object_type == PUCLASS_INPUT ) || + (active_widget->object_type == PUCLASS_LARGEINPUT ) ) + { + /*int minx, int miny, int maxx, int maxy */ + /* Disabled, and Valid Data list */ + puInput *properties_input_validdata = new puInput(20, 150, 260, 170); + properties_input_validdata->setLabel("Allowed Characters for Input:"); + properties_input_validdata->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_input_validdata->setCallback(cb_allowed); + properties_input_validdata->setDownCallback(cb_allowed); + properties_input_validdata->setValue(active_widget->allowed); + + static char *properties_input_enabled_callback_entries [] = { "Yes", NULL } ; + puButtonBox *properties_input_enabled = new puButtonBox(300, 140, 450, 170, properties_input_enabled_callback_entries, FALSE); + properties_input_enabled->setLabel("Input Allowed? (enabled)"); + properties_input_enabled->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_input_enabled->setValue(active_widget->boolval1); + properties_input_enabled->setCallback(cb_bool1); + + } if ((active_widget->object_type == PUCLASS_LARGEINPUT) || + (active_widget->object_type == PUCLASS_SCROLLBAR) ) + { + /*Arrows*/ + static char *properties_largeinput_arrows_callback_entries [] = { "None", "Normal", "Normal and Fast", NULL } ; + puButtonBox *properties_largeinput_arrows = new puButtonBox(20, 10, 200, 130, properties_largeinput_arrows_callback_entries, TRUE); + properties_largeinput_arrows->setLabel("Which Arrow Buttons:"); + properties_largeinput_arrows->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_largeinput_arrows->setValue(active_widget->intval1); + properties_largeinput_arrows->setCallback(cb_int1); + + } if (active_widget->object_type == PUCLASS_LARGEINPUT) + { + /*Slider Size*/ + puaSpinBox *properties_largeinput_sliderwidth = new puaSpinBox(300,90,400,110); + properties_largeinput_sliderwidth->setLabel("Slider Width:"); + properties_largeinput_sliderwidth->setLabelPlace(PUPLACE_TOP_CENTERED); + properties_largeinput_sliderwidth->setMinValue(0.0f); + properties_largeinput_sliderwidth->setMaxValue(150.0f); + properties_largeinput_sliderwidth->setValue(active_widget->intval2); + properties_largeinput_sliderwidth->setStepSize(1.0f); + properties_largeinput_sliderwidth->setCallback(cb_int2); + } + + /* TODO: + * puaSliderWithInput: is input above or below slider? + * puaList: can we add the transparency field? + */ + properties_group->close () ; + + return 0 ; +} + + + diff --git a/demos/p-guide/src/StatusWindow.cxx b/demos/p-guide/src/StatusWindow.cxx new file mode 100644 index 0000000..13f46f7 --- /dev/null +++ b/demos/p-guide/src/StatusWindow.cxx @@ -0,0 +1,1146 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: StatusWindow.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + +// Status Window + +#include "WidgetList.h" + +// From the Main Window: + +extern int max_layer ; + +extern WidgetList *active_widget ; +extern puObject *active_object ; + +extern char pguide_current_directory [ PUSTRING_MAX ] ; + +extern bool main_window_changed ; + +extern char main_window_name [ PUSTRING_MAX ] ; + +extern bool autolock ; + +// Status window parameters +int status_window = 0 ; // Window handle +extern int main_window ; + +// Status window widgets + +puInput *object_size_x ; +puInput *object_size_y ; +puInput *object_position_x ; +puInput *object_position_y ; + +static puFrame *widget_section_frame ; + +static puInput *object_label ; +static puaComboBox *object_vert_label_place ; +static puaComboBox *object_horz_label_place ; +static puInput *object_legend ; +static puaComboBox *object_vert_legend_place ; +static puaComboBox *object_horz_legend_place ; + +static puInput *object_name ; +static puButtonBox *object_callbacks ; + +static puFrame *layer_manipulation_frame ; +static puButtonBox *object_visible ; +static puOneShot *reveal_all_objects ; +static puOneShot *hide_all_objects ; + +static puOneShot *reveal_all_layer ; +static puOneShot *hide_all_layer ; + +static puaSpinBox *layer_to_act_on ; +static puaSpinBox *object_layer ; + +static puText *object_layer_text ; +static puFrame *layer_to_act_on_frame ; +static puFrame *layer_all_frame ; + +puInput *window_name ; +puInput *window_size_x ; +puInput *window_size_y ; +puInput *window_position_x ; +puInput *window_position_y ; + +static puText *window_color_label ; +puaSpinBox *window_color_r ; +puaSpinBox *window_color_g ; +puaSpinBox *window_color_b ; +puaSpinBox *window_color_a ; + +static puButtonBox *autolock_toggle ; + +static puDialogBox *dialog = (puDialogBox *)NULL ; +static puInput *newname = (puInput *)NULL ; + +static puMenuBar *menubar ; + +// Saving Window Parameters + +puaFileSelector *file_selector ; + +static int write_window = 0 ; + +// Extra needed prototyping +static void dupname_ok_cb ( puObject *ob ) ; + +// Function to set the widgets from the active object + +static void setHorizontalPlaceWidget ( puaComboBox *ob, int place ) +{ + switch ( place ) + { + case PUPLACE_TOP_LEFT : + case PUPLACE_ABOVE_LEFT : + case PUPLACE_UPPER_LEFT : + case PUPLACE_CENTERED_LEFT : + case PUPLACE_LOWER_LEFT : + case PUPLACE_BELOW_LEFT : + case PUPLACE_BOTTOM_LEFT : ob->setValue ( "Left" ) ; break ; + case PUPLACE_TOP_CENTERED : + case PUPLACE_CENTERED_CENTERED : + case PUPLACE_BOTTOM_CENTERED : ob->setValue ( "Center" ) ; break ; + case PUPLACE_TOP_RIGHT : + case PUPLACE_ABOVE_RIGHT : + case PUPLACE_UPPER_RIGHT : + case PUPLACE_CENTERED_RIGHT : + case PUPLACE_LOWER_RIGHT : + case PUPLACE_BELOW_RIGHT : + case PUPLACE_BOTTOM_RIGHT : ob->setValue ( "Right" ) ; break ; + } +} + +static void setVerticalPlaceWidget ( puaComboBox *ob, int place ) +{ + switch ( place ) + { + case PUPLACE_TOP_LEFT : + case PUPLACE_TOP_CENTERED : + case PUPLACE_TOP_RIGHT : ob->setValue ( "Top" ) ; break ; + case PUPLACE_ABOVE_LEFT : + case PUPLACE_ABOVE_RIGHT : ob->setValue ( "Above" ) ; break ; + case PUPLACE_UPPER_LEFT : + case PUPLACE_UPPER_RIGHT : ob->setValue ( "Upper" ) ; break ; + case PUPLACE_CENTERED_LEFT : + case PUPLACE_CENTERED_CENTERED : + case PUPLACE_CENTERED_RIGHT : ob->setValue ( "Center" ) ; break ; + case PUPLACE_LOWER_LEFT : + case PUPLACE_LOWER_RIGHT : ob->setValue ( "Lower" ) ; break ; + case PUPLACE_BELOW_LEFT : + case PUPLACE_BELOW_RIGHT : ob->setValue ( "Below" ) ; break ; + case PUPLACE_BOTTOM_LEFT : + case PUPLACE_BOTTOM_CENTERED : + case PUPLACE_BOTTOM_RIGHT : ob->setValue ( "Bottom" ) ; break ; + } +} + +void setStatusWidgets ( WidgetList *wid ) +{ + if ( wid ) + { + puObject *ob = wid->obj ; + + int a, b ; + ob->getSize ( &a, &b ) ; + object_size_x->setValue ( a ) ; + object_size_y->setValue ( b ) ; + + ob->getPosition ( &a, &b ) ; + object_position_x->setValue ( a ) ; + object_position_y->setValue ( b ) ; + + if ( wid->label_text ) + object_label->setValue ( wid->label_text ) ; + else + object_label->clrValue () ; + + setHorizontalPlaceWidget ( object_horz_label_place, ob->getLabelPlace () ) ; + setVerticalPlaceWidget ( object_vert_label_place, ob->getLabelPlace () ) ; + + if ( wid->legend_text ) + object_legend->setValue ( wid->legend_text ) ; + else + object_legend->clrValue () ; + + setHorizontalPlaceWidget ( object_horz_legend_place, ob->getLegendPlace () ) ; + setVerticalPlaceWidget ( object_vert_legend_place, ob->getLegendPlace () ) ; + + object_name->setValue ( wid->object_name ) ; + object_callbacks->setValue ( wid->callbacks ) ; + + object_visible->setValue ( wid->visible ? 1 : 0 ) ; + autolock_toggle->setValue ( autolock ); + object_layer->setValue ( (int) wid->layer ) ; + } + else + { + object_size_x->setValue ( 0 ) ; + object_size_y->setValue ( 0 ) ; + + object_position_x->setValue ( 0 ) ; + object_position_y->setValue ( 0 ) ; + + object_label->setValue ( "" ) ; + setHorizontalPlaceWidget ( object_horz_label_place, PUPLACE_CENTERED_LEFT ) ; + setVerticalPlaceWidget ( object_vert_label_place, PUPLACE_CENTERED_LEFT ) ; + + object_legend->setValue ( "" ) ; + setHorizontalPlaceWidget ( object_horz_legend_place, PUPLACE_CENTERED_LEFT ) ; + setVerticalPlaceWidget ( object_vert_legend_place, PUPLACE_CENTERED_LEFT ) ; + + object_name->setValue ( "" ) ; + object_callbacks->setValue ( 0 ) ; + + object_visible->setValue ( 0 ) ; + autolock_toggle->setValue ( autolock ); + object_layer->setValue ( 0 ) ; + } +} + +// GLUT Status Window Callbacks + +static void status_window_specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + + glutPostRedisplay () ; +} + +static void status_window_keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; + + glutPostRedisplay () ; +} + +static void status_window_motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + + glutPostRedisplay () ; +} + +static void status_window_mousefn ( int button, int updown, int x, int y ) +{ + puMouse ( button, updown, x, y ) ; + + glutPostRedisplay () ; +} + + +static void status_window_displayfn ( void ) +{ + glutSetWindow ( status_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + puDisplay () ; + + /* Update GLUT */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +static void selection_window_displayfn ( void ) +{ + glutSetWindow ( write_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + puDisplay () ; + + /* Update GLUT */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +// PUI Callback Functions: + +extern void write_code ( puObject *ob ) ; + +extern void saveProject ( puObject *ob ) ; + +extern void loadProject ( puObject *ob ) ; + +static void write_window_reshapefn ( int w, int h ) +{ + file_selector->setSize ( w, h ) ; +} + +// Clear Objects callbacks + +static void window_wiper ( puObject *ob ) +{ + + // Prepare to clear the properties window + extern int properties_window ; + // Clear the widget list + extern WidgetList *widgets ; + + WidgetList *wid = widgets ; + + while ( wid ) + { + widgets = wid->next ; + delete wid->object_type_name ; + delete wid->legend_text ; + delete wid->label_text ; + delete wid->obj ; + delete wid ; + wid = widgets ; + } + + active_widget = (WidgetList *)NULL ; + active_object = (puObject *)NULL ; + window_color_r->setValue(1.0f) ; + window_color_g->setValue(1.0f) ; + window_color_b->setValue(1.0f) ; + window_color_a->setValue(1.0f) ; + window_name->setValue("PUI GUI Builder"); + window_size_x->setValue(600) ; + window_size_y->setValue(600) ; + window_position_x->setValue(0) ; + window_position_y->setValue(0) ; + main_window_changed = false ; + autolock = false ; + autolock_toggle->setValue(0); + + glutSetWindow ( main_window ) ; + glutReshapeWindow ( 600, 600 ) ; + + if (properties_window) + { + extern puGroup *properties_group; + /* Delete the widgets */ + puDeleteObject( properties_group ); + glutDestroyWindow( properties_window ); + properties_window = 0; + } + + glutSetWindow ( status_window ) ; + + setStatusWidgets ( active_widget ) ; +} + +static void write_code_cb ( puObject *ob ) +{ + int w = 320, h = 270 ; + if ( write_window ) + { + glutSetWindow ( write_window ) ; + glutSetWindowTitle( "Writing " ); + } + else + { + write_window = glutCreateWindow ( "Writing " ) ; + glutDisplayFunc ( selection_window_displayfn ) ; + glutKeyboardFunc ( status_window_keyfn ) ; + glutSpecialFunc ( status_window_specialfn ) ; + glutMouseFunc ( status_window_mousefn ) ; + glutMotionFunc ( status_window_motionfn ) ; + glutPassiveMotionFunc ( status_window_motionfn ) ; + glutIdleFunc ( selection_window_displayfn ) ; + glutReshapeFunc ( write_window_reshapefn ) ; + } + + glutShowWindow () ; + glutReshapeWindow ( w, h ) ; + glutPositionWindow ( ( 640 - w ) / 2, ( 480 - h ) / 2 ) ; + + file_selector = new puaFileSelector ( 0, 0, w, h, 1, pguide_current_directory, "Pick File to Write To" ) ; + file_selector -> setCallback ( write_code ) ; + file_selector->setChildStyle ( PUCLASS_ONESHOT, PUSTYLE_BOXED ) ; + file_selector->setChildBorderThickness ( PUCLASS_ONESHOT, 5 ) ; + file_selector->setChildColour ( PUCLASS_SLIDER, 0, 0.5, 0.5, 0.5 ) ; +} + +static void saveProject_cb ( puObject *ob ) +{ + int w = 320, h = 270 ; + if ( write_window ) + { + glutSetWindow ( write_window ) ; + glutSetWindowTitle( "Saving " ); + } + else + { + write_window = glutCreateWindow ( "Saving " ) ; + glutDisplayFunc ( selection_window_displayfn ) ; + glutKeyboardFunc ( status_window_keyfn ) ; + glutSpecialFunc ( status_window_specialfn ) ; + glutMouseFunc ( status_window_mousefn ) ; + glutMotionFunc ( status_window_motionfn ) ; + glutPassiveMotionFunc ( status_window_motionfn ) ; + glutIdleFunc ( selection_window_displayfn ) ; + glutReshapeFunc ( write_window_reshapefn ) ; + } + + glutShowWindow () ; + glutReshapeWindow ( w, h ) ; + glutPositionWindow ( ( 640 - w ) / 2, ( 480 - h ) / 2 ) ; + + file_selector = new puaFileSelector ( 0, 0, w, h, 1, pguide_current_directory, "Pick .XML File to Save To" ) ; + file_selector -> setCallback ( saveProject ) ; + file_selector->setChildStyle ( PUCLASS_ONESHOT, PUSTYLE_BOXED ) ; + file_selector->setChildBorderThickness ( PUCLASS_ONESHOT, 5 ) ; + file_selector->setInitialValue(".xml"); + file_selector->setChildColour ( PUCLASS_SLIDER, 0, 0.5, 0.5, 0.5 ) ; +} + +static void loadProject_ok_cb ( puObject *ob ) +{ + puDeleteObject ( dialog ) ; // Delete the dialog box + dialog = (puDialogBox *)NULL ; + + window_wiper( ob ); + + int w = 320, h = 270 ; + if ( write_window ) + { + glutSetWindow ( write_window ) ; + glutSetWindowTitle( "Loading " ); + } + else + { + write_window = glutCreateWindow ( "Loading " ) ; + glutDisplayFunc ( selection_window_displayfn ) ; + glutKeyboardFunc ( status_window_keyfn ) ; + glutSpecialFunc ( status_window_specialfn ) ; + glutMouseFunc ( status_window_mousefn ) ; + glutMotionFunc ( status_window_motionfn ) ; + glutPassiveMotionFunc ( status_window_motionfn ) ; + glutIdleFunc ( selection_window_displayfn ) ; + glutReshapeFunc ( write_window_reshapefn ) ; + } + + glutShowWindow () ; + glutReshapeWindow ( w, h ) ; + glutPositionWindow ( ( 640 - w ) / 2, ( 480 - h ) / 2 ) ; + + file_selector = new puaFileSelector ( 0, 0, w, h, 1, pguide_current_directory, "Pick P-Guide .XML File to load" ) ; + file_selector -> setCallback ( loadProject ) ; + file_selector->setChildStyle ( PUCLASS_ONESHOT, PUSTYLE_BOXED ) ; + file_selector->setChildBorderThickness ( PUCLASS_ONESHOT, 5 ) ; + file_selector->setInitialValue("*.xml"); + file_selector->setChildColour ( PUCLASS_SLIDER, 0, 0.5, 0.5, 0.5 ) ; +} + +static void clear_ok_cb ( puObject *ob ) +{ + window_wiper( ob ); + puDeleteObject ( dialog ) ; // Delete the dialog box + dialog = (puDialogBox *)NULL ; +} + +static void cancel_cb ( puObject *ob ) +{ + // Don't do whatever you were planning to do (clear the widget list or quit) + // but do delete the dialog box. + puDeleteObject ( dialog ) ; + dialog = (puDialogBox *)NULL ; +} + +static void clear_cb ( puObject *ob ) +{ + if ( main_window_changed ) + { + dialog = new puDialogBox ( 50, 300 ) ; + new puFrame ( 0, 0, 125, 70 ) ; + puText *text = new puText ( 10, 40 ) ; + text->setLabel ( "Are you sure?" ) ; + puOneShot *ok = new puOneShot ( 10, 10, "Yes" ) ; + ok->setCallback ( clear_ok_cb ) ; + puOneShot *cancel = new puOneShot ( 60, 10, "Cancel" ) ; + cancel->setCallback ( cancel_cb ) ; + dialog->close () ; + dialog->reveal () ; + } + else + clear_ok_cb ( (puObject *)NULL ) ; +} + +static void loadProject_cb ( puObject *ob ) +{ + if ( main_window_changed ) + { + dialog = new puDialogBox ( 50, 300 ) ; + new puFrame ( 0, 0, 125, 70 ) ; + puText *text = new puText ( 10, 40 ) ; + text->setLabel ( "Are you sure?" ) ; + puOneShot *ok = new puOneShot ( 10, 10, "Yes" ) ; + ok->setCallback ( loadProject_ok_cb ) ; + puOneShot *cancel = new puOneShot ( 60, 10, "Cancel" ) ; + cancel->setCallback ( cancel_cb ) ; + dialog->close () ; + dialog->reveal () ; + } + else + loadProject_ok_cb ( (puObject *)NULL ) ; +} + +static void quit_ok_cb ( puObject *ob ) +{ + exit ( 0 ) ; +} + +static void quit_cb ( puObject *ob ) +{ + if ( main_window_changed ) + { + dialog = new puDialogBox ( 50, 300 ) ; + new puFrame ( 0, 0, 125, 70 ) ; + puText *text = new puText ( 10, 40 ) ; + text->setLabel ( "Are you sure?" ) ; + puOneShot *ok = new puOneShot ( 10, 10, "Yes" ) ; + ok->setCallback ( quit_ok_cb ) ; + puOneShot *cancel = new puOneShot ( 60, 10, "Cancel" ) ; + cancel->setCallback ( cancel_cb ) ; + dialog->close () ; + dialog->reveal () ; + } + else + quit_ok_cb ( (puObject *)NULL ) ; +} + +static void object_size_cb ( puObject *ob ) +{ + if ( active_object ) + { + int w, h ; + active_object->getSize ( &w, &h ) ; + if ( ob == object_size_x ) + { + w = ob->getIntegerValue () ; + if ( w < 1 ) w = 1 ; + ob->setValue ( w ) ; + } + else + { + h = ob->getIntegerValue () ; + if ( h < 1 ) h = 1 ; + ob->setValue ( h ) ; + } + + active_object->setSize ( w, h ) ; + } +} + +static void object_position_cb ( puObject *ob ) +{ + if ( active_object ) + { + int x, y ; + active_object->getPosition ( &x, &y ) ; + if ( ob == object_position_x ) + x = ob->getIntegerValue () ; + else + y = ob->getIntegerValue () ; + + ob->setValue ( ob->getIntegerValue () ) ; + active_object->setPosition ( x, y ) ; + } +} + +static void window_size_cb ( puObject *ob ) +{ + extern int main_window_width ; + extern int main_window_height ; + + extern int main_window ; + + glutSetWindow ( main_window ) ; + if ( ob == window_size_x ) + main_window_width = ob->getIntegerValue () ; + else + main_window_height = ob->getIntegerValue () ; + + ob->setValue ( ob->getIntegerValue () ) ; + glutReshapeWindow ( main_window_width, main_window_height ) ; + + glutSetWindow ( status_window ) ; +} + +static void window_name_cb ( puObject *ob ) +{ + extern int main_window ; + + glutSetWindow ( main_window ) ; + strcpy ( main_window_name, ob->getStringValue () ) ; + glutSetWindowTitle ( main_window_name ) ; + glutSetWindow ( status_window ) ; +} + +static void label_cb ( puObject *ob ) +{ + if ( active_widget ) + { + if (active_widget->label_text) + delete active_widget->label_text ; + active_widget->label_text = new char [ strlen ( ob->getStringValue () ) + 1 ] ; + strcpy ( active_widget->label_text, ob->getStringValue () ) ; + active_widget->obj->setLabel ( active_widget->label_text ) ; + } +} + +static void label_place_cb ( puObject *ob ) +{ + if ( active_object ) + { + int horz = object_horz_label_place->getCurrentItem () ; + int vert = object_vert_label_place->getCurrentItem () ; + + switch ( horz ) + { + case 0 : // Left + switch ( vert ) + { + case 0 : // Top + active_object->setLabelPlace ( PUPLACE_TOP_LEFT ) ; break ; + case 1 : // Above + active_object->setLabelPlace ( PUPLACE_ABOVE_LEFT ) ; break ; + case 2 : // Upper + active_object->setLabelPlace ( PUPLACE_UPPER_LEFT ) ; break ; + case 3 : // Center + active_object->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; break ; + case 4 : // Lower + active_object->setLabelPlace ( PUPLACE_LOWER_LEFT ) ; break ; + case 5 : // Below + active_object->setLabelPlace ( PUPLACE_BELOW_LEFT ) ; break ; + case 6 : // Bottom + active_object->setLabelPlace ( PUPLACE_BOTTOM_LEFT ) ; break ; + } + + break ; + + case 1 : // Center + switch ( vert ) + { + case 0 : // Top + active_object->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; break ; + case 1 : // Above -- not allowed + case 2 : // Upper -- not allowed + case 3 : // Center -- not allowed + case 4 : // Lower -- not allowed + case 5 : // Below -- not allowed + if ( ob == object_horz_label_place ) + { + ob->setValue ( "Left" ) ; // Left + active_object->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + } + else + { + ob->setValue ( "Top" ) ; // Top + active_object->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + } + + break ; + case 6 : // Bottom + active_object->setLabelPlace ( PUPLACE_BOTTOM_CENTERED ) ; break ; + } + + break ; + + case 2 : // Right + switch ( vert ) + { + case 0 : // Top + active_object->setLabelPlace ( PUPLACE_TOP_RIGHT ) ; break ; + case 1 : // Above + active_object->setLabelPlace ( PUPLACE_ABOVE_RIGHT ) ; break ; + case 2 : // Upper + active_object->setLabelPlace ( PUPLACE_UPPER_RIGHT ) ; break ; + case 3 : // Center + active_object->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; break ; + case 4 : // Lower + active_object->setLabelPlace ( PUPLACE_LOWER_RIGHT ) ; break ; + case 5 : // Below + active_object->setLabelPlace ( PUPLACE_BELOW_RIGHT ) ; break ; + case 6 : // Bottom + active_object->setLabelPlace ( PUPLACE_BOTTOM_RIGHT ) ; break ; + } + + break ; + } + } +} + +static void legend_cb ( puObject *ob ) +{ + if ( active_widget ) + { + if (active_widget->legend_text) + delete active_widget->legend_text ; + active_widget->legend_text = new char [ strlen ( ob->getStringValue () ) + 1 ] ; + strcpy ( active_widget->legend_text, ob->getStringValue () ) ; + } +} + +static void legend_place_cb ( puObject *ob ) +{ + if ( active_object ) + { + int horz = object_horz_legend_place->getCurrentItem () ; + int vert = object_vert_legend_place->getCurrentItem () ; + + switch ( horz ) + { + case 0 : // Left + switch ( vert ) + { + case 0 : // Top + active_object->setLegendPlace ( PUPLACE_TOP_LEFT ) ; break ; + case 1 : // Above -- not allowed + case 2 : // Upper -- not allowed + object_vert_legend_place->setValue ( "Top" ) ; + active_object->setLegendPlace ( PUPLACE_TOP_LEFT ) ; break ; + case 3 : // Center + active_object->setLegendPlace ( PUPLACE_CENTERED_LEFT ) ; break ; + case 4 : // Lower -- not allowed + case 5 : // Below -- not allowed + object_vert_legend_place->setValue ( "Bottom" ) ; + active_object->setLegendPlace ( PUPLACE_BOTTOM_LEFT ) ; break ; + case 6 : // Bottom + active_object->setLegendPlace ( PUPLACE_BOTTOM_LEFT ) ; break ; + } + + break ; + + case 1 : // Center + switch ( vert ) + { + case 0 : // Top + active_object->setLegendPlace ( PUPLACE_TOP_CENTERED ) ; break ; + case 1 : // Above -- not allowed + case 2 : // Upper -- not allowed + object_vert_legend_place->setValue ( "Top" ) ; + active_object->setLegendPlace ( PUPLACE_TOP_CENTERED ) ; break ; + case 3 : // Center + active_object->setLegendPlace ( PUPLACE_CENTERED_CENTERED ) ; break ; + case 4 : // Lower -- not allowed + case 5 : // Below -- not allowed + object_vert_legend_place->setValue ( "Bottom" ) ; + active_object->setLegendPlace ( PUPLACE_BOTTOM_CENTERED ) ; break ; + case 6 : // Bottom + active_object->setLegendPlace ( PUPLACE_BOTTOM_CENTERED ) ; break ; + } + + break ; + + case 2 : // Right + switch ( vert ) + { + case 0 : // Top + active_object->setLegendPlace ( PUPLACE_TOP_RIGHT ) ; break ; + case 1 : // Above -- not allowed + case 2 : // Upper -- not allowed + object_vert_legend_place->setValue ( "Top" ) ; + active_object->setLegendPlace ( PUPLACE_TOP_RIGHT ) ; break ; + case 3 : // Center + active_object->setLegendPlace ( PUPLACE_CENTERED_RIGHT ) ; break ; + case 4 : // Lower -- not allowed + case 5 : // Below -- not allowed + object_vert_legend_place->setValue ( "Bottom" ) ; + active_object->setLegendPlace ( PUPLACE_BOTTOM_RIGHT ) ; break ; + case 6 : // Bottom + active_object->setLegendPlace ( PUPLACE_BOTTOM_RIGHT ) ; break ; + } + + break ; + } + } +} + +static void name_cb ( puObject *ob ) +{ + if ( active_widget ) + { + //strcpy ( active_widget->object_name, ob->getStringValue () ) ; + strcpy ( active_widget->object_name, object_name->getStringValue () ) ;/* Because ob is not _always_ the right puInput! */ + + // Remove spaces from the widget name + char *spc = strchr ( active_widget->object_name, ' ' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( active_widget->object_name, ' ' ) ; + } + /* Make sure the widget's name doesn't already exist. If so, prompt user */ + /* This really should be made to use the chk_dupname function from LoadSave.cxx */ + extern WidgetList *widgets ; + WidgetList *wid = widgets ; + while ( wid ) + { + if ( (strcmp(active_widget->object_name,wid->object_name) == 0) && (active_widget != wid) ) + { + /* Popup a dialog telling user something's bad! */ + dialog = new puDialogBox ( 20, 20 ) ; + new puFrame ( 0, 0, 460, 120 ) ; + puText *text = new puText ( 80, 85 ) ; + text->setLabel ( "ERROR: Name already used." ) ; + text->setLabelFont(PUFONT_TIMES_ROMAN_24); + puText *directions = new puText ( 90, 65 ) ; + directions->setLabel ( "Please type in a new, unique name to continue." ) ; + directions->setLabelFont(PUFONT_HELVETICA_12); + puInput *newname = new puInput (20,40,440,60) ; + newname->setValuator(active_widget->object_name); + newname->setValidData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_012345679"); + puOneShot *ok = new puOneShot ( 200, 10, "Accept" ) ; + ok->setCallback ( dupname_ok_cb ) ; + dialog->close () ; + dialog->reveal () ; + break ; + } + wid = wid->next ; + } + ob->setValue(active_widget->object_name); + } +} + +static void dupname_ok_cb ( puObject *ob ) +{ + /* The user's new name from the dialog has already been set to the object_name*/ + /* Close dialogbox and update main window ...*/ + puDeleteObject ( dialog ) ; + dialog = (puDialogBox *)NULL ; + newname = (puInput *)NULL ; + setStatusWidgets(active_widget); + /*but force a recheck afterwards to ensure it's an okay substitution. */ + name_cb(ob); +} + +static void callback_cb ( puObject *ob ) +{ + if ( active_widget ) active_widget->callbacks = ob->getIntegerValue () ; +} + +static void reveal_all_cb ( puObject *ob ) +{ + extern WidgetList *widgets ; + WidgetList *wid = widgets ; + while ( wid ) + { + wid->visible = true ; + wid = wid->next ; + } +} + +static void hide_all_cb ( puObject *ob ) +{ + extern WidgetList *widgets ; + WidgetList *wid = widgets ; + while ( wid ) + { + wid->visible = false ; + wid = wid->next ; + } +} + +static void hide_all_layer_cb ( puObject *ob ) +{ + extern WidgetList *widgets ; + WidgetList *wid = widgets ; + while ( wid ) + { + if ( wid->layer == layer_to_act_on->getIntegerValue() ) + wid->visible = false ; + wid = wid->next; + } +} + +static void reveal_all_layer_cb ( puObject *ob ) +{ + extern WidgetList *widgets ; + WidgetList *wid = widgets ; + while ( wid ) + { + if ( wid->layer == layer_to_act_on->getIntegerValue() ) + wid->visible = true ; + wid = wid->next; + } +} + +static void visible_cb ( puObject *ob ) +{ + if ( active_widget ) active_widget->visible = ( ob->getIntegerValue () != 0 ) ; +} + +static void layer_cb ( puObject *ob ) +{ + if ( ob->getIntegerValue () < 0 ) ob->setValue ( 0 ) ; + if ( active_widget ) + { + active_widget->layer = ob->getIntegerValue () ; + if ( max_layer <= active_widget->layer ) max_layer = active_widget->layer + 1 ; + } +} + +static void autolock_cb ( puObject *ob ) +{ + if ( ob->getIntegerValue () == 1 ) + autolock = true ; + else + autolock = false ; +} + +// Setup Menubar + +static char *file_submenu [] = { "Exit", "------------", "Export Code", "------------", "Save Project", "Load Project", "New Project", NULL } ; +static puCallback file_submenu_cb [] = { quit_cb, NULL, write_code_cb, NULL, saveProject_cb, loadProject_cb, clear_cb, NULL } ; + +// Function to define the window + +int define_status_window () +{ + status_window = glutCreateWindow ( "Status Window" ) ; + + int total_screen_width = glutGet( GLUT_SCREEN_WIDTH ) ; + + glutPositionWindow ( total_screen_width-520, 266 ) ; + glutReshapeWindow ( 500, 380 ) ; + glutDisplayFunc ( status_window_displayfn ) ; + glutKeyboardFunc ( status_window_keyfn ) ; + glutSpecialFunc ( status_window_specialfn ) ; + glutMotionFunc ( status_window_motionfn ) ; + glutMouseFunc ( status_window_mousefn ) ; + + // Set up the widgets + + puGroup *status_group = new puGroup ( 0, 0 ) ; + new puFrame ( 0, 0, 500, 380 ) ; + + menubar = new puMenuBar () ; + { + menubar -> add_submenu ( "File", file_submenu, file_submenu_cb ) ; + } + menubar -> close() ; + + widget_section_frame = new puFrame ( 0, 1, 500, 250 ) ; + widget_section_frame->setLegendFont ( PUFONT_HELVETICA_18 ) ; + widget_section_frame->setLegend ( "Widget Options:" ) ; + widget_section_frame->setLegendPlace ( PUPLACE_TOP_LEFT ) ; + + object_size_x = new puInput ( 380, 10, 430, 30 ) ; + object_size_x->setLabel ( "Object Size:" ) ; + object_size_x->setLabelPlace ( PUPLACE_LEFT ) ; + object_size_x->setCallback ( object_size_cb ) ; + object_size_x->setDownCallback ( object_size_cb ) ; + + object_size_y = new puInput ( 430, 10, 480, 30 ) ; + object_size_y->setCallback ( object_size_cb ) ; + object_size_y->setDownCallback ( object_size_cb ) ; + + object_position_x = new puInput ( 160, 10, 210, 30 ) ; + object_position_x->setLabel ( "Object Position:" ) ; + object_position_x->setLabelPlace ( PUPLACE_LEFT ) ; + object_position_x->setCallback ( object_position_cb ) ; + object_position_x->setDownCallback ( object_position_cb ) ; + + object_position_y = new puInput ( 210, 10, 260, 30 ) ; + object_position_y->setCallback ( object_position_cb ) ; + object_position_y->setDownCallback ( object_position_cb ) ; + + + static char *vert_place_entries [] = { "Top", "Above", "Upper", "Center", + "Lower", "Below", "Bottom", NULL } ; + + static char *horz_place_entries [] = { "Left", "Center", "Right", NULL } ; + + object_label = new puInput ( 65, 40, 260, 60 ) ; + object_label->setLabel ( "Label:" ) ; + object_label->setLabelPlace ( PUPLACE_LEFT ) ; + object_label->setCallback ( label_cb ) ; + object_label->setDownCallback ( label_cb ) ; + + object_vert_label_place = new puaComboBox ( 310, 40, 400, 60, vert_place_entries, FALSE ) ; + object_vert_label_place->setLabel ( "Place" ) ; + object_vert_label_place->setLabelPlace ( PUPLACE_LEFT ) ; + object_vert_label_place->setCallback ( label_place_cb ) ; + + object_horz_label_place = new puaComboBox ( 400, 40, 490, 60, horz_place_entries, FALSE ) ; + object_horz_label_place->setCallback ( label_place_cb ) ; + + object_legend = new puInput ( 65, 70, 260, 90 ) ; + object_legend->setLabel ( "Legend:" ) ; + object_legend->setLabelPlace ( PUPLACE_LEFT ) ; + object_legend->setCallback ( legend_cb ) ; + object_legend->setDownCallback ( legend_cb ) ; + + object_vert_legend_place = new puaComboBox ( 310, 70, 400, 90, vert_place_entries, FALSE ) ; + object_vert_legend_place->setLabel ( "Place" ) ; + object_vert_legend_place->setLabelPlace ( PUPLACE_LEFT ) ; + object_vert_legend_place->setCallback ( legend_place_cb ) ; + + object_horz_legend_place = new puaComboBox ( 400, 70, 490, 90, horz_place_entries, FALSE ) ; + object_horz_legend_place->setCallback ( legend_place_cb ) ; + + object_name = new puInput ( 65, 195, 260, 215 ) ; + object_name->setLabel ( "Name:" ) ; + object_name->setValidData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789"); + object_name->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + object_name->setCallback ( name_cb ) ; + object_name->setDownCallback ( name_cb ) ; + + static char *callback_entries [] = { "Up", "Active", "Down", NULL } ; + object_callbacks = new puButtonBox ( 120, 100, 260, 170, callback_entries, FALSE ) ; + object_callbacks->setLabel ( "Widget Callbacks:" ) ; + object_callbacks->setLabelPlace ( PUPLACE_TOP_LEFT ) ; + object_callbacks->setCallback ( callback_cb ) ; + + static char *visible_entries [] = { "Visible", NULL } ; + object_visible = new puButtonBox ( 10, 140, 115, 170, visible_entries, FALSE ) ; + object_visible->setCallback ( visible_cb ) ; + + object_layer = new puaSpinBox ( 65, 100, 115, 120) ; + object_layer->setMinValue(0.0); + object_layer->setMaxValue(999.0); + object_layer->setStepSize(1.00f); + object_layer->setValue(0.0f); + object_layer->setCallback ( layer_cb ) ; + object_layer->setDownCallback ( layer_cb ) ; + + object_layer_text = new puText( 65, 112 ); + object_layer_text->setLabel ( "Layer:" ) ; + object_layer_text->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + + static char *autolock_entries [] = { "Autolock", NULL } ; + autolock_toggle = new puButtonBox ( 370, 252, 490, 282, autolock_entries, FALSE ) ; + autolock_toggle->setCallback ( autolock_cb ) ; + + layer_manipulation_frame = new puFrame (300, 120, 495, 215) ; + layer_manipulation_frame->setLegend ( "Layer Manipulation" ); + layer_manipulation_frame->setLegendFont ( PUFONT_HELVETICA_12 ) ; + layer_manipulation_frame->setLegendPlace ( PUPLACE_TOP_CENTERED ); + + layer_to_act_on_frame = new puFrame ( 300, 145, 495, 190 ); + layer_to_act_on_frame->setLegend ( "Affect Single Layer:" ) ; + layer_to_act_on_frame->setLegendPlace ( PUPLACE_TOP_CENTERED ) ; + + layer_all_frame = new puFrame ( 300, 121, 495, 148 ); + + hide_all_objects = new puOneShot ( 360, 124, 420, 144 ) ; + hide_all_objects->setLegend ( "Hide" ) ; + hide_all_objects->setLabel ( "All:" ) ; + hide_all_objects->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + hide_all_objects->setCallback ( hide_all_cb ) ; + + reveal_all_objects = new puOneShot ( 425, 124, 485, 144 ) ; + reveal_all_objects->setLegend ( "Reveal" ) ; + reveal_all_objects->setCallback ( reveal_all_cb ) ; + + layer_to_act_on = new puaSpinBox ( 310, 150, 355, 170 ) ; + layer_to_act_on->setMinValue(0.0); + layer_to_act_on->setMaxValue(999.0); + layer_to_act_on->setStepSize(1.00f); + layer_to_act_on->setValue(0.0f); + + hide_all_layer = new puOneShot ( 360, 150, 420, 170 ) ; + hide_all_layer->setLegend ( "Hide" ) ; + hide_all_layer->setCallback ( hide_all_layer_cb ) ; + + reveal_all_layer = new puOneShot ( 425, 150, 485, 170 ) ; + reveal_all_layer->setLegend ( "Reveal" ) ; + reveal_all_layer->setCallback ( reveal_all_layer_cb ) ; + + window_name = new puInput ( 130, 340, 430, 360 ) ; + window_name->setValue( main_window_name ); + window_name->setLabel ( "Window Name:" ) ; + window_name->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + window_name->setCallback ( window_name_cb ) ; + window_name->setDownCallback ( window_name_cb ) ; + + window_size_x = new puInput ( 130, 310, 180, 330 ) ; + window_size_x->setLabel ( "Window Size:" ) ; + window_size_x->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + window_size_x->setCallback ( window_size_cb ) ; + window_size_x->setDownCallback ( window_size_cb ) ; + + window_size_y = new puInput ( 180, 310, 230, 330 ) ; + window_size_y->setCallback ( window_size_cb ) ; + window_size_y->setDownCallback ( window_size_cb ) ; + + extern int main_window_x, main_window_y ; + + window_position_x = new puInput ( 130, 285, 180 , 305 ) ; + window_position_x->setLabel ( "Position:" ) ; + window_position_x->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + window_position_x->setValuator ( &main_window_x ) ; + + window_position_y = new puInput ( 180, 285, 230, 305 ) ; + window_position_y->setValuator ( &main_window_y ) ; + + extern float main_window_color_r, main_window_color_g, + main_window_color_b, main_window_color_a ; + + window_color_label = new puText (340, 320) ; + window_color_label->setLabel ( "Color:" ) ; + + window_color_r = new puaSpinBox ( 270, 285, 320, 305 ) ; + window_color_r->setMinValue(0.0); + window_color_r->setMaxValue(1.0); + window_color_r->setStepSize(0.05f); + window_color_r->setLabel ( "Red" ) ; + window_color_r->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + window_color_r->setValuator ( &main_window_color_r ) ; + window_color_r->setValue(1.0f); + + window_color_g = new puaSpinBox ( 320, 285, 370, 305 ) ; + window_color_g->setMinValue(0.0); + window_color_g->setMaxValue(1.0); + window_color_g->setStepSize(0.05f); + window_color_g->setLabel ( "Green" ) ; + window_color_g->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + window_color_g->setValuator ( &main_window_color_g ) ; + window_color_g->setValue(1.0f); + + window_color_b = new puaSpinBox ( 370, 285, 420, 305 ) ; + window_color_b->setMinValue(0.0); + window_color_b->setMaxValue(1.0); + window_color_b->setStepSize(0.05f); + window_color_b->setLabel ( "Blue" ) ; + window_color_b->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + window_color_b->setValuator ( &main_window_color_b ) ; + window_color_b->setValue(1.0f); + + window_color_a = new puaSpinBox ( 420, 285, 470, 305 ) ; + window_color_a->setMinValue(0.0); + window_color_a->setMaxValue(1.0); + window_color_a->setStepSize(0.05f); + window_color_a->setLabel ( "Alpha" ) ; + window_color_a->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + window_color_a->setValuator ( &main_window_color_a ) ; + window_color_a->setValue(1.0f); + + status_group->close () ; + + return 0 ; +} + diff --git a/demos/p-guide/src/WidgetList.h b/demos/p-guide/src/WidgetList.h new file mode 100644 index 0000000..f7a8f76 --- /dev/null +++ b/demos/p-guide/src/WidgetList.h @@ -0,0 +1,75 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: WidgetList.h 2083 2006-05-01 13:14:50Z bram $ +*/ + +// Data Structure Definition + +#ifndef WIDGET_LIST_H +#define WIDGET_LIST_H + +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + + +// PUI Widget List for Main Window + +struct WidgetList +{ + puObject *obj ; + char *object_type_name ; + int object_type ; + char *label_text ; + char *legend_text ; + short callbacks ; + char object_name [ PUSTRING_MAX ] ; + bool visible ; + bool locked ; + int layer ; // GUI layer: 0 - in back, positive nubmers - greater in front of lesser + WidgetList *next ; + /* Additional data for the extended properties of widgets */ + char *items ; + char *allowed ; + int intval1 ; + int intval2 ; + bool boolval1 ; + bool boolval2 ; + bool boolval3 ; + float floatval1 ; + float floatval2 ; + float floatval3 ; + float floatval4 ; + float floatval5 ; + float floatval6 ; +} ; + + +#endif // WIDGET_LIST_H + diff --git a/demos/p-guide/src/WidgetWindow.cxx b/demos/p-guide/src/WidgetWindow.cxx new file mode 100644 index 0000000..fb9edea --- /dev/null +++ b/demos/p-guide/src/WidgetWindow.cxx @@ -0,0 +1,460 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: WidgetWindow.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + +// Widget Window + +#include + +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +// Widget Window Data + +bool selected_object_sticky = false ; +int selected_object_type ; +char *selected_type_string ; +puButton *active_button = (puButton *)NULL ; + +// Widget window parameters +static int widget_window = 0 ; // Window handle + +// GLUT Widget Window Callbacks + +static void widget_window_specialfn ( int key, int, int ) +{ + glutPostRedisplay () ; +} + +static void widget_window_keyfn ( unsigned char key, int, int ) +{ + if ( selected_object_type ) + { + if ( key == 27 ) // Escape key, deactivate the selected button + { + selected_object_type = 0 ; + active_button->setValue ( 0 ) ; + } + } + + glutPostRedisplay () ; +} + +static void widget_window_motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + + glutPostRedisplay () ; +} + +static void widget_window_mousefn ( int button, int updown, int x, int y ) +{ + if ( ! puMouse ( button, updown, x, y ) ) + { + // PUI didn't absorb the mouse click; deactivate the selected object type + if ( selected_object_type ) + { + selected_object_type = 0 ; + active_button->setValue ( 0 ) ; + } + } + + glutPostRedisplay () ; +} + + +static void widget_window_displayfn ( void ) +{ + glutSetWindow ( widget_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + puDisplay () ; + + /* Update GLUT */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +// PUI Callback Functions: + +static void frame_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_FRAME ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void text_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_TEXT ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void menu_bar_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_MENUBAR ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void vertical_menu_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_VERTMENU ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void spin_box_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_SPINBOX ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void list_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_LIST ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + + + + +static void input_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_INPUT ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void large_input_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_LARGEINPUT ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void select_box_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_SELECTBOX ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void file_selector_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_FILESELECTOR ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void list_box_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_LISTBOX ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void scroll_bar_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_SCROLLBAR ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + + + + +static void button_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_BUTTON ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void one_shot_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_ONESHOT ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void arrow_button_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_ARROW ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void button_box_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_BUTTONBOX ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void combo_box_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_COMBOBOX ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void chooser_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_CHOOSER ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + + + + +static void dial_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_DIAL ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void slider_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_SLIDER ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void bislider_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_BISLIDER ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void trislider_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_TRISLIDER ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void slider_with_input_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_SLIDERWITHINPUT ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +static void bislider_with_ends_cb ( puObject *ob ) +{ + selected_object_sticky = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ? 1 : 0 ; + selected_object_type = PUCLASS_BISLIDERWITHENDS ; + strcpy ( selected_type_string, ob->getLegend () ) ; + active_button = (puButton *)ob ; +} + +// Function to define the window +// The widgets are arranged in a rectangle: +// Frame Input Button Dial +// Text Large Input One Shot Slider +// Menu Bar Select Box Arrow Button Bi-Slider +// Vertical Menu File Selector Button Box Tri-Slider +// Spin Box List Box Combo Box Slider with Input +// List Scroll Bar Chooser Bi-Slider with Ends +// +// The following widgets are not offered: +// Group Interface Popup Popup Menu +// Dialog Box + +int define_widget_window () +{ + widget_window = glutCreateWindow ( "Widget List" ) ; + int ln = 120 ; // Button length + int ht = 20 ; // Button height + int sp = 10 ; // Button spacing + int total_screen_width = glutGet( GLUT_SCREEN_WIDTH ) ; + + glutReshapeWindow ( 4 * ln + 5 * sp, 6 * ht + 7 * sp ) ; + glutPositionWindow ( total_screen_width - (4 * ln + 5 * sp) - 20, 20 ) ; + glutDisplayFunc ( widget_window_displayfn ) ; + glutKeyboardFunc ( widget_window_keyfn ) ; + glutSpecialFunc ( widget_window_specialfn ) ; + glutMotionFunc ( widget_window_motionfn ) ; + glutMouseFunc ( widget_window_mousefn ) ; + + // Global variable initialization (type string) + selected_type_string = new char [ PUSTRING_MAX ] ; + + // Set up the widgets + + puGroup *widget_group = new puGroup ( 0, 0 ) ; + + new puFrame ( 0, 0, 4 * ln + 5 * sp, 6 * ht + 7 * sp ) ; + + puButton *button = (puButton *)NULL ; + button = new puButton ( sp, 6*sp+5*ht, sp+ln, 6*sp+6*ht ) ; + button->setLegend ( "puFrame" ) ; + button->setCallback ( frame_cb ) ; + + button = new puButton ( sp, 5*sp+4*ht, sp+ln, 5*sp+5*ht ) ; + button->setLegend ( "puText" ) ; + button->setCallback ( text_cb ) ; + + button = new puButton ( sp, 4*sp+3*ht, sp+ln, 4*sp+4*ht ) ; + button->setLegend ( "puMenuBar" ) ; + button->setCallback ( menu_bar_cb ) ; + + button = new puButton ( sp, 3*sp+2*ht, sp+ln, 3*sp+3*ht ) ; + button->setLegend ( "puaVerticalMenu" ) ; + button->setCallback ( vertical_menu_cb ) ; + + button = new puButton ( sp, 2*sp+ht, sp+ln, 2*sp+2*ht ) ; + button->setLegend ( "puaSpinBox" ) ; + button->setCallback ( spin_box_cb ) ; + + button = new puButton ( sp, sp, sp+ln, sp+ht ) ; + button->setLegend ( "puaList" ) ; + button->setCallback ( list_cb ) ; + + + + button = new puButton ( 2*sp+ln, 6*sp+5*ht, 2*sp+2*ln, 6*sp+6*ht ) ; + button->setLegend ( "puInput" ) ; + button->setCallback ( input_cb ) ; + + button = new puButton ( 2*sp+ln, 5*sp+4*ht, 2*sp+2*ln, 5*sp+5*ht ) ; + button->setLegend ( "puaLargeInput" ) ; + button->setCallback ( large_input_cb ) ; + + button = new puButton ( 2*sp+ln, 4*sp+3*ht, 2*sp+2*ln, 4*sp+4*ht ) ; + button->setLegend ( "puaSelectBox" ) ; + button->setCallback ( select_box_cb ) ; + + button = new puButton ( 2*sp+ln, 3*sp+2*ht, 2*sp+2*ln, 3*sp+3*ht ) ; + button->setLegend ( "puaFileSelector" ) ; + button->setCallback ( file_selector_cb ) ; + + button = new puButton ( 2*sp+ln, 2*sp+ht, 2*sp+2*ln, 2*sp+2*ht ) ; + button->setLegend ( "puListBox" ) ; + button->setCallback ( list_box_cb ) ; + + button = new puButton ( 2*sp+ln, sp, 2*sp+2*ln, sp+ht ) ; + button->setLegend ( "puaScrollBar" ) ; + button->setCallback ( scroll_bar_cb ) ; + + + + button = new puButton ( 3*sp+2*ln, 6*sp+5*ht, 3*sp+3*ln, 6*sp+6*ht ) ; + button->setLegend ( "puButton" ) ; + button->setCallback ( button_cb ) ; + + button = new puButton ( 3*sp+2*ln, 5*sp+4*ht, 3*sp+3*ln, 5*sp+5*ht ) ; + button->setLegend ( "puOneShot" ) ; + button->setCallback ( one_shot_cb ) ; + + button = new puButton ( 3*sp+2*ln, 4*sp+3*ht, 3*sp+3*ln, 4*sp+4*ht ) ; + button->setLegend ( "puArrowButton" ) ; + button->setCallback ( arrow_button_cb ) ; + + button = new puButton ( 3*sp+2*ln, 3*sp+2*ht, 3*sp+3*ln, 3*sp+3*ht ) ; + button->setLegend ( "puButtonBox" ) ; + button->setCallback ( button_box_cb ) ; + + button = new puButton ( 3*sp+2*ln, 2*sp+ht, 3*sp+3*ln, 2*sp+2*ht ) ; + button->setLegend ( "puaComboBox" ) ; + button->setCallback ( combo_box_cb ) ; + + button = new puButton ( 3*sp+2*ln, sp, 3*sp+3*ln, sp+ht ) ; + button->setLegend ( "puaChooser" ) ; + button->setCallback ( chooser_cb ) ; + // TODO: It appears that the "puaChooser" file is not in the puAux library yet. + button->hide (); + + + button = new puButton ( 4*sp+3*ln, 6*sp+5*ht, 4*sp+4*ln, 6*sp+6*ht ) ; + button->setLegend ( "puDial" ) ; + button->setCallback ( dial_cb ) ; + + button = new puButton ( 4*sp+3*ln, 5*sp+4*ht, 4*sp+4*ln, 5*sp+5*ht ) ; + button->setLegend ( "puSlider" ) ; + button->setCallback ( slider_cb ) ; + + button = new puButton ( 4*sp+3*ln, 4*sp+3*ht, 4*sp+4*ln, 4*sp+4*ht ) ; + button->setLegend ( "puaBiSlider" ) ; + button->setCallback ( bislider_cb ) ; + + button = new puButton ( 4*sp+3*ln, 3*sp+2*ht, 4*sp+4*ln, 3*sp+3*ht ) ; + button->setLegend ( "puaTriSlider" ) ; + button->setCallback ( trislider_cb ) ; + + button = new puButton ( 4*sp+3*ln, 2*sp+ht, 4*sp+4*ln, 2*sp+2*ht ) ; + button->setLegend ( "puaSliderWithInput" ) ; + button->setCallback ( slider_with_input_cb ) ; + + button = new puButton ( 4*sp+3*ln, sp, 4*sp+4*ln, sp+ht ) ; + button->setLegend ( "puaBiSliderWithEnds" ) ; + button->setCallback ( bislider_with_ends_cb ) ; + + widget_group->close () ; + + return 0 ; +} + diff --git a/demos/p-guide/src/WriteCode.cxx b/demos/p-guide/src/WriteCode.cxx new file mode 100644 index 0000000..631db86 --- /dev/null +++ b/demos/p-guide/src/WriteCode.cxx @@ -0,0 +1,616 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: WriteCode.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + +// Function to write the actual code + +#include + +#include "WidgetList.h" + +#ifdef WIN32 + #define PATH_SEPARATOR '\\' +#else + #define PATH_SEPARATOR '/' +#endif + +// From the Main Window: + +extern WidgetList *widgets ; +extern int max_layer ; + +extern int main_window_width ; +extern int main_window_height ; + +extern char main_window_name [ PUSTRING_MAX ] ; +extern char pguide_current_directory [ PUSTRING_MAX ] ; + +extern int main_window_x ; +extern int main_window_y ; + +extern bool main_window_changed ; + +extern float main_window_color_r, main_window_color_g, + main_window_color_b, main_window_color_a ; + +// From the Status Window + +extern int status_window ; +extern puaFileSelector *file_selector ; + +const char* trueOrFalse(bool tf) +{ + if (tf) return "TRUE"; + else return "FALSE"; +} + +// The function itself + +void write_code ( puObject *ob ) +{ + // Get the file name and open the file + char* filename ; + file_selector -> getValue ( &filename ) ; + if (filename[0] == '\0') + { + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + return ; + } + + /* Save the new current directory */ + strcpy(pguide_current_directory, filename) ; + int i = strlen(pguide_current_directory); + while (pguide_current_directory[i] != PATH_SEPARATOR) { + if (i>0) i-- ; + else break ; + } + pguide_current_directory[i+1] = '\0' ; + + /* If they didn't give an extension, then tack ".cxx" onto the end. */ + if(!strstr(filename, ".")) + sprintf(filename, "%s.cxx", filename); + + FILE *out = fopen ( filename, "wt" ) ; + if ( !out ) + printf ( "ERROR opening file <%s> for writing\n", filename ) ; + + puDeleteObject ( file_selector ) ; + file_selector = (puaFileSelector *)NULL ; + glutHideWindow () ; + glutSetWindow ( status_window ) ; + + if ( !out ) return ; + + // Start writing code: + + fprintf ( out, "// TODO: Initial documentation\n" ) ; + fprintf ( out, "// TODO: Configuration Management system flags\n\n" ) ; + + fprintf ( out, "// TODO: Any additional includes you may need\n\n" ) ; + + fprintf ( out, "#include \n\n" ) ; + fprintf ( out, "#include \n\n" ) ; + fprintf ( out, "#include // TODO: Decide if you really need this\n\n" ) ; + + fprintf ( out, "// GLUT Window Handle\n" ) ; + fprintf ( out, "int window_handle ;\n\n" ) ; + + WidgetList *wid = widgets ; + fprintf ( out, "// PUI Widget Handles:\n" ) ; + + while ( wid ) + { + fprintf ( out, "%s *%s = (%s *)NULL ;\n", wid->object_type_name, wid->object_name, + wid->object_type_name ) ; + wid = wid->next ; + } + + fprintf ( out, "\n" ) ; + + // GLUT Callbacks: + + fprintf ( out, "// GLUT Callbacks:\n" ) ; + fprintf ( out, "static void specialfn ( int key, int x, int y )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put any of your own special-key functionality in here\n" ) ; + fprintf ( out, " puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ;\n" ) ; + fprintf ( out, " glutPostRedisplay () ;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + fprintf ( out, "static void keyfn ( unsigned char key, int x, int y )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put any of your own keyboard functionality in here\n" ) ; + fprintf ( out, " puKeyboard ( key, PU_DOWN ) ;\n" ) ; + fprintf ( out, " glutPostRedisplay () ;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + fprintf ( out, "static void motionfn ( int x, int y )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put any of your own mouse motion functionality in here\n" ) ; + fprintf ( out, " puMouse ( x, y ) ;\n" ) ; + fprintf ( out, " glutPostRedisplay () ;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + fprintf ( out, "static void mousefn ( int button, int updown, int x, int y )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put any of your own mouse click functionality in here\n" ) ; + fprintf ( out, " puMouse ( button, updown, x, y ) ;\n" ) ; + fprintf ( out, " glutPostRedisplay () ;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + fprintf ( out, "static void displayfn ( void )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // Clear the screen\n\n" ) ; + fprintf ( out, " glClearColor ( %f, %f, %f, %f ) ;\n", main_window_color_r, main_window_color_g, + main_window_color_b, main_window_color_a ) ; + fprintf ( out, " glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;\n\n" ) ; + + fprintf ( out, " // TODO: Put any of your own window display in here\n\n" ) ; + + fprintf ( out, " // Make PUI display\n" ) ; + fprintf ( out, " puDisplay () ;\n\n" ) ; + + fprintf ( out, " glutSwapBuffers () ;\n" ) ; + fprintf ( out, " glutPostRedisplay () ;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + fprintf ( out, "// TODO: Put any of your own additional GLUT callbacks in here\n\n" ) ; + + // PUI Callbacks + + fprintf ( out, "// PUI Callbacks:\n\n" ) ; + + wid = widgets ; + + while ( wid ) + { + if ( wid->callbacks & 0x01 ) // Up-callback defined + { + fprintf ( out, "void %s_cb ( puObject *ob )\n", wid->object_name ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put your up-callback functionality in here\n" ) ; + fprintf ( out, "}\n\n" ) ; + } + + if ( wid->callbacks & 0x02 ) // Active callback defined + { + fprintf ( out, "void %s_active_cb ( puObject *ob )\n", wid->object_name ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put your active callback functionality in here\n" ) ; + fprintf ( out, "}\n\n" ) ; + } + + if ( wid->callbacks & 0x04 ) // Down-callback defined + { + fprintf ( out, "void %s_down_cb ( puObject *ob )\n", wid->object_name ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Put your down-callback functionality in here\n" ) ; + fprintf ( out, "}\n\n" ) ; + } + + wid = wid->next ; + } + + fprintf ( out, "\n\n" ) ; + + // The Main Program + + fprintf ( out, "//**********************************************************\n" ) ; + fprintf ( out, "//* The Main Program *\n" ) ; + fprintf ( out, "//**********************************************************\n\n" ) ; + + fprintf ( out, "int main ( int argc, char *argv[] )\n" ) ; + fprintf ( out, "{\n" ) ; + fprintf ( out, " // TODO: Add any non-GLUT functionality you want to\n\n" ) ; + + fprintf ( out, " // Create the GLUT window:\n" ) ; + fprintf ( out, " glutInitWindowPosition( %d, %d ) ;\n", main_window_x, main_window_y ) ; + fprintf ( out, " glutInitWindowSize ( %d, %d ) ;\n", main_window_width, main_window_height ) ; + fprintf ( out, " glutInit ( &argc, argv ) ;\n" ) ; + fprintf ( out, " glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ;\n" ) ; + fprintf ( out, " window_handle = glutCreateWindow ( \"%s\" ) ;\n\n", main_window_name ) ; + + fprintf ( out, " // GLUT Windowing Callbacks:\n" ) ; + fprintf ( out, " glutDisplayFunc ( displayfn ) ;\n" ) ; + fprintf ( out, " glutKeyboardFunc ( keyfn ) ;\n" ) ; + fprintf ( out, " glutSpecialFunc ( specialfn ) ;\n" ) ; + fprintf ( out, " glutMouseFunc ( mousefn ) ;\n" ) ; + fprintf ( out, " glutMotionFunc ( motionfn ) ;\n" ) ; + fprintf ( out, " glutPassiveMotionFunc ( motionfn ) ;\n" ) ; + fprintf ( out, " glutIdleFunc ( displayfn ) ;\n" ) ; + fprintf ( out, " // TODO: Add any additional GLUT callbacks that you defined earlier\n\n" ) ; + + fprintf ( out, " // Initialize PUI:\n" ) ; + fprintf ( out, " puInit () ;\n\n" ) ; + + // TO DO: Allow the user to select his own default fonts, style, and colour scheme + + fprintf ( out, " // PUI Default Style, and Colors:\n" ) ; + fprintf ( out, " puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ;\n" ) ; + fprintf ( out, " puSetDefaultColourScheme ( 0.3f, 0.4f, 0.6f, 1.0f ) ;\n" ) ; + fprintf ( out, " // TODO: Customize this as you like\n\n" ) ; + + fprintf ( out, " // Define the widgets:\n\n" ) ; + + fprintf ( out, " puGroup *window_group = new puGroup ( 0, 0 ) ;\n\n" ) ; + + int layer ; + for ( layer = 0; layer < max_layer; layer++ ) + { + wid = widgets ; + while ( wid ) + { + if ( layer == wid->layer ) + { + puObject *ob = wid->obj ; + int x, y, w, h ; + ob->getPosition ( &x, &y ) ; + ob->getSize ( &w, &h ) ; + + /* Customize widget's constructor and extra details */ + /* fprintf ( out, " %s = new %s ( %d, %d, %d, %d ) ;\n", wid->object_name, + wid->object_type_name, x, y, x+w, y+h ) ; */ + + /* General minx, miny, maxx, maxy constructor */ + if ( (wid->object_type == PUCLASS_FRAME ) || + (wid->object_type == PUCLASS_BUTTON ) || + (wid->object_type == PUCLASS_INPUT ) || + (wid->object_type == PUCLASS_BISLIDERWITHENDS)) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h) ; + } + + /* General minx, miny constructor */ + if (wid->object_type == PUCLASS_TEXT) + { + /* Add in font properties? */ + fprintf ( out, " %s = new %s (%d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y) ; + } + + if (wid->object_type == PUCLASS_ONESHOT) + { + /* + Add support for both constructors! + puButton::puButton ( int minx, int miny, const char *legend ) ; + puButton::puButton ( int minx, int miny, int maxx, int maxy ) ; + + */ + fprintf ( out, " %s = new %s (%d, %d, %d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h) ; + } + + if ( (wid->object_type == PUCLASS_MENUBAR) || + (wid->object_type == PUCLASS_VERTMENU) ) { + + if (wid->items) + { + char *temp_items = wid->items ; + char *cr = strchr(temp_items, '\n'); + while ( cr != '\0' ) + { + char *spc = strchr ( temp_items, ' ' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( temp_items, ' ' ) ; + } + spc = strchr ( temp_items, ':' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( temp_items, ':' ) ; + } + spc = strchr ( temp_items, ';' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( temp_items, ';' ) ; + } + spc = strchr ( temp_items, ',' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( temp_items, ',' ) ; + } + spc = strchr ( temp_items, '.' ) ; + while ( spc ) + { + *spc = '_' ; + spc = strchr ( temp_items, '.' ) ; + } + *cr = '\0'; + fprintf ( out, " static char *%s_%s_submenu [] = { \"Filler\", NULL } ;\n", wid->object_name, temp_items) ; + fprintf ( out, " /* TODO: You need to create your own callbacks here, such as { \"exit_cb\",\"delete_cb\",NULL } */ \n") ; + fprintf ( out, " puCallback %s_%s_submenu_cb [] = { NULL, NULL } ;\n\n", wid->object_name, temp_items) ; + *cr = '\n'; + temp_items = cr + 1; + cr = strchr(temp_items, '\n'); + } + } + + if (wid->object_type == PUCLASS_MENUBAR) { + /* TODO: add in "height" support */ + fprintf ( out, " %s = new %s ( ) ;\n", wid->object_name, wid->object_type_name) ; + } + + if (wid->object_type == PUCLASS_VERTMENU) { + /* TODO: add in "lock to corner" support */ + fprintf ( out, " %s = new %s (%d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y) ; + } + + if (wid->items) + { + char *temp_items = wid->items ; + char unspaced_items[PUSTRING_MAX]; + char *cr = strchr(temp_items, '\n'); + fprintf ( out, " {\n") ; + while ( cr != '\0' ) + { + *cr = '\0'; + strcpy(unspaced_items, temp_items); + /* Put back the space */ + char *spc = strchr ( temp_items, '_' ) ; + while ( spc ) + { + *spc = ' ' ; + spc = strchr ( temp_items, '_' ) ; + } + /* Now "unspaced_items" writes "I_have_no_spaces" while temp_items writes "I have no spaces" (AND LIES!) */ + fprintf ( out, " %s->add_submenu (\"%s\", %s_%s_submenu, %s_%s_submenu_cb ) ;\n", wid->object_name, temp_items, wid->object_name, unspaced_items, wid->object_name, unspaced_items ) ; + *cr = '\n'; + temp_items = cr + 1; + cr = strchr(temp_items, '\n'); + } + fprintf ( out, " }\n") ; + } + fprintf ( out, " %s->close() ;\n", wid->object_name) ; + } + + if ( (wid->object_type == PUCLASS_LISTBOX) || + (wid->object_type == PUCLASS_COMBOBOX) || + (wid->object_type == PUCLASS_SELECTBOX) || + (wid->object_type == PUCLASS_BUTTONBOX) || + (wid->object_type == PUCLASS_LIST)) + { + char data[1024]; + char onedata[PUSTRING_MAX]; + + if (wid->items) + { + char *temp_items = wid->items ; + char *cr = strchr(temp_items, '\n'); + sprintf ( data, " "); + while ( cr != '\0' ) + { + *cr = '\0'; + sprintf ( onedata, "\"%s\",", temp_items); + *cr = '\n'; + temp_items = cr + 1; + cr = strchr(temp_items, '\n'); + strcat(data, onedata); + } + } + + fprintf ( out, " static char *%s_entries [] = { %s NULL } ;\n", wid->object_name, data) ; + if ( (wid->object_type == PUCLASS_LISTBOX) || + (wid->object_type == PUCLASS_LIST) ) + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %s_entries ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, wid->object_name) ; + if (wid->object_type == PUCLASS_BUTTONBOX) + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %s_entries, %s ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, wid->object_name, trueOrFalse(wid->boolval1)) ; + if (wid->object_type == PUCLASS_COMBOBOX) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %s_entries, %s ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, wid->object_name, trueOrFalse(wid->boolval1)) ; + fprintf ( out, " %s->setCurrentItem(%d) ;\n", wid->object_name, wid->intval1) ; + } + if (wid->object_type == PUCLASS_SELECTBOX) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %s_entries ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, wid->object_name) ; + fprintf ( out, " %s->setCurrentItem(%d) ;\n", wid->object_name, wid->intval1) ; + } + } + + if (wid->object_type == PUCLASS_POPUPMENU) + { + /* Allow a list of menu items, and remember to point out these are hidden when created, and must be reveal()ed. */ + /* NOT CURRENTLY IMPLEMENTED */ + } + + /* puRange */ + if ( (wid->object_type == PUCLASS_SLIDER ) || + (wid->object_type == PUCLASS_BISLIDER ) || + (wid->object_type == PUCLASS_TRISLIDER ) || + (wid->object_type == PUCLASS_DIAL ) || + (wid->object_type == PUCLASS_SPINBOX ) || + (wid->object_type == PUCLASS_SCROLLBAR ) ) + { + + /* Constructors */ + if ( (wid->object_type == PUCLASS_SLIDER ) || + (wid->object_type == PUCLASS_BISLIDER ) || + (wid->object_type == PUCLASS_TRISLIDER )) + { + /* Sliders */ + fprintf ( out, " %s = new %s (%d, %d, %d, %s, %d ) ;\n", wid->object_name, wid->object_type_name, x, y, (wid->boolval2==1)?h:w, trueOrFalse(wid->boolval2), (wid->boolval2==1)?w:h) ; + /* Slider value options */ + if (wid->object_type == PUCLASS_SLIDER ) + fprintf ( out, " %s->setValue(%ff) ;\n", wid->object_name, wid->floatval4) ; + if ( (wid->object_type == PUCLASS_BISLIDER ) || (wid->object_type == PUCLASS_TRISLIDER ) ) + { + fprintf ( out, " %s->setCurrentMax(%ff) ;\n", wid->object_name, wid->floatval4) ; + fprintf ( out, " %s->setCurrentMin(%ff) ;\n", wid->object_name, wid->floatval5) ; + if (wid->object_type == PUCLASS_TRISLIDER ) + { + if (!wid->boolval3) + fprintf ( out, " %s->setFreezeEnds(FALSE) ;\n", wid->object_name ) ; + fprintf ( out, " %s->setValue(%ff) ;\n", wid->object_name, wid->floatval6) ; + fprintf ( out, " %s->setSliderFraction(0.1f) ;\n", wid->object_name) ; + /*Allow setting the slider fraction?*/ + } + } + } else if (wid->object_type == PUCLASS_DIAL ) + { + fprintf ( out, " %s = new %s (%d, %d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y, (h+w)/2) ; + fprintf ( out, " %s->setWrap(%s) ;\n", wid->object_name, trueOrFalse(wid->boolval2)) ; + + } else if (wid->object_type == PUCLASS_SPINBOX ) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %s ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, trueOrFalse(wid->boolval2) ) ; + fprintf ( out, " %s->setArrowHeight(%ff) ;\n", wid->object_name, wid->floatval4 ) ; + + } else if (wid->object_type == PUCLASS_SCROLLBAR ) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %d, %s ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h, wid->intval1, trueOrFalse(wid->boolval2) ) ; + fprintf ( out, " %s->setArrowHeight(%ff) ;\n", wid->object_name, wid->floatval4 ) ; + } + /* All puRange options */ + fprintf ( out, " %s->setMaxValue (%ff) ;\n", wid->object_name, wid->floatval1 ) ; + fprintf ( out, " %s->setMinValue (%ff) ;\n", wid->object_name, wid->floatval2 ) ; + fprintf ( out, " %s->setStepSize (%ff) ;\n", wid->object_name, wid->floatval3 ) ; + char cbmodetext[20] = "PUSLIDER_ALWAYS"; + if (wid->boolval1) + strcpy(cbmodetext,"PUSLIDER_ALWAYS"); + else + strcpy(cbmodetext,"PUSLIDER_CLICK"); + fprintf ( out, " %s->setCBMode (%s) ;\n", wid->object_name, cbmodetext) ; + } + + if (wid->object_type == PUCLASS_DIALOGBOX) + { + /* Not yet implemented as a class */ + } + + if (wid->object_type == PUCLASS_ARROW) + { + char arr_name[20] = "PUARROW_UP"; + if (wid->items) + { + if (strstr(wid->items,"Double-Up")) + strcpy(arr_name, "PUARROW_FASTUP"); + else if (strstr(wid->items,"Double-Down")) + strcpy(arr_name, "PUARROW_FASTDOWN"); + else if (strstr(wid->items,"Double-Left")) + strcpy(arr_name, "PUARROW_FASTLEFT"); + else if (strstr(wid->items,"Double-Right")) + strcpy(arr_name, "PUARROW_FASTRIGHT"); + else if (strstr(wid->items,"Up")) + strcpy(arr_name, "PUARROW_UP"); + else if (strstr(wid->items,"Down")) + strcpy(arr_name, "PUARROW_DOWN"); + else if (strstr(wid->items,"Left")) + strcpy(arr_name, "PUARROW_LEFT"); + else if (strstr(wid->items,"Right")) + strcpy(arr_name, "PUARROW_RIGHT"); + } + fprintf ( out, " %s = new puArrowButton (%d, %d, %d, %d, %s ) ;\n", wid->object_name, x, y, x+w, y+h, arr_name) ; + } + + if (wid->object_type == PUCLASS_CHOOSER) + { + // Just give it a dummy legend; set the legend later on + fprintf ( out, " %s = new %s (%d, %d, %d, %d, "" ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h) ; + } + + if (wid->object_type == PUCLASS_SLIDERWITHINPUT) + { + // Extra argument for input box being on the bottom of the slider + fprintf ( out, " %s = new %s (%d, %d, %d, %d, 0 ) ;\n", wid->object_name, wid->object_type_name, x, y, x+w, y+h) ; + } + + if (wid->object_type == PUCLASS_INPUT ) + { + /*Already had its constructor defined by the general -- look up*/ + if (wid->boolval1 == false) + fprintf ( out, " %s->rejectInput() ;\n", wid->object_name ) ; + if ( wid->allowed) + fprintf ( out, " %s->setValidData(\"%s\") ;\n", wid->object_name, wid->allowed ) ; + } + + if ( wid->object_type == PUCLASS_LARGEINPUT ) + { + fprintf ( out, " %s = new %s (%d, %d, %d, %d, %d, %d ) ;\n", wid->object_name, wid->object_type_name, x, y, w, h, wid->intval1, wid->intval2 ) ; + if (wid->boolval1 == false) + fprintf ( out, " %s->rejectInput() ;\n", wid->object_name ) ; + if ( wid->allowed) + fprintf ( out, " %s->setValidData(\"%s\") ;\n", wid->object_name, wid->allowed ) ; + + } + /* Done custom constructor and extra details */ + + if ( wid->callbacks & 0x01 ) // Up-callback defined + fprintf ( out, " %s->setCallback ( %s_cb ) ;\n", wid->object_name, wid->object_name ) ; + + if ( wid->callbacks & 0x02 ) // Active callback defined + fprintf ( out, " %s->setActiveCallback ( %s_active_cb ) ;\n", wid->object_name, wid->object_name ) ; + + if ( wid->callbacks & 0x04 ) // Down-callback defined + fprintf ( out, " %s->setDownCallback ( %s_down_cb ) ;\n", wid->object_name, wid->object_name ) ; + + // TO DO: Allow the user to customize colour and style + + const char *place_name [] = { "PUPLACE_TOP_LEFT", "PUPLACE_TOP_CENTERED", "PUPLACE_TOP_RIGHT", + "PUPLACE_CENTERED_LEFT", "PUPLACE_CENTERED_RIGHT", + "PUPLACE_BOTTOM_LEFT", "PUPLACE_BOTTOM_CENTERED", "PUPLACE_BOTTOM_RIGHT", + "PUPLACE_CENTERED_CENTERED", + "PUPLACE_ABOVE_LEFT", "PUPLACE_ABOVE_RIGHT", + "PUPLACE_BELOW_LEFT", "PUPLACE_BELOW_RIGHT", + "PUPLACE_UPPER_LEFT", "PUPLACE_UPPER_RIGHT", + "PUPLACE_LOWER_LEFT", "PUPLACE_LOWER_RIGHT" } ; + + if ( wid->label_text ) + { + fprintf ( out, " %s->setLabel ( \"%s\" ) ;\n", wid->object_name, wid->label_text ) ; + fprintf ( out, " %s->setLabelPlace ( %s ) ;\n", wid->object_name, place_name [ ob->getLabelPlace () ] ) ; + // TO DO: Check for label font and print it + } + + if ( wid->legend_text ) + { + fprintf ( out, " %s->setLegend ( \"%s\" ) ;\n", wid->object_name, wid->legend_text ) ; + fprintf ( out, " %s->setLegendPlace ( %s ) ;\n", wid->object_name, place_name [ ob->getLegendPlace () ] ) ; + // TO DO: Check for legend font and print it + } + + if( !wid->visible ) + fprintf ( out, " %s->hide () ;\n", wid->object_name ) ; + + fprintf ( out, " \n" ) ; + + } + wid = wid->next ; + } + } + + fprintf ( out, "\n\n" ) ; + + fprintf ( out, " window_group->close () ;\n\n\n" ) ; + + fprintf ( out, " // GLUT Main Loop\n" ) ; + fprintf ( out, " glutMainLoop () ;\n" ) ; + fprintf ( out, " return 0;\n" ) ; + fprintf ( out, "}\n\n" ) ; + + // Close up shop + fclose ( out ) ; + main_window_changed = false ; +} + diff --git a/demos/p-guide/src/example.xml b/demos/p-guide/src/example.xml new file mode 100644 index 0000000..9d0d744 --- /dev/null +++ b/demos/p-guide/src/example.xml @@ -0,0 +1,504 @@ + + + + +VERSION +Test Project +TRUE + + Sample P-Guide File + + 600 + 400 + + + 0 + 0 + + + 0.750000 + 0.550000 + 0.650000 + 1.000000 + + + + + + left_arrow + puArrowButton + Up + 1 + TRUE + TRUE + + 24 + 101 + + + 93 + 96 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + Double-Left + + + + right_arrow + puArrowButton + Up + 1 + TRUE + TRUE + + 25 + 101 + + + 127 + 96 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + Double-Right + + + + combo + puComboBox + Up + 1 + TRUE + TRUE + + 166 + 70 + + + 89 + 20 + + + 2 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + Option 1 + Option 2 + Option 3 + Final + + + + oneshot3 + + Run Away + PUPLACE_CENTERED_CENTERED + + puOneShot + Up + 1 + TRUE + TRUE + + 90 + 20 + + + 154 + 127 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + oneshot2 + + Shoot + PUPLACE_CENTERED_CENTERED + + puOneShot + Up + 1 + TRUE + TRUE + + 90 + 20 + + + 154 + 152 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + oneshot1 + + Execute + PUPLACE_CENTERED_CENTERED + + puOneShot + Up + 1 + TRUE + TRUE + + 90 + 20 + + + 154 + 177 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + dizzy + puSpinBox + Up + 1 + TRUE + TRUE + + 166 + 40 + + + 89 + 205 + + + 0 + 0 + TRUE + TRUE + FALSE + 20.000000 + 0.000000 + 1.000000 + 0.500000 + 0.000000 + 0.000000 + + + + libris + + puLargeInput + Up + 1 + TRUE + TRUE + + 334 + 232 + + + 258 + 13 + + + 2 + 5 + TRUE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + + dial2 + puDial + Up + 0 + TRUE + TRUE + + 50 + 50 + + + 88 + 255 + + + 0 + 0 + TRUE + TRUE + FALSE + 5.000000 + 0.000000 + 1.000000 + 0.000000 + 0.000000 + 0.000000 + + + + dial1 + puDial + Up + 0 + TRUE + FALSE + + 50 + 50 + + + 87 + 309 + + + 0 + 0 + TRUE + TRUE + FALSE + 1.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + slider + puSlider + Up + 0 + TRUE + TRUE + + 35 + 356 + + + 50 + 7 + + + 0 + 0 + TRUE + TRUE + FALSE + 10.000000 + 0.000000 + 0.000000 + 0.500000 + 0.000000 + 0.000000 + + + + trislider + puTriSlider + Up + 0 + TRUE + FALSE + + 47 + 356 + + + 1 + 7 + + + 0 + 0 + TRUE + TRUE + FALSE + 17.000000 + -5.000000 + 2.000000 + 16.000000 + -0.500000 + 0.500000 + + + + menu_bar + puMenuBar + Up + 0 + TRUE + TRUE + + 120 + 30 + + + 0 + 367 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + File + Edit + View + Project + Build + Debug + Tools + Options + Window + Bookmarks + Help + + + + buttonbox + puButtonBox + Up + 1 + TRUE + TRUE + + 163 + 134 + + + 428 + 249 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + On + Off + Automatic + + + + widget1 + + puText + Up + 1 + TRUE + TRUE + + 5 + 20 + + + 173 + 360 + + + 0 + 0 + FALSE + FALSE + FALSE + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + + diff --git a/demos/p-guide/src/pGuide.cxx b/demos/p-guide/src/pGuide.cxx new file mode 100644 index 0000000..b8a0d51 --- /dev/null +++ b/demos/p-guide/src/pGuide.cxx @@ -0,0 +1,916 @@ +/* + This file is part of P-GUIDE - + PUI-based Graphical User Interface Designer. + Copyright (C) 2002, 2006 John F. Fay + + P-GUIDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + P-GUIDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with P-GUIDE; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: pGuide.cxx 2083 2006-05-01 13:14:50Z bram $ +*/ + +#ifdef VERSION +#undef VERSION +#endif + +#define VERSION = "0.1a" + +// Program to allow the user to build a PUI Graphical User Interface + +#include "WidgetList.h" + +WidgetList *widgets = (WidgetList *)NULL ; +int max_layer = 1 ; + +puObject *active_object = (puObject *)NULL ; +WidgetList *active_widget = (WidgetList *)NULL ; +static short activity_flag = 0 ; // 0 - inactive; 1 - moving; 2 - resizing xmin, 3 - resizing ymin, 4 - resizing xmax, 5 - resizing ymax +static int resize_symmetric = 1 ; +static int resize_corner = 0 ; + +static int mouse_x_position_in_object = 0 ; +static int mouse_y_position_in_object = 0 ; + +static int pguide_last_buttons = 0 ; // Because puMouse isn't called, we have to do our own last_buttons checking + +// Main window parameters +int main_window = 0 ; // Main window handle +int main_window_width = 600 ; +int main_window_height = 600 ; +char main_window_name [ PUSTRING_MAX ] ; +int main_window_x = 0, main_window_y = 0 ; + +float main_window_color_r = 1.0, main_window_color_g = 1.0, + main_window_color_b = 1.0, main_window_color_a = 1.0 ; + +bool main_window_changed = false ; +static bool done_first_setup = false ; +bool currently_loading = false ; + +static int ctrl_key_down = 0 ; + +static int mouse_x = 0 ; +static int mouse_y = 0 ; + +char pguide_current_directory [ PUSTRING_MAX ] ; + +// Widget count +int widget_number = 0 ; + +// Upon creation of new widgets, should they automatically be made "locked"? +bool autolock = false ; + +// Properties popup +static puPopupMenu *context_menu; + +// From the status window: +extern void setStatusWidgets ( WidgetList *wid ) ; + +// From the properties window: +extern int properties_window; + +// Definitions +#define RESIZING_BORDER_WIDTH 5 +#define RESIZING_CORNER_BORDER_WIDTH 4 + /** 4 seems to be the best number for this define... this makes sure * + * that the default widget size allows you to still resize along the * + * X axis without difficulty - 23 Jan 03 JCJ **/ + + +// Properties Callback + +static void cb_edit_properties ( puObject *ob ) +{ + if (properties_window) + { + extern puGroup *properties_group; + /* Delete the widgets */ + puDeleteObject( properties_group ); + glutDestroyWindow( properties_window ); + properties_window = 0; + glutSetWindow( main_window ); + } + extern int define_properties_window () ; + // Open the properties menu + define_properties_window(); +} + +static void cb_lock_toggle ( puObject *ob ) +{ + WidgetList *wid = widgets ; + while ( wid ) + { + if ( wid->obj == active_object ) + { + if (wid->locked == false) + wid->locked = true ; + else + wid->locked = false ; + } + wid = wid->next ; + } +} + +static void cb_popup_delete ( puObject *ob ) +{ + WidgetList *wid = widgets ; + WidgetList *prv = (WidgetList *)NULL ; + while ( wid ) + { + if ( wid->obj == active_object ) + { + if ( prv ) // Remove the widget from the linked list + prv->next = wid->next ; + else + widgets = wid->next ; + + delete wid->object_type_name ; // Delete the widget + delete wid->legend_text ; + delete wid->label_text ; + delete wid->obj ; + delete wid->allowed ; + delete wid->items ; + delete wid ; + active_widget = (WidgetList *)NULL ; + active_object = (puObject *)NULL ; + wid = (WidgetList *)NULL ; + if (properties_window) + { + extern puGroup *properties_group; + /* Delete the widgets */ + puDeleteObject( properties_group ); + glutDestroyWindow( properties_window ); + properties_window = 0; + glutSetWindow( main_window ); + } + } + else + { + prv = wid ; + wid = wid->next ; + } + } +} + + +// GLUT Main Window Callbacks + +static void process_key ( int key ) +{ + extern int selected_object_type ; + extern puButton *active_button ; + + ctrl_key_down = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ; +#define NUDGE_DISTANCE 1 +#define LARGE_NUDGE_DISTANCE 10 + + if ( active_object ) // Active object; check the keys + { + main_window_changed = true ; + + int xo, yo ; + active_object->getPosition ( &xo, &yo ) ; + int dist = ctrl_key_down ? LARGE_NUDGE_DISTANCE : NUDGE_DISTANCE ; + + if ( key == GLUT_KEY_LEFT ) + active_object->setPosition ( xo-dist, yo ) ; + else if ( key == GLUT_KEY_RIGHT ) + active_object->setPosition ( xo+dist, yo ) ; + else if ( key == GLUT_KEY_UP ) + active_object->setPosition ( xo, yo+dist ) ; + else if ( key == GLUT_KEY_DOWN ) + active_object->setPosition ( xo, yo-dist ) ; + else if ( ( key == 127 ) || ( key == 8 ) ) // Delete or Backspace + { + WidgetList *wid = widgets ; + WidgetList *prv = (WidgetList *)NULL ; + while ( wid ) + { + if ( wid->obj == active_object ) + { + if ( prv ) // Remove the widget from the linked list + prv->next = wid->next ; + else + widgets = wid->next ; + + delete wid->object_type_name ; // Delete the widget + delete wid->legend_text ; + delete wid->label_text ; + delete wid->obj ; + delete wid->allowed ; + delete wid->items ; + delete wid ; + active_widget = (WidgetList *)NULL ; + active_object = (puObject *)NULL ; + wid = (WidgetList *)NULL ; + } + else + { + prv = wid ; + wid = wid->next ; + } + } + } + else if ( key == 27 ) // Escape key, deactivate the object + { + active_widget = (WidgetList *)NULL ; + active_object = (puObject *)NULL ; + } + + setStatusWidgets ( active_widget ) ; + } + + if ( selected_object_type ) + { + if ( key == 27 ) // Escape key, deactivate the selected button + { + selected_object_type = 0 ; + active_button->setValue ( 0 ) ; + } + } + + glutPostRedisplay () ; +} + +static void main_window_specialfn ( int key, int, int ) +{ + process_key ( key ) ; +} + +static void main_window_keyfn ( unsigned char key, int, int ) +{ + process_key ( key ) ; +} + +static void main_window_motionfn ( int x, int yy ) +{ + int y = main_window_height - yy ; + + mouse_x = x ; + mouse_y = y ; + + // Dragging the mouse: If there is an active object, slide it around + if ( ( active_object ) && ( pguide_last_buttons != 4 ) ) + { + WidgetList *wid = widgets ; + while ( wid ) + { + if ( wid->obj == active_object ) + { + if (wid->locked == false) + { + extern puInput *object_position_x ; + extern puInput *object_position_y ; + extern puInput *object_size_x ; + extern puInput *object_size_y ; + + main_window_changed = true ; + + int dist ; + int xo, yo ; + active_object->getPosition ( &xo, &yo ) ; + int w, h ; + active_object->getSize ( &w, &h ) ; + + if (resize_corner == 1) + { + switch ( activity_flag ) + { + case 2 : // Resize top-right + case 3 : + activity_flag = 6; + break ; + + case 4 : // Resize bottom_left + case 5 : + activity_flag = 7; + break ; + } + } + + switch ( activity_flag ) + { + case 1 : // Moving the object + active_object->setPosition ( x - mouse_x_position_in_object, + y - mouse_y_position_in_object ) ; + break ; + + case 2 : // Resizing on x-min + dist = x - xo ; + if (w - dist < 5) break ; + if ( resize_symmetric * dist > w ) dist = w / resize_symmetric - 1 ; + active_object->setPosition ( x, yo ) ; + active_object->setSize ( w - resize_symmetric * dist, h ) ; + break ; + + case 3 : // Resizing on y-min + dist = y - yo ; + if (h - dist < 5) break ; + if ( resize_symmetric * dist > h ) dist = h / resize_symmetric - 1 ; + active_object->setPosition ( xo, y ) ; + active_object->setSize ( w, h - resize_symmetric * dist ) ; + break ; + + case 4 : // Resizing on x-max + dist = x - xo - w ; + if (w + dist < 5) break ; + if ( resize_symmetric * dist < -w ) dist = 1 - w / resize_symmetric ; + active_object->setSize ( w + resize_symmetric * dist, h ) ; + if ( resize_symmetric == 2 ) active_object->setPosition ( xo - dist, yo ) ; + break ; + + case 5 : // Resizing on y-max + dist = y - yo - h ; + if (h + dist < 5) break ; + if ( resize_symmetric * dist < -h ) dist = 1 - h / resize_symmetric ; + active_object->setSize ( w, h + resize_symmetric * dist ) ; + if ( resize_symmetric == 2 ) active_object->setPosition ( xo, yo - dist ) ; + break ; + + case 6 : // Resizing bottom-left + if ( yo - y > xo - x ) + dist = yo - y; + else + dist = xo - x; + if ( (w + dist < 5) || (h + dist < 5) ) break ; + active_object->setPosition ( xo - dist, yo - dist ) ; + active_object->setSize ( w + dist, h + dist ) ; + break ; + + case 7 : // Resizing top-right + if (y-yo-h>x-xo-w) + dist = y - yo - h ; + else + dist = x - xo - w ; + if ( (w + dist < 5) || (h + dist < 5) ) break ; + active_object->setSize ( w + dist, h + dist ) ; + break ; + + case 8 : // Resizing top-left + if (y-yo-h>x-xo-w) + dist = y - yo - h ; + else + dist = xo - x ; + if ( (w + dist < 5) || (h + dist < 5) ) break ; + active_object->setPosition ( xo - dist, yo ) ; + active_object->setSize ( w + dist, h + dist ) ; + break ; + + case 9 : // Resizing bottom-right + if (y-yo-h>x-xo-w) + dist = yo - y ; + else + dist = x - xo - w ; + if ( (w + dist < 5) || (h + dist < 5) ) break ; + active_object->setPosition ( xo , yo - dist ) ; + active_object->setSize ( w + dist, h + dist ) ; + break ; + } + + int a, b ; + active_object->getPosition ( &a, &b ) ; + object_position_x->setValue ( a ) ; + object_position_y->setValue ( b ) ; + + active_object->getSize ( &a, &b ) ; + object_size_x->setValue ( a ) ; + object_size_y->setValue ( b ) ; + } + } + wid = wid->next ; + } + } + glutPostRedisplay () ; +} + +static void main_window_passivefn ( int x, int yy ) +{ + int y = main_window_height - yy ; + + // Dragging the mouse without a button down: save the mouse coordinates + mouse_x = x ; + mouse_y = y ; + /*if (context_menu->isVisible() == 1) + { + puBox *cont_box = context_menu->getABox(); + if ( ( x >= cont_box->min[0] ) && ( x <= cont_box->max[0] ) && + ( y >= cont_box->min[1] ) && ( y <= cont_box->max[1] ) ) + { + context_menu->checkHit(-1, 0, mouse_x, mouse_y); + } + }*/ +} + +static void main_window_mousefn ( int button, int updown, int x, int yy ) +{ + int y = main_window_height - yy ; + + mouse_x = x ; + mouse_y = y ; + + if ( updown == PU_DOWN ) + pguide_last_buttons |= ( 1 << button ) ; + else + pguide_last_buttons &= ~( 1 << button ) ; + + if ( button == PU_RIGHT_BUTTON ) + { + /* Pressed the right mouse button -- popup the manipulation window. */ + WidgetList *wid = widgets ; + while ( wid ) + { + puBox *box = wid->obj->getABox () ; + if ( wid->visible && ( x >= box->min[0] ) && ( x <= box->max[0] ) && + ( y >= box->min[1] ) && ( y <= box->max[1] ) ) + { + + active_widget = wid ; + active_object = wid->obj ; + /* We can modify mouse_x and mouse_y, because theyre not used again if we're in the right mouse function */ + /* If we're real close to the right of the screen, modify the X position ...*/ + if (mouse_x > main_window_width - 110 ) mouse_x = main_window_width - 110 ; + /* If we're real close to the top of the screen, modify the Y position...*/ + if (mouse_y > main_window_height - 140) mouse_y = main_window_height - 140 ; + + context_menu->setPosition(mouse_x, mouse_y) ; + context_menu->reveal() ; + //puSetActiveWidget(context_menu, mouse_x, mouse_y) ; + break ; + } + wid = wid->next ; + } + + } else { + if ( puActiveWidget() && ( active_object != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + ctrl_key_down = ( glutGetModifiers () & GLUT_ACTIVE_CTRL ) ; + + if ( ( context_menu->isVisible() == 1) && ( active_widget ) ) + { + puBox *cont_box = context_menu->getABox(); + if ( ( x >= cont_box->min[0] ) && ( x <= cont_box->max[0] ) && + ( y >= cont_box->min[1] ) && ( y <= cont_box->max[1] ) ) + { + context_menu->checkHit(button, updown, mouse_x, mouse_y); + } else { + context_menu->hide(); + } + } else /*if ( context_menu->isVisible() == 1) { context_menu->hide(); } * The user did something funny, so hide the menu since they've been naughty */ + { + // Downclick: Place a new widget, activate an existing widget, deactivate widget, or select from menu. + if ( updown == GLUT_DOWN ) + { + // If there is a selected object, create a new one of that type. + // extern puObject *createWidget ( int type ) ; + extern int selected_object_type ; + + if ( selected_object_type ) + { + extern bool selected_object_sticky ; + extern char *selected_type_string ; + extern puButton *active_button ; + + main_window_changed = true ; + + // puObject *new_obj = createWidget ( selected_object->getType () ) ; + puObject *new_obj = new puFrame ( 0, 0, 90, 20 ) ; + char *object_type_name = new char [ PUSTRING_MAX ] ; + strcpy ( object_type_name, selected_type_string ) ; + new_obj->setLegend ( object_type_name ) ; + + // Add the new object to the list of widgets + WidgetList *new_wid = new WidgetList ; + new_wid->obj = new_obj ; + new_wid->label_text = (char *)NULL ; + new_wid->legend_text = (char *)NULL ; + new_wid->allowed = (char *)NULL ; + new_wid->items = (char *)NULL ; + new_wid->object_type_name = object_type_name ; + new_wid->object_type = selected_object_type ; + new_wid->callbacks = 1 ; // Default: up callback enabled, others disabled + sprintf ( new_wid->object_name, "widget%d", widget_number++ ) ; + new_wid->visible = true ; + new_wid->locked = autolock ; /* Set it to the autolock var (true or false ) */ + new_wid->layer = max_layer - 1 ; + new_wid->next = widgets ; + widgets = new_wid ; + + // Set the new widget's position + new_obj->setPosition ( x, y ) ; + + mouse_x_position_in_object = 0 ; + mouse_y_position_in_object = 0 ; + + + /* Set up the widget's default data values */ + new_wid->intval1 = 0; + new_wid->intval2 = 0; + new_wid->boolval1 = false; + new_wid->boolval2 = false; + new_wid->boolval3 = false; + new_wid->floatval1 = 0.0f; + new_wid->floatval2 = 0.0f; + new_wid->floatval3 = 0.0f; + new_wid->floatval4 = 0.0f; + new_wid->floatval5 = 0.0f; + new_wid->floatval6 = 0.0f; + + if (new_wid->object_type == PUCLASS_TEXT) + { + new_obj->setSize(5,20); + } + if (new_wid->object_type == PUCLASS_COMBOBOX) + { + new_wid->intval1 = 1; + new_wid->boolval1 = true; + } + + if (new_wid->object_type == PUCLASS_SELECTBOX) + { + new_wid->intval1 = 1; + } + + if (new_wid->object_type == PUCLASS_BUTTONBOX) + { + new_wid->boolval1 = true; + } + + if (new_wid->object_type == PUCLASS_COMBOBOX) + { + new_wid->intval1 = 1; + new_wid->boolval1 = true; + } + + if ( (new_wid->object_type == PUCLASS_SLIDER ) || + (new_wid->object_type == PUCLASS_BISLIDER ) || + (new_wid->object_type == PUCLASS_TRISLIDER ) || + (new_wid->object_type == PUCLASS_DIAL ) || + (new_wid->object_type == PUCLASS_SPINBOX ) || + (new_wid->object_type == PUCLASS_SCROLLBAR ) ) + { + new_wid->floatval1 = 1.0f; + new_wid->floatval2 = 0.0f; + new_wid->floatval3 = 0.0f; + new_wid->boolval1 = true; + } + + if ( (new_wid->object_type == PUCLASS_SLIDER ) || + (new_wid->object_type == PUCLASS_BISLIDER ) || + (new_wid->object_type == PUCLASS_TRISLIDER ) ) + { + new_wid->boolval2 = true; + } + + if (new_wid->object_type == PUCLASS_SLIDER) + { + new_wid->floatval4 = 0.5f; + } + + if (new_wid->object_type == PUCLASS_BISLIDER) + { + new_wid->floatval4 = 1.0f; + new_wid->floatval5 = 0.0f; + } + + if (new_wid->object_type == PUCLASS_TRISLIDER) + { + new_wid->floatval4 = 1.0f; + new_wid->floatval5 = 0.0f; + new_wid->floatval6 = 0.5f; + new_wid->boolval3 = false; + } + + if (new_wid->object_type == PUCLASS_DIAL) + { + new_obj->setSize(50,50); + new_wid->boolval2 = true; + } + + if (new_wid->object_type == PUCLASS_SPINBOX) + { + new_wid->boolval2 = true; + new_wid->floatval4 = 0.5f; + } + + if (new_wid->object_type == PUCLASS_SCROLLBAR) + { + /* Not Yet Implemented */ + } + + if (new_wid->object_type == PUCLASS_SPINBOX) + { + new_wid->boolval2 = true; + new_wid->floatval4 = 0.5f; + } + + if (new_wid->object_type == PUCLASS_INPUT) + { + new_wid->boolval1 = true; + } + + if (new_wid->object_type == PUCLASS_LARGEINPUT) + { + new_wid->boolval1 = true; + new_wid->intval1 = 1; + new_wid->intval2 = 5; + } + + if (new_wid->object_type == PUCLASS_MENUBAR) + { + new_obj->setSize( 120, 30) ; + new_obj->setPosition( 0, puGetWindowHeight()-30) ; + new_wid->locked = true ; + } + + if (new_wid->object_type == PUCLASS_VERTMENU) + { + new_obj->setSize( 45, 120) ; + new_obj->setPosition( 0, puGetWindowHeight()-120) ; + new_obj->setLegend(""); + new_obj->setLabelPlace(PUPLACE_BOTTOM_LEFT); + new_obj->setLabel("puVerticalMenu"); + new_wid->locked = true ; + } + + // Make the new widget the active widget + active_object = new_obj ; + active_widget = new_wid ; + + // If the "sticky" flag is not set, reset the selected object + if ( !selected_object_sticky ) + { + selected_object_type = 0 ; + active_button->setValue ( 0 ) ; + } + } + else + { + // Clicking on a widget in the main widget list activates it + WidgetList *wid = widgets ; + while ( wid ) + { + puBox *box = wid->obj->getABox () ; + if ( wid->visible && ( x >= box->min[0] ) && ( x <= box->max[0] ) && + ( y >= box->min[1] ) && ( y <= box->max[1] ) ) + { + active_widget = wid ; + active_object = wid->obj ; + if ( abs ( x - box->min[0] ) < RESIZING_BORDER_WIDTH ) + activity_flag = 2 ; // Resizing on x-min + else if ( abs ( x - box->max[0] ) < RESIZING_BORDER_WIDTH ) + activity_flag = 4 ; // Resizing on x-max + else if ( abs ( y - box->min[1] ) < RESIZING_BORDER_WIDTH ) + activity_flag = 3 ; // Resizing on y-min + else if ( abs ( y - box->max[1] ) < RESIZING_BORDER_WIDTH ) + activity_flag = 5 ; // Resizing on y-max + else + activity_flag = 1 ; // Away from edges, we're moving it + + /* Now we check and see if we're clicking on a corner - the sensitivity for + the determiniation of whether we're clicking a corner or not is held by the + definition of "RESIZING_CORNER_BORDER_WIDTH" which is at the top of this file */ + + int corner_resize_width = RESIZING_CORNER_BORDER_WIDTH + (((box->max[0] - box->min[0]) + (box->max[1] - box->min[1]) / 2 ) / 50 ) * 2 ; + + if ( activity_flag != 1 ) + { + // Bottom Right Corner + if ( ( abs( y - box->min[1] ) < corner_resize_width ) && (abs ( x - box->max[0] ) < corner_resize_width ) ) + activity_flag = 9; + + // Bottom Left Corner + if ( ( abs( y - box->min[1] ) < corner_resize_width ) && (abs ( x - box->min[0] ) < corner_resize_width ) ) + activity_flag = 6; + + // Top Right Corner + if ( ( abs( y - box->max[1] ) < corner_resize_width ) && (abs ( x - box->max[0] ) < corner_resize_width ) ) + activity_flag = 7; + + // Top Left Corner + if ( ( abs( y - box->max[1] ) < corner_resize_width ) && (abs ( x - box->min[0] ) < corner_resize_width ) ) + activity_flag = 8; + } + + /* If CTRL is held down, symmetric resize is forced. */ + + resize_symmetric = ctrl_key_down ? 2 : 1 ; + + if (active_widget->object_type == PUCLASS_DIAL) + resize_corner = 1 ; + else + resize_corner = 0; + + int object_x, object_y ; + active_object->getPosition ( &object_x, &object_y ) ; + mouse_x_position_in_object = x - object_x ; + mouse_y_position_in_object = y - object_y ; + + break ; + } + + wid = wid->next ; + } + + if ( ( !wid ) && ( context_menu->isVisible()==0 ) ) // Ran through the entire list, deactivate any active widget + { + active_widget = (WidgetList *)NULL ; + active_object = (puObject *)NULL ; + } + } +} + + setStatusWidgets ( active_widget ) ; + } + } + glutPostRedisplay () ; +} + + +static void main_window_reshapefn ( int w, int h ) +{ + extern puInput *window_size_x ; + extern puInput *window_size_y ; + + if (done_first_setup) + main_window_changed = true ; + else + done_first_setup = true ; + + if ( ( !currently_loading ) && ( + ( mouse_x < main_window_width/2 ) || // Grabbed the left edge ... + ( mouse_y < main_window_height/2 ) ) ) // or the bottom edge, move the widgets + { + WidgetList *wid = widgets ; + int deltax = 0 ; + int deltay = 0 ; + + if ( mouse_x < main_window_width/2 ) deltax = w - main_window_width ; + if ( mouse_y < main_window_height/2 ) deltay = h - main_window_height ; + + while ( wid ) + { + int x, y ; + wid->obj->getPosition ( &x, &y ) ; + wid->obj->setPosition ( x + deltax, y + deltay ) ; + + wid = wid->next ; + } + } + + main_window_width = w ; + main_window_height = h ; + + window_size_x->setValue ( w ) ; + window_size_y->setValue ( h ) ; + + currently_loading = false ; +} + +static void main_window_displayfn ( void ) +{ + /* Clear the screen */ + glutSetWindow ( main_window ) ; + + glClearColor ( main_window_color_r, main_window_color_g, main_window_color_b, main_window_color_a ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + // Set the OpenGL to draw PUI widgets + + int w = puGetWindowWidth () ; + int h = puGetWindowHeight () ; + + glPushAttrib ( GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; + + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + + glViewport ( 0, 0, w, h ) ; + + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + gluOrtho2D ( 0, w, 0, h ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; + + // Draw the widgets + int layer ; + for ( layer = 0; layer < max_layer; layer++ ) + { + WidgetList *wid = widgets ; + while ( wid ) + { + if ( wid->visible && ( wid->layer == layer ) ) wid->obj->draw ( 0, 0 ) ; + wid = wid->next ; + } + } + + if ( active_object ) + { + puBox *box = active_object->getBBox () ; + glColor4f ( 0.0f, 0.0f, 0.0f, 1.0f ) ; + glLineWidth ( 3.0f ) ; + glBegin ( GL_LINES ) ; + glVertex2i ( box->min[0]-1, box->min[1]-1 ) ; + glVertex2i ( box->max[0]+1, box->min[1]-1 ) ; + glVertex2i ( box->max[0]+1, box->max[1]+1 ) ; + glVertex2i ( box->min[0]-1, box->max[1]+1 ) ; + glVertex2i ( box->min[0]-1, box->min[1]-1 ) ; + glEnd () ; + } + + context_menu->draw(0,0); + + /* Update GLUT */ + + glutSwapBuffers () ; + glutPostRedisplay () ; + +} + +int main ( int argc, char **argv ) +{ + extern int define_widget_window () ; + extern int define_status_window () ; + int i = 0; + + strcpy ( main_window_name, "PUI GUI Builder" ) ; + + glutInitWindowPosition( 100, 0 ) ; + glutInitWindowSize ( main_window_width, main_window_height ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + main_window = glutCreateWindow ( main_window_name ) ; + glutDisplayFunc ( main_window_displayfn ) ; + glutKeyboardFunc ( main_window_keyfn ) ; + glutSpecialFunc ( main_window_specialfn ) ; + glutMouseFunc ( main_window_mousefn ) ; + glutMotionFunc ( main_window_motionfn ) ; + glutPassiveMotionFunc ( main_window_passivefn ) ; + glutReshapeFunc ( main_window_reshapefn ) ; + glutIdleFunc ( main_window_displayfn ) ; + +// If we're using windows, ignore the C: or whatnot + +#ifdef WIN32 + #define PATH_SEPARATOR '\\' + strcpy (pguide_current_directory, argv[0]+2); +#else + #define PATH_SEPARATOR '/' + strcpy (pguide_current_directory, argv[0]); +#endif + i = strlen(pguide_current_directory); + + + while (pguide_current_directory[i] != PATH_SEPARATOR) { + if (i>0) i-- ; + else break ; + } + pguide_current_directory[i+1] = '\0'; + + puInit () ; + +#ifdef VOODOO + puShowCursor () ; +#endif + + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.8f, 0.8f, 0.8f, 1.0f ) ; + + // Add the properties context menu + context_menu = new puPopupMenu( 0, 0 ) ; + context_menu->add_item("Properties", cb_edit_properties) ; + context_menu->add_item("Lock/Unlock", cb_lock_toggle) ; + context_menu->add_item("Delete", cb_popup_delete) ; + context_menu->add_item("Cut", NULL) ; + context_menu->add_item("Copy", NULL) ; + context_menu->add_item("Change Class", NULL) ; + context_menu->close() ; + + // Set up the other windows + define_widget_window () ; + define_status_window () ; + + glutMainLoop () ; + return 0 ; +} + diff --git a/demos/p-guide/src/pGuide.dsp b/demos/p-guide/src/pGuide.dsp new file mode 100644 index 0000000..5d869a5 --- /dev/null +++ b/demos/p-guide/src/pGuide.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="pGuide" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pGuide - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pGuide.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pGuide.mak" CFG="pGuide - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pGuide - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pGuide - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +F90=df.exe +RSC=rc.exe + +!IF "$(CFG)" == "pGuide - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\..\\" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 pui.lib puAux.lib sg.lib fnt.lib ul.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib opengl32.lib glu32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "pGuide - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pGuide___Win32_Debug" +# PROP BASE Intermediate_Dir "pGuide___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "pGuide___Win32_Debug" +# PROP Intermediate_Dir "pGuide___Win32_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 pui_d.lib puAux_d.lib sg_d.lib fnt_d.lib ul_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib opengl32.lib glu32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"LIBCMTD" /pdbtype:sept /libpath:"..\..\..\..\plib" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "pGuide - Win32 Release" +# Name "pGuide - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\LoadSave.cxx +# End Source File +# Begin Source File + +SOURCE=.\pGuide.cxx +# End Source File +# Begin Source File + +SOURCE=.\PropertiesWindow.cxx +# End Source File +# Begin Source File + +SOURCE=.\StatusWindow.cxx +# End Source File +# Begin Source File + +SOURCE=.\WidgetWindow.cxx +# End Source File +# Begin Source File + +SOURCE=.\WriteCode.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\WidgetList.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/demos/simon/AUTHORS b/demos/simon/AUTHORS new file mode 100644 index 0000000..d7afed4 --- /dev/null +++ b/demos/simon/AUTHORS @@ -0,0 +1,3 @@ +SIMON was written by Steve Baker. +AstroPong was written by Oliver Baker. + diff --git a/demos/simon/COPYING b/demos/simon/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/demos/simon/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/demos/simon/Makefile b/demos/simon/Makefile new file mode 100644 index 0000000..5af4d27 --- /dev/null +++ b/demos/simon/Makefile @@ -0,0 +1,13 @@ + +all: + (cd src ; make) + +install: + (cd src ; make install) + +uninstall: + (cd src ; make uninstall) + +clean: + (cd src ; make clean) + diff --git a/demos/simon/README b/demos/simon/README new file mode 100644 index 0000000..8f34edc --- /dev/null +++ b/demos/simon/README @@ -0,0 +1,60 @@ + +WELCOME TO S.I.M.O.N +==================== + +Which stands for: + + Simple + Interface for + Making + Oliver's programs + Nice. + +This is a simple set of functions that hide much of the nastiness +of 3D programming to make it possible for someone with only the +very minimum of programming experience to write simple 3D applications. + + + +INSTALLATION +============ + +Right now, you build SIMON by running 'make' and install it by +running 'make install' as root. You must already have PLIB installed. + +Simon installs into /usr/local/simon/libsimon.a /usr/local/simon/simon.h +and places an executable into /usr/local/bin/simon + + + +RUNNING SIMON +============= + +There are two ways to write SIMON programs: + + 1) Write your code in C++ and link it to libsimon + (the LIBRARY version of SIMON). + + 2) Write your code in PSL (The PLIB Scripting Language) + and use the 'simon' program to run it. + +Documentation for both methods are in the 'doc' directory. + + + +DEMO'S +====== + +AstroPong is my son's first ever game (his first ever program actually). +It was originally written in C++ using libsimon - but has subsequently +been converted into PSL for use as a demonstration program. + +Once you've done a 'make install', you can change directory down into +the 'simon' directory and run: + + simon astropong.psl + +...move your paddle using the joystick - don't let the herring +escape from the ice cube! + + diff --git a/demos/simon/astropong/astropong.psl b/demos/simon/astropong/astropong.psl new file mode 100644 index 0000000..8844689 --- /dev/null +++ b/demos/simon/astropong/astropong.psl @@ -0,0 +1,249 @@ +/* + AstroPong - A 3D Pong Game + Copyright (C) 2002 Oliver Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: astropong.psl 1699 2002-09-22 15:22:21Z sjbaker $ +*/ + + +#define ARENA_SIZE 8.0 /* meters */ +#define ARENA_LENGTH 10.0 /* meters */ +#define BALL_SIZE 2.5 /* meters */ + +#define BOUNCE (ARENA_SIZE/2.0 - BALL_SIZE/2.0) +#define BOUNCEY (ARENA_LENGTH/2.0 - BALL_SIZE/2.0) + +#define BAT_SIZE (1.0 + BALL_SIZE/2.0) /* From the center to the sides */ + +int arena ; +int shadow ; +int difficulty ; +int scoreBoardR ; +int scoreBoardB ; +int ball ; +int bPaddle ; +int rPaddle ; +int camera ; +int scoreR ; +int scoreB ; + +float Blue_batspeed = .34; +float scoreSpinR ; +float scoreSpinB ; +float ballSpinH ; +float ballSpinSH ; +float ballSpinP ; +float ballSpinSP ; +float ballSpinR ; +float ballSpinSR ; +float ballX ; +float ballY ; +float ballZ ; +float ballSX ; +float ballSY ; +float ballSZ ; +float bpaddleX ; +float bpaddleZ ; +float rpaddleX ; +float rpaddleZ ; +float userX ; +float userZ ; +int frameno = 0 ; + +int main() +{ + camera = 0 ; + arena = siLoad ( "arena2.ac") ; + ball = siLoad ( "ball3.ac") ; + bPaddle = siLoad ( "bluepaddle2.ac"); + rPaddle = siLoad ( "redpaddle2.ac") ; + scoreBoardR = siLoad ( "Scoreboard.ac") ; + scoreBoardB = siLoad ( "Scoreboard.ac") ; + shadow = siLoad ( "shadow2.ac") ; + + scoreSpinR = 0 ; + scoreSpinB = 0 ; + ballSX = 0.166 ; + ballSY = 0.155 ; + ballSZ = 0.016 ; + ballSpinSH = 4 ; + ballSpinSP = 4 ; + ballSpinSR = 4 ; + + difficulty = 1 ; + + if ( difficulty == 1 ) { Blue_batspeed = .275 ; } + else { Blue_batspeed = .34 ; } + + while ( 1 ) + { + frameno = frameno + 1 ; + + /* Read the joystick */ + + userX = siJoystickLR(); + userZ = siJoystickUD(); + + /* Move the ball */ + + ballX += ballSX ; + ballY += ballSY ; + ballZ += ballSZ ; + ballSpinH += ballSpinSH ; + ballSpinP += ballSpinSP ; + ballSpinR += ballSpinSR ; + + /* Bounce off the sides? */ + + if ( ballX > BOUNCE || ballX < -BOUNCE ) + { + ballSX = -ballSX ; + } + + /* Bounce off the top and bottom? */ + + if ( ballZ > BOUNCE || ballZ < -BOUNCE ) + { + ballSZ = -ballSZ ; + } + + + + /* What happens at the ends? */ + + if ( ballY > BOUNCEY ) /* Passed the blue paddle? */ + { + /* Is the blue bat there? */ + + if (ballX > bpaddleX - BAT_SIZE && ballX < bpaddleX + BAT_SIZE && + ballZ > bpaddleZ - BAT_SIZE && ballZ < bpaddleZ + BAT_SIZE ) + { + float hitX = ballX - bpaddleX ; + float hitZ = ballZ - bpaddleZ ; + + if ( ballSX > 0 ) + ballSX = fabs ( hitX / 2.0 ) ; + else + ballSX = -fabs ( hitX / 2.0 ) ; + + if ( ballSZ > 0 ) + ballSZ = fabs ( hitZ / 2.0 ) ; + else + ballSZ = -fabs ( hitZ / 2.0 ) ; + + ballSY = -ballSY ; + } + else + { + scoreR++ ; + + print ("The score is Red:", scoreR, " Blue:", scoreB, "\n" ) ; + + ballSX = 0.1 ; + ballSY = 0.1 ; + ballSZ = 0.1; + ballSY = -ballSY ; + ballX = 0.0 ; + ballY = 0.0 ; + ballZ = 0.0; + for ( int i = 0 ; i < 60 ; i++ ) pause ; + } + } + + + if ( ballY < -BOUNCEY ) /* Passed the red paddle? */ + { + /* Was the red bat there? */ + + if (ballX > rpaddleX - BAT_SIZE && ballX < rpaddleX + BAT_SIZE && + ballZ > rpaddleZ - BAT_SIZE && ballZ < rpaddleZ + BAT_SIZE ) + { + float hitX = ballX - rpaddleX ; + float hitZ = ballZ - rpaddleZ ; + + if ( ballSX > 0 ) + ballSX = fabs ( hitX / 2.0 ) ; + else + ballSX = -fabs ( hitX / 2.0 ) ; + + if ( ballSZ > 0 ) + ballSZ = fabs ( hitZ / 2.0 ) ; + else + ballSZ = -fabs ( hitZ / 2.0 ) ; + + ballSY = -ballSY ; + } + else + { + scoreB++ ; + print ("The score is Red:", scoreR, " Blue:", scoreB, "\n" ) ; + ballSX = .1 ; + ballSY = .1 ; + ballSZ = .1; + ballSY = -ballSY ; + ballX = 0.0 ; + ballY = 0.0 ; + ballZ = 0.0; + for ( int i = 0 ; i < 60 ; i++ ) pause ; + } + } + + /* Update the scoreboard */ + + scoreSpinR = scoreR * -36.0 ; + scoreSpinB = scoreB * -36.0 ; + + /* How should the computer move the paddle? */ + + if ( ballZ > bpaddleZ ) { bpaddleZ += Blue_batspeed ; } + else { bpaddleZ -= Blue_batspeed ; } + + if ( ballX > bpaddleX ) { bpaddleX += Blue_batspeed ; } + else { bpaddleX -= Blue_batspeed ; } + + /* How did the player move the paddle? */ + + rpaddleX += userX * -.5; + rpaddleZ += userZ * .5; + + /* Did the paddle move off the edge of the screen? */ + + if ( rpaddleX > BOUNCE ) rpaddleX = BOUNCE ; + if ( rpaddleX < -BOUNCE ) rpaddleX = -BOUNCE ; + if ( rpaddleZ > BOUNCE ) rpaddleZ = BOUNCE ; + if ( rpaddleZ < -BOUNCE ) rpaddleZ = -BOUNCE ; + + /* Tell SIMON where to position the paddle, ball and camera */ + + /* MODEL X, Y, Z, H, P, R */ + siPosition ( scoreBoardR, 1, 0, 0, 0, scoreSpinR, 0 ) ; + siPosition ( scoreBoardB, -1, 0, 0, 0, scoreSpinB, 0 ) ; + siPosition ( bPaddle , bpaddleX, 5, bpaddleZ, 0, 0 , 0 ) ; + siPosition ( rPaddle , rpaddleX, -5, rpaddleZ, 0, 0 , 0 ) ; + siPosition ( ball , ballX, ballY, ballZ, + ballSpinH,ballSpinP,ballSpinR); + siPosition ( arena , 0, 0, 0, 0, 0 , 0 ) ; + siPosition ( shadow , ballX, ballY, -4, 0, 0 , 0 ) ; + siPosition ( camera , 0, -10, 0, 0, 0 , 0 ) ; + + pause ; + } +} + + diff --git a/demos/simon/astropong/data/Penguin_beak.rgb b/demos/simon/astropong/data/Penguin_beak.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b1e0e799dad81ab6bd43bea26320cc1867836989 GIT binary patch literal 11194 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 literal 0 HcmV?d00001 diff --git a/demos/simon/astropong/data/Penguin_body.rgb b/demos/simon/astropong/data/Penguin_body.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/demos/simon/astropong/data/Penguin_body2.rgb b/demos/simon/astropong/data/Penguin_body2.rgb new file mode 100644 index 0000000000000000000000000000000000000000..208e6efe7b080f2982517534badd9d014124b3cd GIT binary patch literal 21709 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 Ba2? zZU)JCwQ{vNsWdDiws_fI)yoxov1k`b$9d_&^A}5YN~Q9QOPx>8#9^EagzvBaEXXj} zVmHNxAr=gkoGq;2LUl>3BrGEH(vznZ@gfm7Aj_@$to5ist*S?$wwq%Of+ivgLMARg z4w{hcTh~M$9aWV#+&C03i7L3!R-Yr6P;Z`#*OYMz5DE8)roVTblyLo zC;^Rd)rsoHE0v{(iZ0xEYO$H_7Vi`%ajbHv=VKKOk6-KgWPZ+}s_Uq{s%JV9p4Xtq zOxN~Mki!(*%xlKwzqfaM(1YCEtpzUkZIsBBFnp}9n+&`SB**>#!blL_nxo6 z_wZr6er(Qdx>FP@PC;^kfY9fN`ZKj|b)OAQO?S|zOaIB?oq;|N(BYj$*V~@7wYOf* zj~0bfvnV?M^4-V$6LZ_WEBSGvJ{P1B%$K!if z4#x6(nAHQ+$ok@+n1X$cp|6VCTUb`x?o=O$<^z4v{)mLVqT>GiRAnUXSElBRi~A3x z#iUa?+e01TjPiuNNBENd{1inb*RM#)7vnrFI-SGk9E`l)JZV2`zDF5s-i2($Canuu zj7?hbs*j?mk3&J=Y}3npIxmgJ7PZ|{C~$j#2(yDw)i4k*grpAs!Cpd_Cgm&PQc zJZE1{E`Z9pqMV_9!II<{X#q#xtt*60w)=2p;>QD(#|yF&BKSNZ3bNgW7ZU3RE|eBz zCMswi^L(Y=fwWkmULO5CJo(tor$&>(U^40g2R#mYlS$ui>erjPAKtohrf5&JB$pzA zK9{E)Z+O;k&>M{Xdf{LU9t^_O^9Q%ClozHb#km}k2z{3}ZQ6H3by;qTLd5!}?zkiM zT{@l7V1QXY4o1DuIcOQgI*ZYy?QA->M=tPqwd=@@r&^eXStEcZMp1y1Sa0b!YT6r* zrpg5pM%FE_bD$D>(zBX@&f%azqBFm^Umxt1_N&TUc(qfj)4f^M)X(b%jfOsT&)w50 zVf$DxM$*pQQ}^_!`*i5EUu2kKbc2NSda%=LdSBit-xIcv1%q`iR(o}CI;T;qncJ(n zrbbnHtCm-f_vv)6$AZ_5vhbLON37Pt?wdui0+Ux675948J?dU9gb9X&H%Rp(TBCV* zWO&H(qP}P+pA(>Q_EXT%tm#*$0}GAN@1QZ%FEy#oKG1>3nCA6>h{3{awL2V6m)+L; zaej18ARZO>wVLkk9?)R%a0TL_*K7K;`hnpI$E4F~A6M7qM+r|?W8uj%RO)#&?q z1FsipU+J}4jn+8qa(jIXUhkZ(?M$+gGw?K?#l@LY*2k#wK|PPD^gq5 zF+gpqi!bb>%hfd+UN_X+r|Dy3$TI5WJwppV@8Z0Bpyg;RA4v^j@h%pkuVz$t+eVDq zo)<5{i}kP9YI?y@YcN{IoOAQug$1wAGi|<8vLli+@?tR`$JmXk&Ua!n4t9d?Vx7KM zq&Dkx{TtfKr6auba8v)Z%Qn!bR`>Ecy+)#*g6`H)n`6e~S@a4FJ(Ie|Lp#KZAjK@M zAGZfQHxX9YWOO^nEqzFEMpFG!-0RdB21jfT_uQOM;OX@`^-YB_qKIXt%xXkI<0`90 z4YE?CcJ}H`gX7Lsc3r0C)pn^(Diu+&a^Llrc8_Ozq+i3U4O)2V*7o)pEH>NJ^sHyz zKM(1A^NW5jTj6C{lw@))J`8shqUap7oS$`#8Z|F_Aw{qHCEx4P8ZE=)PWPPG>x1g> z*ymet=|9d6S8#gD^E)|`wSi@c+SO+;4cJ}l+hxtd_kjT#j1D3s$+W2v%b|V`W-1RPb&Fjz&X3%oA=K+Z5E?W*NcQO zTSq6S-Sd6~Xmx}=F5`{;3J}iXzQgmf#G}n-r-wywcG70S{HD=hnuMp*vqEfFN7(B& z)#oX|H=BN2B8@NIhS9zB+tCG0mRS!3M-*qLCr7PTvuR-1W_QhcP=w&DgxBZwE==}b zOb^eXBpY;N4mFx*=Gj>C`sSu5ZNt_9v&9Oo9)TuOe>u9y3a|4;b(%DTBQP3Uv?ZgC z?sJJ^W|V#>JiAuwX0#Apq^W0a+A%)ioOXLWK0k`sq_dn3;Om>UwI16U;2WM(t8t+X zi!SORi|rG3r^`JzA0Wz-9{&UWpq6K>r6d-7v+2Z}R(Z$#W>$xqi(+6ki;hcEDm!iS ziz~#Wp1IKFvKwr)VbFkj&*fCsjvEh4J=ZzDOb7n*UW3QOdqcAVYb*F692n`jmzPrc zbe6vB8TD*v)aQP7Ayu4?F+;!4$DVj#on4$?#f%Luzduy8;s+*P7T|gRaPy&Pw9eut zca+|@&a&7BsRiQ6`{vdb36U0RS(*#6Y22>q{^lZPI)nazmt|j7PYRrZseu`u&AuD^ zz36$>ergwM&i#N&ksQvIc&_BaY6(_87m~!@ekvZ^w`<+m5wK2wC{iA|V{$DsWwgD* zB)n1BJ(xmfwAZqwD#rfz`KaRCMi(1&f#h9DjW@&yM4tDWeY0pURx$eTX&<*j>E-wJ z<7^DVvM?@r&$xSTc4pZn_Cc6O@0Ly*2V0ND3bZGssxuD_vpJk`^>}9a>50j}U>4|mQ8Ycx zJM^zaHtVo;7^|Ya=W-@Er*mKDm1wWFymGl`!5ja*o>{lc<#b|#4rRI5GlP;z#{@ra z7#rb-&Ern{%bG0k4iR{6ntXQxgIFHz>RN#SoeoPVscU_v9dtNmJ#2=LIk2mXOHvH1F@PqkM2@XVb|L<7XGH;s z;+(LJ3|WlWmRfO}V6|#nOOvE~m-qM^|Q6e}2ckml;b|40YKC3~<{-9^Vx1Iw>>#yf;`cA` z9-DjG`N}qije^NC{IWJXB88F^>=5xpJv{Bq{a!2jVRMnN{9~cz=ROba5>_t^aBP0g z?VhsRMh6FsChMrWCQA;NQs@*fcsXbqzju~#hx#% z6a>;Pxaq~{{?eG-xq z8&k8@+;S`q{-tm~6-l_*BTf3r*>#25$Umm=+{~oSIxuXycRWt;?`JIN*xwo7ht`P% z&`PByWkI6>BkT{~^p6dZHK`dYg5!SgjPuo)b?oKM0(la8;(ii&s8NrVCMetLJbYWx ztaD;?a8!Nspgf5Kr=KuR)6-b%RtN-R9wNZz1EfG;atqX2hZFrtQsr|8ZV~-#7qx1G z7+Ct3i`J-z{;)dEV%Q9s+bZ@1yi2a^a-f%B+O3m`8yC&F9HYbLwzH`L?>-Qx6#5Hs z%*ltN9yqg&NMU2|!J2^ljNlVlg8qV!KKW=2E^bG`z+T^pXS52M$R-%Z?Xm5{a7(7Y z?Q{?^Tni4jnY*6G>UrrBa9)CE7>e}pZ#*n&{dG*#Aw$80}S>*YKaCfrV)Ek=l zJl0zU3c=%FDfZvA%&y}Mc00mOA#hg`bXHI_5RupC=~+wTL3sl6_yZBN)E{I7cT<-* zrLx<_zLveYrr|&Myy`XJh*ie?w&VYV?#pw?Dvb6g4e9aA?Dm#4L$5;5QXc>x}Nxs z2PKh$*B_iC{dLm0=k9=WuV*(l*<19wG@vzl?3!WDtsSiB5%(xt@j9sW2jl2?EO1Pu zzfl(6whG&N;kn3av1g{Ix02$nV3*u)6ot13nb2>ljXUC%=Rj)`ngwZ1(NxGE9}B*&Iqqbzmc8?o=5uK-rpn?eN#uT;RMF`Wd|9jdj0=4(JNfz) zwqCBg)YW^WVIlWF99L&|Re&q~ZEjtE=%MqPkv`?zlKAt(jrq!e|NH8UY(KV+`tW}% z>U)M73zRGVe{inx^mly3v4`VldYKroLi1SMSLI!#JP3oRZu?=hm|#a+PUQjR0JdN zMR279lAL<8$2ZUWe}-x3^?87;0b7{|S8`xLoXdLk@;=t9~D>X^; zn}!#}>-z6>v8^9mc$dw**EjPdm;ojU`t8CY)s}0b&wZ1J*2hE3sPHILvki zGvEtgN{!OOmT{^7r)&4PTiy3S`=IYeNzvVL@zT#1-?*3Y%(&U}@YPe|IxEGhqWO^L zADm^#?;oz?_Z%Ar7_o1;oe}v5kQmF78zqPCj7ygO0Jns%i;HKy_u z2c7<46azko`G2_1Fn`B1m-vm#^L$We{UY!C@$2r*vJw7z5a2$G!JA3%^ZR|io=9Mbp)Yc+ObUVA5p!sypAnSIaBCh+VHnxG6}{trMdr0Dmysc5+0qrGc-0l-Rv*M?&md%(qRXk^bc#I3u31xFk#8Vj`_P z^6@f_HeW6LiY4PTo(ACAPvZOYpho49*0FH^SCJTjZJpoGcDkFb2ui=nW4R$b2uB*8ZzEPUIxO^tD-kDcNE z&sVmHn}63A?Kh6M1bG|dPPR|VmcGWmiVY3wH}4Vf3Hw+WCO*b{{BDrgqhJ^P8ACgH_3Aw%$O>_DlC}qaNpt;GJZyTB~2uR{C&(PQDb7|i^WLT4t{ka z+^nv@OIP~wE0l)(X!b2(5@$?2-|Y_e0++18xN{ss;l~W#S&g1U_ULt7qra z<-+?5+qFr}(p^Vd2WO-G!s|Pm$d4_XM_ZdA!&@`SoLL-K+-jMP3`BKV<)FNK=LF$K5!-BE94iQSuLs9|3OUR6rr%>0f+w+z$DC9g1XGx0ZyW*9hH z8*}A&(Q13v*lQoT*ECa6{uzk9;r+SHKj0qezJ6qv6bw^2Q)KS7UZ-+t79)_!y|U|A zovCcZiPB&*6fg5{%Mvy7QTyw(u5dqFMK&BWi}UuE4W+3uV*I2d z3rkO4UEfsL(wE-G?p062iL_`@79+QHL)jQE*_V1IeKK}ey3uf%AJyC~&rslZMcJG= zBKy({hhMtnd-K5}+Y|_k&w^(}bEiBr62EfVs)fCDK%Q3iiDAMc1)U8jxhzj_R_4ab z@zbxp+<;tlx>;-Uh?f>NrsNuYR)2nfd^moHwp9x`)FO+^Jb$Cp>Jn`@S|^NcwI@^Y zmm4U_;VjafS(WuqtuB7UQ93`ae^`4;6(ioyk#{q|>Sx9>1G#jGk5-+iZ8g~N#?_lK z@=RG?HdhxWDf3a6TcR|3qTcnRn4_j|tJ|!+T9p0Cy;0y5~Hz3}TS$yu} z7QW5;@KIaa15_w078RKa_a(|{A-8$+!?Zt2bx>MtI&vs8UVeg-Lj0a9T9qGGWIB`` zFXu}+5~^Z8awsQJAt}YrVK@B|?Eke-Z{TU1LJ=W~F({ejKmBZ;unXA&D$fOgKvMw>d;N39 zMl4|LjlGQhE`YJ$?`CY|9>(5OGxnB&u~GDVZwE5=P7-5(Ff;bY3dY{8X6#QV8GEmf zu|K;p_Wp3j#{7w~54;)s(4VofG(Mv7@npu_1sZXTjib58k*sm|8T;g8!ajkp528^= z_(KT$BEmkB#(xt2TEd?}_=5>QJ;yVf2>&|5zJRcgBkVMv8|lJrfbe$^{&vEDi|~{F zo;ybP=~M`Ir0-%r>l5_XcMpz9s82|LMhqU%(@A?!4tb3fsy=X!?jcco{1wv6zH z68^P>eHLM-`-lFSu+y^)8%@}MPuLx4bQAtdg#RMpzef146aG7d|8IoDKLx1wQV9P!!cWis zek)-oIUl}7*xx7YG(LWo2CawtM#61NV>ONQg#Be2+ZkiD{>+00VPKPJc+#Lb*jJW8 zb1|BWeQo)hbjD`WAbXXD_4@_%H^vegb7@So49NPD24s9g!<)u(8VfB0pIc?Dq4$tK zhsI*du+E41aUEok&e;shpfzACX<%N`2U|r0GI1ZqXEcD3{DRRMu@N+0qVc?CjHB02 zX?#NCFEoIE3Jp9r2CzV$7Y)pfS3HaLdt8e-rqaN@c=qWupi2yVkLOrM1NvD(1Ng1_ z!spP*Ldz>Y$Ni9rv5*F2L*I~%_t32s|3-Shfd-xtI)FYP6S~5fOapSrF4>zjfE~k% zAD;s|^z$K&Q8Zqo@jJ`-J-tG2AJF)S2Cl(-ykbE1cp7*n;K$#vrwNvU=XR%oIUonG zAJcHR3|tSr12bgcdOS1M7z6r)y+QB518mm!R(!aA91YwLyM-+4{Nw{{Bn{wr9JhpD zJuu+?r|0C!)2H*9!W{MN*?dmyym@oy&YnGO zny2Sq{xWv#TW^gR@%rm8y`)q=DL?}OoN4UrIOl@K*4Djqd|X^&Vsf&WZcIr@PEJWVbSNNT z+qQ4M`SMF)st$KN`>a+w*aBwoK!hwt5dQL)>Cg z_39-{=FR*3^N&7y_g!I>&du#<2^vTcnA*F-6M9 zxC}&Gx#H+ZsnBWlY9F6{`;5lO$oTlQG@hZ$%`GlAo2#mn)nVo3rKKiQN=iV$=FKZt zE?@rk+qSj>YPPR;%0}wr7u@ zpRezR4PIU_(DCD6e9^|{7m_r1CeG9XJ-8YORHA6lMFlO7+SXQla$@Cx=I5VJoZu|k z)pgmjb?f%+J9scACN(uHD?dLkudJ-P`qZg&=Pq2Rsi~_IFNA&f-KkSmRc3Q)YGB~b zotrnm_uimE3a0t;%fJ6!X@T-kVyvU1yZeL*z_Mb+x^-K&`1owwhOl8UgoG%=B0@p} z1NZOOXm;;jzkcDu88fC%ef3pa+y70P+Qvq-e@$KwTU(bW=Tr>j|MXK~A!5Lgx*)Wo*!(=boE9dFITOEBEa?bSOFb$dRI=^72!sE?%s!Z)&=I`|jQQ_j$Lrr>CQX z-n2#BzTMb(^=eJc$&;m}85v<=YW2j4PEIPO89n;S6~VGx!a%Mp-Nwe*dF04<-~H^f zufO*8UcY|p)}1@mYJ(vxEG8x`Eh7WwT~I&^o6mE2Mp$ZULPA7DV4zyPb?dTavuBSQ zC5pcgt$}EY05&#uc5o=QJ=vo|P;*Y!`1n)s0Zm7TMngv9?EKkhlP7z5ZQdLh7#Ek7 zRahuPyng-m?bcS_sqX3F{mFfOJw2VBU0v<%ckbM{(a>=9YHjU_69okc34wwB{!^#A zy3(_Z8g=$8x4?Wffhdj`F>>UGAAbJ1m)G*;n>Ov(v1gCL5FVbGc=+&F16va`aX3w58XYu0A zn*##kb^{PE<;bLYxSynla4NL*ZcdQMJ30g?=jY~TXCsNGq=bj>-MeX%xA($@9v*IPznnqBED|&kmcrn!InTmIo; zcXv$79v`(oMMF$6^k~^r$z(xAc73$!GoR-{ruj+y3{Q0R<1(xjO zGYAI|+Pb>#-|z00KzDWZ(UNO>djXoa8CzSMnl4^E zcFbhT%}r0AH*YWl-iKmi;mza6dw6{I*^C+U=X-l^-015Y5D*!8_;5}RFVqwlmy~c? zZ8gBEi;8$LFNiRiN=sqyIXN*gdcB|D_U&7@jvM!sWQtjwJX!K)PN<3Ch@wJ)!9~GA z5V^vIY=Eeat*v~k@`2{~@$usoEOPkp_3Jlp-nDE0{;;s5r2PEK%Jb(N8t&cW-J1LN zJ3DzFr}b6#tGl{{`$mMYhK7q5&zw1N;@Gh@Yn~Do>+0(1`T6J5r+@X;;>D|1Z`-za zZ)j*@BAlh5fSc6i<$_sVRwkH*QeI+|*S4w(0Y54#Fp%$8PoDgQL?vZS9kM>>K%~Wg z@K#ztQPe3EDwVUdQt2X6wE8~*O?;rq%6jun1MDYc_9|V`EAR*6rN6Yu9ey zhD&yJwYIjkb$54lb$2WJgSxxh+k1NY`uh9(`ntP0ma(O!si~pi+O?}!uUzr-bCnGB zq4ZRx@||})JSI*0;tMR~nl(N?8jZma8JU)rlT%UxgDfw1#Y!s8AxDo^Rh5_L<|65Y zhlhpDne&7+Hw0aKd!?v%a#AQ99i5yIfwc14aaEABvx|#L<>ch-jBg}vE{cwhN~OKM zEJt{tiI0Ef6$MNF)vr7~zxigxicOpL>_L>s%`GW8aiXRM*4ok0*Vo(2JN3ZX+lwLe z>$Fu}O-90=ePo6z{;lkCcw{G?G z)9DT#jEl?6EGU4vmX*D1)|Z!8RaI6N6(uF{LmF@Ip+ldLLs69Mm60z3wbsGG*;%FH z&N|qwgM*Wk(wPjGn2m~5AkwEwr9xqAD~sR{G)CjFVG>pk59ogF+U?u*`snD)Op~dq z>g?G|mztV7I(mEi`^CNxtY=S8Tbqm-3(qMH8T$GVqY#E~-Xx?aPAp#hl=wtv=TW1^ zjB$6LIC1)PFE4NJEn9qj_wEe{h=@o^%E~G(b}?IhRb>t-E32%mswyeT$cT+SbV#T3 z@OVm`5e{l&V{6MfhCNjzY6`Y4+}QxsN+lNA*_kVYu+jjGS7Ifw1ItS51_mBH*t5sg zRml>E50~TP=FM7dWMq1JQBg$&82nkcME6d7qbK~RrA@x^ZJt-*&m9QM1P(-?jY9}YbMd$2{@I=(YMR3-sR1OZX zdQR{u`3i_t^dgWX;yfERfSaX0|2(RwHEYz=u)s5vm!CXYQ*-4?b8~w;GHY)yGL9bJ z+tGm!ct53DEIU4suaTWw=9skVH8oqex;!>V;*cSqeB$9DXWI4aQK=m`fFdd_EibRQ zxU^J3Nk;=K7tMOCIOqoAbx1`;RaHerUS3iX8YxEOj2Vy1rPM=fF0*E$6Ig33ycU?9 zokbUev$LZkEFVVC!L;mkFbNe^tt3aYV1b>TiXDF81r+%!R`~mehJs?4&Bu?QJ9p(u zV)6>9)JOr$^`)`gEP0@^`#n*W26Carf?x8w6cL#Nv_Y#Gymo-N%ia zI1wtg(n%vVg;0|qyDC8~N=iU3C_9yx^9qGoT~WcS4aLQ&sle>zWoP$DpoCbW&T#M$ zc(5n$WqBF_1+N%RmhX&v$U3MR#F|wzj|f-QvZ|mihP?3<(Kf&hYH- zzrS$7iU$yp7HC&yOGf>8I1DFIa$} zD^n3t-O95Z{(Pyf$efkLi_i7Q0dV9rwMIQ>^4yhwtpxvo8jzPH zjXY9>$dX@tanq*6#IUgN@W{xdB&#B@?1)n+egxpAu-wAi)C9jjQl=iVp(9qIf{+yv zBnRl5Hyax2>m@S~*56Acf(j&905TEXmpW%oTwGXKL_}0nLPAD{$y8l^_H1o!L&NRcDF0;N<~>0G z+tSk7ij<6m6oGV%;t-zS)n!f0ihd)kduHaJ|E$C`+OcETtnv2d%Oh5;+PHDo zF11>(4-JipL0$rM3f#cXE260^7B(u6BGCE|KI~zBXABEQ4ftE>Vd0K6og4=5H6`MadZSV z2U`VG6|E&E-7c`i*Irw-%4m$Bj&O2vc6Mp09Q)DrYj2l}W-@m~Ludz%LiIC6iB1hD z8wHD=9_km|r!FF*r2&p|_ikgO#V-Vw@!WHhCM{Xw?Y(m4+O??hvvb5MDCW#!SM1qJEpX=y80l6@0%0c{0-G(((;vXK`6 zJ{4$kWi8QIOR^A@8mNM!J0?ZQ|MT!;y}S|$`uytE(SG4z|$VPVo}`GPy@;2_i_385vSw5LooMackEuU%qoi@f*w>>hLCSIBRir&NJ*ih0->R_6iy%#rHz(jMoCFgk;w!rC@ag$OHcRrm!g8D zL8c%_4?#jeOhE3p@N^f^Sp#ZERSV_KAb5xzH4q)>vWKyOv)G9fLqiWAPD{(o%*!*I zPo1i%sjo+ZfI3_yyC>}Wwl=vk@T0OL+ns;bsj2f?y7V2H*~IbdcK@yi!+G zl$jYCI%$$L4fQAlL5ZF)QUy5$niT^6qGApe9W>zVEVyW)6>=L#u8{l2bd}Ut8#G9M z-c_p*pY!riNgY3a{yd7fd-q@heSHtLk$nGtOAAZ@|M&Hw9RnHxb;!jmMG4{(6hvS! zEiKK>4GjkmN)se|dhXh_eY=m(jvan}`}PF}MnrGnXj zNP$>@xJk^BG(yFW$%NhwnGq^`vpFv>BV)q`SramS66O9l>B0$70$~x>AVL@5{!o&{ zBc;#fe99f^Dx!}L)LW)#VP{7I6Y0ptoU}BMHrV0Oqi4>1|NZsr=pD<Q<{$frX;W6@OQyQ2O4(9gh5 ziA*S=fU+{M1hN#l1xZeW+#zQXs7=bZS`Z8ht>uyp!O?iNzOoYBA|__;T$)CM-7ObV z5LXJwA@CniY{5B1E}GEvKw|?5m6RlBTT%0yff(7Ak^8fpy>#1?^mutvu302z{BQ|u;bdhYK$%Q#U=OZlO?kP=L}ma+ z4c=rj$<(`|;@B}XIxVp2fnCY!sZaoqlxr$SH97=@Or#FM!*+pe0a#&cmmXJ9G_@3$ za1Gje`L_aK0qXPTxk)m#s;V<*>gujuy?*`99rEfv+H;}83hNE_*hLJYhcZTcJ1xB2 zAj0;HYd`h8J38PfmZk6PymROK?++XhNp@-~_lm%5lW@Odd{QU#7?V0n2#L2~k{RgnM17m`V!n;mYkZtV9oG( z`jcXw)c2GsaEb>~YO8bC}h+@d^b1B3F>Rh*Fb9 ziu_X0b0K;~SwXI}q`DJk0PvIpkfctZuBo|v`PwxZK5bhd6~NPpH_{?^P~)?;6`x5e zfI(pI8Aj48#Ye*5-Hj{|6Jy060sL~8B`oaFA-T+?1umfoK&@Xn6{x_UkP}dL^0L9^ zAlL*N5c&C;nLa*N^a$z>oN~Ps`?y1poB}}+CD1Bzb=q@v(LfR0*{GzhF!-hPsiX{Q zhErrz63?O}A+rStKYCO!CCeJPaG|cws)N>6&}NxZqmMx3s9jBsHi9js1)UrK;WN?} zyj*oE`bBt5YinbptOvbb@QwBN-@8|*3k)6&LdoE!gzdt$6AeUli=MNcn_CE|(Nyw1VnLtg+0T!6(5WF)YOvb_A$7 z2NRvRSK6oFwhLK#U(hYy1`2qqQvA#|msh?J!Wkf@W-hd+_LVz3T5jo&Mu`2mfqK`@e8~Wf&Ruv?dmL5BH z?p$pxRB-w7rArqtLJ6SQ;E*r_WuI171M#OGA`HX&$X~!AdwUgfs6>772)`aD-vR<) z2x4%E(TILra`It1eIr`})t}Z<13f8Bnzp?;!3g)SpmQ)I7u;sc9JBzC>oGitV$t&k$sA4vw!R0p$-fK1D&1UehUKs z?YI1*zOk{Z>+apGEUJUfMxYu;Ie`|0OeXQF1hedw&aI#@Z)p))HTUkdwD49vT2}au z3J!`YbQy$Qzg|@}mwpF8CwOSK8#iKNe0_KC*6DaqbVf#Pt=YU~%k0?(Lm(ZOjE)As zL_DS5RAnWvHlp=VUM?Tr$;->l6|;;-sEw7Emz^yiaYAsR{x*)iP*1by#c$Grl?;wA(r?bB} zt4y+J&-K9r3I=!8)Pd`c{_)2L4}SV7Az{dnb?eTYL3qUhHl!LUol@%-ssOvfuG`I< zO-;g01CB4^fD<~uoZ1;e7gc1)xGgPc;+;CB)6pp}X{I}O_yu)c9lspg+WO!@Utds= zi_4TL0RcE78y6=B*Afb>(pCmqI&dc(a^l(A^mH7Y=E*{;E+s{7uh4-%N_h{eX`_3KSdU0v35f0j93dyNN>_22{&TD`Qjg~~)gOf4=h zD9F#x&Q429N{WqDMn=ZO#Kk2fq@-kIV50~H8iFqLLcMT0NR_V1&OUzpsR@W*%9M*j zFDe-1Bhmu;I|xRy7N8%H5sE$T?p6WPVN7ZQoC!jdy?nX3S#Dj(q#Oo<5*%u{dGp$} z+FIp>tcw>fU%pCbk)e#+(t#X!QHQimfU`KaZr!-i+}zR8-~YrU+1WfG2*e~R3iK4% zIF+YiaziOIGdVdXCOrJ$K_n)@5E2|58j7u%xHx2{ygXb@UPP^n656@WrL&;f*+oUC zPd_mSRRzw*M^sEs%u11xvcz7X8ssJEj3lYb2`mje*#dbLedpx#$tPBM!a(HDKexBb zZgc4pGLDs{34O*c=?Dti+I4l`etYZ~uP#LQ;MlR#r_Y{+CNEvOcCDe|=FNNeU?0d- z;O=;L_wJoLu$@Qh5eBaO;~%xP@SEUZ90h&U(}Ba_q%Y!0Mg|Tc1qSML`}eEWd-tl< zd-j0RAcu#996A&mix#?klINkK5Rp*xKT?!15Z2!x6=lsjGIfL9kmo3{nFI|{By!+R z)I6uu4YmU^gIXU=9@jPHy@Z|HF)B`4#!6Vf`dTy%jzch!&d zBMxYO{4pbgx*aM?))vK}%|pZu3{EbLsKG(rq>MoNh6h`gLrs1qMb$q@;ky@I0PlNKenmNKOt9hb{ykzK#2&`uU+kgt>%d|lnOYeK#L^5vSEvuCTSii^cOZGQgY!wLnR8wd=%bV;n&H#9Uh zHaFkB+up9|QUCR?)^qbDr=w%vKCKq54%E0j-hh(<;o(N3R=aDLxJ~Qhvta{yG1Dww z92O=X(nm$%_erpK5ftRyxks{e*RBm2A~97sz6u>X+9P0;9Khry4og)Dos)3ZxVq9$ z-7F{BT?h1k`y008Po6w|ntw-`^9&u9J$e*f7Db*WHg?b;71R3pRae*4@OmAfU>{ZN zq1N8t{$~B914-%b*6H@{g+WqoG#CN`4jj;E{QYs)aldLuiv{T_NagS(fSJ-x~Z=(FwoCW+^zNZ z-?3x+cJ3o?Q!idjH+g#Eq4oNh7>X7pbZr01mB((od-u&Z(Mq9M3Nr7;a8^o13RO63 zh7aGg$$F{Vfc78%ICd;AkLQQw=E4<^9LdO#8Dws*PB)C{yu7APUAwluUHq&1(GQjA zz8`)F4c$hLO+WikZPRSs>g7cjj~h37^sHIb_x@Jh&|v*lliPr%z5V_7-RR`VFh+FI z&hF)x6`xZ6uwF0}o_^v4#g1$ZQU<3WLaX8H`vTKE{$oVC?#CY^BR6m6o0Qu$vu4el z`HKsvRqx^9g0}o+dYU)283xry;qy!z|WC7yQS6#Kmpc zuz9lxL{p~x;sUaxFTPOXL^S75eK~wU^Ur^N_Z^AO$yu?2z8o{AynMJtG=2HgPibi+ ccFUHD6My;T4?iqiC^1Ay>{ECCe-``y064sm>i_@% literal 0 HcmV?d00001 diff --git a/demos/simon/astropong/data/Scoreboard.ac b/demos/simon/astropong/data/Scoreboard.ac new file mode 100644 index 0000000..cb68697 --- /dev/null +++ b/demos/simon/astropong/data/Scoreboard.ac @@ -0,0 +1,2294 @@ +AC3Db +MATERIAL "ac3dmat0" rgb 0 0 0 amb 0 0 0 emis 0 0 0 spec 0 0 0 shi 128 trans 0 +MATERIAL "ac3dmat1" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0.502 0.502 0.502 shi 10 trans 0 +OBJECT world +kids 3 +OBJECT poly +name "cylinder" +loc -2.5332e-07 0.000827358 0.00013657 +texture "Scoreboard.rgb" +numvert 162 +0.5 -1.41445 0.499339 +0.5 -1.3715 0.607445 +0.5 -1.3203 0.7119 +0.5 -1.26117 0.812075 +0.5 -1.19445 0.90736 +0.5 -1.12054 0.997195 +0.5 -1.0399 1.08103 +0.5 -0.952995 1.15836 +0.5 -0.860365 1.22873 +0.5 -0.76256 1.29171 +0.5 -0.66017 1.34692 +0.5 -0.55381 1.39402 +0.5 -0.444117 1.43274 +0.5 -0.331755 1.46285 +0.5 -0.217397 1.48416 +0.5 -0.101732 1.49654 +0.5 0.0145443 1.49993 +0.5 0.130734 1.49429 +0.5 0.246137 1.47967 +0.5 0.36006 1.45615 +0.5 0.471817 1.42386 +0.5 0.580735 1.38302 +0.5 0.686165 1.33386 +0.5 0.787465 1.27667 +0.5 0.88403 1.21181 +0.5 0.97528 1.13967 +0.5 1.06066 1.06066 +0.5 1.13967 0.975275 +0.5 1.21181 0.88403 +0.5 1.27667 0.787465 +0.5 1.33386 0.686165 +0.5 1.38302 0.580735 +0.5 1.42386 0.471817 +0.5 1.45615 0.360059 +0.5 1.47967 0.246137 +0.5 1.49429 0.130734 +0.5 1.49993 0.0145439 +0.5 1.49654 -0.101733 +0.5 1.48416 -0.217398 +0.5 1.46285 -0.331755 +0.5 1.43274 -0.444118 +0.5 1.39402 -0.55381 +0.5 1.34692 -0.66017 +0.5 1.29171 -0.76256 +0.5 1.22873 -0.860365 +0.5 1.15836 -0.952995 +0.5 1.08103 -1.0399 +0.5 0.997195 -1.12054 +0.5 0.90736 -1.19445 +0.5 0.812075 -1.26117 +0.5 0.7119 -1.3203 +0.5 0.607445 -1.3715 +0.5 0.499339 -1.41445 +0.5 0.388229 -1.44889 +0.5 0.274784 -1.47461 +0.5 0.159687 -1.49147 +0.5 0.0436284 -1.49936 +0.5 -0.072692 -1.49824 +0.5 -0.188575 -1.4881 +0.5 -0.303324 -1.46901 +0.5 -0.416249 -1.44109 +0.5 -0.52667 -1.4045 +0.5 -0.633925 -1.35947 +0.5 -0.737365 -1.30625 +0.5 -0.836375 -1.24519 +0.5 -0.93035 -1.17663 +0.5 -1.01873 -1.10099 +0.5 -1.10099 -1.01874 +0.5 -1.17662 -0.930355 +0.5 -1.24518 -0.83638 +0.5 -1.30624 -0.737375 +0.5 -1.35946 -0.633935 +0.5 -1.4045 -0.52668 +0.5 -1.44108 -0.416258 +0.5 -1.46901 -0.303333 +0.5 -1.4881 -0.188585 +0.5 -1.49823 -0.0727015 +0.5 -1.49936 0.0436186 +0.5 -1.49147 0.159677 +0.5 -1.47462 0.274774 +0.5 -1.44889 0.38822 +-0.5 -1.41445 0.499339 +-0.5 -1.3715 0.607445 +-0.5 -1.3203 0.7119 +-0.5 -1.26117 0.812075 +-0.5 -1.19445 0.90736 +-0.5 -1.12054 0.997195 +-0.5 -1.0399 1.08103 +-0.5 -0.952995 1.15836 +-0.5 -0.860365 1.22873 +-0.5 -0.76256 1.29171 +-0.5 -0.66017 1.34692 +-0.5 -0.55381 1.39402 +-0.5 -0.444117 1.43274 +-0.5 -0.331755 1.46285 +-0.5 -0.217397 1.48416 +-0.5 -0.101732 1.49654 +-0.5 0.0145443 1.49993 +-0.5 0.130734 1.49429 +-0.5 0.246137 1.47967 +-0.5 0.36006 1.45615 +-0.5 0.471817 1.42386 +-0.5 0.580735 1.38302 +-0.5 0.686165 1.33386 +-0.5 0.787465 1.27667 +-0.5 0.88403 1.21181 +-0.5 0.97528 1.13967 +-0.5 1.06066 1.06066 +-0.5 1.13967 0.975275 +-0.5 1.21181 0.88403 +-0.5 1.27667 0.787465 +-0.5 1.33386 0.686165 +-0.5 1.38302 0.580735 +-0.5 1.42386 0.471817 +-0.5 1.45615 0.360059 +-0.5 1.47967 0.246137 +-0.5 1.49429 0.130734 +-0.5 1.49993 0.0145439 +-0.5 1.49654 -0.101733 +-0.5 1.48416 -0.217398 +-0.5 1.46285 -0.331755 +-0.5 1.43274 -0.444118 +-0.5 1.39402 -0.55381 +-0.5 1.34692 -0.66017 +-0.5 1.29171 -0.76256 +-0.5 1.22873 -0.860365 +-0.5 1.15836 -0.952995 +-0.5 1.08103 -1.0399 +-0.5 0.997195 -1.12054 +-0.5 0.90736 -1.19445 +-0.5 0.812075 -1.26117 +-0.5 0.7119 -1.3203 +-0.5 0.607445 -1.3715 +-0.5 0.499339 -1.41445 +-0.5 0.388229 -1.44889 +-0.5 0.274784 -1.47461 +-0.5 0.159687 -1.49147 +-0.5 0.0436284 -1.49936 +-0.5 -0.072692 -1.49824 +-0.5 -0.188575 -1.4881 +-0.5 -0.303324 -1.46901 +-0.5 -0.416249 -1.44109 +-0.5 -0.52667 -1.4045 +-0.5 -0.633925 -1.35947 +-0.5 -0.737365 -1.30625 +-0.5 -0.836375 -1.24519 +-0.5 -0.93035 -1.17663 +-0.5 -1.01873 -1.10099 +-0.5 -1.10099 -1.01874 +-0.5 -1.17662 -0.930355 +-0.5 -1.24518 -0.83638 +-0.5 -1.30624 -0.737375 +-0.5 -1.35946 -0.633935 +-0.5 -1.4045 -0.52668 +-0.5 -1.44108 -0.416258 +-0.5 -1.46901 -0.303333 +-0.5 -1.4881 -0.188585 +-0.5 -1.49823 -0.0727015 +-0.5 -1.49936 0.0436186 +-0.5 -1.49147 0.159677 +-0.5 -1.47462 0.274774 +-0.5 -1.44889 0.38822 +numsurf 162 +SURF 0x30 +mat 1 +refs 3 +0 1 0.538754 +82 0 0.577274 +81 0 0.538754 +SURF 0x30 +mat 1 +refs 3 +0 1 0.538754 +1 1 0.577274 +82 0 0.577274 +SURF 0x30 +mat 1 +refs 3 +1 1 0.577274 +83 0 0.61533 +82 0 0.577274 +SURF 0x30 +mat 1 +refs 3 +1 1 0.577274 +2 1 0.61533 +83 0 0.61533 +SURF 0x30 +mat 1 +refs 3 +2 1 0.61533 +84 0 0.652692 +83 0 0.61533 +SURF 0x30 +mat 1 +refs 3 +2 1 0.61533 +3 1 0.652692 +84 0 0.652692 +SURF 0x30 +mat 1 +refs 3 +3 1 0.652692 +85 0 0.689135 +84 0 0.652692 +SURF 0x30 +mat 1 +refs 3 +3 1 0.652692 +4 1 0.689135 +85 0 0.689135 +SURF 0x30 +mat 1 +refs 3 +4 1 0.689135 +86 0 0.724442 +85 0 0.689135 +SURF 0x30 +mat 1 +refs 3 +4 1 0.689135 +5 1 0.724442 +86 0 0.724442 +SURF 0x30 +mat 1 +refs 3 +5 1 0.724442 +87 0 0.758398 +86 0 0.724442 +SURF 0x30 +mat 1 +refs 3 +5 1 0.724442 +6 1 0.758398 +87 0 0.758398 +SURF 0x30 +mat 1 +refs 3 +6 1 0.758398 +88 0 0.790801 +87 0 0.758398 +SURF 0x30 +mat 1 +refs 3 +6 1 0.758398 +7 1 0.790801 +88 0 0.790801 +SURF 0x30 +mat 1 +refs 3 +7 1 0.790801 +89 0 0.821454 +88 0 0.790801 +SURF 0x30 +mat 1 +refs 3 +7 1 0.790801 +8 1 0.821454 +89 0 0.821454 +SURF 0x30 +mat 1 +refs 3 +8 1 0.821454 +90 0 0.850175 +89 0 0.821454 +SURF 0x30 +mat 1 +refs 3 +8 1 0.821454 +9 1 0.850175 +90 0 0.850175 +SURF 0x30 +mat 1 +refs 3 +9 1 0.850175 +91 0 0.876789 +90 0 0.850175 +SURF 0x30 +mat 1 +refs 3 +9 1 0.850175 +10 1 0.876789 +91 0 0.876789 +SURF 0x30 +mat 1 +refs 3 +10 1 0.876789 +92 0 0.901137 +91 0 0.876789 +SURF 0x30 +mat 1 +refs 3 +10 1 0.876789 +11 1 0.901137 +92 0 0.901137 +SURF 0x30 +mat 1 +refs 3 +11 1 0.901137 +93 0 0.923073 +92 0 0.901137 +SURF 0x30 +mat 1 +refs 3 +11 1 0.901137 +12 1 0.923073 +93 0 0.923073 +SURF 0x30 +mat 1 +refs 3 +12 1 0.923073 +94 0 0.942464 +93 0 0.923073 +SURF 0x30 +mat 1 +refs 3 +12 1 0.923073 +13 1 0.942464 +94 0 0.942464 +SURF 0x30 +mat 1 +refs 3 +13 1 0.942464 +95 0 0.959194 +94 0 0.942464 +SURF 0x30 +mat 1 +refs 3 +13 1 0.942464 +14 1 0.959194 +95 0 0.959194 +SURF 0x30 +mat 1 +refs 3 +14 1 0.959194 +96 0 0.973163 +95 0 0.959194 +SURF 0x30 +mat 1 +refs 3 +14 1 0.959194 +15 1 0.973163 +96 0 0.973163 +SURF 0x30 +mat 1 +refs 3 +15 1 0.973163 +97 0 0.984286 +96 0 0.973163 +SURF 0x30 +mat 1 +refs 3 +15 1 0.973163 +16 1 0.984286 +97 0 0.984286 +SURF 0x30 +mat 1 +refs 3 +16 1 0.984286 +98 0 0.992496 +97 0 0.984286 +SURF 0x30 +mat 1 +refs 3 +16 1 0.984286 +17 1 0.992496 +98 0 0.992496 +SURF 0x30 +mat 1 +refs 3 +17 1 0.992496 +99 0 0.997745 +98 0 0.992496 +SURF 0x30 +mat 1 +refs 3 +17 1 0.992496 +18 1 0.997745 +99 0 0.997745 +SURF 0x30 +mat 1 +refs 3 +18 1 0.997745 +100 0 1 +99 0 0.997745 +SURF 0x30 +mat 1 +refs 3 +18 1 0.997745 +19 1 1 +100 0 1 +SURF 0x30 +mat 1 +refs 3 +19 1 1 +101 0 0.999248 +100 0 1 +SURF 0x30 +mat 1 +refs 3 +19 1 1 +20 1 0.999248 +101 0 0.999248 +SURF 0x30 +mat 0 +refs 3 +20 1 0.999248 +102 0 0.995493 +101 0 0.999248 +SURF 0x30 +mat 0 +refs 3 +20 1 0.999248 +21 1 0.995493 +102 0 0.995493 +SURF 0x30 +mat 0 +refs 3 +21 1 0.995493 +103 0 0.988759 +102 0 0.995493 +SURF 0x30 +mat 0 +refs 3 +21 1 0.995493 +22 1 0.988759 +103 0 0.988759 +SURF 0x30 +mat 0 +refs 3 +22 1 0.988759 +104 0 0.979085 +103 0 0.988759 +SURF 0x30 +mat 0 +refs 3 +22 1 0.988759 +23 1 0.979085 +104 0 0.979085 +SURF 0x30 +mat 0 +refs 3 +23 1 0.979085 +105 0 0.96653 +104 0 0.979085 +SURF 0x30 +mat 0 +refs 3 +23 1 0.979085 +24 1 0.96653 +105 0 0.96653 +SURF 0x30 +mat 0 +refs 3 +24 1 0.96653 +106 0 0.951169 +105 0 0.96653 +SURF 0x30 +mat 0 +refs 3 +24 1 0.96653 +25 1 0.951169 +106 0 0.951169 +SURF 0x30 +mat 0 +refs 3 +25 1 0.951169 +107 0 0.933094 +106 0 0.951169 +SURF 0x30 +mat 0 +refs 3 +25 1 0.951169 +26 1 0.933094 +107 0 0.933094 +SURF 0x30 +mat 0 +refs 3 +26 1 0.933094 +108 0 0.912415 +107 0 0.933094 +SURF 0x30 +mat 0 +refs 3 +26 1 0.933094 +27 1 0.912415 +108 0 0.912415 +SURF 0x30 +mat 0 +refs 3 +27 1 0.912415 +109 0 0.889256 +108 0 0.912415 +SURF 0x30 +mat 0 +refs 3 +27 1 0.912415 +28 1 0.889256 +109 0 0.889256 +SURF 0x30 +mat 0 +refs 3 +28 1 0.889256 +110 0 0.863755 +109 0 0.889256 +SURF 0x30 +mat 0 +refs 3 +28 1 0.889256 +29 1 0.863755 +110 0 0.863755 +SURF 0x30 +mat 0 +refs 3 +29 1 0.863755 +111 0 0.836067 +110 0 0.863755 +SURF 0x30 +mat 0 +refs 3 +29 1 0.863755 +30 1 0.836067 +111 0 0.836067 +SURF 0x30 +mat 0 +refs 3 +30 1 0.836067 +112 0 0.806358 +111 0 0.836067 +SURF 0x30 +mat 0 +refs 3 +30 1 0.836067 +31 1 0.806358 +112 0 0.806358 +SURF 0x30 +mat 0 +refs 3 +31 1 0.806358 +113 0 0.774806 +112 0 0.806358 +SURF 0x30 +mat 0 +refs 3 +31 1 0.806358 +32 1 0.774806 +113 0 0.774806 +SURF 0x30 +mat 0 +refs 3 +32 1 0.774806 +114 0 0.741602 +113 0 0.774806 +SURF 0x30 +mat 0 +refs 3 +32 1 0.774806 +33 1 0.741602 +114 0 0.741602 +SURF 0x30 +mat 0 +refs 3 +33 1 0.741602 +115 0 0.706944 +114 0 0.741602 +SURF 0x30 +mat 0 +refs 3 +33 1 0.741602 +34 1 0.706944 +115 0 0.706944 +SURF 0x30 +mat 0 +refs 3 +34 1 0.706944 +116 0 0.671042 +115 0 0.706944 +SURF 0x30 +mat 0 +refs 3 +34 1 0.706944 +35 1 0.671042 +116 0 0.671042 +SURF 0x30 +mat 0 +refs 3 +35 1 0.671042 +117 0 0.634111 +116 0 0.671042 +SURF 0x30 +mat 0 +refs 3 +35 1 0.671042 +36 1 0.634111 +117 0 0.634111 +SURF 0x30 +mat 0 +refs 3 +36 1 0.634111 +118 0 0.596374 +117 0 0.634111 +SURF 0x30 +mat 0 +refs 3 +36 1 0.634111 +37 1 0.596374 +118 0 0.596374 +SURF 0x30 +mat 0 +refs 3 +37 1 0.596374 +119 0 0.558057 +118 0 0.596374 +SURF 0x30 +mat 0 +refs 3 +37 1 0.596374 +38 1 0.558057 +119 0 0.558057 +SURF 0x30 +mat 0 +refs 3 +38 1 0.558057 +120 0 0.519391 +119 0 0.558057 +SURF 0x30 +mat 0 +refs 3 +38 1 0.558057 +39 1 0.519391 +120 0 0.519391 +SURF 0x30 +mat 0 +refs 3 +39 1 0.519391 +121 0 0.480609 +120 0 0.519391 +SURF 0x30 +mat 0 +refs 3 +39 1 0.519391 +40 1 0.480609 +121 0 0.480609 +SURF 0x30 +mat 0 +refs 3 +40 1 0.480609 +122 0 0.441943 +121 0 0.480609 +SURF 0x30 +mat 0 +refs 3 +40 1 0.480609 +41 1 0.441943 +122 0 0.441943 +SURF 0x30 +mat 0 +refs 3 +41 1 0.441943 +123 0 0.403626 +122 0 0.441943 +SURF 0x30 +mat 0 +refs 3 +41 1 0.441943 +42 1 0.403626 +123 0 0.403626 +SURF 0x30 +mat 0 +refs 3 +42 1 0.403626 +124 0 0.365888 +123 0 0.403626 +SURF 0x30 +mat 0 +refs 3 +42 1 0.403626 +43 1 0.365888 +124 0 0.365888 +SURF 0x30 +mat 0 +refs 3 +43 1 0.365888 +125 0 0.328958 +124 0 0.365888 +SURF 0x30 +mat 0 +refs 3 +43 1 0.365888 +44 1 0.328958 +125 0 0.328958 +SURF 0x30 +mat 0 +refs 3 +44 1 0.328958 +126 0 0.293056 +125 0 0.328958 +SURF 0x30 +mat 0 +refs 3 +44 1 0.328958 +45 1 0.293056 +126 0 0.293056 +SURF 0x30 +mat 0 +refs 3 +45 1 0.293056 +127 0 0.258398 +126 0 0.293056 +SURF 0x30 +mat 0 +refs 3 +45 1 0.293056 +46 1 0.258398 +127 0 0.258398 +SURF 0x30 +mat 0 +refs 3 +46 1 0.258398 +128 0 0.225194 +127 0 0.258398 +SURF 0x30 +mat 0 +refs 3 +46 1 0.258398 +47 1 0.225194 +128 0 0.225194 +SURF 0x30 +mat 0 +refs 3 +47 1 0.225194 +129 0 0.193642 +128 0 0.225194 +SURF 0x30 +mat 0 +refs 3 +47 1 0.225194 +48 1 0.193642 +129 0 0.193642 +SURF 0x30 +mat 0 +refs 3 +48 1 0.193642 +130 0 0.163933 +129 0 0.193642 +SURF 0x30 +mat 0 +refs 3 +48 1 0.193642 +49 1 0.163933 +130 0 0.163933 +SURF 0x30 +mat 0 +refs 3 +49 1 0.163933 +131 0 0.136245 +130 0 0.163933 +SURF 0x30 +mat 0 +refs 3 +49 1 0.163933 +50 1 0.136245 +131 0 0.136245 +SURF 0x30 +mat 0 +refs 3 +50 1 0.136245 +132 0 0.110744 +131 0 0.136245 +SURF 0x30 +mat 0 +refs 3 +50 1 0.136245 +51 1 0.110744 +132 0 0.110744 +SURF 0x30 +mat 0 +refs 3 +51 1 0.110744 +133 0 0.0875849 +132 0 0.110744 +SURF 0x30 +mat 0 +refs 3 +51 1 0.110744 +52 1 0.0875849 +133 0 0.0875849 +SURF 0x30 +mat 0 +refs 3 +52 1 0.0875849 +134 0 0.0669059 +133 0 0.0875849 +SURF 0x30 +mat 0 +refs 3 +52 1 0.0875849 +53 1 0.0669059 +134 0 0.0669059 +SURF 0x30 +mat 0 +refs 3 +53 1 0.0669059 +135 0 0.0488315 +134 0 0.0669059 +SURF 0x30 +mat 0 +refs 3 +53 1 0.0669059 +54 1 0.0488315 +135 0 0.0488315 +SURF 0x30 +mat 0 +refs 3 +54 1 0.0488315 +136 0 0.0334705 +135 0 0.0488315 +SURF 0x30 +mat 0 +refs 3 +54 1 0.0488315 +55 1 0.0334705 +136 0 0.0334705 +SURF 0x30 +mat 0 +refs 3 +55 1 0.0334705 +137 0 0.0209153 +136 0 0.0334705 +SURF 0x30 +mat 0 +refs 3 +55 1 0.0334705 +56 1 0.0209153 +137 0 0.0209153 +SURF 0x30 +mat 0 +refs 3 +56 1 0.0209153 +138 0 0.0112412 +137 0 0.0209153 +SURF 0x30 +mat 0 +refs 3 +56 1 0.0209153 +57 1 0.0112412 +138 0 0.0112412 +SURF 0x30 +mat 0 +refs 3 +57 1 0.0112412 +139 0 0.00450672 +138 0 0.0112412 +SURF 0x30 +mat 0 +refs 3 +57 1 0.0112412 +58 1 0.00450672 +139 0 0.00450672 +SURF 0x30 +mat 0 +refs 3 +58 1 0.00450672 +140 0 0.000752114 +139 0 0.00450672 +SURF 0x30 +mat 0 +refs 3 +58 1 0.00450672 +59 1 0.000752114 +140 0 0.000752114 +SURF 0x30 +mat 1 +refs 3 +59 1 0.000752114 +141 0 0 +140 0 0.000752114 +SURF 0x30 +mat 1 +refs 3 +59 1 0.000752114 +60 1 0 +141 0 0 +SURF 0x30 +mat 1 +refs 3 +60 1 0 +142 0 0.00225491 +141 0 0 +SURF 0x30 +mat 1 +refs 3 +60 1 0 +61 1 0.00225491 +142 0 0.00225491 +SURF 0x30 +mat 1 +refs 3 +61 1 0.00225491 +143 0 0.00750333 +142 0 0.00225491 +SURF 0x30 +mat 1 +refs 3 +61 1 0.00225491 +62 1 0.00750333 +143 0 0.00750333 +SURF 0x30 +mat 1 +refs 3 +62 1 0.00750333 +144 0 0.0157136 +143 0 0.00750333 +SURF 0x30 +mat 1 +refs 3 +62 1 0.00750333 +63 1 0.0157136 +144 0 0.0157136 +SURF 0x30 +mat 1 +refs 3 +63 1 0.0157136 +145 0 0.0268365 +144 0 0.0157136 +SURF 0x30 +mat 1 +refs 3 +63 1 0.0157136 +64 1 0.0268365 +145 0 0.0268365 +SURF 0x30 +mat 1 +refs 3 +64 1 0.0268365 +146 0 0.040805 +145 0 0.0268365 +SURF 0x30 +mat 1 +refs 3 +64 1 0.0268365 +65 1 0.040805 +146 0 0.040805 +SURF 0x30 +mat 1 +refs 3 +65 1 0.040805 +147 0 0.0575352 +146 0 0.040805 +SURF 0x30 +mat 1 +refs 3 +65 1 0.040805 +66 1 0.0575352 +147 0 0.0575352 +SURF 0x30 +mat 1 +refs 3 +66 1 0.0575352 +148 0 0.0769264 +147 0 0.0575352 +SURF 0x30 +mat 1 +refs 3 +66 1 0.0575352 +67 1 0.0769264 +148 0 0.0769264 +SURF 0x30 +mat 1 +refs 3 +67 1 0.0769264 +149 0 0.0988619 +148 0 0.0769264 +SURF 0x30 +mat 1 +refs 3 +67 1 0.0769264 +68 1 0.0988619 +149 0 0.0988619 +SURF 0x30 +mat 1 +refs 3 +68 1 0.0988619 +150 0 0.12321 +149 0 0.0988619 +SURF 0x30 +mat 1 +refs 3 +68 1 0.0988619 +69 1 0.12321 +150 0 0.12321 +SURF 0x30 +mat 1 +refs 3 +69 1 0.12321 +151 0 0.149824 +150 0 0.12321 +SURF 0x30 +mat 1 +refs 3 +69 1 0.12321 +70 1 0.149824 +151 0 0.149824 +SURF 0x30 +mat 1 +refs 3 +70 1 0.149824 +152 0 0.178544 +151 0 0.149824 +SURF 0x30 +mat 1 +refs 3 +70 1 0.149824 +71 1 0.178544 +152 0 0.178544 +SURF 0x30 +mat 1 +refs 3 +71 1 0.178544 +153 0 0.209197 +152 0 0.178544 +SURF 0x30 +mat 1 +refs 3 +71 1 0.178544 +72 1 0.209197 +153 0 0.209197 +SURF 0x30 +mat 1 +refs 3 +72 1 0.209197 +154 0 0.2416 +153 0 0.209197 +SURF 0x30 +mat 1 +refs 3 +72 1 0.209197 +73 1 0.2416 +154 0 0.2416 +SURF 0x30 +mat 1 +refs 3 +73 1 0.2416 +155 0 0.275556 +154 0 0.2416 +SURF 0x30 +mat 1 +refs 3 +73 1 0.2416 +74 1 0.275556 +155 0 0.275556 +SURF 0x30 +mat 1 +refs 3 +74 1 0.275556 +156 0 0.310862 +155 0 0.275556 +SURF 0x30 +mat 1 +refs 3 +74 1 0.275556 +75 1 0.310862 +156 0 0.310862 +SURF 0x30 +mat 1 +refs 3 +75 1 0.310862 +157 0 0.347306 +156 0 0.310862 +SURF 0x30 +mat 1 +refs 3 +75 1 0.310862 +76 1 0.347306 +157 0 0.347306 +SURF 0x30 +mat 1 +refs 3 +76 1 0.347306 +158 0 0.384668 +157 0 0.347306 +SURF 0x30 +mat 1 +refs 3 +76 1 0.347306 +77 1 0.384668 +158 0 0.384668 +SURF 0x30 +mat 1 +refs 3 +77 1 0.384668 +159 0 0.422723 +158 0 0.384668 +SURF 0x30 +mat 1 +refs 3 +77 1 0.384668 +78 1 0.422723 +159 0 0.422723 +SURF 0x30 +mat 1 +refs 3 +78 1 0.422723 +160 0 0.461243 +159 0 0.422723 +SURF 0x30 +mat 1 +refs 3 +78 1 0.422723 +79 1 0.461243 +160 0 0.461243 +SURF 0x30 +mat 1 +refs 3 +79 1 0.461243 +161 0 0.499997 +160 0 0.461243 +SURF 0x30 +mat 1 +refs 3 +79 1 0.461243 +80 1 0.499997 +161 0 0.499997 +SURF 0x30 +mat 1 +refs 3 +80 1 0.499997 +81 0 0.538754 +161 0 0.499997 +SURF 0x30 +mat 1 +refs 3 +80 1 0.499997 +0 1 0.538754 +81 0 0.538754 +kids 0 +OBJECT poly +name "disk" +loc -0.5 0.000827358 0.00013657 +numvert 82 +0 -1.44889 0.388229 +0 -1.41445 0.499339 +0 -1.3715 0.607445 +0 -1.3203 0.7119 +0 -1.26117 0.812075 +0 -1.19445 0.90736 +0 -1.12054 0.997195 +0 -1.0399 1.08103 +0 -0.952995 1.15836 +0 -0.860365 1.22873 +0 -0.76256 1.29171 +0 -0.66017 1.34692 +0 -0.55381 1.39402 +0 -0.444118 1.43274 +0 -0.331755 1.46285 +0 -0.217397 1.48416 +0 -0.101732 1.49654 +0 0.0145442 1.49993 +0 0.130734 1.49429 +0 0.246137 1.47967 +0 0.360059 1.45615 +0 0.471817 1.42386 +0 0.580735 1.38302 +0 0.686165 1.33386 +0 0.787465 1.27667 +0 0.88403 1.21181 +0 0.97528 1.13967 +0 1.06066 1.06066 +0 1.13967 0.975275 +0 1.21181 0.88403 +0 1.27667 0.787465 +0 1.33386 0.686165 +0 1.38302 0.580735 +0 1.42386 0.471817 +0 1.45615 0.360059 +0 1.47967 0.246137 +0 1.49429 0.130734 +0 1.49993 0.0145441 +0 1.49654 -0.101733 +0 1.48416 -0.217398 +0 1.46285 -0.331755 +0 1.43274 -0.444118 +0 1.39402 -0.55381 +0 1.34692 -0.66017 +0 1.29171 -0.76256 +0 1.22873 -0.860365 +0 1.15836 -0.952995 +0 1.08103 -1.0399 +0 0.997195 -1.12054 +0 0.90736 -1.19445 +0 0.812075 -1.26117 +0 0.7119 -1.3203 +0 0.607445 -1.3715 +0 0.499339 -1.41445 +0 0.388229 -1.44889 +0 0.274783 -1.47461 +0 0.159685 -1.49147 +0 0.0436268 -1.49936 +0 -0.072694 -1.49824 +0 -0.188578 -1.4881 +0 -0.303326 -1.46901 +0 -0.416252 -1.44109 +0 -0.526675 -1.4045 +0 -0.63393 -1.35946 +0 -0.73737 -1.30625 +0 -0.836375 -1.24518 +0 -0.930355 -1.17663 +0 -1.01874 -1.10099 +0 -1.10099 -1.01874 +0 -1.17663 -0.930355 +0 -1.24518 -0.836375 +0 -1.30625 -0.73737 +0 -1.35946 -0.633925 +0 -1.4045 -0.526675 +0 -1.44109 -0.416251 +0 -1.46901 -0.303326 +0 -1.4881 -0.188577 +0 -1.49823 -0.072693 +0 -1.49936 0.0436274 +0 -1.49147 0.159686 +0 -1.47461 0.274783 +0 0 0 +numsurf 81 +SURF 0x20 +mat 1 +refs 3 +0 1 0.5 +1 0.998496 0.538754 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +1 0.998496 0.538754 +2 0.993993 0.577274 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +2 0.993993 0.577274 +3 0.986517 0.61533 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +3 0.986517 0.61533 +4 0.976115 0.652692 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +4 0.976115 0.652692 +5 0.962848 0.689135 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +5 0.962848 0.689135 +6 0.946796 0.724442 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +6 0.946796 0.724442 +7 0.928056 0.758398 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +7 0.928056 0.758398 +8 0.906741 0.790801 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +8 0.906741 0.790801 +9 0.882978 0.821454 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +9 0.882978 0.821454 +10 0.856911 0.850175 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +10 0.856911 0.850175 +11 0.828696 0.876789 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +11 0.828696 0.876789 +12 0.798504 0.901137 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +12 0.798504 0.901137 +13 0.766514 0.923073 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +13 0.766514 0.923073 +14 0.732921 0.942464 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +14 0.732921 0.942464 +15 0.697926 0.959194 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +15 0.697926 0.959194 +16 0.66174 0.973163 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +16 0.66174 0.973163 +17 0.624579 0.984286 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +17 0.624579 0.984286 +18 0.586669 0.992496 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +18 0.586669 0.992496 +19 0.548236 0.997745 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +19 0.548236 0.997745 +20 0.509511 1 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +20 0.509511 1 +21 0.470729 0.999248 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +21 0.470729 0.999248 +22 0.432121 0.995493 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +22 0.432121 0.995493 +23 0.39392 0.988759 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +23 0.39392 0.988759 +24 0.356356 0.979085 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +24 0.356356 0.979085 +25 0.319655 0.96653 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +25 0.319655 0.96653 +26 0.284038 0.951169 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +26 0.284038 0.951169 +27 0.249718 0.933094 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +27 0.249718 0.933094 +28 0.216902 0.912415 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +28 0.216902 0.912415 +29 0.185788 0.889256 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +29 0.185788 0.889256 +30 0.156562 0.863755 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +30 0.156562 0.863755 +31 0.129401 0.836067 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +31 0.129401 0.836067 +32 0.104467 0.806358 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +32 0.104467 0.806358 +33 0.0819109 0.774806 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +33 0.0819109 0.774806 +34 0.0618681 0.741602 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +34 0.0618681 0.741602 +35 0.0444592 0.706944 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +35 0.0444592 0.706944 +36 0.0297888 0.671042 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +36 0.0297888 0.671042 +37 0.0179453 0.634111 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +37 0.0179453 0.634111 +38 0.00899967 0.596374 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +38 0.00899967 0.596374 +39 0.00300592 0.558057 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +39 0.00300592 0.558057 +40 0 0.519391 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +40 0 0.519391 +41 0 0.480609 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +41 0 0.480609 +42 0.00300592 0.441943 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +42 0.00300592 0.441943 +43 0.00899971 0.403626 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +43 0.00899971 0.403626 +44 0.0179453 0.365888 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +44 0.0179453 0.365888 +45 0.0297889 0.328958 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +45 0.0297889 0.328958 +46 0.0444593 0.293056 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +46 0.0444593 0.293056 +47 0.0618682 0.258398 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +47 0.0618682 0.258398 +48 0.0819109 0.225194 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +48 0.0819109 0.225194 +49 0.104467 0.193642 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +49 0.104467 0.193642 +50 0.129401 0.163933 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +50 0.129401 0.163933 +51 0.156562 0.136245 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +51 0.156562 0.136245 +52 0.185788 0.110744 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +52 0.185788 0.110744 +53 0.216902 0.0875849 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +53 0.216902 0.0875849 +54 0.249718 0.0669058 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +54 0.249718 0.0669058 +55 0.284038 0.0488314 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +55 0.284038 0.0488314 +56 0.319655 0.0334704 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +56 0.319655 0.0334704 +57 0.356356 0.0209151 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +57 0.356356 0.0209151 +58 0.39392 0.0112411 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +58 0.39392 0.0112411 +59 0.432121 0.0045066 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +59 0.432121 0.0045066 +60 0.470729 0.000752034 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +60 0.470729 0.000752034 +61 0.509511 0 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +61 0.509511 0 +62 0.548236 0.00225503 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +62 0.548236 0.00225503 +63 0.586669 0.00750353 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +63 0.586669 0.00750353 +64 0.62458 0.015714 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +64 0.62458 0.015714 +65 0.66174 0.026837 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +65 0.66174 0.026837 +66 0.697926 0.0408057 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +66 0.697926 0.0408057 +67 0.732921 0.057536 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +67 0.732921 0.057536 +68 0.766515 0.0769273 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +68 0.766515 0.0769273 +69 0.798504 0.098863 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +69 0.798504 0.098863 +70 0.828696 0.123211 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +70 0.828696 0.123211 +71 0.856911 0.149826 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +71 0.856911 0.149826 +72 0.882978 0.178546 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +72 0.882978 0.178546 +73 0.906741 0.209199 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +73 0.906741 0.209199 +74 0.928057 0.241602 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +74 0.928057 0.241602 +75 0.946796 0.275558 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +75 0.946796 0.275558 +76 0.962848 0.310865 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +76 0.962848 0.310865 +77 0.976115 0.347308 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +77 0.976115 0.347308 +78 0.986517 0.384671 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +78 0.986517 0.384671 +79 0.993993 0.422726 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +79 0.993993 0.422726 +80 0.998496 0.461247 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +80 0.998496 0.461247 +0 1 0.5 +81 0.499812 0.5 +kids 0 +OBJECT poly +name "disk" +loc 0.5 0.000827343 0.000136574 +numvert 82 +0 -1.44889 0.388229 +0 -1.41445 0.499339 +0 -1.3715 0.607445 +0 -1.3203 0.7119 +0 -1.26117 0.812075 +0 -1.19445 0.90736 +0 -1.12054 0.997195 +0 -1.0399 1.08103 +0 -0.952995 1.15836 +0 -0.860365 1.22873 +0 -0.76256 1.29171 +0 -0.66017 1.34692 +0 -0.55381 1.39402 +0 -0.444118 1.43274 +0 -0.331755 1.46285 +0 -0.217397 1.48416 +0 -0.101732 1.49654 +0 0.0145442 1.49993 +0 0.130734 1.49429 +0 0.246137 1.47967 +0 0.360059 1.45615 +0 0.471817 1.42386 +0 0.580735 1.38302 +0 0.686165 1.33386 +0 0.787465 1.27667 +0 0.88403 1.21181 +0 0.97528 1.13967 +0 1.06066 1.06066 +0 1.13967 0.975275 +0 1.21181 0.88403 +0 1.27667 0.787465 +0 1.33386 0.686165 +0 1.38302 0.580735 +0 1.42386 0.471817 +0 1.45615 0.360059 +0 1.47967 0.246137 +0 1.49429 0.130734 +0 1.49993 0.0145441 +0 1.49654 -0.101733 +0 1.48416 -0.217398 +0 1.46285 -0.331755 +0 1.43274 -0.444118 +0 1.39402 -0.55381 +0 1.34692 -0.66017 +0 1.29171 -0.76256 +0 1.22873 -0.860365 +0 1.15836 -0.952995 +0 1.08103 -1.0399 +0 0.997195 -1.12054 +0 0.90736 -1.19445 +0 0.812075 -1.26117 +0 0.7119 -1.3203 +0 0.607445 -1.3715 +0 0.499339 -1.41445 +0 0.388229 -1.44889 +0 0.274783 -1.47461 +0 0.159685 -1.49147 +0 0.0436268 -1.49936 +0 -0.072694 -1.49824 +0 -0.188578 -1.4881 +0 -0.303326 -1.46901 +0 -0.416252 -1.44109 +0 -0.526675 -1.4045 +0 -0.63393 -1.35946 +0 -0.73737 -1.30625 +0 -0.836375 -1.24518 +0 -0.930355 -1.17663 +0 -1.01874 -1.10099 +0 -1.10099 -1.01874 +0 -1.17663 -0.930355 +0 -1.24518 -0.836375 +0 -1.30625 -0.73737 +0 -1.35946 -0.633925 +0 -1.4045 -0.526675 +0 -1.44109 -0.416251 +0 -1.46901 -0.303326 +0 -1.4881 -0.188577 +0 -1.49823 -0.072693 +0 -1.49936 0.0436274 +0 -1.49147 0.159686 +0 -1.47461 0.274783 +0 0 0 +numsurf 81 +SURF 0x20 +mat 1 +refs 3 +0 1 0.5 +1 0.998496 0.538754 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +1 0.998496 0.538754 +2 0.993993 0.577274 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +2 0.993993 0.577274 +3 0.986517 0.61533 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +3 0.986517 0.61533 +4 0.976115 0.652692 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +4 0.976115 0.652692 +5 0.962848 0.689135 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +5 0.962848 0.689135 +6 0.946796 0.724442 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +6 0.946796 0.724442 +7 0.928056 0.758398 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +7 0.928056 0.758398 +8 0.906741 0.790801 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +8 0.906741 0.790801 +9 0.882978 0.821454 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +9 0.882978 0.821454 +10 0.856911 0.850175 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +10 0.856911 0.850175 +11 0.828696 0.876789 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +11 0.828696 0.876789 +12 0.798504 0.901137 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +12 0.798504 0.901137 +13 0.766514 0.923073 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +13 0.766514 0.923073 +14 0.732921 0.942464 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +14 0.732921 0.942464 +15 0.697926 0.959194 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +15 0.697926 0.959194 +16 0.66174 0.973163 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +16 0.66174 0.973163 +17 0.624579 0.984286 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +17 0.624579 0.984286 +18 0.586669 0.992496 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +18 0.586669 0.992496 +19 0.548236 0.997745 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +19 0.548236 0.997745 +20 0.509511 1 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +20 0.509511 1 +21 0.470729 0.999248 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +21 0.470729 0.999248 +22 0.432121 0.995493 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +22 0.432121 0.995493 +23 0.39392 0.988759 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +23 0.39392 0.988759 +24 0.356356 0.979085 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +24 0.356356 0.979085 +25 0.319655 0.96653 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +25 0.319655 0.96653 +26 0.284038 0.951169 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +26 0.284038 0.951169 +27 0.249718 0.933094 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +27 0.249718 0.933094 +28 0.216902 0.912415 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +28 0.216902 0.912415 +29 0.185788 0.889256 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +29 0.185788 0.889256 +30 0.156562 0.863755 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +30 0.156562 0.863755 +31 0.129401 0.836067 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +31 0.129401 0.836067 +32 0.104467 0.806358 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +32 0.104467 0.806358 +33 0.0819109 0.774806 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +33 0.0819109 0.774806 +34 0.0618681 0.741602 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +34 0.0618681 0.741602 +35 0.0444592 0.706944 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +35 0.0444592 0.706944 +36 0.0297888 0.671042 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +36 0.0297888 0.671042 +37 0.0179453 0.634111 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +37 0.0179453 0.634111 +38 0.00899967 0.596374 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +38 0.00899967 0.596374 +39 0.00300592 0.558057 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +39 0.00300592 0.558057 +40 0 0.519391 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +40 0 0.519391 +41 0 0.480609 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +41 0 0.480609 +42 0.00300592 0.441943 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +42 0.00300592 0.441943 +43 0.00899971 0.403626 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +43 0.00899971 0.403626 +44 0.0179453 0.365888 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +44 0.0179453 0.365888 +45 0.0297889 0.328958 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +45 0.0297889 0.328958 +46 0.0444593 0.293056 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +46 0.0444593 0.293056 +47 0.0618682 0.258398 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +47 0.0618682 0.258398 +48 0.0819109 0.225194 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +48 0.0819109 0.225194 +49 0.104467 0.193642 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +49 0.104467 0.193642 +50 0.129401 0.163933 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +50 0.129401 0.163933 +51 0.156562 0.136245 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +51 0.156562 0.136245 +52 0.185788 0.110744 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +52 0.185788 0.110744 +53 0.216902 0.0875849 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +53 0.216902 0.0875849 +54 0.249718 0.0669058 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +54 0.249718 0.0669058 +55 0.284038 0.0488314 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +55 0.284038 0.0488314 +56 0.319655 0.0334704 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +56 0.319655 0.0334704 +57 0.356356 0.0209151 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +57 0.356356 0.0209151 +58 0.39392 0.0112411 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +58 0.39392 0.0112411 +59 0.432121 0.0045066 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +59 0.432121 0.0045066 +60 0.470729 0.000752034 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +60 0.470729 0.000752034 +61 0.509511 0 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +61 0.509511 0 +62 0.548236 0.00225503 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +62 0.548236 0.00225503 +63 0.586669 0.00750353 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +63 0.586669 0.00750353 +64 0.62458 0.015714 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +64 0.62458 0.015714 +65 0.66174 0.026837 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +65 0.66174 0.026837 +66 0.697926 0.0408057 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +66 0.697926 0.0408057 +67 0.732921 0.057536 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +67 0.732921 0.057536 +68 0.766515 0.0769273 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +68 0.766515 0.0769273 +69 0.798504 0.098863 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +69 0.798504 0.098863 +70 0.828696 0.123211 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +70 0.828696 0.123211 +71 0.856911 0.149826 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +71 0.856911 0.149826 +72 0.882978 0.178546 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +72 0.882978 0.178546 +73 0.906741 0.209199 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +73 0.906741 0.209199 +74 0.928057 0.241602 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +74 0.928057 0.241602 +75 0.946796 0.275558 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +75 0.946796 0.275558 +76 0.962848 0.310865 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +76 0.962848 0.310865 +77 0.976115 0.347308 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +77 0.976115 0.347308 +78 0.986517 0.384671 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +78 0.986517 0.384671 +79 0.993993 0.422726 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +79 0.993993 0.422726 +80 0.998496 0.461247 +81 0.499812 0.5 +SURF 0x20 +mat 1 +refs 3 +80 0.998496 0.461247 +0 1 0.5 +81 0.499812 0.5 +kids 0 diff --git a/demos/simon/astropong/data/Scoreboard.rgb b/demos/simon/astropong/data/Scoreboard.rgb new file mode 100644 index 0000000000000000000000000000000000000000..2710893a0a8f3f2837070cdc4f4fc2cc5cb4203a GIT binary patch literal 47150 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 i016Wip%{{4|ACEBm + + + + WELCOME TO S.I.M.O.N + + + +

WELCOME TO S.I.M.O.N

+
by Steve Baker
+ +SIMON stands for: +
+
+     Simple
+     Interface for
+     Making
+     Oliver's programs
+     Nice.
+
+
+This is a simple set of functions that hide much of the nastiness +of 3D programming to make it possible for someone with only the +very minimum of programming experience to write simple 3D applications. +

+If you want something more complicated or with better features then +don't complain about SIMON - just use raw PLIB (upon which SIMON is +based). + +

HOW TO WRITE YOUR PROGRAM.

+ +You have to write TWO C (or C++) functions: +
+
+   int main ()
+
+
+...and... +
+
+   void siUpdate ()
+
+
+The 'main' function has to load whatever 3D models the program needs +and then call a function called 'siRun()'. +

+siRun does things like opening the 3D window, clearing the screen and +drawing all of the models. When it's time to move the models around, +it calls your function 'siUpdate'. +

+All SIMON programs have to start with the line: +

+
+   #include <simon.h>
+
+
+...this brings in all the information the compiler needs in order for you to +write a SIMON program. Think of it like '#include <stdio.h>' but for +programs that are just doing 3D rendering. +

+Then, you have to declare an integer variable for everything in the scene +that your program needs - which includes at least the scenery and the camera. +

+For example: +

+
+  int camera  ;
+  int tux     ;
+  int scenery ;
+ 
+
+Next, you have your 'main' program - which loads up the models and then +calls the 'siRun()' function and then exits. +
+
+  int main ()
+  {
+    scenery = siLoad ( "scenery.ac" ) ;
+    tux     = siLoad ( "tuxedo.ac"  ) ;
+
+    camera  = 0 ;
+ 
+    siRun () ;
+    return 0 ;
+  }
+
+
+The 'siLoad' function loads a 3D model and returns a number for +that model that you can save into a variable. That number can be +used to move the model around later on. The final part of this +'loading' stage is to set the number for the 'camera'. In SIMON, +the camera is always model number zero and you can't 'siLoad' a +model for it or it won't work. +

+Then, you call 'siRun()' - which runs the entire update process. If +'siRun' ever returns, you can just exit the program. +

+Now we have our scene all set up and ready to go - so the program +will call your 'siUpdate' function to move the models (and the +camera) around - and then draw the scene. +

+
+  int frameno = 0 ;
+
+  void siUpdate ()
+  {
+    frameno = frameno + 1 ;
+ 
+    siPosition ( tux   , 0,  0, 0, frameno, 0, 0 ) ;
+    siPosition ( camera, 0, -5, 1, 0, 0, 0 ) ;
+  }
+ 
+
+Here we defined a simple variable called 'frameno' which is increased +by one every time the 'siUpdate' function is called to give the program +an idea of how much time has gone by. +

+Finally, we set the position of the camera and tux by calling +'siPosition' for each of them. +

+'siPosition' is a function that needs SEVEN things to be passed to it. +

    +
  1. The first is the variable that contains the number of the object you + want to position. +
  2. The second is the X coordinate that you want that object to have + (in Meters). +
  3. The third is the Y coordinate. +
  4. The fourth is the Z coordinate (the height). +
  5. The fifth is the 'heading' - which is the direction that the object + is pointing. If you model your objects with their noses pointing + North in the modeller then this number will be the angle in degrees + measured in an anticlockwise direction from due North. +
  6. The sixth is the 'pitch' - which is the amount that the object is + leaning backwards (in degrees). +
  7. The seventh (and last) is the 'roll' - which is the amount that the + object is leaning sideways (also in degrees). +
+That's a lot of information - but it's what you need to tell the +object for it to know where to appear in the scene. +

+In our example program, we first had: +

+
+    siPosition ( tux, 0, 0, 0, frameno, 0, 0 ) ;
+
+
+...which puts Tux at X=0, Y=0, Z=0 (which is at the origin of the scene) +and sets his 'heading' equal to this 'frameno' variable. Thus, as time +goes on, and we keep adding one to 'frameno', Tux's heading number will +gradually increase from zero upwards. This should cause him to rotate +in place. Since pitch and roll are both zero, he'll be standing upright. +

+The slPosition command for the Camera: +

+
+    siPosition ( camera, 0, -5, 1, 0, 0, 0 ) ;
+
+
+...puts the camera at X=0, Y=-5, Z=1 - which is back 5 meters from where +Tux is - and one meter up into the air. This should give us a nice view +of him as he spins. + +

READING THE JOYSTICK.

+ +These functions let you read the Left/Right and Up/Down axes of the +joystick: +
+
+  leftright = siJoystickLR () ;
+  updown    = siJoystickUD () ;
+ 
+
+The numbers that this will put into the variables 'leftright' and +'updown' range from -1.0 to +1.0. Leftwards and Upwards movements +generate 1, Rightwards and Downwards movements generate -1, when the +stick is in the middle you get zeroes. +

+To read the buttons, you can use these functions: +

+
+  buttonA =  siJoystickA () ;
+  buttonB =  siJoystickB () ;
+  buttonC =  siJoystickC () ;
+  buttonD =  siJoystickD () ;
+  buttonL =  siJoystickL () ;
+  buttonR =  siJoystickR () ;
+
+
+...the variables should be 'int' varaibles and will be set to 1 if the +button is pressed or 0 if it's not. + +

OTHER MOVEMENT COMMANDS.

+ +Other commands let you set the Velocity for objects: +
+
+    siVelocity ( object, x, y, z, h, p, r ) ;
+
+
+...and set a speed and a direction: +
+
+    siSpeedAndDirection ( object, speed, heading, pitch ) ;
+
+
+...you can also get the current position for the object: +
+
+    x = siGetPositionX ( object ) ;
+    y = siGetPositionY ( object ) ;
+    z = siGetPositionZ ( object ) ;
+    h = siGetPositionH ( object ) ;
+    p = siGetPositionP ( object ) ;
+    r = siGetPositionR ( object ) ;
+
+
+

THE MAKEFILE.

+ +Once you start to have things like SIMON being used with your program, +it can be quite a complicated process to compile your program. It's a +lot of typing and you'll make lots of mistakes. +

+For that reason, Linux uses a command called 'make' to compile complicated +programs according to some rules that you tell it in a special file that's +called 'Makefile'. Once you have a 'Makefile' set up for your program, +you only ever need to type 'make' in order to compile your program - +no matter how complicated it gets to be. +

+A good 'Makefile' for a SIMON program would be: +

+
+  #
+  # List the names of the programs we want to build here...
+  #
+  TARGETS = test1
+ 
+  #======================== DON'T CHANGE THIS PART =============================
+  #
+  SIMONDIR = /usr/local/simon
+  SIMON = -I${SIMONDIR} -L${SIMONDIR}
+  LIBS  = -lsimon -lplibssg -lplibsg -lplibul -lglut -lGLU -lGL -L/usr/X11/lib -lX11 -lm
+ 
+  all: ${TARGETS}
+ 
+  #=============================================================================
+ 
+  #
+  # Copy this for every program you want to write - changing 'test1'
+  # to whatever your program is called.
+  #
+ 
+  test1 : test1.cxx ; g++ ${SIMON} -o test1 test1.cxx ${LIBS}                                               
+
+ + + diff --git a/demos/simon/src/Makefile b/demos/simon/src/Makefile new file mode 100644 index 0000000..1d3e57c --- /dev/null +++ b/demos/simon/src/Makefile @@ -0,0 +1,31 @@ +CXX=g++ + +SIMON = libsimon.a simon +LIBS = -lplibpsl -lplibssg -lplibsg -lplibjs -lplibul -lglut -lGLU -lGL -lpthread -L/usr/X11/lib -lX11 -lm + +all: ${SIMON} + +simon : libsimon.a simon.o + $(CXX) -o simon simon.o libsimon.a ${LIBS} -L$(HOME)/lib -L/usr/X11R6/lib + +simon.o : simon.cxx simon.h + $(CXX) -I. -o simon.o -c simon.cxx + +simon_api.o : simon_api.cxx simon.h + $(CXX) -o simon_api.o -c simon_api.cxx + +libsimon.a : simon_api.o + ar cru libsimon.a simon_api.o + +install: libsimon.a simon.h simon + mkdir -p /usr/local/simon + cp libsimon.a simon.h /usr/local/simon + cp simon /usr/local/bin + +uninstall: + rm -r /usr/local/simon + rm /usr/local/bin/simon + +clean: + rm -f simon libsimon.a *.o + diff --git a/demos/simon/src/simon.cxx b/demos/simon/src/simon.cxx new file mode 100644 index 0000000..043b0ea --- /dev/null +++ b/demos/simon/src/simon.cxx @@ -0,0 +1,231 @@ +/* + S.I.M.O.N - Simple Interface for Making Oliver's programs Nice + Copyright (C) 2002 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: simon.cxx 1699 2002-09-22 15:22:21Z sjbaker $ +*/ + + +#include +#include + + +pslValue my_print ( int argc, pslValue *argv, pslProgram *p ) +{ + for ( int i = 0 ; i < argc ; i++ ) + { + switch ( argv[i].getType () ) + { + case PSL_INT : printf ( "%d ", argv[i].getInt () ) ; break ; + case PSL_FLOAT : printf ( "%f ", argv[i].getFloat () ) ; break ; + case PSL_STRING : printf ( "%s", argv[i].getString () ) ; break ; + case PSL_VOID : printf ( "(void) " ) ; break ; + } + } + + pslValue ret ; + return ret ; +} + + +pslValue my_siJoystickUD ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickUD () ) ; + return ret ; +} + + +pslValue my_siJoystickLR ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickLR () ) ; + return ret ; +} + + +pslValue my_siJoystickA ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickA () ) ; + return ret ; +} + + +pslValue my_siJoystickB ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickB () ) ; + return ret ; +} + + +pslValue my_siJoystickC ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickC () ) ; + return ret ; +} + + +pslValue my_siJoystickD ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickD () ) ; + return ret ; +} + + +pslValue my_siJoystickL ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickL () ) ; + return ret ; +} + + +pslValue my_siJoystickR ( int, pslValue *, pslProgram * ) +{ + pslValue ret ; + ret . set ( siJoystickR () ) ; + return ret ; +} + + +pslValue my_siLoad ( int argc, pslValue *argv, pslProgram *p ) +{ + if ( argc != 1 || argv[0].getType() != PSL_STRING ) + fprintf ( stderr, "siLoad: ERROR - Parameter must be a single string!\n" ) ; + + pslValue ret ; + ret . set ( siLoad ( argv[0].getString () ) ) ; + return ret ; +} + + + +pslValue my_fabs ( int argc, pslValue *argv, pslProgram *p ) +{ + pslValue ret ; + ret . set ( (float) fabs ( argv[0].getFloat () ) ) ; + return ret ; +} + + +pslValue my_siPosition ( int argc, pslValue *argv, pslProgram *p ) +{ + if ( argc != 7 ) + fprintf ( stderr, "siPosition: ERROR - Expected seven parameters?!?\n" ) ; + + siPosition ( argv[0].getInt (), + argv[1].getFloat (), + argv[2].getFloat (), + argv[3].getFloat (), + argv[4].getFloat (), + argv[5].getFloat (), + argv[6].getFloat () ) ; + + pslValue ret ; + return ret ; +} + + + + +pslExtension extensions [] = +{ + { "siLoad" , 1, my_siLoad }, + { "siPosition" , 7, my_siPosition }, + { "siJoystickUD", 0, my_siJoystickUD }, + { "siJoystickLR", 0, my_siJoystickLR }, + { "siJoystickA" , 0, my_siJoystickA }, + { "siJoystickB" , 0, my_siJoystickB }, + { "siJoystickC" , 0, my_siJoystickC }, + { "siJoystickD" , 0, my_siJoystickD }, + { "siJoystickL" , 0, my_siJoystickL }, + { "siJoystickR" , 0, my_siJoystickR }, + { "fabs" , 1, my_fabs }, + { "print" ,-1, my_print }, + { NULL, 0, NULL } +} ; + + +pslProgram *prog = NULL ; +ulClock ck ; + +int main ( int argc, char **argv ) +{ + if ( argc != 2 ) + { + fprintf ( stderr, "simon: Usage -\n\n" ) ; + fprintf ( stderr, " simon filename\n\n" ) ; + fprintf ( stderr, "Where: 'filename' is a PSL sourcefile\n\n" ) ; + exit ( 1 ) ; + } + + pslInit () ; + + /* Create program and compile it. */ + + prog = new pslProgram ( extensions, argv[1] ) ; + + ck.setMaxDelta ( 100000.0 ) ; + ck.update () ; + + prog -> compile ( argv[1] ) ; + + ck.update () ; + fprintf(stderr, "Compile Time: %fs.\n", ck.getDeltaTime () ) ; + + fprintf(stderr, "-------------------------------\n" ) ; + siRun () ; + return 0 ; +} + + +void siUpdate () +{ + static int nframes = 0 ; + static double total_time = 0.0 ; + + pslResult res ; + + ck.update () ; + do + { + res = prog -> step () ; + + } while ( res == PSL_PROGRAM_CONTINUE ) ; + + if ( res == PSL_PROGRAM_END ) + exit ( 0 ) ; + + ck.update () ; + total_time += ck.getDeltaTime () ; + nframes++ ; + + if ( nframes == 100 ) + { + fprintf ( stderr, "Avg PSL Interp time = %fs.\n", total_time / nframes ) ; + nframes = 0 ; + total_time = 0.0 ; + } +} + + diff --git a/demos/simon/src/simon.dsp b/demos/simon/src/simon.dsp new file mode 100644 index 0000000..9a9b967 --- /dev/null +++ b/demos/simon/src/simon.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="simon" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=simon - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "simon.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "simon.mak" CFG="simon - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "simon - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "simon - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +F90=df.exe +RSC=rc.exe + +!IF "$(CFG)" == "simon - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE F90 /compile_only /nologo /warn:nofileopt +# ADD F90 /compile_only /nologo /warn:nofileopt +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "..\..\..\..\" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 ul.lib js.lib psl.lib ssg.lib sg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "simon - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE F90 /check:bounds /compile_only /dbglibs /debug:full /nologo /traceback /warn:argument_checking /warn:nofileopt +# ADD F90 /check:bounds /compile_only /dbglibs /debug:full /nologo /traceback /warn:argument_checking /warn:nofileopt +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "..\..\..\..\" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ul_d.lib js_d.lib psl_d.lib ssg_d.lib sg_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcd.lib" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "simon - Win32 Release" +# Name "simon - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;f90;for;f;fpp" +# Begin Source File + +SOURCE=.\simon.cxx +# End Source File +# Begin Source File + +SOURCE=.\simon_api.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\simon.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/demos/simon/src/simon.dsw b/demos/simon/src/simon.dsw new file mode 100644 index 0000000..0ffb6b5 --- /dev/null +++ b/demos/simon/src/simon.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "simon"=".\simon.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/demos/simon/src/simon.h b/demos/simon/src/simon.h new file mode 100644 index 0000000..4975e81 --- /dev/null +++ b/demos/simon/src/simon.h @@ -0,0 +1,83 @@ +/* + S.I.M.O.N - Simple Interface for Making Oliver's programs Nice + Copyright (C) 2002 Steve Baker + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: simon.h 1681 2002-09-15 02:51:44Z ude $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + +#define SI_MAX_MODELS 500 + +void siUpdate () ; +void siRun () ; +int siLoad ( const char *filename ) ; +void siPosition ( int hh, float x, float y, + float z = 0.0f, + float h = 0.0f, + float p = 0.0f, + float r = 0.0f ) ; + +void siSpeedAndDirection ( int hh, float s, float h, float p = 0.0f ) ; + +void siVelocity ( int hh, float x, float y, + float z = 0.0f, + float h = 0.0f, + float p = 0.0f, + float r = 0.0f ) ; + +float siGetPositionX ( int h ) ; +float siGetPositionY ( int h ) ; +float siGetPositionZ ( int h ) ; +float siGetPositionH ( int h ) ; +float siGetPositionP ( int h ) ; +float siGetPositionR ( int h ) ; + +float siJoystickLR () ; +float siJoystickUD () ; + +bool siJoystickA () ; +bool siJoystickB () ; +bool siJoystickC () ; +bool siJoystickD () ; +bool siJoystickL () ; +bool siJoystickR () ; + diff --git a/demos/simon/src/simon_api.cxx b/demos/simon/src/simon_api.cxx new file mode 100644 index 0000000..fa83962 --- /dev/null +++ b/demos/simon/src/simon_api.cxx @@ -0,0 +1,365 @@ +/* + S.I.M.O.N - Simple Interface for Making Oliver's programs Nice + Copyright (C) 2002 Steve Baker + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: simon_api.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "simon.h" +#include +#include +#include + +class siEntity ; + +static int siNextModel = 0 ; +static bool siInitialised = false ; +static ssgRoot *siScene = NULL ; +static siEntity *siModels [ SI_MAX_MODELS ] ; + +class siEntity +{ +protected: + + int handle ; + char *name ; + sgCoord pos ; + sgCoord vel ; + +public: + + siEntity ( const char *_fname ) + { + siModels [ siNextModel ] = this ; + + handle = siNextModel++ ; + + if ( siNextModel >= SI_MAX_MODELS ) + { + fprintf ( stderr, "simon: ERROR - More than %d models loaded!\n", SI_MAX_MODELS ) ; + exit ( 1 ) ; + } + + name = ulStrDup ( _fname ) ; + sgZeroCoord ( & pos ) ; + sgZeroCoord ( & vel ) ; + } + + int getHandle () const { return handle ; } + + float getPositionX () const { return pos.xyz[0] ; } + float getPositionY () const { return pos.xyz[1] ; } + float getPositionZ () const { return pos.xyz[2] ; } + float getPositionH () const { return pos.hpr[0] ; } + float getPositionP () const { return pos.hpr[1] ; } + float getPositionR () const { return pos.hpr[2] ; } + + void setSpeedAndDirection ( float s, float h, float p ) + { + sgMat4 tmp ; + sgMakeRotMat4 ( tmp, h, p, 0 ) ; + + sgSetVec3 ( vel.xyz, 0, s, 0 ) ; + sgXformPnt3 ( vel.xyz, tmp ) ; + sgSetVec3 ( pos.hpr, h, p, 0 ) ; + sgZeroVec3 ( vel.hpr ) ; + } + + void setVelocity ( float x, float y, float z, float h, float p, float r ) + { + sgSetCoord ( &vel, x, y, z, h, p, r ) ; + } + + void setPosition ( float x, float y, float z, float h, float p, float r ) + { + sgSetCoord ( &pos, x, y, z, h, p, r ) ; + } + + void updatePhysics ( double dt ) + { + sgAddScaledVec3 ( pos . xyz, vel . xyz, (float)dt ) ; + sgAddScaledVec3 ( pos . hpr, vel . hpr, (float)dt ) ; + } + + virtual void update ( double dt ) = 0 ; +} ; + + + +class siCamera : public siEntity +{ +public: + + siCamera () : siEntity ( "Camera" ) + { + } + + virtual void update ( double dt ) + { + updatePhysics ( dt ) ; + ssgSetCamera ( & pos ) ; + } +} ; + + + +class siModel : public siEntity +{ + ssgEntity *ent ; + ssgTransform *tra ; + +public: + + siModel ( const char *_fname ) : siEntity ( _fname ) + { + tra = new ssgTransform ; + ent = ssgLoad ( name ) ; + tra -> addKid ( ent ) ; + + ssgFlatten ( ent ) ; + ssgStripify ( tra ) ; + + siScene -> addKid ( tra ) ; + } + + virtual void update ( double dt ) + { + updatePhysics ( dt ) ; + tra -> setTransform ( & pos ) ; + } +} ; + + +static siEntity *getModel ( int i ) +{ + if ( i < 0 || i >= SI_MAX_MODELS || + siModels [ i ] == NULL ) + { + fprintf ( stderr, "simon: There is no such model as '%d'\n", i ) ; + exit ( 1 ) ; + } + + return siModels [ i ] ; +} + + +float siGetPositionX(int hh) { return getModel ( hh ) -> getPositionX () ; } +float siGetPositionY(int hh) { return getModel ( hh ) -> getPositionY () ; } +float siGetPositionZ(int hh) { return getModel ( hh ) -> getPositionZ () ; } +float siGetPositionH(int hh) { return getModel ( hh ) -> getPositionH () ; } +float siGetPositionP(int hh) { return getModel ( hh ) -> getPositionP () ; } +float siGetPositionR(int hh) { return getModel ( hh ) -> getPositionR () ; } + + +void siPosition ( int hh, float x, float y, float z, + float h, float p, float r ) +{ + getModel ( hh ) -> setPosition ( x, y, z, h, p, r ) ; +} + +void siSpeedAndDirection ( int hh, float s, float h, float p ) +{ + getModel ( hh ) -> setSpeedAndDirection ( s, h, p ) ; +} + +void siVelocity ( int hh, float x, float y, float z, + float h, float p, float r ) +{ + getModel ( hh ) -> setVelocity ( x, y, z, h, p, r ) ; +} + + +/* + The GLUT window reshape event +*/ + +static void siReshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void siKeyboard ( unsigned char k, int, int ) +{ + if ( k == 0x03 ) exit ( 0 ) ; +} + + + +static jsJoystick js (0) ; + +static bool A, B, C, D, L, R ; + +static float leftright ; +static float updown ; + +static void read_joystick () +{ + int b ; + float ax [ 10 ] ; + + js.read ( &b, ax ) ; + + leftright = ax[0] ; + updown = ax[1] ; + + A = (( b & 1 ) != 0) ; + B = (( b & 2 ) != 0) ; + C = (( b & 4 ) != 0) ; + D = (( b & 8 ) != 0) ; + R = (( b & 16 ) != 0) ; + L = (( b & 32 ) != 0) ; +} + +float siJoystickLR () { return -leftright ; } +float siJoystickUD () { return -updown ; } + +bool siJoystickA () { return A ; } +bool siJoystickB () { return B ; } +bool siJoystickC () { return C ; } +bool siJoystickD () { return D ; } +bool siJoystickL () { return L ; } +bool siJoystickR () { return R ; } + +/* + The GLUT redraw event +*/ + +static ulClock *siFrameTimer ; + +static void siRedraw () +{ + siFrameTimer -> update () ; + + double dt = siFrameTimer -> getDeltaTime () ; + + read_joystick () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + siUpdate () ; + + for ( int i = 0 ; i < siNextModel ; i++ ) + siModels [ i ] -> update ( dt ) ; + + ssgCullAndDraw ( siScene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; + ulSleep(16); +} + + + +void siInit () +{ + if ( siInitialised ) + { + fprintf ( stderr, "simon: ERROR - siInit() was called more than once!\n" ) ; + exit ( 1 ) ; + } + + siInitialised = true ; + + siFrameTimer = new ulClock ; + + siFrameTimer -> update () ; + + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "Simon" ; + fake_argv[1] = "Simon Window." ; + fake_argv[2] = NULL ; + + /* Initialise GLUT */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 600, 600 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( siRedraw ) ; + glutReshapeFunc ( siReshape ) ; + glutKeyboardFunc ( siKeyboard ) ; + + /* Initialise SSG */ + + ssgInit () ; + + /* Some basic OpenGL setup */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* Set up the viewing parameters */ + + ssgSetFOV ( 90.0f, 90.0f ) ; + ssgSetNearFar ( 0.1f, 700.0f ) ; + + /* Set up the Sun. */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + + /* Set up the path to the data files */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* Create a root node. */ + + siScene = new ssgRoot ; + + for ( int i = 0 ; i < SI_MAX_MODELS ; i++ ) + siModels [ i ] = NULL ; + + siNextModel = 0 ; + new siCamera () ; /* Thus guaranteeing this is handle zero */ +} + + + +/* Load a simple model */ + +int siLoad ( const char *filename ) +{ + if ( ! siInitialised ) + siInit () ; + + siModel *m = new siModel ( filename ) ; + + return m -> getHandle () ; +} + + +void siRun () +{ + if ( ! siInitialised ) + siInit () ; + + glutMainLoop () ; +} + diff --git a/demos/ttt3d/AUTHORS b/demos/ttt3d/AUTHORS new file mode 100644 index 0000000..a957b7c --- /dev/null +++ b/demos/ttt3d/AUTHORS @@ -0,0 +1,2 @@ +TTT3D was written by Steve Baker. + diff --git a/demos/ttt3d/COPYING b/demos/ttt3d/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/demos/ttt3d/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/demos/ttt3d/ChangeLog b/demos/ttt3d/ChangeLog new file mode 100644 index 0000000..06f3320 --- /dev/null +++ b/demos/ttt3d/ChangeLog @@ -0,0 +1,8 @@ + +TTT3D Change Log. +================= + +TTT3D v0.0.0 (July 21st 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * First release. + diff --git a/demos/ttt3d/INSTALL b/demos/ttt3d/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/demos/ttt3d/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/demos/ttt3d/Makefile.am b/demos/ttt3d/Makefile.am new file mode 100644 index 0000000..3daeafd --- /dev/null +++ b/demos/ttt3d/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src wavs images data mods fonts doc + +EXTRA_DIST = autogen.sh README acinclude.m4 TTT3D.dsw TTT3D.dsp + diff --git a/demos/ttt3d/NEWS b/demos/ttt3d/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/demos/ttt3d/README b/demos/ttt3d/README new file mode 100644 index 0000000..58615fc --- /dev/null +++ b/demos/ttt3d/README @@ -0,0 +1,54 @@ + +TicTacToe 3D. +~~~~~~~~~~~~~ + by Steve Baker + + +INSTALLATION: + +Installation instructions are pretty basic, +and are covered in the documentation directory 'doc'. + + OpenGL (or Mesa) + GLUT (the GL Utility Toolkit) + PLIB (which can be obtained from one of: + http://plib.sourceforge.net + +Once that is done, it should be a simple matter of changing +to the directory where all this stuff is unpacked and type: + + ./configure + make + make install + +However, you might want to check the full documentation - +especially if you need to install Mesa/GLIDE/GLUT before +you can start. + +All documents are in HTML - so view them with your web browser. + +LICENSE: + +This software is released under the provisions of +the GNU General Public License (GPL) which can be +found in the file 'COPYING' in the current directory. + +DIRECTORY STRUCTURE: + +src -- Where the source code lives. + +data -- This contains all kinds of miscellaneous data + files. Right now, this is limited to the level + description files. + +doc -- Documentation - all in HTML + +images -- Texture maps and other images needed by the + game. These are currently all in 'SGI' image + format. + +wavs -- Sound files - mostly sound effects. + +mods -- Music files. + + diff --git a/demos/ttt3d/TTT3D.dsp b/demos/ttt3d/TTT3D.dsp new file mode 100644 index 0000000..e05fb7d --- /dev/null +++ b/demos/ttt3d/TTT3D.dsp @@ -0,0 +1,172 @@ +# Microsoft Developer Studio Project File - Name="TTT3D" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=TTT3D - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "TTT3D.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "TTT3D.mak" CFG="TTT3D - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "TTT3D - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "TTT3D - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "TTT3D - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../../" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl.lib fnt.lib pui.lib ssgAux.lib ssg.lib sg.lib ul.lib glut32.lib opengl32.lib winmm.lib /nologo /subsystem:console /machine:I386 /out:"TTT3D.exe" /libpath:"../../" + +!ELSEIF "$(CFG)" == "TTT3D - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../../" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl_d.lib fnt_d.lib pui_d.lib ssgAux_d.lib ssg_d.lib sg_d.lib ul_d.lib glut32.lib opengl32.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"TTT3D.exe" /pdbtype:sept /libpath:"../../" + +!ENDIF + +# Begin Target + +# Name "TTT3D - Win32 Release" +# Name "TTT3D - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\src\cell.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\game.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\gfx.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\gui.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\material.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\puzzle.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\sound.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\status.cxx +# End Source File +# Begin Source File + +SOURCE=.\src\ttt3d.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\src\cell.h +# End Source File +# Begin Source File + +SOURCE=.\src\game.h +# End Source File +# Begin Source File + +SOURCE=.\src\gfx.h +# End Source File +# Begin Source File + +SOURCE=.\src\gui.h +# End Source File +# Begin Source File + +SOURCE=.\src\level.h +# End Source File +# Begin Source File + +SOURCE=.\src\material.h +# End Source File +# Begin Source File + +SOURCE=.\src\p3d.h +# End Source File +# Begin Source File + +SOURCE=.\src\puzzle.h +# End Source File +# Begin Source File + +SOURCE=.\src\sound.h +# End Source File +# Begin Source File + +SOURCE=.\src\status.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/demos/ttt3d/TTT3D.dsw b/demos/ttt3d/TTT3D.dsw new file mode 100644 index 0000000..ccb0384 --- /dev/null +++ b/demos/ttt3d/TTT3D.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "TTT3D"=.\TTT3D.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/demos/ttt3d/acinclude.m4 b/demos/ttt3d/acinclude.m4 new file mode 100755 index 0000000..3463ad8 --- /dev/null +++ b/demos/ttt3d/acinclude.m4 @@ -0,0 +1,8 @@ +AC_DEFUN(MY_EXPAND_DIR, [ + $1=$2 + $1=`( + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" + eval echo \""[$]$1"\" + )` +]) diff --git a/demos/ttt3d/autogen.sh b/demos/ttt3d/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/demos/ttt3d/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/demos/ttt3d/configure.in b/demos/ttt3d/configure.in new file mode 100644 index 0000000..a120ca2 --- /dev/null +++ b/demos/ttt3d/configure.in @@ -0,0 +1,174 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/ttt3d.cxx) +AM_INIT_AUTOMAKE(ttt3d, 0.2.0) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL + +plib_suffix="-lplibjs -lplibsl -lplibssgaux -lplibssg -lplibpu -lplibfnt -lplibsg -lplibul" + +AC_LANG_CPLUSPLUS + + +dnl Command line arguments. + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + +if test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + LIBS="$LIBS -lwinmm -lglut32 -lglu32 -lopengl32 -luser32 -lgdi32" + ;; +*-apple-darwin*) + dnl Mac OS X + + LIBS="$LIBS -framework GLUT -lobjc -framework OpenGL -framework Carbon" + ;; +*) + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + LIBS="$LIBS $X_PRE_LIBS -lXi -lXmu -lXext -lX11 $X_EXTRA_LIBS -lm" + +dnl ========================================================= +dnl if test "x$x_includes" != "x"; then +dnl CPPFLAGS="$CPPFLAGS -I$x_includes" +dnl fi +dnl ========================================================= + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList,, + AC_MSG_ERROR([could not find working GL library])) + fi + + AC_CHECK_LIB(GLU, gluLookAt) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt,, + AC_MSG_ERROR([could not find working GLU library])) + fi + + dnl check for glut...or freeglut + AC_CHECK_LIB(freeglut, glutGetModifiers) + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xno" ; then + dnl if no freeglut, check for Real GLUT + AC_CHECK_LIB(glut, glutGetModifiers,, + AC_MSG_ERROR([could not find working GLUT library])) + fi + + AC_CHECK_LIB(dl, dlclose) + + dnl Check SGI audio library + AC_CHECK_LIB(audio, ALopenport, SND_LIBS="-laudio") + ;; +esac + +dnl Check PLIB library +AC_CHECK_LIB(plibul, ulInit, true, +AC_MSG_ERROR([there seems to be a problem with the PLIB libraries])) + +LIBS="$plib_suffix $LIBS" + + +dnl Checks for header files. + +AC_HEADER_STDC + +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADERS(OpenGL/gl.h OpenGL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GLUT/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*) + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + AC_CHECK_HEADER(GL/freeglut.h, AC_DEFINE(FREEGLUT_IS_PRESENT), + AC_MSG_ERROR([freeglut header file not found])) + else + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + fi + ;; +esac + +AC_CHECK_HEADER(plib/ul.h,, AC_MSG_ERROR([PLIB header files not found])) + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + + +MY_EXPAND_DIR(ttt3d, "$datadir/games/$PACKAGE") +AC_DEFINE_UNQUOTED(TTT3D_DATADIR, "$ttt3d_datadir") + +AC_OUTPUT( Makefile \ + data/Makefile \ + doc/Makefile \ + fonts/Makefile \ + images/Makefile \ + mods/Makefile \ + src/Makefile \ + wavs/Makefile \ + ) + diff --git a/demos/ttt3d/data/Makefile.am b/demos/ttt3d/data/Makefile.am new file mode 100644 index 0000000..a2be649 --- /dev/null +++ b/demos/ttt3d/data/Makefile.am @@ -0,0 +1,7 @@ +# data/ + +pkgdatadir = $(datadir)/games/@PACKAGE@/data + +pkgdata_DATA = $(wildcard *.dat) + +EXTRA_DIST = $(pkgdata_DATA) \ No newline at end of file diff --git a/demos/ttt3d/data/levels.dat b/demos/ttt3d/data/levels.dat new file mode 100644 index 0000000..4880471 --- /dev/null +++ b/demos/ttt3d/data/levels.dat @@ -0,0 +1,2 @@ +Placeholder. + diff --git a/demos/ttt3d/doc/Makefile.am b/demos/ttt3d/doc/Makefile.am new file mode 100644 index 0000000..15b5d80 --- /dev/null +++ b/demos/ttt3d/doc/Makefile.am @@ -0,0 +1,5 @@ +# doc/ + +pkgdata_DATA = $(wildcard *.html) $(wildcard *.gif) + +EXTRA_DIST = $(pkgdata_DATA) diff --git a/demos/ttt3d/doc/index.html b/demos/ttt3d/doc/index.html new file mode 100644 index 0000000..ee2d550 --- /dev/null +++ b/demos/ttt3d/doc/index.html @@ -0,0 +1,95 @@ + + + + + Steve's 3D TicTacToe Player. + + +

Steve's 3D TicTacToe Player.

+By +Steve Baker <sjbaker1@airmail.net> +

Introduction.

+TicTacToe - the worlds most boring game. Well, when played +in three dimensions with a 4x4 board, this simple game takes +on an entirely new level of intellectual challenge. +

Installation:

+
+
+    ./configure
+    make 
+    make install
+
+
+If you are running Windoze or something that can't +run configure scripts and makefiles, don't sweat it. +There is just one C++ source file - no special options or libraries. +

+No, I won't provide a binary version, project +files and such. +

License:

+This program is offered under the terms of the GNU +Public License (GPL). +

Instructions:

+Run this from any shell: +
+
+    ttt3d
+
+
+The computer takes 'O's and lets you go first. The first +person to make a row of 4 wins. +
+
+                         ~~~ 3D TicTacToe ~~~
+                            by Steve Baker
+
+
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   |   |   |         |   |   |         |   |   |         |   |   |      
+_1_|_2_|_3_|_4_   17_|18_|19_|20_   33_|34_|35_|36_   49_|50_|51_|52_   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   | X |   |         | O |   |         |   |   |         |   |   |      
+_5_|___|_7_|_8_   21_|___|23_|24_   37_|38_|39_|40_   53_|54_|55_|56_   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   | X |   |         |   |   |         |   |*O*|         |   |   |      
+_9_|___|11_|12_   25_|26_|27_|28_   41_|42_|___|44_   57_|58_|59_|60_   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   |   |   |         |   |   |         |   |   |         |   |   |      
+13 |14 |15 |16    29 |30 |31 |32    45 |46 |47 |48    61 |62 |63 |64    
+
+
+Where would you like to play your X ? 
+[1..64]: 
+
+
+(In the example above, the computer just played in square 43 the +asterisks help you see where he just played). +

+Imagine the four grids stacked on top of each other. (Either +from left to right or right to left - it doesn't matter). +You can form your row of four X's either within one layer - +or across layers. Anything that would be a straight line in +3D. So in this example: +

+
+   |   |   |         |   |   |         |   |   |         |   |   |      
+ X |   |   |         |   |   |         |   | O |      *O*|   |   | X    
+___|_2_|_3_|_4_   17_|18_|19_|20_   33_|34_|___|36_   ___|50_|51_|___   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   | X |   |         |   |   |         |*O*|   |       X | O |   |      
+_5_|___|_7_|_8_   21_|22_|23_|24_   37_|___|39_|40_   ___|___|55_|56_   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+   |   |   |         |   |*O*|         |   |   |         | O | X | X    
+_9_|10_|11_|12_   25_|26_|___|28_   41_|42_|43_|44_   57_|___|___|___   
+   |   |   |         |   |   |         |   |   |         |   |   |      
+ X |   |   |*O*      |   |   | X       |   |   |       O | X |   | O    
+   |14 |15 |      29 |30 |31 |      45 |46 |47 |48       |   |63 |      
+
+
+...the computer just won with a diagonal line (when someone wins, +the winning line is highlighted with asterisks). +

+You'll find the computer is an extremely hard opponent to beat, +but it CAN BE DONE! + + diff --git a/demos/ttt3d/fonts/Makefile.am b/demos/ttt3d/fonts/Makefile.am new file mode 100644 index 0000000..0cac0fc --- /dev/null +++ b/demos/ttt3d/fonts/Makefile.am @@ -0,0 +1,7 @@ +# fonts/ + +pkgdatadir = $(datadir)/games/@PACKAGE@/fonts + +pkgdata_DATA = $(wildcard *.txf) + +EXTRA_DIST = $(pkgdata_DATA) \ No newline at end of file diff --git a/demos/ttt3d/fonts/sorority.txf b/demos/ttt3d/fonts/sorority.txf new file mode 100644 index 0000000000000000000000000000000000000000..8b457aafb72a70f703aaf9c754eb64da85709072 GIT binary patch literal 9304 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/images/X.rgb b/demos/ttt3d/images/X.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b6fb4737b6099815aa30f54298f70fbce207f002 GIT binary patch literal 15081 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/images/controls.rgb b/demos/ttt3d/images/controls.rgb new file mode 100644 index 0000000000000000000000000000000000000000..09565e9706da47bf46f8b65f56efa0a622ddab28 GIT binary patch literal 15646 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/images/ground.rgb b/demos/ttt3d/images/ground.rgb new file mode 100644 index 0000000000000000000000000000000000000000..9874e2faf2d942f482a4bac2a7793abc08e47aab GIT binary patch literal 15020 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 Q#Tcvgrt>qb<45-Y1s_ + +static ssgBranch *wire_cube = NULL ; +static ssgBranch * X_cube = NULL ; +static ssgBranch * O_cube = NULL ; + + +ssgTransform *makeGround () +{ + ssgTransform *ground = new ssgTransform () ; + + sgVec4 *colour = new sgVec4 [ 1 ] ; + sgVec3 *normal = new sgVec3 [ 1 ] ; + sgVec2 *tcoord = new sgVec2 [ 4 ] ; + sgVec3 *scoord = new sgVec3 [ 4 ] ; + + sgSetVec4 ( colour[0], 1, 1, 1, 1 ) ; + + sgSetVec2 ( tcoord [ 0 ], 0, 0 ) ; + sgSetVec2 ( tcoord [ 1 ], 25, 0 ) ; + sgSetVec2 ( tcoord [ 2 ], 25, 25 ) ; + sgSetVec2 ( tcoord [ 3 ], 0, 25 ) ; + + sgSetVec3 ( normal [ 0 ], 0, 0, 1 ) ; + + sgSetVec3 ( scoord [ 0 ], -60, -60, -5.6f ) ; + sgSetVec3 ( scoord [ 1 ], 60, -60, -5.6f ) ; + sgSetVec3 ( scoord [ 2 ], 60, 60, -5.6f ) ; + sgSetVec3 ( scoord [ 3 ], -60, 60, -5.6f ) ; + + ssgVTable *vt1 = new ssgVTable ( (GLenum) GL_TRIANGLE_FAN, + 4, scoord, + 1, normal, + 4, tcoord, + 1, colour ) ; + + vt1 -> setState ( ground_gst ) ; ground -> addKid ( vt1 ) ; + + colour = new sgVec4 [ 1 ] ; + normal = new sgVec3 [ 1 ] ; + tcoord = new sgVec2 [ 6 ] ; + scoord = new sgVec3 [ 6 ] ; + + sgSetVec4 ( colour[0], 1, 1, 1, 1 ) ; + + sgSetVec2 ( tcoord [ 0 ], 0, 0 ) ; + sgSetVec2 ( tcoord [ 1 ], 1, 0 ) ; + sgSetVec2 ( tcoord [ 2 ], 1, 1 ) ; + sgSetVec2 ( tcoord [ 3 ], 0, 1 ) ; + sgSetVec2 ( tcoord [ 4 ], 1, 1 ) ; + sgSetVec2 ( tcoord [ 5 ], 1, 0 ) ; + + sgSetVec3 ( normal [ 0 ], 0, 0, 1 ) ; + + sgSetVec3 ( scoord [ 0 ], -2, -2, -2.5 ) ; + sgSetVec3 ( scoord [ 1 ], 1, -2, -2.5 ) ; + sgSetVec3 ( scoord [ 2 ], 1, 1, -2.5 ) ; + sgSetVec3 ( scoord [ 3 ], -2, 1, -2.5 ) ; + sgSetVec3 ( scoord [ 4 ], 1, 1, -2.5 ) ; + sgSetVec3 ( scoord [ 5 ], 1, -2, -2.5 ) ; + + vt1 = new ssgVTable ( (GLenum) GL_TRIANGLE_FAN, + 6, scoord, + 1, normal, + 6, tcoord, + 1, colour ) ; + + vt1 -> setState ( ctrls_gst ) ; ground -> addKid ( vt1 ) ; + + return ground ; +} ; + +ssgTransform *makeBlueWireCube() +{ + ssgTransform *sel_cube = new ssgTransform () ; + + sgVec4 *colour = new sgVec4 [ 1 ] ; + sgVec3 *normal = new sgVec3 [ 1 ] ; + sgVec2 *tcoord = new sgVec2 [ 1 ] ; + sgVec3 *scoord = new sgVec3 [ 8 ] ; + + sgSetVec4 ( colour[0], 1, 0, 1, 1 ) ; + sgSetVec3 ( normal[0], 1, 0, 0 ) ; + sgSetVec2 ( tcoord[0], 0, 0 ) ; + + sgSetVec3 ( scoord [ 0 ], -0.5, -0.5, -0.5 ) ; + sgSetVec3 ( scoord [ 1 ], -0.5, -0.5, 0.5 ) ; + sgSetVec3 ( scoord [ 2 ], 0.5, -0.5, 0.5 ) ; + sgSetVec3 ( scoord [ 3 ], 0.5, -0.5, -0.5 ) ; + + sgSetVec3 ( scoord [ 4 ], -0.5, 0.5, -0.5 ) ; + sgSetVec3 ( scoord [ 5 ], -0.5, 0.5, 0.5 ) ; + sgSetVec3 ( scoord [ 6 ], 0.5, 0.5, 0.5 ) ; + sgSetVec3 ( scoord [ 7 ], 0.5, 0.5, -0.5 ) ; + + unsigned short *sindex = new unsigned short [ 7 ] ; + unsigned short *cindex = new unsigned short [ 1 ] ; + unsigned short *nindex = new unsigned short [ 1 ] ; + unsigned short *tindex = new unsigned short [ 1 ] ; + + sindex [ 0 ] = 0 ; + sindex [ 1 ] = 1 ; + sindex [ 2 ] = 2 ; + sindex [ 3 ] = 6 ; + sindex [ 4 ] = 7 ; + sindex [ 5 ] = 3 ; + sindex [ 6 ] = 2 ; + + cindex [ 0 ] = 0 ; + nindex [ 0 ] = 0 ; + tindex [ 0 ] = 0 ; + + ssgVTable *vt1 = new ssgVTable ( (GLenum) GL_LINE_STRIP, + 7, sindex, scoord, + 1, nindex, normal, + 1, tindex, tcoord, + 1, cindex, colour ) ; + + sindex = new unsigned short [ 5 ] ; + + sindex [ 0 ] = 3 ; + sindex [ 1 ] = 0 ; + sindex [ 2 ] = 4 ; + sindex [ 3 ] = 5 ; + sindex [ 4 ] = 1 ; + + ssgVTable *vt2 = new ssgVTable ( (GLenum) GL_LINE_STRIP, + 5, sindex, scoord, + 1, nindex, normal, + 1, tindex, tcoord, + 1, cindex, colour ) ; + + sindex = new unsigned short [ 2 ] ; + + sindex [ 0 ] = 5 ; + sindex [ 1 ] = 6 ; + + ssgVTable *vt3 = new ssgVTable ( (GLenum) GL_LINE_STRIP, + 2, sindex, scoord, + 1, nindex, normal, + 1, tindex, tcoord, + 1, cindex, colour ) ; + + sindex = new unsigned short [ 2 ] ; + + sindex [ 0 ] = 4 ; + sindex [ 1 ] = 7 ; + + ssgVTable *vt4 = new ssgVTable ( (GLenum) GL_LINE_STRIP, + 2, sindex, scoord, + 1, nindex, normal, + 1, tindex, tcoord, + 1, cindex, colour ) ; + + vt1 -> setState ( default_gst ) ; sel_cube -> addKid ( vt1 ) ; + vt2 -> setState ( default_gst ) ; sel_cube -> addKid ( vt2 ) ; + vt3 -> setState ( default_gst ) ; sel_cube -> addKid ( vt3 ) ; + vt4 -> setState ( default_gst ) ; sel_cube -> addKid ( vt4 ) ; + + return sel_cube ; +} + + +static void makeWireCube() +{ + wire_cube = new ssgBranch () ; + + sgVec4 *colour = new sgVec4 [ 1 ] ; + sgVec3 *normal = new sgVec3 [ 1 ] ; + sgVec2 *tcoord = new sgVec2 [ 1 ] ; + sgVec3 *scoord = new sgVec3 [ 4 ] ; + + sgSetVec4 ( colour[0], 1.0, 1.0, 0.0, 0.7f ) ; + sgSetVec3 ( normal[0], 1, 0, 0 ) ; + sgSetVec2 ( tcoord[0], 0, 0 ) ; + + sgSetVec3 ( scoord [ 0 ], -0.5, -0.5, -0.5 ) ; + sgSetVec3 ( scoord [ 1 ], -0.5, 0.5, -0.5 ) ; + sgSetVec3 ( scoord [ 2 ], 0.5, 0.5, -0.5 ) ; + sgSetVec3 ( scoord [ 3 ], 0.5, -0.5, -0.5 ) ; + + unsigned short *sindex = new unsigned short [ 4 ] ; + unsigned short *cindex = new unsigned short [ 1 ] ; + unsigned short *nindex = new unsigned short [ 1 ] ; + unsigned short *tindex = new unsigned short [ 1 ] ; + + sindex [ 0 ] = 0 ; + sindex [ 1 ] = 1 ; + sindex [ 2 ] = 2 ; + sindex [ 3 ] = 3 ; + + cindex [ 0 ] = 0 ; + nindex [ 0 ] = 0 ; + tindex [ 0 ] = 0 ; + + ssgVTable *vt = new ssgVTable ( (GLenum) GL_LINE_LOOP, + 4, sindex, scoord, 1, nindex, normal, + 1, tindex, tcoord, 1, cindex, colour ) ; + + vt -> setState ( default_gst ) ; + wire_cube -> addKid ( vt ) ; +} + + +static ssgBranch *X () +{ + ssgaCube *XXX = new ssgaCube () ; + + XXX -> setSize ( 0.5 ) ; + XXX -> setKidState ( X_gst ) ; + + return XXX ; +} + + +static ssgBranch *O () +{ + ssgaSphere *OOO = new ssgaSphere ( 300 ) ; + + OOO -> setSize ( 0.7f ) ; + OOO -> setKidState ( O_gst ) ; + + return OOO ; +} + + +static void makeXcube () +{ + X_cube = new ssgBranch () ; + X_cube -> addKid ( X () ) ; +} + + +static void makeOcube () +{ + O_cube = new ssgBranch () ; + O_cube -> addKid ( O () ) ; +} + + +Cell::Cell ( int x, int y, int z, int size ) +{ + if ( wire_cube == NULL ) makeWireCube() ; + if ( X_cube == NULL ) makeXcube () ; + if ( O_cube == NULL ) makeOcube () ; + + sgSetCoord ( &c, (float)x - (float) size / 2.0f + 0.5f, + (float)y - (float) size / 2.0f + 0.5f, + (float)z - (float) size / 2.0f + 0.5f, 0,0,0 ) ; + + posn = new ssgTransform ( & c ) ; + cell = new ssgSelector () ; + + posn -> addKid ( cell ) ; + + /* Don't change the order of these without changing the defines */ + + cell -> addKid ( wire_cube ) ; + cell -> addKid ( X_cube ) ; + cell -> addKid ( O_cube ) ; + + what = WIRE_CELL ; + cell -> selectStep ( what ) ; +} + + diff --git a/demos/ttt3d/src/cell.h b/demos/ttt3d/src/cell.h new file mode 100644 index 0000000..f550573 --- /dev/null +++ b/demos/ttt3d/src/cell.h @@ -0,0 +1,55 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: cell.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +ssgTransform *makeBlueWireCube() ; +ssgTransform *makeGround() ; + +#define WIRE_CELL 0 +#define X_CELL 1 +#define O_CELL 2 + +class Cell +{ + sgCoord c ; + ssgTransform *posn ; + ssgSelector *cell ; + int what ; + +public: + + Cell ( int x, int y, int z, int size ) ; + + ssgBranch *getSSG () { return posn ; } + + int get () { return what ; } + + void set ( int n ) + { + what = n ; + cell -> selectStep ( n ) ; + } + +} ; + + diff --git a/demos/ttt3d/src/game.cxx b/demos/ttt3d/src/game.cxx new file mode 100644 index 0000000..f606796 --- /dev/null +++ b/demos/ttt3d/src/game.cxx @@ -0,0 +1,483 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: game.cxx 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#include +#include +#include +#include "game.h" + +static char board [4][4][4] ; +static int last_x = -1 ; +static int last_y = -1 ; +static int last_z = -1 ; + +#define NUM_ROWS ( 3 * 16 + 3 * 8 + 4 ) + +static char *row [ NUM_ROWS ][ 4 ] ; +static char rowptr [ 4 ][ 4 ][ 4 ][ 8 ] ; +static char rowptrctr [ 4 ][ 4 ][ 4 ] ; + + +static void setupRowArray () +{ + int x, y, z; + + for ( x = 0 ; x < 4 ; x++ ) + for ( y = 0 ; y < 4 ; y++ ) + for ( z = 0 ; z < 4 ; z++ ) + { + rowptrctr [ x ][ y ][ z ] = 0 ; + + for ( int i = 0 ; i < 8 ; i++ ) + rowptr [ x ][ y ][ z ][ i ] = 127 ; + } + + int r = 0 ; + + /* 16 Z axis rows */ + + for ( x = 0 ; x < 4 ; x++ ) + for ( y = 0 ; y < 4 ; y++ ) + { + for ( z = 0 ; z < 4 ; z++ ) + { + row [ r ][ z ] = & board [ x ][ y ][ z ] ; + rowptr [ x ][ y ][ z ][ rowptrctr[x][y][z]++ ] = r ; + } + r++ ; + } + + /* 16 Y axis rows */ + + for ( x = 0 ; x < 4 ; x++ ) + for ( z = 0 ; z < 4 ; z++ ) + { + for ( y = 0 ; y < 4 ; y++ ) + { + row [ r ][ y ] = & board [ x ][ y ][ z ] ; + rowptr [ x ][ y ][ z ][ rowptrctr[x][y][z]++ ] = r ; + } + + r++ ; + } + + /* 16 Y axis rows */ + + for ( z = 0 ; z < 4 ; z++ ) + for ( y = 0 ; y < 4 ; y++ ) + { + for ( x = 0 ; x < 4 ; x++ ) + { + row [ r ][ x ] = & board [ x ][ y ][ z ] ; + rowptr [ x ][ y ][ z ][ rowptrctr[x][y][z]++ ] = r ; + } + + r++ ; + } + + /* 8 xy plane diagonals */ + + for ( z = 0 ; z < 4 ; z++ ) + { + int xy; + + for ( xy = 0 ; xy < 4 ; xy++ ) + { + row [ r ][ xy ] = & board [ xy ][ xy ][ z ] ; + rowptr [ xy ][ xy ][ z ][ rowptrctr[xy][xy][z]++ ] = r ; + } + + r++ ; + + for ( xy = 0 ; xy < 4 ; xy++ ) + { + row [ r ][ xy ] = & board [ xy ][ 3 - xy ][ z ] ; + rowptr [ xy ][3-xy][ z ][ rowptrctr[xy][3-xy][z]++ ] = r ; + } + + r++ ; + } + + /* 8 xz plane diagonals */ + + for ( y = 0 ; y < 4 ; y++ ) + { + int xz; + + for ( xz = 0 ; xz < 4 ; xz++ ) + { + row [ r ][ xz ] = & board [ xz ][ y ][ xz ] ; + rowptr [ xz ][ y ][ xz ][ rowptrctr[xz][y][xz]++ ] = r ; + } + + r++ ; + + for ( xz = 0 ; xz < 4 ; xz++ ) + { + row [ r ][ xz ] = & board [ xz ][ y ][ 3 - xz ] ; + rowptr [ xz ][ y ][3-xz][ rowptrctr[xz][y][3-xz]++ ] = r ; + } + + r++ ; + } + + /* 8 yz plane diagonals */ + + for ( x = 0 ; x < 4 ; x++ ) + { + int yz; + for ( yz = 0 ; yz < 4 ; yz++ ) + { + row [ r ][ yz ] = & board [ x ][ yz ][ yz ] ; + rowptr [ x ][ yz ][ yz ][ rowptrctr[x][yz][yz]++ ] = r ; + } + + r++ ; + + for ( yz = 0 ; yz < 4 ; yz++ ) + { + row [ r ][ yz ] = & board [ x ][ yz ][ 3 - yz ] ; + rowptr [ x ][ yz ][3-yz][ rowptrctr[x][yz][3-yz]++ ] = r ; + } + + r++ ; + } + + /* 4 grand diagonals. */ + + int xyz; + for ( xyz = 0 ; xyz < 4 ; xyz++ ) + { + row [ r ][ xyz ] = & board [ xyz ][ xyz ][ xyz ] ; + rowptr [ xyz ][ xyz ][ xyz ][ rowptrctr[xyz][xyz][xyz]++ ] = r ; + } + + r++ ; + + for ( xyz = 0 ; xyz < 4 ; xyz++ ) + { + row [ r ][ xyz ] = & board [ xyz ][ xyz ][ 3-xyz ] ; + rowptr [ xyz ][ xyz ][ 3-xyz ][ rowptrctr[xyz][xyz][3-xyz]++ ] = r ; + } + + r++ ; + + for ( xyz = 0 ; xyz < 4 ; xyz++ ) + { + row [ r ][ xyz ] = & board [ xyz ][ 3-xyz ][ xyz ] ; + rowptr [ xyz ][ 3-xyz ][ xyz ][ rowptrctr[xyz][3-xyz][xyz]++ ] = r ; + } + + r++ ; + + for ( xyz = 0 ; xyz < 4 ; xyz++ ) + { + row [ r ][ xyz ] = & board [ xyz ][ 3-xyz ][ 3-xyz ] ; + rowptr [ xyz ][ 3-xyz ][ 3-xyz ][ rowptrctr[xyz][3-xyz][3-xyz]++ ] = r ; + } + + r++ ; + + if ( r != NUM_ROWS ) + { + fprintf ( stderr, "%d rows?!?\n", r ) ; + exit ( 1 ) ; + } +} + + +static void clearBoard () +{ + for ( int x = 0 ; x < 4 ; x++ ) + for ( int y = 0 ; y < 4 ; y++ ) + for ( int z = 0 ; z < 4 ; z++ ) + board [ x ][ y ][ z ] = ' ' ; +} + + +/*void printBoard ( int winrow ) +{ + int x, y, z; + + for ( int i = 0 ; i < 50 ; i++ ) + printf ( "\n" ) ; + + printf ( " ~~~ 3D TicTacToe ~~~\n" ) ; + printf ( " by Steve Baker\n"); + printf ( "\n" ) ; + printf ( "\n" ) ; + + for ( y = 0 ; y < 4 ; y++ ) + { + for ( z = 0 ; z < 4 ; z++ ) + { + for ( x = 0 ; x < 4 ; x++ ) + { + printf ( " " ) ; + + if ( x != 3 ) + printf ( "|" ) ; + } + printf ( " " ) ; + } + printf ( "\n" ) ; + + for ( z = 0 ; z < 4 ; z++ ) + { + for ( x = 0 ; x < 4 ; x++ ) + { + int q ; + + for ( q = 0 ; q < 4 ; q++ ) + if ( row[winrow][q] == & board[x][y][z] ) + break ; + + if ( ( x == last_x && y == last_y && z == last_z ) || + q < 4 ) + printf ( "*%c*", board [ x ][ y ][ z ] ) ; + else + printf ( " %c ", board [ x ][ y ][ z ] ) ; + + if ( x != 3 ) + printf ( "|" ) ; + } + printf ( " " ) ; + } + printf ( "\n" ) ; + + for ( z = 0 ; z < 4 ; z++ ) + { + for ( x = 0 ; x < 4 ; x++ ) + { + if ( board [ x ][ y ][ z ] == ' ' ) + { + if ( y != 3 ) + { + if ( x+y*4+z*16+1 < 10 ) + printf ( "_%d_", x + y * 4 + z * 16 + 1 ) ; + else + printf ( "%2d_", x + y * 4 + z * 16 + 1 ) ; + } + else + printf ( "%2d ", x + y * 4 + z * 16 + 1 ) ; + } + else + if ( y != 3 ) + printf ( "___" ) ; + else + printf ( " " ) ; + + if ( x != 3 ) + printf ( "|" ) ; + } + printf ( " " ) ; + } + printf ( "\n" ) ; + } + + printf ( "\n" ) ; + printf ( "\n" ) ; +}*/ + + + +static int value [4][4] = /* Value of 1000000 means play here to win. */ +{ + { 1, 3,10, 1000000 }, /* No enemy, 0,1,2,3 of ours */ + { 2, 0, 0, 0 }, /* One enemy, 0,1,2,3 of ours */ + { 20, 0, 0, 0 }, /* Two enemy, 0,1,2,3 of ours */ + { 1000, 0, 0, 0 }, /* Three enemy, 0,1,2,3 of ours */ +} ; + + + +static int win = STILL_PLAYING ; + + +static int countRow ( int r ) +{ + int x = 0 ; + int o = 0 ; + + for ( int i = 0 ; i < 4 ; i++ ) + { + if ( *(row[r][i]) == 'X' ) x++ ; + if ( *(row[r][i]) == 'O' ) o++ ; + } + + if ( x == 4 ) { /* printBoard (r) ; */ + printf ( "** Human wins! **\n\n" ) ; + win = HUMAN_WIN ; + return 0 ; } + + if ( o == 4 ) { /* printBoard (r) ; */ + printf ( "** Computer wins! **\n\n" ) ; + win = COMPUTER_WIN ; + return 0 ; } + + return value [ x ][ o ] ; +} + + + +/*int getLoc ( char *msg ) +{ + + while ( 1 ) + { + int res = -1 ; + char s [ 21 ] ; + + printf ( "%s\n[1..64]: ", msg ) ; + fgets ( s, 20, stdin ) ; + + char *p = s ; + + while ( *p == ' ' || *p == '\t' ) p++ ; + + if ( isdigit ( *p ) ) + { + res = atoi ( p ) - 1 ; + + if ( res >= 0 && res <= 63 && board[res%4][(res%16)/4][res/16] == ' ' ) + return res ; + + if ( res >= 0 && res <= 63 ) + printf ( "You can't play there - it's already taken!\n" ) ; + else + printf ( "Please enter a number in the range 0 to 63.\n" ) ; + } + else + printf ( "Please type a NUMBER in the range 0 to 63.\n" ) ; + } +}*/ + + + +void game_init () +{ + win = STILL_PLAYING ; + setupRowArray () ; + clearBoard () ; +} + + +char get_board_entry ( int x, int y, int z ) +{ + return board[x][y][z] ; +} + + +int game_update ( int x, int y, int z ) +{ + int q; + + if ( win != STILL_PLAYING ) + game_init () ; + + /* printBoard (-1) ; */ + + board[x][y][z] = 'X' ; + + int rc = rowptrctr[x][y][z] ; + + for ( q = 0 ; q < rc ; q++ ) + { + int r = rowptr[x][y][z][q] ; + + countRow ( r ) ; /* Just to check for a win! */ + + if ( win != STILL_PLAYING ) + return win ; + } + + int best = -1 ; + int best_sum = 0 ; + + for ( int p = 0 ; p < 64 ; p++ ) + { + x = p % 4 ; + y = (p % 16)/4 ; + z = p/16 ; + + if ( board[x][y][z] != ' ' ) + continue ; + + rc = rowptrctr[x][y][z] ; + + int sum = 0 ; + + for ( int q = 0 ; q < rc ; q++ ) + { + int r = rowptr[x][y][z][q] ; + + sum += countRow ( r ) ; /* Just to check for a win! */ + + if ( win != STILL_PLAYING ) + { + board[x][y][z] = 'O' ; + return win ; + } + } + + if ( sum >= best_sum ) + { + best = p ; + best_sum = sum ; + } + } + + if ( best == -1 ) + { + /* printBoard (-1) ; */ + printf ( "** It's a draw! **\n\n" ) ; + return DRAWN_GAME ; + } + + x = best % 4 ; + y = (best % 16)/4 ; + z = best/16 ; + + board[x][y][z] = 'O' ; + + last_x = x ; + last_y = y ; + last_z = z ; + + rc = rowptrctr[x][y][z] ; + + for ( q = 0 ; q < rc ; q++ ) + { + int r = rowptr[x][y][z][q] ; + + countRow ( r ) ; /* Just to check for a win! */ + + if ( win != STILL_PLAYING ) + return win ; + } + + return STILL_PLAYING ; +} + + diff --git a/demos/ttt3d/src/game.h b/demos/ttt3d/src/game.h new file mode 100644 index 0000000..2642145 --- /dev/null +++ b/demos/ttt3d/src/game.h @@ -0,0 +1,33 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: game.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#define STILL_PLAYING 0 +#define HUMAN_WIN 1 +#define COMPUTER_WIN 2 +#define DRAWN_GAME 3 + +void game_init () ; +char get_board_entry ( int x, int y, int z ) ; +int game_update ( int x, int y, int z ) ; + diff --git a/demos/ttt3d/src/gfx.cxx b/demos/ttt3d/src/gfx.cxx new file mode 100644 index 0000000..4443f5b --- /dev/null +++ b/demos/ttt3d/src/gfx.cxx @@ -0,0 +1,143 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: gfx.cxx 1465 2002-08-30 14:05:59Z wolfram_kuss $ +*/ + + +#include "p3d.h" + +static unsigned int lastGLUTKeystroke = 0 ; + +static void getGLUTSpecialKeystroke ( int key, int, int ) +{ + lastGLUTKeystroke = 256 + key ; +} + +static void getGLUTKeystroke ( unsigned char key, int, int ) +{ + lastGLUTKeystroke = key ; +} + +int getGLUTKeystroke () +{ + int k = lastGLUTKeystroke ; + lastGLUTKeystroke = 0 ; + return k ; +} + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + +static void initWindow ( int w, int h ) +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + + fake_argv[0] = "TTT3D" ; + fake_argv[1] = "TTT3D by Steve Baker." ; + fake_argv[2] = NULL ; + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( w, h ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( ttt3dMainLoop ) ; + glutKeyboardFunc ( getGLUTKeystroke ) ; + glutSpecialFunc ( getGLUTSpecialKeystroke ) ; + glutReshapeFunc ( reshape ) ; +#ifndef WIN32 + glutIdleFunc ( glutPostRedisplay ) ; +#endif +} + + +GFX::GFX () +{ + initWindow ( 640, 480 ) ; + + ssgInit () ; + + static int firsttime = 1 ; + + if ( firsttime ) + { + firsttime = 0 ; + initMaterials () ; + } + + ssgSetFOV ( 75.0f, 0.0f ) ; + ssgSetNearFar ( 0.01f, 10.0f ) ; + + sgCoord cam ; + sgSetVec3 ( cam.xyz, 0, 0, 0 ) ; + sgSetVec3 ( cam.hpr, 0, 0, 0 ) ; + ssgSetCamera ( & cam ) ; +} + + +void GFX::update () +{ + sgVec3 sunposn ; + sgVec4 skycol ; + sgVec4 white = { 1.0, 1.0, 1.0, 1.0 } ; + + sgSetVec3 ( sunposn, -0.2f, -0.5f, 0.2f ) ; + + sgSetVec4 ( skycol, 0.4f, 0.4f, 0.8f, 1.0f ) ; + + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + ssgGetLight ( 0 ) -> setColour ( GL_AMBIENT , white ) ; + ssgGetLight ( 0 ) -> setColour ( GL_DIFFUSE , white ) ; + ssgGetLight ( 0 ) -> setColour ( GL_SPECULAR, white ) ; + + /* Clear the screen */ + + glClearColor ( skycol[0], skycol[1], skycol[2], skycol[3] ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + glEnable ( GL_DEPTH_TEST ) ; + + glFogf ( GL_FOG_DENSITY, 3.0f / 100.0f ) ; + glFogfv( GL_FOG_COLOR , skycol ) ; + glFogf ( GL_FOG_START , 0.0 ) ; + glFogi ( GL_FOG_MODE , GL_EXP ) ; + glHint ( GL_FOG_HINT , GL_NICEST ) ; + + sgCoord cam ; + sgSetVec3 ( cam.xyz, 0, 0, 0 ) ; + sgSetVec3 ( cam.hpr, 0, 0, 0 ) ; + ssgSetCamera ( & cam ) ; + + glEnable ( GL_FOG ) ; + ssgCullAndDraw ( scene ) ; + glDisable ( GL_FOG ) ; +} + + +void GFX::done () +{ + glutPostRedisplay () ; + glutSwapBuffers () ; +} + diff --git a/demos/ttt3d/src/gfx.h b/demos/ttt3d/src/gfx.h new file mode 100644 index 0000000..78ba048 --- /dev/null +++ b/demos/ttt3d/src/gfx.h @@ -0,0 +1,33 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: gfx.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +class GFX +{ +public: + + GFX () ; + void update () ; + void done () ; +} ; + diff --git a/demos/ttt3d/src/gui.cxx b/demos/ttt3d/src/gui.cxx new file mode 100644 index 0000000..1ad32d4 --- /dev/null +++ b/demos/ttt3d/src/gui.cxx @@ -0,0 +1,246 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: gui.cxx 1465 2002-08-30 14:05:59Z wolfram_kuss $ +*/ + + +#include "p3d.h" +#include "plib/pu.h" + +static int mouse_x ; +static int mouse_y ; +static int mouse_dx = 0 ; +static int mouse_dy = 0 ; +static int mouse_buttons = 0 ; + +fntTexFont *font ; + +static void motionfn ( int x, int y ) +{ + mouse_x = x ; + mouse_y = y ; + mouse_dx += mouse_x - 320 ; + mouse_dy += mouse_y - 240 ; + puMouse ( x, y ) ; +} + +static void mousefn ( int button, int updown, int x, int y ) +{ + mouse_x = x ; + mouse_y = y ; + + if ( updown == GLUT_DOWN ) + mouse_buttons |= (1<disable_music () ; } +static void music_on_cb ( puObject * ) { sound->enable_music () ; } +static void sfx_off_cb ( puObject * ) { sound->disable_sfx () ; } +static void sfx_on_cb ( puObject * ) { sound->enable_sfx () ; } + +static void exit_cb ( puObject * ) +{ + fprintf ( stderr, "Exiting TTT3D.\n" ) ; + exit ( 1 ) ; +} + +/* Menu bar entries: */ + +static char *exit_submenu [] = { "Exit", NULL } ; +static puCallback exit_submenu_cb [] = { exit_cb, NULL } ; + +static char *sound_submenu [] = { "Turn off Music", "Turn off Sounds", "Turn on Music", "Turn on Sounds", NULL } ; +static puCallback sound_submenu_cb [] = { music_off_cb, sfx_off_cb, music_on_cb, sfx_on_cb, NULL } ; + +static char *help_submenu [] = { "Versions...", "Credits...", "About...", "Help", NULL } ; +static puCallback help_submenu_cb [] = { versions_cb, credits_cb, about_cb, help_cb, NULL } ; + + + +GUI::GUI () +{ + hidden = TRUE ; + mouse_x = 320 ; + mouse_y = 240 ; + + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + + ssgInit () ; + puInit () ; + + font = new fntTexFont ; + font -> load ( "fonts/sorority.txf" ) ; + puFont ff ( font, 15 ) ; + puSetDefaultFonts ( ff, ff ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.8f, 0.8f, 0.8f, 0.6f ) ; + + /* Make the menu bar */ + + main_menu_bar = new puMenuBar () ; + + main_menu_bar -> add_submenu ( "Exit" , exit_submenu , exit_submenu_cb ) ; + main_menu_bar -> add_submenu ( "Sound", sound_submenu, sound_submenu_cb ) ; + main_menu_bar -> add_submenu ( "Help" , help_submenu , help_submenu_cb ) ; + + main_menu_bar -> close () ; + main_menu_bar -> hide () ; +} + + +void GUI::show () +{ + hide_status () ; + hidden = FALSE ; + main_menu_bar -> reveal () ; +} + +void GUI::hide () +{ + hidden = TRUE ; + hide_status () ; + main_menu_bar -> hide () ; +} + +void GUI::update () +{ + int score = 0 ; /* INSERT SCORE-GETTER HERE */ + + keyboardInput () ; + drawStatusText ( score ) ; + + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glEnable ( GL_BLEND ) ; + + puDisplay () ; +} + + +void GUI::keyboardInput () +{ + int c = getGLUTKeystroke () ; + + if ( c <= 0 ) + return ; + + switch ( c ) + { + case 0x1B /* Escape */ : + case 0x03 /* Control-C */ : exit ( 0 ) ; + + case 'h' : hide_status () ; help () ; return ; + + case ' ' : if ( isHidden () ) + { + sound->playSfx ( SOUND_WHO_ELSE ) ; + show () ; + } + else + { + sound->playSfx ( SOUND_UGH ) ; + hide () ; + } + return ; + + case 'r' : + case 'R' : startLevel () ; return ; + + case (256+GLUT_KEY_LEFT ) : spinLeft () ; return ; + case (256+GLUT_KEY_RIGHT ) : spinRight () ; return ; + case (256+GLUT_KEY_UP ) : spinUp () ; return ; + case (256+GLUT_KEY_DOWN ) : spinDown () ; return ; + case (256+GLUT_KEY_PAGE_UP ) : zoomIn () ; return ; + case (256+GLUT_KEY_PAGE_DOWN ) : zoomOut () ; return ; + + default : /* DO NOTHING */ break ; + } + + if ( game_state == GAME_DEBRIEF ) + { + switch ( c ) + { + case 'r' : + case 'R' : startLevel () ; return ; + default : /* DO NOTHING */ break ; + } + } + + if ( game_state == GAME_RUNNING ) + { + switch ( c ) + { + case 'r' : + case 'R' : startLevel () ; return ; + + case '\n': + case '\r': makeMove () ; return ; + + case 'a' : puzzle->cursor_up () ; return ; + case 'z' : puzzle->cursor_down () ; return ; + case 's' : puzzle->cursor_in () ; return ; + case 'd' : puzzle->cursor_out () ; return ; + case 'x' : puzzle->cursor_left () ; return ; + case 'c' : puzzle->cursor_right () ; return ; + + default : /* DO NOTHING */ break ; + } + } +} + + diff --git a/demos/ttt3d/src/gui.h b/demos/ttt3d/src/gui.h new file mode 100644 index 0000000..8e40b4c --- /dev/null +++ b/demos/ttt3d/src/gui.h @@ -0,0 +1,46 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: gui.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +class GUI +{ + class puMenuBar *main_menu_bar ; + + int hidden ; + +public: + + GUI () ; + void update () ; + void show () ; + void hide () ; + int isHidden () { return hidden ; } + + void keyboardInput () ; +} ; + + +int getGLUTKeystroke () ; + +extern fntTexFont *font ; + diff --git a/demos/ttt3d/src/level.h b/demos/ttt3d/src/level.h new file mode 100644 index 0000000..f1357d5 --- /dev/null +++ b/demos/ttt3d/src/level.h @@ -0,0 +1,36 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: level.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +class Level +{ + int number ; + char *intro ; +} ; + + +#define GAME_INTRO 0 +#define GAME_RUNNING 1 +#define GAME_DEBRIEF 2 +#define GAME_START_OF_DAY 3 + diff --git a/demos/ttt3d/src/material.cxx b/demos/ttt3d/src/material.cxx new file mode 100644 index 0000000..5fbdaf9 --- /dev/null +++ b/demos/ttt3d/src/material.cxx @@ -0,0 +1,175 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: material.cxx 1465 2002-08-30 14:05:59Z wolfram_kuss $ +*/ + + +#include "p3d.h" + +ssgSimpleState *default_gst, *O_gst, *X_gst, *ground_gst, *ctrls_gst ; + +static Material gs_setup [] = +{ + /* gst texture_map clamp, trans,aref,light,frctn,0 */ + { &default_gst, "", FALSE, TRUE , 0.0f, FALSE, 1.0, 0 }, + { & ground_gst, "images/ground.rgb", FALSE, FALSE, 0.2f, FALSE, 1.0, 0 }, + { & ctrls_gst, "images/controls.rgb",FALSE, TRUE , 0.2f, FALSE, 1.0, 0 }, + { & X_gst, "images/X.rgb" , FALSE, FALSE, 0.2f, TRUE , 1.0, 0 }, + { & O_gst, "images/O.rgb" , FALSE, FALSE, 0.2f, TRUE , 1.0, 0 }, + { NULL, "", FALSE, FALSE, 0.0, FALSE, 1.0, 0 } +} ; + + +void Material::install ( int index ) +{ + *gst = new ssgSimpleState ; + + (*gst) -> ref () ; + (*gst) -> setExternalPropertyIndex ( index ) ; + + if ( texture_map [ 0 ] != '\0' ) + { + (*gst) -> setTexture ( texture_map, !clamp_tex, !clamp_tex ) ; + (*gst) -> enable ( GL_TEXTURE_2D ) ; + } + else + (*gst) -> disable ( GL_TEXTURE_2D ) ; + + if ( lighting ) + (*gst) -> enable ( GL_LIGHTING ) ; + else + (*gst) -> disable ( GL_LIGHTING ) ; + + (*gst) -> setShadeModel ( GL_SMOOTH ) ; + (*gst) -> disable ( GL_COLOR_MATERIAL ) ; + (*gst) -> enable ( GL_CULL_FACE ) ; + (*gst) -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + (*gst) -> setMaterial ( GL_AMBIENT, 0.3f, 0.3f, 0.3f, 1 ) ; + (*gst) -> setMaterial ( GL_DIFFUSE, 1, 1, 1, 1 ) ; + (*gst) -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + (*gst) -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + (*gst) -> setShininess ( 0 ) ; + + if ( transparency ) + { + (*gst) -> setTranslucent () ; + (*gst) -> enable ( GL_ALPHA_TEST ) ; + (*gst) -> setAlphaClamp ( alpha_ref ) ; + (*gst) -> enable ( GL_BLEND ) ; + } + else + { + (*gst) -> setOpaque () ; + (*gst) -> disable ( GL_BLEND ) ; + } +} + + + +static ssgState *getAppState ( char *fname ) +{ + if ( fname == NULL || fname[0] == '\0' ) + return gs_setup [ 0 ] . getState() ; + + char *fn ; + + /* Remove all leading path information. */ + + for ( fn = & fname [ strlen ( fname ) - 1 ] ; fn != fname && + *fn != '/' ; fn-- ) + /* Search back for a '/' */ ; + + if ( *fn == '/' ) + fn++ ; + + char basename [ 1024 ] ; + + strcpy ( basename, fn ) ; + + /* Remove last trailing extension. */ + + for ( fn = & basename [ strlen ( basename ) - 1 ] ; fn != basename && + *fn != '.' ; fn-- ) + /* Search back for a '.' */ ; + + if ( *fn == '.' ) + *fn = '\0' ; + + for ( int i = 0 ; ! gs_setup [ i ] . isNull () ; i++ ) + { + char *fname2 = gs_setup [ i ] . getTexFname () ; + + if ( fname2 != NULL && fname2[0] != '\0' ) + { + char *fn2 ; + + /* Remove all leading path information. */ + + for ( fn2 = & fname2 [ strlen ( fname2 ) -1 ] ; fn2 != fname2 && + *fn2 != '/' ; fn2-- ) + /* Search back for a '/' */ ; + + if ( *fn2 == '/' ) + fn2++ ; + + char basename2 [ 1024 ] ; + + strcpy ( basename2, fn2 ) ; + + /* Remove last trailing extension. */ + + for ( fn2 = & basename2 [ strlen ( basename2 ) - 1 ] ; fn2 != basename2 && + *fn2 != '.' ; fn2-- ) + /* Search back for a '.' */ ; + + if ( *fn2 == '.' ) + *fn2 = '\0' ; + + if ( strcmp ( basename, basename2 ) == 0 ) + return gs_setup [ i ] . getState() ; + } + } + + return NULL ; +} + + + +void initMaterials () +{ + for ( int i = 0 ; ! gs_setup [ i ] . isNull () ; i++ ) + gs_setup [ i ] . install ( i ) ; + + ssgSetAppStateCallback ( getAppState ) ; +} + + +/*Material *getMaterial ( ssgState *s ) +{ + return & ( gs_setup [ s -> getExternalPropertyIndex () ] ) ; +} + + +Material *getMaterial ( ssgLeaf *l ) +{ + return getMaterial ( l -> getState () ) ; +}*/ + diff --git a/demos/ttt3d/src/material.h b/demos/ttt3d/src/material.h new file mode 100644 index 0000000..456fb31 --- /dev/null +++ b/demos/ttt3d/src/material.h @@ -0,0 +1,51 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: material.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +struct Material +{ +public: + ssgSimpleState **gst ; + + char *texture_map ; + int clamp_tex ; + int transparency ; + float alpha_ref ; + int lighting ; + float friction ; + unsigned int flags ; + + int isNull () { return gst == NULL ; } ; + void install ( int index ) ; + + ssgState *getState () { return *gst ; } + char *getTexFname () { return texture_map ; } +} ; + + +void initMaterials () ; +/*Material *getMaterial ( ssgState *s ) ; +Material *getMaterial ( ssgLeaf *l ) ;*/ + +extern ssgSimpleState *default_gst, *O_gst, *X_gst, *ground_gst, *ctrls_gst ; + diff --git a/demos/ttt3d/src/p3d.h b/demos/ttt3d/src/p3d.h new file mode 100644 index 0000000..dd9bbf0 --- /dev/null +++ b/demos/ttt3d/src/p3d.h @@ -0,0 +1,95 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: p3d.h 1687 2002-09-15 17:54:09Z ude $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include + +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include + +#ifndef M_PI +# define M_PI 3.14159265 +#endif + +#include "game.h" + +class GFX ; +class GUI ; +class SoundSystem ; +class Puzzle ; +class Cell ; +class Level ; + +extern GFX *gfx ; +extern GUI *gui ; +extern SoundSystem *sound ; +extern Puzzle *puzzle ; +extern Level level ; + +extern int game_state ; + +extern ssgRoot *scene ; +//extern const char *ttt3d_datadir ; + +void startLevel () ; +void makeMove () ; +void ttt3dMainLoop () ; +void initMaterials () ; + +#include "level.h" +#include "gfx.h" +#include "gui.h" +#include "material.h" +#include "status.h" +#include "sound.h" +#include "cell.h" +#include "puzzle.h" + +void spinUp () ; +void spinDown () ; +void spinLeft () ; +void spinRight () ; +void zoomIn () ; +void zoomOut () ; + diff --git a/demos/ttt3d/src/puzzle.cxx b/demos/ttt3d/src/puzzle.cxx new file mode 100644 index 0000000..0ea1b41 --- /dev/null +++ b/demos/ttt3d/src/puzzle.cxx @@ -0,0 +1,102 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puzzle.cxx 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#include "p3d.h" + +static int size[3] ; + + +Puzzle::Puzzle () +{ + size[0] = 4 ; + size[1] = 4 ; + size[2] = 4 ; + + sgSetCoord ( &pos, 0, 0, 0, 0, 0, 0 ) ; + + ground = makeGround () ; + selector = makeBlueWireCube () ; + + spin = new ssgTransform ( &pos ) ; + spin -> addKid ( ground ) ; + spin -> addKid ( selector ) ; + + sel [ 0 ] = 0 ; + sel [ 1 ] = 0 ; + sel [ 2 ] = 0 ; + + for ( int x = 0 ; x < size [ 0 ] ; x++ ) + for ( int y = 0 ; y < size [ 1 ] ; y++ ) + for ( int z = 0 ; z < size [ 2 ] ; z++ ) + { + cell[x][y][z] = new Cell ( x, y, z, size[0] ) ; + spin -> addKid ( cell[x][y][z]->getSSG() ) ; + } + + reset () ; +} + + +void Puzzle::reset () +{ + cursor ( 0, 0, 0 ) ; + sgSetCoord ( &pos, 0, 0, 0, 0, 0, 0 ) ; + + for ( int x = 0 ; x < size [ 0 ] ; x++ ) + for ( int y = 0 ; y < size [ 1 ] ; y++ ) + for ( int z = 0 ; z < size [ 2 ] ; z++ ) + cell[x][y][z]->set(WIRE_CELL) ; + + setGameState ( STILL_PLAYING ) ; + game_init () ; +} + + +void Puzzle::update () +{ + static int flasher = 0 ; + sgCoord spos ; + sgSetVec3 ( spos.hpr, 0, 0, 0 ) ; + + flasher++ ; + if ( flasher > 5 ) + sgSetVec3 ( spos.xyz, (float)sel[0] - (float)size[0]/2.0f + 0.51f, + (float)sel[1] - (float)size[1]/2.0f + 0.51f, + (float)sel[2] - (float)size[2]/2.0f + 0.51f ) ; + else + sgSetVec3 ( spos.xyz, 1000000, 1000000, 1000000 ) ; + + if ( flasher > 60 ) + flasher = 0 ; + + selector -> setTransform ( & spos ) ; +} + + +Puzzle::~Puzzle () +{ + delete spin ; +} + + diff --git a/demos/ttt3d/src/puzzle.h b/demos/ttt3d/src/puzzle.h new file mode 100644 index 0000000..606e1e5 --- /dev/null +++ b/demos/ttt3d/src/puzzle.h @@ -0,0 +1,90 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puzzle.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#define MAX_GRID 4 + + +class Puzzle +{ + sgCoord pos ; + + int sel [ 3 ] ; + + ssgTransform *spin ; + ssgTransform *selector ; + ssgTransform *ground ; + + Cell *cell [ MAX_GRID ][ MAX_GRID ][ MAX_GRID ] ; + + int game_state ; + +public: + Puzzle () ; + ~Puzzle () ; + + void reset () ; + void update () ; + + int getGameState () { return game_state ; } + void setGameState ( int gs ) { game_state = gs ; } + + int gameOver () { return game_state != STILL_PLAYING ; } + + void cursor ( int x, int y, int z ) + { + sel[0] = x & 3 ; + sel[1] = y & 3 ; + sel[2] = z & 3 ; + } + + int getCx () { return sel[0] ; } + int getCy () { return sel[1] ; } + int getCz () { return sel[2] ; } + + void step_up () { pos . xyz [ 1 ] += 0.5f ; spin->setTransform(&pos); } + void step_down () { pos . xyz [ 1 ] -= 0.5f ; spin->setTransform(&pos); } + void spin_up () { pos . hpr [ 2 ] += 5.0f ; spin->setTransform(&pos); } + void spin_down () { pos . hpr [ 2 ] -= 5.0f ; spin->setTransform(&pos); } + void spin_left () { pos . hpr [ 0 ] -= 5.0f ; spin->setTransform(&pos); } + void spin_right () { pos . hpr [ 0 ] += 5.0f ; spin->setTransform(&pos); } + + void cursor_up () { cursor(sel[0],sel[1],sel[2]+1) ; } + void cursor_down () { cursor(sel[0],sel[1],sel[2]-1) ; } + void cursor_in () { cursor(sel[0],sel[1]+1,sel[2]) ; } + void cursor_out () { cursor(sel[0],sel[1]-1,sel[2]) ; } + void cursor_left () { cursor(sel[0]-1,sel[1],sel[2]) ; } + void cursor_right () { cursor(sel[0]+1,sel[1],sel[2]) ; } + + void put_empty() { cell[sel[0]][sel[1]][sel[2]]->set(WIRE_CELL) ; } + void put_X () { cell[sel[0]][sel[1]][sel[2]]->set( X_CELL) ; } + void put_O () { cell[sel[0]][sel[1]][sel[2]]->set( O_CELL) ; } + + void put_empty ( int x, int y, int z ) { cell[x][y][z] -> set (WIRE_CELL);} + void put_X ( int x, int y, int z ) { cell[x][y][z] -> set ( X_CELL) ; } + void put_O ( int x, int y, int z ) { cell[x][y][z] -> set ( O_CELL) ; } + + ssgTransform *getSSG () { return spin ; } +} ; + + diff --git a/demos/ttt3d/src/sound.cxx b/demos/ttt3d/src/sound.cxx new file mode 100644 index 0000000..a8ad302 --- /dev/null +++ b/demos/ttt3d/src/sound.cxx @@ -0,0 +1,122 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sound.cxx 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#include "p3d.h" + +struct Sound +{ + const char *fname ; + slSample *s ; +} ; + + +static Sound sfx [] = +{ + { "wavs/ahooga.wav" , NULL }, + { "wavs/clap.wav" , NULL }, + { "wavs/frog.wav" , NULL }, + { "wavs/glasbk.wav" , NULL }, + { "wavs/pop.wav" , NULL }, + { "wavs/ugh.wav" , NULL }, + { "wavs/who_else.wav" , NULL }, + { NULL, NULL } +} ; + +static int music_off = FALSE ; +static int sfx_off = FALSE ; + +void SoundSystem::disable_music () +{ + sched -> stopMusic () ; + sched -> update () ; /* Ugh! Nasty Kludge! */ + sched -> update () ; /* Ugh! Nasty Kludge! */ + + music_off = TRUE ; +} + + +void SoundSystem::change_track ( const char *fname ) +{ + if ( fname == NULL ) + fname = "" ; + + if ( strcmp ( fname, current_track ) != 0 ) + { + strcpy ( current_track, fname ) ; + + if ( ! music_off ) + enable_music () ; + } +} + +void SoundSystem::enable_music () +{ + sched -> stopMusic () ; + + if ( current_track [ 0 ] != '\0' ) + sched -> loopMusic ( current_track ) ; + + music_off = FALSE ; +} + + +void SoundSystem::disable_sfx () { sfx_off = TRUE ; } +void SoundSystem:: enable_sfx () { sfx_off = FALSE ; } + + + +void SoundSystem::playSfx ( int sfx_num ) +{ + if ( ! sfx_off ) + sched -> playSample ( sfx[sfx_num].s, 1, SL_SAMPLE_MUTE, 2, NULL ) ; +} + + +SoundSystem::SoundSystem () +{ + sched = new slScheduler ; + + setSafetyMargin () ; + + for ( Sound *currsfx = &(sfx[0]) ; currsfx -> fname != NULL ; currsfx++ ) + currsfx -> s = new slSample ( currsfx -> fname, sched ) ; + + enable_sfx () ; + change_track ( "" ) ; + enable_music () ; +} + + +void SoundSystem::update () +{ +/* + Comment this next line out if the + sound causes big glitches on your + IRIX machine! +*/ + + sched -> update () ; +} + + diff --git a/demos/ttt3d/src/sound.h b/demos/ttt3d/src/sound.h new file mode 100644 index 0000000..6bfb1e1 --- /dev/null +++ b/demos/ttt3d/src/sound.h @@ -0,0 +1,56 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sound.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +#define SOUND_AHOOGA 0 +#define SOUND_CLAP 1 +#define SOUND_FROG 2 +#define SOUND_GLASBK 3 +#define SOUND_POP 4 +#define SOUND_UGH 5 +#define SOUND_WHO_ELSE 6 + +class SoundSystem +{ + char current_track [ 256 ] ; + slScheduler *sched ; + +public: + SoundSystem () ; + + void update () ; + void playSfx ( int sound ) ; + + void setSafetyMargin ( float t = 0.25 ) + { + sched -> setSafetyMargin ( t ) ; + } + + void change_track ( const char *fname ) ; + void disable_music () ; + void enable_music () ; + + void disable_sfx () ; + void enable_sfx () ; +} ; + diff --git a/demos/ttt3d/src/status.cxx b/demos/ttt3d/src/status.cxx new file mode 100644 index 0000000..800e077 --- /dev/null +++ b/demos/ttt3d/src/status.cxx @@ -0,0 +1,358 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: status.cxx 2100 2006-11-03 22:31:54Z fayjf $ +*/ + + +#include "p3d.h" +#include + +#define MAX_STRING 30 +#define MAX_STRING_LENGTH 256 + +static int displayed_score = 0 ; + +static fntRenderer *text = NULL ; + +char debug_strings [ MAX_STRING ][ MAX_STRING_LENGTH ] ; +/*int next_string = 0 ; +int stats_enabled = FALSE ;*/ + +static int about_timer = 99999 ; +static int versions_timer = 99999 ; +static int credits_timer = 99999 ; +static int intro_timer = 0 ; +static int help_timer = 99999 ; + +void hide_status () { versions_timer = credits_timer = + intro_timer = help_timer = about_timer = 99999 ; } +void about () { about_timer = 0 ; } +void credits () { credits_timer = 0 ; } +void versions () { versions_timer = 0 ; } +void help () { help_timer = 0 ; } + +static void drawText ( const char *str, int sz, int x, int y ) +{ + text -> setFont ( font ) ; + text -> setPointSize ( (float)sz ) ; + + text -> begin () ; + text -> start2f ( (float)x, (float)y ) ; + text -> puts ( str ) ; + text -> end () ; +} + + +static void drawInverseDropShadowText ( const char *str, int sz, int x, int y ) +{ + glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + drawText ( str, sz, x, y ) ; + glColor4f ( 0.0f, 0.0f, 0.0f, 1.0f ) ; + drawText ( str, sz, x+1, y+1 ) ; +} + + +static void drawDropShadowText ( const char *str, int sz, int x, int y ) +{ + glColor4f ( 0.0f, 0.0f, 0.0f, 1.0f ) ; + + drawText ( str, sz, x, y ) ; + + glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + + drawText ( str, sz, x+1, y+1 ) ; +} + + +static void drawHelpText () +{ + drawDropShadowText ( "Press SPACE to toggle the menu.", 18, 70, 400 ) ; + drawDropShadowText ( "Press ESCAPE to exit the game.", 18, 70, 370 ) ; + drawDropShadowText ( "Use arrow keys and PgUp,PgDown,Home,End,Ins", 18, 70, 340 ) ; + drawDropShadowText ( " or Del to move the grid around.", 18, 70, 310 ) ; + drawDropShadowText ( "Move the current pipe with A,S,Z,W,E,X", 18, 70, 280 ) ; +} + + +static void drawTitleText () +{ + drawDropShadowText ( "TTT3D", 20, 70, 400 ) ; + drawDropShadowText ( "By Steve Baker", 12, 170, 385 ) ; +} + + +static void drawIntroText () +{ + drawTitleText () ; + + if ( intro_timer & 8 ) + drawDropShadowText ( "Press SPACE bar for menu.", + 15, 10, 430 ) ; +} + + +static const char *aboutText [] = +{ + " Yada, yada, yada.", + NULL +} ; + + +static void drawVersionsText () +{ + char str [ 256 ] ; + +#ifdef VERSION + sprintf ( str, "TTT3D: Version: %s", VERSION ) ; +#else + sprintf ( str, "TTT3D: Unknown Version." ) ; +#endif + drawDropShadowText ( str, 15, 20, 250 ) ; + + sprintf ( str, "PLIB Version: %s", ssgGetVersion() ) ; + drawDropShadowText ( str, 15, 20, 225 ) ; + + sprintf ( str, "OpenGL Version: %s", glGetString ( GL_VERSION ) ) ; + drawDropShadowText ( str, 15, 20, 200 ) ; + + sprintf ( str, "OpenGL Vendor: %s", glGetString ( GL_VENDOR ) ) ; + drawDropShadowText ( str, 15, 20, 175 ) ; + + sprintf ( str, "OpenGL Renderer: %s", glGetString ( GL_RENDERER ) ) ; + + if ( strlen ( str ) > 50 ) + { + int l = strlen ( str ) ; + int ll = 0 ; + + for ( int i = 0 ; i < l ; i++, ll++ ) + { + if ( ll > 40 && str[i] == ' ' ) + { + str[i] = '\n' ; + ll = 0 ; + } + } + } + + drawDropShadowText ( str, 15, 20, 150 ) ; + + if ( versions_timer & 8 ) + drawDropShadowText ( "Press SPACE to continue", + 15, 10, 430 ) ; +} + + +static void drawAboutText () +{ + drawTitleText () ; + + for ( int i = 0 ; aboutText [ i ] != NULL ; i++ ) + drawDropShadowText ( aboutText [ i ], + 10, 40, 230 - 10 * i ) ; + + if ( about_timer & 8 ) + drawDropShadowText ( "Press SPACE to continue", + 15, 10, 430 ) ; +} + + +static const char *creditsText [] = +{ + " Steve Baker - Coding, design, bug insertion.", + NULL +} ; + + + +static void drawCreditsText () +{ + drawTitleText () ; + + drawDropShadowText ( "Credits:", + 20, 70, 250 ) ; + + for ( int i = 0 ; creditsText [ i ] != NULL ; i++ ) + drawDropShadowText ( creditsText [ i ], + 12, 100, 230 - 12 * i ) ; + + if ( credits_timer & 8 ) + drawDropShadowText ( "Press SPACE to continue", + 15, 10, 430 ) ; +} + + + +static void drawScore () +{ + if ( displayed_score > 0 ) + { + char str [ 20 ] ; + sprintf ( str, "%9d", displayed_score ) ; + drawDropShadowText ( str, 18, 500, 410 ) ; + } +} + + + +static void drawInGameScore () +{ + char str [ 50 ] ; + + sprintf ( str, "TicTacToe 3D" ) ; + drawInverseDropShadowText ( str, 18, 360, 460 ) ; + + sprintf ( str, "A,Z : Move UP or DOWN" ) ; + drawDropShadowText ( str, 18, 360, 420 ) ; + sprintf ( str, "S,D,X,C : Move Sideways" ) ; + drawDropShadowText ( str, 18, 360, 400 ) ; + sprintf ( str, "Enter : Play at cursor" ) ; + drawDropShadowText ( str, 18, 360, 380 ) ; + sprintf ( str, "Arrow Keys : Spin Board" ) ; + drawDropShadowText ( str, 18, 360, 360 ) ; + + sprintf ( str, "Four in a row to win" ) ; + drawInverseDropShadowText ( str, 18, 60, 420 ) ; +} + + + +static void drawGameOverText ( int score ) +{ + static int timer = 0 ; + + displayed_score = score ; + + drawTitleText () ; + drawScore () ; + + glColor4f ( (float)sin ( (float)timer/5.1f ) / 2.0f + 0.5f, + (float)sin ( (float)timer/6.3f ) / 2.0f + 0.5f, + (float)sin ( (float)timer/7.2f ) / 2.0f + 0.5f, 0.5 ) ; + + + switch ( puzzle -> getGameState () ) + { + case STILL_PLAYING : break ; + + case HUMAN_WIN : drawText ( "YOU" , 50, 220, 230 ) ; + drawText ( "WON!", 50, 150, 180 ) ; + break ; + + case COMPUTER_WIN : drawText ( "YOU" , 50, 220, 230 ) ; + drawText ( "LOSE!", 50, 150, 180 ) ; + break ; + + case DRAWN_GAME : drawText ( "ITS A", 50, 220, 230 ) ; + drawText ( "DRAW!", 50, 150, 180 ) ; + break ; + } + + if ( timer++ & 8 ) + { + drawDropShadowText ( "Press R to reset and play again", 15, 10, 30 ) ; + } +} + + +static void drawGameIntroText () +{ + static int timer = 0 ; + + if ( timer++ & 8 ) + drawDropShadowText ( "Press S to start", 15, 10, 430 ) ; + + if ( help_timer++ < 400 ) + drawHelpText () ; + else + if ( intro_timer++ < 400 ) + drawIntroText () ; + else + if ( credits_timer++ < 1600 ) + drawCreditsText () ; + else + if ( about_timer++ < 1600 ) + drawAboutText () ; + else + if ( versions_timer++ < 1600 ) + drawVersionsText () ; +} + + +static void drawGameRunningText ( int score ) +{ + displayed_score = score ; + drawInGameScore () ; + + glColor4f ( 0.6f, 0.0f, 0.6f, 1.0f ) ; + + if ( help_timer++ < 400 ) + drawHelpText () ; + else + if ( credits_timer++ < 1600 ) + drawCreditsText () ; + else + if ( about_timer++ < 1600 ) + drawAboutText () ; + else + if ( versions_timer++ < 1600 ) + drawVersionsText () ; +} + + + +void drawStatusText ( int score ) +{ + if ( text == NULL ) + text = new fntRenderer () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; + + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + + glPushAttrib ( GL_ENABLE_BIT | GL_LIGHTING_BIT ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_CULL_FACE ) ; + glDisable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + + glOrtho ( 0, 640, 0, 480, 0, 100 ) ; + + switch ( game_state ) + { + case GAME_INTRO : drawGameIntroText () ; break ; + case GAME_DEBRIEF : drawGameOverText ( score ) ; break ; + case GAME_RUNNING : drawGameRunningText ( score ) ; break ; + } + + glPopAttrib () ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; +} + + diff --git a/demos/ttt3d/src/status.h b/demos/ttt3d/src/status.h new file mode 100644 index 0000000..932bbce --- /dev/null +++ b/demos/ttt3d/src/status.h @@ -0,0 +1,40 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: status.h 1427 2002-08-18 00:09:46Z ude $ +*/ + + +void drawStatusText ( int score ) ; + +/*void stPrintf ( char *fmt, ... ) ; +void stToggle () ;*/ + +//void memorial () ; +void about () ; +void credits () ; +void versions () ; +void help () ; +void hide_status () ; + +/*double read_time_of_day () ; +void start_stopwatch () ; + +extern int time_out ;*/ diff --git a/demos/ttt3d/src/ttt3d.cxx b/demos/ttt3d/src/ttt3d.cxx new file mode 100644 index 0000000..0f4539f --- /dev/null +++ b/demos/ttt3d/src/ttt3d.cxx @@ -0,0 +1,286 @@ +/* + This file is part of TTT3D - Steve's 3D TicTacToe Player. + Copyright (C) 2001 Steve Baker + + TTT3D is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + TTT3D is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TTT3D; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ttt3d.cxx 1480 2002-08-31 08:05:49Z wolfram_kuss $ +*/ + + +#include "p3d.h" +#include + +#if defined(WIN32) && !defined(__CYGWIN__) +# include +# include + +# define access _access +# define chdir _chdir +# define F_OK 04 +#endif + +static const char *ttt3d_datadir = NULL ; +GFX *gfx = NULL ; +GUI *gui = NULL ; +SoundSystem *sound = NULL ; +ssgRoot *scene = NULL ; +static ssgTransform *spin = NULL ; +Puzzle *puzzle = NULL ; +static float spin_rot = 0.0f ; +static float spin_alt = 0.0f ; +static float spin_pit = 0.0f ; +static float zoom = 60.0f ; +int game_state = GAME_START_OF_DAY ; + +Level level ; + +static void banner () +{ + printf ( "\n\n" ) ; + printf ( " TTT3D.\n" ) ; + printf ( "\n" ) ; + printf ( " / / \n" ) ; + printf ( " ____/____/____\n" ) ; + printf ( " / / \n" ) ; + printf ( " ____/____/____ \n" ) ; + printf ( " / / \n" ) ; + printf ( " / / \n" ) ; + printf ( "\n" ) ; + printf ( " by Steve Baker\n" ) ; + printf ( " \n" ) ; + printf ( "\n\n" ) ; +} + +static void cmdline_help () +{ + banner () ; + + printf ( "Usage:\n\n" ) ; + printf ( " ttt3d [OPTIONS]...\n\n" ) ; + printf ( "Options:\n" ) ; + printf ( " -h, --help Display this help message.\n" ) ; + printf ( " -D, --datadir DIR Load the game data from DIR defaults\n" ) ; + printf ( " to /usr/local/share/games/ttt3d\n" ) ; + printf ( "\n" ) ; +} + + +int main ( int argc, char **argv ) +{ + for ( int i = 1 ; i < argc ; i++ ) + { + if ( argv[i][0] == '-' ) + switch ( argv[i][1] ) + { + case '-' : + { + if ( strcmp ( & argv[i][2], "help" ) == 0 ) + { + cmdline_help () ; + exit ( 0 ) ; + } + } + break ; + + case 'h' : case 'H' : cmdline_help () ; exit ( 0 ) ; + default : break ; + } + } + + /* Set tux_aqfh_datadir to the correct directory */ + + if ( ttt3d_datadir == NULL ) + { + if ( getenv ( "TTT3D_DATADIR" ) != NULL ) + ttt3d_datadir = getenv ( "TTT3D_DATADIR" ) ; + else + if ( access ( "data/levels.dat", F_OK ) == 0 ) + ttt3d_datadir = "." ; + else + if ( access ( "../data/levels.dat", F_OK ) == 0 ) + ttt3d_datadir = ".." ; + else +#ifdef TTT3D_DATADIR + ttt3d_datadir = TTT3D_DATADIR ; +#else + ttt3d_datadir = "/usr/local/share/games/ttt3d" ; +#endif + } + + fprintf ( stderr, "Data files will be fetched from: '%s'\n", + ttt3d_datadir ) ; + + if ( chdir ( ttt3d_datadir ) == -1 ) + { + fprintf ( stderr, "Couldn't chdir() to '%s'.\n", ttt3d_datadir ) ; + exit ( 1 ) ; + } + + banner () ; + + gfx = new GFX ; + + sound = new SoundSystem ; + sound -> change_track ( "mods/mfarmer.mod" ) ; + + gui = new GUI ; + + scene = new ssgRoot ; + spin = new ssgTransform ; + scene -> addKid ( spin ) ; + + puzzle = new Puzzle () ; + + spin -> addKid ( puzzle -> getSSG () ) ; + spinLeft () ; + + signal ( 11, SIG_DFL ) ; + glutMainLoop () ; + return 0 ; +} + + + +static void spinPuzzle () +{ + sgMat4 rot, pit, final ; + + sgMakeRotMat4 ( rot, spin_rot, 0, 0 ) ; + sgMakeRotMat4 ( pit, 0, spin_pit, 0 ) ; + sgMultMat4 ( final, pit, rot ) ; + sgSetVec3 ( final[3], 0, 8, 0 /* spin_alt */ ) ; + + spin -> setTransform ( final ) ; +} + +void spinUp () +{ + spin_alt -= 0.4f ; + spin_pit += 3.5f ; + spinPuzzle () ; +} + +void spinDown () +{ + spin_alt += 0.4f ; + spin_pit -= 3.5f ; + spinPuzzle () ; +} + +void spinRight () +{ + spin_rot += 4.4f ; + spinPuzzle () ; +} + +void spinLeft () +{ + spin_rot -= 4.4f ; + spinPuzzle () ; +} + +void zoomIn () +{ + zoom *= 1.1f ; + + if ( zoom > 160 ) zoom = 160 ; + + ssgSetFOV ( zoom, zoom * 3.0f/4.0f ) ; +} + +void zoomOut () +{ + if ( zoom < 6 ) zoom = 6 ; + + zoom /= 1.1f ; + + ssgSetFOV ( zoom, zoom * 3.0f/4.0f ) ; +} + + +void startLevel () +{ + game_state = GAME_RUNNING ; + puzzle -> reset () ; + puzzle -> update () ; + sound -> playSfx ( SOUND_CLAP ) ; +} + + +void makeMove () +{ + if ( get_board_entry ( puzzle->getCx(), + puzzle->getCy(), + puzzle->getCz() ) != ' ' ) + sound->playSfx ( SOUND_FROG ) ; + else + { + int res = game_update ( puzzle->getCx(), + puzzle->getCy(), + puzzle->getCz() ) ; + + for ( int x = 0 ; x < 4 ; x++ ) + for ( int y = 0 ; y < 4 ; y++ ) + for ( int z = 0 ; z < 4 ; z++ ) + switch ( get_board_entry ( x, y, z ) ) + { + case ' ' : puzzle->put_empty ( x, y, z ) ; break ; + case 'X' : puzzle->put_X ( x, y, z ) ; break ; + case 'O' : puzzle->put_O ( x, y, z ) ; break ; + default : puzzle->put_empty ( x, y, z ) ; break ; + } + + puzzle->setGameState ( res ) ; + + sound->playSfx ( SOUND_POP ) ; + + if ( res == HUMAN_WIN ) + sound->playSfx ( SOUND_CLAP ) ; + else + if ( res == COMPUTER_WIN ) + sound->playSfx ( SOUND_GLASBK ) ; + else + if ( res == DRAWN_GAME ) + sound->playSfx ( SOUND_AHOOGA ) ; + } +} + + +void ttt3dMainLoop () +{ + switch ( game_state ) + { + case GAME_START_OF_DAY : startLevel () ; break ; + case GAME_INTRO : startLevel () ; break ; + case GAME_DEBRIEF : break ; + + case GAME_RUNNING : + if ( puzzle->gameOver () ) + game_state = GAME_DEBRIEF ; + break ; + } + + glLineWidth ( 2 ) ; + puzzle -> update () ; + gfx -> update () ; + gui -> update () ; + sound -> update () ; + gfx -> done () ; /* Swap buffers! */ +} + + diff --git a/demos/ttt3d/wavs/Makefile.am b/demos/ttt3d/wavs/Makefile.am new file mode 100644 index 0000000..10c6e8e --- /dev/null +++ b/demos/ttt3d/wavs/Makefile.am @@ -0,0 +1,7 @@ +# wavs/ + +pkgdatadir = $(datadir)/games/@PACKAGE@/wavs + +pkgdata_DATA = $(wildcard *.wav) + +EXTRA_DIST = $(pkgdata_DATA) \ No newline at end of file diff --git a/demos/ttt3d/wavs/ahooga.wav b/demos/ttt3d/wavs/ahooga.wav new file mode 100644 index 0000000000000000000000000000000000000000..fb9fe260f4150915570c291f858ab3be9ff5bb16 GIT binary patch literal 9588 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 MTtyAa0Gsvy07^8EFaQ7m literal 0 HcmV?d00001 diff --git a/demos/ttt3d/wavs/clap.wav b/demos/ttt3d/wavs/clap.wav new file mode 100644 index 0000000000000000000000000000000000000000..56076032135979eb9206106acfe0ce15e8ee9fab GIT binary patch literal 30764 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/wavs/glasbk.wav b/demos/ttt3d/wavs/glasbk.wav new file mode 100644 index 0000000000000000000000000000000000000000..857040fcf1598703fd4274f97a7253dc0a3d6ba1 GIT binary patch literal 26975 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/wavs/pop.wav b/demos/ttt3d/wavs/pop.wav new file mode 100644 index 0000000000000000000000000000000000000000..524b8e225ca49e04d5938f41c7fc3ed134423d6d GIT binary patch literal 4486 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 literal 0 HcmV?d00001 diff --git a/demos/ttt3d/wavs/who_else.wav b/demos/ttt3d/wavs/who_else.wav new file mode 100644 index 0000000000000000000000000000000000000000..dc2706a6cc1212ce58738f7bc1ba9832013ec83b GIT binary patch literal 4764 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 literal 0 HcmV?d00001 diff --git a/doc/FNT.png b/doc/FNT.png new file mode 100644 index 0000000000000000000000000000000000000000..ab49b9eee28f47e9aba50305cba33878d284a933 GIT binary patch literal 1580 zcmV+{2GjY8P)000>X0{{R4Infic00004XF*Lt006O% z3;baP0003pP)t-s3K|MBnlctf7TU&ADuOB+RvMnBLYksNB61=MA_^X29zME0Vu~_e zhB7{~K8Bh)B5oosk}eJ|4kmgg9%>#IQWh?bE)qf#HmWvqnl{3|N=kYzKF&T$f-V|b z8XjgI7D^T>hAI*^5;k%sHlj8XJ`z60J|cD^9$p?QiYg|4CKgT>E|xA5Iua&wCL&@Y zUXn5qljtmsp}a&nJ1_Dp)m^MjK%H z3&Y38*nR*ns)A%mmSu5Vd6`NXKEW|jtNc(Rs0pNGRV&KN5)-aSqTI4GZQ;ani9op4 zR`wNK2xK+ zTK3%B+rE5wAZ^bX?(QzPFE1}sHm&YO8Wd>}kQ7AVOe6fd&{Y0dH$iKyyxISR7OWp{(*RLbdb*8IZD)j1)=O8E)WCK@~)(Anqj1ks>P<`Za$X_C>;3pCR zkTs{>fVI`QPCvPWhh+8JbGTVK6ml+Q8g$tlD1pNObL_Ii%^$cwe!5B7hc78nOx9Cc zph!Sra+GfPivSoXjet=-(M?m<_B9Dg4piORef?SSEV6`BG(JuO(fTK3vh$mLsCJ{nX%CnzNqGG#x$0g)LsEMs-su74+X zy$8GX&S22IXf~T0__=5fHku@Z!N$SK^~T2a_4QzIad9z#a&mC+@$=`OKMn>b2PcDO zErVo5jsy@#J=~c(h;xxxFcG7SL}<8hA4 z6u6b0eu`FO)3&GJi|#EQK+}=HCGh3)fje%7FP>9erjAS3H*ir=zB*lt#--fH;IycP zJRyE72$T$0(jEM2_@|WLkb5|Lwfc!pBzM{njEWX?=|Qk8o5dx^1Q9t9C9_kg3Q~qHf9fIkN)Y+X0xrW zEv!(hYBs}gaO^99VsDo9Q@p>u_VMxY{&M>! literal 0 HcmV?d00001 diff --git a/doc/JS.png b/doc/JS.png new file mode 100644 index 0000000000000000000000000000000000000000..309ae5c013966d7319256beeb336f72c15a1bbe1 GIT binary patch literal 1580 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 bG=YBusy+zsM=6`f00000NkvXXu0mjfQb#7K literal 0 HcmV?d00001 diff --git a/doc/NET.png b/doc/NET.png new file mode 100644 index 0000000000000000000000000000000000000000..6eea299922a0579610dc23ed8841f255ecdc2635 GIT binary patch literal 1563 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 A;s5{u literal 0 HcmV?d00001 diff --git a/doc/PW.png b/doc/PW.png new file mode 100644 index 0000000000000000000000000000000000000000..1fa1994e7658f71d5686e4a966f11f5e2ea1e75a GIT binary patch literal 1939 zcmV;E2WP)000>X0ssI3fo*ix00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3labT3lag+-G2N4000McNliru)B_X-3nQ4mnu-7b2OvpA zK~#9!g;-69C07;x>dm(}HaD>rkNAA{v_tn01`IuK9d{C>2SkKO2xEw0$j*RUap@ws zR7my-%z(=Q#p#VZ6_E%%ppZoxTKj=-@fNkX3}^8XT-;lAZ`FHks-e5O>fE1m&Ue1= z+-hF^<*OD901O}yffm>T1_1Q6D+mIFKR!Bbn&!ci=MZ2Z*&IDQ;(`$JC4l)iN&42~ zpJREE-t|{=`m0l*f8js?1ULY&yXr`$m7e|NK7a?G=7pQ%caPBm5g=etC$>~4F4!C_ z5AvM#jsOtN@~vf91iNF&pTUlS>IBPckA?;K+6h6O;#GuKQF1){>HPo$H zi#*oFK#~Y(jvgJ6lnXfyf`K9oa)IEm*R;3+gw3rv{qQtzN##Y3R0IML`b2pNDh#)7 zef`$K@d0MQm%srq>*Jc9pt_#{GvEOD67fHg1mi`_Q(7OPbgzD7K=LK+DE-Vtl>zKc z*#{FP4KU0DA*3O*90Ds2AQX4vRg@RdY-67%2SKQlTomQvoQh(gBmg!yN!TK1+cN&x zvNam~+url&{?uHj1>sKt%zdF}q^u{@G0z8<17`Eodevuhc;$6O$o zDbWCdq9Brj(pv~J4cny=LLvdCKYr!KEsargRk>a#X}hnK>JnqcvIHV*Zd|ZA{qVFD z?wI(X{^2mVFRnrWR_&(itAA2970Yt-x6Pd&-6@Z7gDC*P=H~8sJ&ug6H8%%GbWr>g zK={_1<@cAIv%S|AJFiMGjQ{P~UJ}W=)RKmj@MuI?08$As)kgP4XLBoi>8-U@O8AhW zJTD-Kpq?hC1T<%h3WYb4S3Nsi;$hrtuk1wi=5X*pK)^8`q?%q|Be3M+BXGbZ+m zNF;;>hE9ZFuH`upA&R8UEp;nmW@mavzT{~l!Q6i3F3xe2CBnpl!SF2 zDN~OSNlK}a_tjakj7PU%2#H>kc7%-Gys6}KR0@D7ApaiLnAF*5pfsUSUwgZ1o0m^t z&b&|;-(KnGzD{7mc{g4C49)SQW4mkf)x038#-YX}UV{QVJQ=3;-9-nXeXT7tSqo|n z=Dsev^A1A0Y7+wTM&rIH45KvdNryS(7qMABT%rRE;cf_rVbmGbmsf%0_#XQtXaJrKkUz~jN@Ec!ing#&A1pq($%O!x1zk34U=l}S3GXCxTZ(na-{CfLelG+O1_i4DH z4$)IDrg^4V5)+1isbESJokg9zFn)eD2uTM~M$W~Qjdk>#w=`?S?Zv_IVR-Q5d7A(H zoz?l*EelHElK`-puutCUYdvL+$;a zj&$&=TjGO*i2vakHzhTf3RX9+_6$*5Vr zwM^ezhJKy%9l6k$F#GmK6vI~-AGWrJPpPO6L0m^OgY{?%J3!F12n*AMDZS<6cr4B< zrb8h$ibt$^6OD>K(M|4gAW=iRH5IG=y6XCl&wJ7)hqY{B`E4T4xUZGjt43o_GDM80Y46yv)G000>X0{{R4Infic00004XF*Lt006O% z3;baP0003#P)t-s3K|M7mM#`X7TU&ACVD2Gra~H48k(j;9%ddsx;_db3Sx>fhMGEF zhB7LODk5$oKC(UzE)H^*Hco;rDuyZ^ULH20HcEOf7E%@>b|S*MN)kd69%>#w&OR!F zDi%r>Hf|;^k}fiuG9q#!5;hVNJ`y&nHXdRgKE^&ab|$W}MlOCX8d@4AekK-97A}r1 z5;_tlawZ~TA|`GoKEggGY9>CmJ~Ey%a*j4CdMX}T9wKHUB3>dMQXXE4GA@QLnyNxB zf-XL;J~pN{9#$SEb|xB58X86#KE6IGekvMD8ZweH3LXkdel9YOGB$c9GL|wTY9cnC zHX>RghN3#czDgE87G91rE{ZN5P992vE;K~#9!O;iC}+cpptk?RD$fl`JkX}zSW z0n^m6V+Yd33OE5fKmrWGXuYq5GKTm6f5P6$blZKFd^(@br+4qfj3f!#$BvZ$t1}qK|Yx-0fNoPpuZ!Y!?{I5qxdi5usw1S@HeF;tOQ)Wl?FhDy>Rov8Ys9 zm=+iL>Dn<{>^;}2LQl0+1#wsXwMxX&5{bY?7Mz3TeU2o#g~R^*^a@Y26TN{!z*C5Egb>=#Zq3|Y&K=!c`* zTP1BgHP?1gDHod})X27N175!6T43SykEwZxMyOe7YFZ$s=b2`Krid-;AY-j!;kT%e zi-j&5%Bbt81*N1yhV1nPh|F{={^;73iyy+uy$pBxu9FM~%X|B0 z%gbkHXM@4<@$mr4{@&iV@85s{x})NsOasGd+7jzbF?&e3@M=g+975`T}!)%A~5 ze(j}H>8$tl55lmE&OI&Ss5X*HSDXl>%dY!T9o*eLiX;A3;NH1gmG6Jz9nAvW*5pH+ z0Rs;jpt-{iPYcU(CAGUONvvs0^nz!Hza9T|sl->#>3dYlXf&dDoHS zs%1)%PLJ$VG^0xwf@1O1vuZWzx;SY}lBOv-!ip`o=F+8C1DyCsI$ZPuigqOzG@Nat zwJDZ`Sl*UtZcS&9)7dFxHl4Lv{i(%&-=G6HIl^t^2yawey00000NkvXX Hu0mjfAE3{j literal 0 HcmV?d00001 diff --git a/doc/SL.png b/doc/SL.png new file mode 100644 index 0000000000000000000000000000000000000000..aa0bdf50d4f871efe2cf81c36da5b332ac1f243b GIT binary patch literal 1775 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 literal 0 HcmV?d00001 diff --git a/doc/UL.png b/doc/UL.png new file mode 100644 index 0000000000000000000000000000000000000000..3970533f33467426ef41e0b128b302fcee33cee0 GIT binary patch literal 1530 zcmV000>X0{{R4Infic00004XF*Lt006O% z3;baP0003RP)t-s3K|M7mM#`X7TU&ACVD0sRvMnFLYk&R9%ddswmu3X3NDT=B61>L zhBAhpIzF;K4lWKRekLMrA|75IHlj9ik~S7n7Cy>8DvBz0mNpVX5>A3H9%>#cf+}8) zG8Re}KDs_Knleg)F2cS_5;hVNJ`y&nHXdRgB6cDwhAJ9b8a~247ETr}k}f84CK5Ul zB4Q#YZYCybCN_2^E`BaDo-!(WDjr%MB3>e{vPL3iA|6s6E`}~Hf-XL;KANgRHl{Wn zRvsEo8a~oKCUzzoMjAf8J}Q1H8cG^6k}?V&3Nnr|GL|wv#y%oyA~v2jB3dFAJ{B&D zE;fE99!?%Mnl=_f7AkfsUWzh4&OU~sIubGx8d4fchAwKt;0^!)1dB;TK~#9!O;wLq z;y4hFv^~IPmm~35Z(n)1u3D^6is#Z^EJE3$;E$-i6S=eh|KD-nq(0w`B%MsBlgT&V zL}sU{J4|<`MZu(2+R|Eyki{G-k0PkGTXuW9dkbOHXaQhID7iYy^npu%T@|yQqCb)_O_WcW4MiJ*) zuvj|f$_e~9lR@B=v5Ym_HMv(u-8Dx!W^(da$(|W|C-avV()Ntu@o|1KpU*>(w?J z%$PP9y}>3EEKM&8!aW5K+``>)(+lFo&Q3and1{JSu)*U?1qT4Yn4j#uJpK=lJ>YS& z!DFJGulLG z%d27$t`r<@C?*@oh@(jGqO|zdvcC5%%c@gW5xy6^9fqvuN3>z`^aLl>@O^lp0i%L8T6=-DB5$)6tY&^)G9$K zsgNQ2^%g{CAFT)~@9tL0hXL%4PMgivRjbuHz~idbJZOu>k#^}7WN$o*Y$w}yU4 z0uP$kv|JnaZ$EzAukZIK=;_*JEXDq5_vWFVl0hBDe8*7`D;5 z%QB7{iPEOxL?9}A)sNJn7M!pZ_@g)B23=qJI&zPjyndrYI}ItKZgW zT*5v8r+Fp1+FBFxx;o{jdcFSdq4314K7;yOm6C~xeij56x7&0fXcjL5*=Q)=$4O(7 zEX&XlGKLlX)bJXQ^Jw%Tc!{@_(7KwA gM(>8>ub4CbA4CsZFWEvn>i_@%07*qoM6N<$f({I<761SM literal 0 HcmV?d00001 diff --git a/doc/auxlibs/index.html b/doc/auxlibs/index.html new file mode 100644 index 0000000..a922ff1 --- /dev/null +++ b/doc/auxlibs/index.html @@ -0,0 +1,55 @@ + + + + + Auxiliary Libraries for PLIB. + + + + + + + + + + +
+

Auxiliary Libraries for PLIB.

+
+ by Steve Baker +
+

Introduction

+PLIB/AUX is a suite of auxiliary libraries that build +higher level classes on top of the basic PLIB libraries. +

+These will be installed and named with the same +conventions as the base PLIB libraries, eg: +

+
+   /usr/include/plib/auxXXX.h
+   /usr/lib/libplibauxXXX.a
+
+
+...where 'XXX' is the name of the aux library. +

+PLIB/AUX functions, classes and constants are all +named after the base PLIB library that they logically +belong with, hence enhancements to SSG will share the +'ssg' prefix but add a 'u': +

+
+  eg
+      class ssguParticleSystem ;
+      class puuFileBrowser ;
+
+
+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + + diff --git a/doc/contacts.html b/doc/contacts.html new file mode 100644 index 0000000..d3287e6 --- /dev/null +++ b/doc/contacts.html @@ -0,0 +1,114 @@ + + + + + PLIB: Contacting Us. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+

Contacting Us - and Contributing to PLIB.

+There are three PLIB mailing lists: +In order to keep the spam off the lists, it's set up so you have +to subscribe in order to send mail. To do that, visit: + +

+Please do not send me personal mail in situations when one of +the public mailing lists would be an appropriate forum - I hate +it when people do that and I'll usually ignore you. +

+The mailing list is set up to prohibit large attachments. HTML mail +and other non-ASCII forms of posting are NOT welcome. +

+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/contacts.png b/doc/contacts.png new file mode 100644 index 0000000000000000000000000000000000000000..ad3b7a53845cd88ae850fa806eb4f3b1f5b04b3e GIT binary patch literal 5264 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 W(QF+iT^;)X0000 + + + + PLIB: Downloading and Installing PLIB. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ +
+

Downloading and Installing PLIB.

+

The Short Answer (for Linux and CygWin users):

+Make sure you have OpenGL installed.
+Download this: PLIB - Version 1.8.4 +
+Run this (as 'root'): +
+     tar xzf plib-1.8.4.tar.gz
+     cd plib-1.8.4
+     ./configure ; make install
+
+Tadaaaa! +

+

The Long Answer: Downloading PLIB.

+You can get PLIB in various versions here. We follow the common Linux +convention where the second digit in the version number is EVEN for +stable versions and ODD for unstable and/or experimental versions. +

+End users should generally download the latest stable version but +application developers may wish to periodically compile their code +against the odd-numbered version so as to keep abreast of developments +in PLIB. +

+

+Download this using your browser by right-clicking on the name. If you +have InternetExplorer, you'll probably fall victim to it's stupid bug +where it unzips the file automatically - but forgets to +rename it to lose the '.gz' off the end. Just rename it yourself +before you try to un-tar it and all will be well. +

+PLIB documentation, example programs, fonts, etc are separated from +PLIB itself to minimise your download time. +

+The online documentation attempts to always track the latest stable +version of PLIB. If you are using some other version, you may want to +download the documentation appropriate to that release which comes with +the plib_examples tarball. + + +

+Un-tar this into a separate directory from PLIB itself - or else +all the configuration and makefiles will clash. Many of the example programs +require the GLUT library (or 'freeglut') - some other use SDL and FLTK. +

+ +More extensive PLIB demonstration programs are also separated from +PLIB itself and are individually tarballed with their own version +numbers. Generally, they'll run with the most recent stable version +of PLIB - but occasionally they'll rely on something in the latest +PLIB beta. +

+

+Un-tar these into a separate directory from PLIB itself - or else +all the configuration and makefiles will clash. + +

The Long Answer: Installing PLIB.

+On most Linux and other UNIX-like OS's, it's simply a matter of +unpacking the tar-ball, changing directory into the plib_x.y.z +directory that this creates and typing: +
+
+   ./configure
+   make install
+
+
+You'll need to +be 'root' to perform the last step. At the end of that process, +you should have a set of header files in /usr/include/plib +and a set of corresponding libraries in /usr/lib. There +are some example programs in the 'plib-x.y.z/examples' subdirectory. +

+WARNING: If you are using 'Mesa' instead of an 'Official' OpenGL, +please make sure you followed the instructions in the Mesa installation +notes that tell you to symbolically link /usr/lib/libGL.so +and /usr/lib/libGLU.so to their Mesa equivelents. More modern +versions of Mesa may install directly into that location - but +make sure that one way or another, /usr/lib/libGL.so points to your +current OpenGL/Mesa library. +

+Some Linux distro's install OpenGL/Mesa in /usr/X11R6/lib - that +is now officially deprecated - if your OpenGL/Mesa is installed +there then I suggest you add symbolic links to make the +files appear to be in /usr/lib. +

+If you are running some non-UNIX OS, (eg MS-Windows) there may be +instructions on how to build PLIB in README.{OS name}. If you +have problems - or can offer to streamline the +process, then please pass on your knowledge via the mailing list. +In general, it isn't complicated to install - you usually just have +to compile all of the source files in each directory and link +each subdirectory into a separate library. Don't try to make +PLIB into a M$ '.DLL' - it's not worth the effort! +

+If you have the Microsoft Visual C++ suite, you'll find a set +of project files - one in each source directory - plus a master +workspace at the top of the tree. +

+If you have all the Cygnus (CygWin) compiler/tools, then use +the UNIX build instructions. +

+There is now a README.mac for Macintosh users. + +

Where Does PLIB Install Itself?

+This is always a controversial topic. In order to maintain +our own sanity, we expect PLIB to be installed in one place +and in one place only. +
+
+  Libraries are in:    /usr/lib/libplibXXX.a
+
+  Headers are in:      /usr/include/plib/XXX.h
+
+
+(Where 'XXX' is the short acronym for the PLIB library in question - +in lowercase). +

+The now *VERY* obsolete PLIB 1.0.* used to install itself +into /usr/local/plib, but from version 1.1.0 onwards, the +modern location was adopted following discussions with +several Linux distributors. +

+Please don't even think about arguing about this decision, +it has been the topic of long and arduous discussion and it's +NEVER going to change! + +

+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/download.png b/doc/download.png new file mode 100644 index 0000000000000000000000000000000000000000..cab20fb3990eb3e3e89740f3638f7c8825246063 GIT binary patch literal 5452 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 + + + + PLIB: A Portable Games Library. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+ +

PLIB: Example Programs and Demonstrations.

+ +PLIB has three sources of sample programs to learn from. +
    +
  • EXAMPLES: There is a bundle of small programs that each show + off one or two small aspects of PLIB and which are + not generally all that useful by themselves. +
  • DEMOS: These are fully worked out demonstration programs + that use a wide range of PLIB features to create complete + and useful programs that are still small enough to understand + quite easily. +
  • APPLICATIONS: Because many PLIB applications are OpenSourced, + there are a large number of complex programs that you can + look at to see how large games, simulations and interactive + tools can be built using this technology. +
+ +

EXAMPLES

+ +The PLIB example programs are distributed in +a separate tarball from the main PLIB distribution - and generally, it +is updated less often - but following the same version numbers as the +main PLIB library. +

+When you install that package, you'll see that the 'src' directory is +split into sections for each PLIB component library - with one or two +different examples for each library. +

+All of the examples are licensed under GPL as bundle and are maintained +under the PLIB SVN archive. +

    +
  • FNT:
      +
    • fnt_test - a basic TXF font browser. +
    • slideshow - a way to present a simple slide-show driven + from an ASCII text file. Who needs PowerPoint + when you have 'vi'? :-) +
    • data - this directory contains 20 different TXF fonts. +
    +
  • JS:
      +
    • js_demo - a program that shows the current output from + whatever joysticks it can detect. Useful for + testing out which buttons set which bits and + which axes are which. +
    +
  • NET:
      +
    • client-server - +
    • http_get - +
    • net_echo - +
    +
  • PUI:
      +
    • simple - the "Hello World" of PUI programs. +
    • complex - a fairly pointless program that shows how + to control a spinning OpenGL cube, and use + a variety of PUI widgets. +
    • widget_list - draws every kind of PUI widget each + in a separate window. This is what we used + to make the screenshots for the PUI manual. +
    +
  • SG:
      +
    • sg_quat_test - tests the SG quaternions...unsuccessfully + I think! +
    +
  • SL:
      +
    • example - plays a number of annoying sounds. +
    • mod_demo - plays a MOD format music file. +
    +
  • SSG:
      +
    • load_save - Load a 3D model - then just save it again. +
    • majik - A test (originally written to help the Majik3D + project) that shows Tux walking around on some + auto-generated terrain. +
    • state_test - Exercises some ssgSimpleState settings. +
    • tux - A model of Tux the Penguin spins around on a + pedestal encrusted in golden Herring. +
    • tween_test - Demonstrated the 'tween' effect. +
    • water - Demonstrates the ssgAux library - water/fire/lensflare/particles plus basic shapes, cube, teapot. +
    • viewer - A program to allow you to view any 3D model + and spin it around using the mouse. +
    +
  • UTIL:
      +
    • test_dir - tests some of the directory handling features. +
    +
+

DEMONSTRATIONS

+ +The PLIB demonstration programs are distributed in +their own separate tarballs - each has it's own version number and it's +own license conditions (although all of them must fall within the SourceForge +definition for OpenSourced software in order to reside on the SourceForge +server). All of the demonstrations are maintained under the PLIB SVN +archive. + +
    +
  • ExPoser - A program to allow you to animate characters for your + games using 'Skin and Bones' animation techniques. + ExPoser needs PLIB 1.5.1 or later. +
  • TTT3D - A complete 3D TicTacToe game played on a 4x4x4 board. + It's theoretically possible to beat it - theoretically. + TTT3D needs PLIB 1.4.0 or later. +
+ +

APPLICATIONS

+ +PLIB applications do not reside on the PLIB web site - but you can find +links to many of them in our image gallery. + +
+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/examples.png b/doc/examples.png new file mode 100644 index 0000000000000000000000000000000000000000..4c419eb87515f76a91b2b3c06027f6b2828fdc65 GIT binary patch literal 5156 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 SmKBHq0000w2G-yk2R@n%G!R5} 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 + + + + + + A Font Library for OpenGL. + + + + + + + + + + +
+

A Font Library for OpenGL

+
+ by Steve Baker +
+

Introduction

+The FNT library was originally written to allow PUI programs to +produce text using texture maps instead of bitmaps because the +latter are typically very slow on consumer-grade 3D hardware. +

An Example.

+This example program is probably all you need in the +way of documentation - so it's the first thing we'll talk +about: +
+
+   /* Declarations */
+
+   fntRenderer texout ;
+
+   /* Load some fonts */
+
+   fntTexFont  TimesRoman ( "times_roman.txf" ) ;
+   fntTexFont  Courier    ( "courier.txf" ) ;
+
+   /* Select a font and pointsize to render with... */
+
+   texout . setFont      ( & TimesRoman ) ;
+   texout . setPointSize ( 24 ) ;
+
+   /* Print "Hello" and "World" */
+
+   texout . begin () ;
+     texout . start2f ( 50.0f, 80.0f ) ;
+     texout . puts ( "Hello" ) ;
+     texout . start2f ( 50.0f, 50.0f ) ;
+     texout . puts ( "World" ) ;
+   texout . end () ;
+ 
+
+

fntInit

+All programs that use the FNT library should call 'fntInit()' sometime +after they have established a valid OpenGL rendering context and +before they make any other FNT library calls. + +

The Classes

+There are three external classes used in FNT: +
+
+  class fntFont         -- An abstract base class from which all
+                           kinds of font representations could be
+                           derived.
+  class fntTexFont      -- A fntFont that uses texture mapping.
+  class fntRenderer     -- A class that draws text using a fntFont.
+
+
+

class fntFont

+Classes derived from the fntFont class all describe fonts. +This is an abstract base class from which fntTexFont is derived. +Other fntFont sub-classes could also be derived in the future. +(This means that you cannot declare a fntFont - but all classes derived +from fntFont will obey this description.) +

+This class appears quite complex since constructing and/or +querying the contents of a texture font is a complicated +business - and consequently, there is a lot of API to +support your ability to do that. Fortunately, most FNT +applications will just load a font from disk and use +fntFont::begin()/ +fntFont::puts()/ +fntFont::end() +functionality. +

+
+  class fntFont
+  {
+  public:
+    fntFont () ;
+    ~fntFont () ;
+    void putch ( sgVec3 curpos, float pointsize, float slant, char  c ) ;
+    void puts  ( sgVec3 curpos, float pointsize, float slant, char *s ) ;
+    void begin () ;
+    void end   () ;
+    void getBBox ( char *s, float pointsize, float slant,
+                            float *left, float *right,
+                            float *bot , float *top  ) ;
+
+    int  load ( char *fname, GLenum mag = GL_NEAREST,
+                             GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ;
+
+    void setFixedPitch ( int fix ) ;
+    int   isFixedPitch ()          ;
+
+    void  setWidth     ( float w ) ;
+    void  setGap       ( float g ) ;
+
+    float getWidth     () ;
+    float getGap       () ;
+
+    int   hasGlyph ( char c ) ;
+  } ;
+
+
+fntFont::getBBox() returns the top, bottom, left and +right extents (in OpenGL units) of the string 's' if it were drawn at the +specified pointsize and slant. This routine knows how to deal with +newline characters. +

+fntFont::putch() draws the character 'c' at the +cursor position specified be 'curpos' (with the specified pointsize and slant) +and advances the cursor +to the right hand edge of the character it just rendered. +

+fntFont::puts() draws the entire string 's' at the +cursor position specified be 'curpos' +(with the specified pointsize and slant) and advances the cursor +to the right hand edge of the last character it rendered. +

+Both putch and puts will automatically switch the case of letters +from upper to lower or vice versa if the required character is +not present in the font but the letter with the reverse case +is present. Other missing characters will simply generate no +output and won't update the cursor position. If a space character +is not defined in the font, then a half-pointsize gap will be +generated instead. +

+puts (but NOT putch) knows how to deal with newline characters, +it drops the text down to the next line - leaving a one-third +pointsize gap between lines. +

+fntFont::begin()/end() since redundant mode changes are +costly in OpenGL, applications may optionally call fntFont::begin() +before rendering some text using a specified font and call fntFont::end() +at the end. Just like a glBegin()/glEnd() pair, +there are some fairly restrictive rules about what you can do between +a fntFont::begin() and a fntFont::end(): +

    +
  • The only OpenGL calls that are permitted are those that + are allowed between a glBegin()/glEnd(). +
  • You may not make calls to other member functions of other + fntFont objects or to fntRenderer objects. +
  • You may not call fntFont::begin() between another + fntFont::begin()/fntFont::end() pair. +
+

+You may call fntFont::putch() and/or +fntFont::puts() without +entering a fntFont::begin()/fntFont::end() +state - but if you do that, each call will result in +OpenGL state switching - which may well be redundant. +

+int load ( char *filename ) loads a font from disk, +returns TRUE on success, FALSE for failure. +

+int load ( char *filename, GLenum mag = GL_NEAREST, +GLenum min = GL_LINEAR_MIPMAP_LINEAR )...you can optionally +specify the OpenGL texture magnification and minification filters +- this is sometimes necessary to get the clearest possible text +at certain point sizes. Experiment! +

+void setFixedPitch ( int fixed ) if 'fixed' is TRUE, forces +the font to be fixed-pitch (so each letter or 'Glyph' is a standard width), +if 'fixed' is false then variable character widths are possible. +

+void setWidth ( float w ) For a fixed width font, this +sets the width of each character. If the actual characters are wider +than this, they will overlap, if they are narrower then there will be +a gap. +

+void setGap ( float g ) Set the gap between letters, can be +negative or positive. +

+The following routines allow you to query the font's setup: +

+
+    int   isFixedPitch ()
+    float getWidth     ()
+    float getGap       ()
+
+
+int hasGlyph() returns TRUE if the font contains a +glyph (graphic image) for a given ASCII character, FALSE otherwise. +Fonts that have only uppercase (or only lowercase) letters will still +return TRUE for corresponding characters of the opposite case because +the putch and puts routines will automatically case-convert in that +case. +

class fntTexFont

+fntTexFont is inherited from fntFont. +All functions of a fntFont are implemented in fntTextFont using +a texture map. +
+
+  class fntTexFont
+  {
+    public:
+      fntTexFont () ;
+      fntTexFont ( char *fname,
+                   GLenum mag = GL_NEAREST,
+                   GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ;
+      ~fntTexFont () ;
+      void putch ( sgVec3 curpos, float pointsize, float slant, char  c ) ;
+      void puts  ( sgVec3 curpos, float pointsize, float slant, char *s ) ;
+      void begin () ;
+      void end   () ;
+      void getBBox ( char *s, float pointsize, float slant,
+			      float *left, float *right,
+			      float *bot , float *top  ) ;
+
+      int  load ( char *fname,
+                  GLenum mag = GL_NEAREST,
+                  GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ;
+
+      void setFixedPitch ( int fix ) ;
+      int   isFixedPitch ()          ;
+
+      void  setWidth     ( float w ) ;
+      void  setGap       ( float g ) ;
+
+      float getWidth     () ;
+      float getGap       () ;
+
+      void setGlyph ( char c,
+		    float tex_left, float tex_right,
+		    float tex_bot , float tex_top  ,
+		    float vtx_left, float vtx_right,
+		    float vtx_bot , float vtx_top  ) ;
+    
+      int  getGlyph ( char c,
+		    float *tex_left = NULL, float *tex_right = NULL,
+		    float *tex_bot  = NULL, float *tex_top   = NULL,
+		    float *vtx_left = NULL, float *vtx_right = NULL,
+		    float *vtx_bot  = NULL, float *vtx_top   = NULL) ;
+    } ;
+
+
+

+int load ( char *filename ) loads a font from disk +using the extension of the filename to determine what format it is +stored in. Currently, the only supported format is Mark Kilgards 'texfont' +format - which has the '.txf' filename extension. Returns TRUE for +success, FALSE for failure. +Details of the TXF file format can be found in + +Marks's document about Textured Fonts +

+Just as with all fntFont derived classes, you can optionally specify +the OpenGL texture filter options for the loaded texture. +

+Each font is made up of zero or more 'Glyphs' (character shapes) packed +into a single texture map. If you didn't use 'load' to load the font +from disk, you'll have to define where each one lies on the map and +how big the quadrilateral it is to be drawn on is: +

+
+    void setGlyph ( char c,
+		    float tex_left, float tex_right,
+		    float tex_bot , float tex_top  ,
+		    float vtx_left, float vtx_right,
+		    float vtx_bot , float vtx_top  ) ;
+    
+
+Where 'c' is the character we are defining, 'tex_*' is the +left, right, top and bottom of the image of that character +in the texture map. Since texture coordinates are in the +range 0..1, these will typically be quite small numbers. +'vtx_*' is the left, right, top and bottom of the character's +rectangle in a coordinate system that has (0,0) at the +baseline of the character cell, and (1,1) at the top-right +corner of the tallest, widest character in the font. Hence, +a lower-case 'y' would have a negative 'vtx_bot'. +

+You can also query all this information: +

+
+    int  getGlyph ( char c,
+		    float *tex_left, float *tex_right,
+		    float *tex_bot , float *tex_top  ,
+		    float *vtx_left, float *vtx_right,
+		    float *vtx_bot , float *vtx_top  ) ;
+
+
+fntFont::getGlyph() returns TRUE if the character has +been defined in this font, FALSE otherwise. +

class fntRenderer

+This class is the one most people are going to be using +to render text in FNT. Most applications will declare +a single fntRenderer class for all their text needs. +
+
+  class fntRenderer
+  {
+  public:
+    fntRenderer ()
+
+    void start3fv ( sgVec3 pos ) ;
+    void start2fv ( sgVec2 pos ) ;
+    void start2f  ( float x, float y ) ;
+    void start3f  ( float x, float y, float z ) ;
+
+    void getCursor ( float *x, float *y, float *z )
+
+    fntFont *getFont () ;
+    void     setFont ( fntFont *f ) ;
+
+    void begin () ;
+    void end   () ;
+
+    void putch ( char  c ) ;
+    void puts  ( char *s ) ;
+  } ;
+
+
+fntRenderer::setFont() is used to tell the renderer which +font is current. fntRenderer::getFont() lets you find out +which font it is. You may not call fntRenderer::setFont() +between a fntRenderer::begin() and fntRenderer::end() +pair. +

+By default, uppercase characters of all fonts are one OpenGL unit high. +You can make characters larger or smaller by setting +fntRenderer::setPointSize(). You can also slant the characters +to form italic or oublique fonts using fntRenderer::setSlant(). +(The slant measures by how many OpenGL units the tops of uppercase +characters are sloped to the right). You can call either setPointSize or +setSlant between fntRenderer::begin() and +fntRenderer::end(). +

+The various kinds of fntRendered::start*() calls are +akin to the OpenGL glVertex* commands and they determine where +the next chunk of text will be drawn. Since this coordinate is +updated as text is drawn, you'll need to call +fntRenderer::getCursor() to +find out where the next chunk of text will be drawn. You can +call fntRenderer::start*(), +fntRenderer::putch() and +fntRenderer::puts() between +fntRenderer::begin()/fntRenderer::end() +calls. +

Making New TXF Fonts.

+The '.txf' font format was designed by Mark Kilgard - who +also produced some tools for dealing with them. +

+Check + +Marks's document about Textured Fonts for further information. +Of particular interest is Mark's "gentexfont" program that can +create a TXF format font from an X-windows font. +

+You can find out which X-fonts are stored on your machine using +the /usr/X11/bin/xlsfonts program. +

+There are over a dozen sample TXF fonts stored +in examples/src/fnt/data. +


+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/gallery.html b/doc/gallery.html new file mode 100644 index 0000000..3159572 --- /dev/null +++ b/doc/gallery.html @@ -0,0 +1,188 @@ + + + + + PLIB: Gallery. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+

ScreenShot Gallery.

+These images come from a wide variety of PLIB applications - some OpenSourced, +some commercial. Click on the thumbnail image for more detail and links to further +information. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

The FlightGear Flight Simulator.

+
+ +FlightGear
+
+

The Chronicles of the Evil Overlord.

+
+ +Chronicles of the Evil Overlord
+
+

The ExPoser Animation Tool.

+
+ +ExPoser
+
+

HardBall.

+
+ +HardBall
+
+

MOTE.

+
+ +MOTE
+
+

The PrettyPoly Modeller.

+
+ +PrettyPoly Modeller
+
+

TicTacToe 3D.

+
+ +TicTacToe 3D
+
+

TORCS Car Racing Simulator.

+
+ +TORCS
+
+

Tuxedo T. Penguin - A Quest for Herring.

+
+ +A Quest for Herring
+
+

TuxKart.

+
+ +TuxKart
+
+

TuxFleet.

+
+ +TuxFleet
+
+

Wizz.

+
+ +Wizz
+
+

Atlas Map Maker.

+
+ +Atlas Map Maker
+
+

Flight Dynamics Simulator.

+
+ +Flight Dynamics Simulator
+
+
+ +
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/gallery.png b/doc/gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..1aa7d3db6f06cc74cf27dd2f6bbc567a605d8471 GIT binary patch literal 4738 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 + + + + PLIB: A Portable Games Library. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ +
+ +

PLIB: A Suite of Portable Game Libraries.

+I (and an ever increasing band of helpers) have been spending +time to build a set of OpenSource (LGPL) libraries that will +permit programmers to write games and other realtime interactive +applications that are 100% portable across a wide range of hardware and +operating systems. + +

+PLIB includes sound effects, music, a complete 3D engine, font +rendering, a simple Windowing library, a game scripting language, +a GUI, networking, 3D math library and a +collection of handy utility functions. All are 100% portable +across nearly all modern computing platforms. What's more, it's +all available on line - and completely free. Each library component +is fairly independent of the others - so if you want to use SDL, +GTK, GLUT, or FLTK instead of PLIB's 'PW' windowing library, you can. +

+The latest stable version of PLIB is 1.8.4 - go to the +DOWNLOAD section to get a +copy - go to the WHAT'S INSIDE +section for documentation and a more detailed description of +what PLIB contains. + +

+If you are looking at other games libraries (and there are a LOT +to choose from) - ask yourself how many games have been written +for that library by people outside the group that wrote it. That +is the acid test of usability and PLIB passes with flying colours. + +

+PLIB is used by many projects (not all games - and not all +OpenSourced), here are some of the more prominent ones - there +are at least a hundred others: +

+ + + + + + + + + + + + + + + + + + + + +
+ Tux_AQFH Logo
+ Tux the Penguin - +
A Quest for herring.
TuxKart
+ + TuxKart Logo
+ FlightGear Logo
+ FlightGear
Majik3D
+ + Majik3D Logo
+ PrettyPoly Logo
+ + PrettyPoly Editor
TuxFleet
+ + TuxFleet Logo
TORCS
+ + TORCS Logo
+ Minigolf Maniacs Logo
+ + Minigolf Maniacs
+
+
+ + + + + + + + +
+ FDS Logo
+ + Flight Dynamics Simulator
+ MOTE Logo
+ + MOTE
+ + Spacething Logo
+ Spacething
+
+

+Unlike the many similar libraries, PLIB is easy to learn and +use - and has an object-oriented C++ interface. It's built +as a set of component libraries that you can pick and choose +between if (for example) you don't want to use PLIB's GUI but +you do want to use it's scene graph API. + +

+ +If you are reading this document from the source distribution on +your local disk, you can find the most up to date version of PLIB +here: +

+PLIB's web presence is currently hosted on SourceForge (with thanks to +VA Linux Inc for providing this free service), and the +project summary page is here: + + +
+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/js/index.html b/doc/js/index.html new file mode 100644 index 0000000..abdc32f --- /dev/null +++ b/doc/js/index.html @@ -0,0 +1,187 @@ + + + + + + + A Joystick Library. + + + + + + + + + + + +
+

A Joystick Library.

+
+ by Steve Baker +
+

Introduction

+JS is now just one component of PLIB. +

+This Joystick Library (JS) is a portable interface that +has no inherent restrictions over the number and type +of joysticks it supports - although in actuality, most +implementations will simply sit on top of an underlying +driver provided by the Operating System. +

+You should include the JS header file '/usr/include/plib/js.h' +and link to the JS library '/usr/lib/libjs.a'. +

+Before using any JS functions, you should initialise the +library by calling: +

+
+    jsInit () ;
+
+
+JS is essentially just a wrapper to make the various +underlying OS mechanisms look the same to application code. +
+
+  class jsJoystick
+  {
+     jsJoystick ( int id = 0 ) ;
+    ~jsJoystick () ;
+
+     const char* getName () const ;
+     int   getNumAxes    () const ;
+     int   getNumButtons () const ;
+     int   notWorking    () const ;
+     void  setError      () ;
+
+     float getDeadBand ( int axis ) const     ;
+     void  setDeadBand ( int axis, float db ) ;
+
+     float getSaturation ( int axis ) const     ;
+     void  setSaturation ( int axis, float st ) ;
+
+     void setMinRange ( float *axes ) ;
+     void setMaxRange ( float *axes ) ;
+     void setCenter   ( float *axes ) ;
+
+     void getMinRange ( float *axes ) const ;
+     void getMaxRange ( float *axes ) const ;
+     void getCenter   ( float *axes ) const ;
+
+     void read    ( int *buttons, float *axes ) ;
+     void rawRead ( int *buttons, float *axes ) ;
+  }
+
+
+You pass the identifier (0..n) of the joystick you wish to +open into the constructor function. If you leave the number +off, it'll grab the first joystick available. +

+If the joystick is unavailable, not working, not installed or +otherwise bad, the package will return zero for all the axes +and all the buttons will appear to be up. If you need to +check for the existance of a particular stick, you can +call notWorking() which returns JS_TRUE if +there is some problem with that stick, JS_FALSE otherwise. +

+getName() tells you the device name of the +joystick, if available through API calls in your OS. A +typical value is "CH PRODUCTS CH PRO PEDALS USB ". If no +name is available, this method will return the empty string. +

+getNumAxes() tells you how many axes the stick +has. Note that it is NOT safe to assume that all sticks have +the same number of axes - or that they all have a maximum of +three or something. When you pass axis data into and out of +JS, your arrays MUST be sized large enough for that stick. +

+When you are testing joystick code, you need to be sure your +program does the right thing when no joystick is installed. +Since it's inconvenient to do keep uninstalling your stick +to do that, we provide setError() which causes +that stick to react exactly as if it were not installed. +

+To read the joystick, just call read(&buttons,axes) +where 'buttons' is an integer and 'axes' is an array of +floating point numbers. The buttons variable will be populated +with a number that has one bit per button (0==Not Pressed, 1==Pressed) +and the elements of the axis array will be scaled from an idealized +-1.0 to +1.0 range. (In practice, your stick may not reach those +limits - or it may marginally exceed them). +

+For a basic two axis stick, axis zero is Left/Right with left being +negative and right positive. Axis one is North/South with north being +negative and right positive (this is counter-intuitive - but that's +what they do). +

+M$-Windows letters its stick axes X,Y,Z,R,U,V. These correspond +to JS axes 0,1,2,3,4,5,6. +

Buttons

+One continually annoying thing about PC joysticks is the lack of +standards for their names. Most sticks have at least two buttons - +mine has six (maybe some have still more). The problem is to know +how to name them. My game pad has buttons labelled A,B,C,D,R,L +corresponding to bits 0,1,2,3,4,5 in the 'buttons' variable. +

+Anyway, some joysticks have features that operate by setting +two or more button bits at once. This is quite easy to catch by +masking multiple bits from the 'buttons' word. +

Demo Program

+The demo program that comes with JS is quite useful for finding out +which buttons set which bits and which axes are which - and in +which direction they operate. +

Calibration

+Since most joysticks are analog devices (and fairly cheap, nasty ones +at that), you will not generally get numbers that are accurately +in the range -1..1 with exactly zero being returned when the joystick +is centered on it's springs. This means that you may wish to calibrate +your joystick. The rawRead routine bypasses all the scaling and +offsetting to return RAW data directly from the OS that you can use during +calibration. +

+For each axis of the joystick, you should provide a maximum number +(that you wish to correspond to 1.0 returned by read()), +a minimum number (corresponding to -1.0) and a center point number +(corresponding to 0.0) by calling +setMaxRange()/setMinRange()/setCenter() as appropriate. +

+However, even with calibration, you should expect the joystick to +drift somewhat over time due to the position of the cable, the +temperature of the potentiometers and probably, the phase of the moon. +This drift is typically not serious at the max and min limits - but +in some applications, the user will expect (for example) for his +character in a game to stop moving completely when he releases the +stick. Since we cannot guarantee to get the same exact number out +of the stick when he does this, you'll need to implement a 'dead band' +in the middle of the stick's travel. This is done with +setDeadBand(axis,amount) where 'axis' is the number of the +axis you wish to have a dead band implemented on - and 'amount' is the +amount of dead band either side of 0.0. +

+There are corresponding 'get' routines for each 'set'. +

+The read routine is auto-calibrated when you create +the joystick class - so if the joystick is not centered at that +moment, you will get bad data from that point onwards. This is +quite common in games - so don't feel too badly about it. Just +arrange to 'new' all your joysticks early on in the program - when +your user's hands are likely to still be on the keyboard or mouse - +and provide a hot-key to pause the game and recalibrate the stick +on demand. +


+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/license.html b/doc/license.html new file mode 100644 index 0000000..a018eca --- /dev/null +++ b/doc/license.html @@ -0,0 +1,112 @@ + + + + + PLIB: Licensing. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+

PLIB Licensing.

+PLIB is licensed under the Library GNU Public License (LGPL). +

+Whilst this permits closed-source applications to use PLIB, you +are bound by certain restrictions under LGPL. Please be sure +to read and understant the 'LICENSE' file in the PLIB distribution +directory before you distribute any work based around or upon PLIB. +

+The PLIB example programs (being complete programs) are licensed +under the GNU General Public License (GPL). +

+Each of the demonstration programs has it's own license - but +they are all 'OpenSource' of one kind or another (as defined by +the SourceForge rules for OpenSourced software. Check the +distribution directory for each one for a LICENSE file describing +how it's licensed. +

Polite Request to Distributors.

+If you are a Linux or other OpenSource software distributor, +please do us the courtesy of only releasing stable versions +of PLIB and NOT versions labelled 'unstable' - or (especially) +any snapshot you may take from SVN. We are aware that legally, +you are entitled to do either of these things - but we ask +that you do not because it makes supporting PLIB that much +more difficult. +

+We would also appreciate being notified of which versions +you plan to ship - and approximately when so that we may +coordinate with you as to the best version to take. +

+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/license.png b/doc/license.png new file mode 100644 index 0000000000000000000000000000000000000000..8aed83d48d54ff9fc13f6fa8c684f187cabfbecf GIT binary patch literal 5267 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 Z@xRu-au&R7rKSJ?002ovPDHLkV1oCi!w&!e literal 0 HcmV?d00001 diff --git a/doc/majik3d.png b/doc/majik3d.png new file mode 100644 index 0000000000000000000000000000000000000000..81c00da93062f1757476221add15b439bbf35362 GIT binary patch literal 3081 zcmV+k4EFPhP)5lw91U96ql^ay~h=FZtu;BX!eF8HY$2(c(!LJEjfm?y8@ zXhHy%1-P*;jmg1g4lP@G#e3M2_sdmU5&p+(J&WKVS}{IqH4H;q;*dEWCZns#XGxM< zN;zYbl~CfEvPN5cOddI!%~oT}@H4>K>{#lO&HX@GNc64FB%Q2O5dRe0)RamLwU`a0 ztK?6_2yslQ+isqcOJ0jB20oDRBiiaY0&Dqsp^5n3_Hqu)RlG=#`XyRN1O$NB)*QEdJ~AcR3J}O_`i% z*{Y{I>R7PS^eZi7b-yo9OTTGheb$8T-Q1nGCCWMHCM+#?^RfEdwpG&BP3klcE-atK zNm@fwXLufKTZ$}7(ru(Nvm4q)WJe8lF9? zMs{nJG7ppjGKVC=A@p&rD232PpHUm+E>hq{ZG&L$r0mg#o}){3xEZV55xzy?IW{SZ zKQOBg{5{3jUQt=o+J$8W%V(C!mk%oX3EK3n)>unJ1h`W0Ph5RUr3Vcc3vd9uuW9bM9o`*v#GH9@BbffM}#V4*v!l4Ga zVTlCL={2k7k@t5iu3B{y!D8u!Br$zz&aR$9nm|ka7G+Aufi=lz&mkcH8uUP4RH3H` z{c)tH7)hEcV*f}I?RrJDYM`fveqVdR+(K|r0a&`|LJV0nz+RimiEF@YpMcw@6$!V^ zJfx_tx@dR=CDQap0Gdw<3=5xn_q%UiH~$KW|9I^tGwVI=KJyCdOiynCskhsAA9{

((rtk0ynW7zhoG{u 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 df{MeM{|_gCxV2Xh=@o? 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 FV1j-^huZ)E literal 0 HcmV?d00001 diff --git a/doc/net/index.html b/doc/net/index.html new file mode 100644 index 0000000..f3ade67 --- /dev/null +++ b/doc/net/index.html @@ -0,0 +1,354 @@ + + + + + + + Pegasus Network Library + + + +

+

Pegasus Network Library

+
+

Introduction

+Pegasus is a C++ library for making networked games. Pegasus is based on +Medusa +and is part of PLIB. +Pegasus, like Medusa, runs as a single process, multiplexing I/O with its various +client and server connections within a single process/thread. Pegasus is supported +on any platform that includes a socket implementation with the select() function. +This includes Win32, Linux, and a majority of Unix implementations. +

+This document assumes a certain degree of knowledge of +TCP/IP and socket programming. +

+To use the NET library, you'll need to '#include ' and +link to libplibnet.a +

Symbol Conventions.

+Pegasus follows the same conventions for symbols and tokens that +are used by OpenGL and GLUT. All Pegasus symbols for classes and +functions start with net and all #define +tokens start with NET. Words within a class or function +name are Capitalised and NOT separated with underscores. Words +within #define tokens may be separated with underscores +to make them readable. +

Initialisation.

+The first NET call in any program must always be netInit(). +

Classes

+The following class hierarchy makes up the core package - which +can be extended to add functionality or to change some underlying +mechanisms. +
+
+   class netAddress
+
+   class netBuffer
+    |__ class netMessage
+
+   class netGuid
+    
+   class netSocket
+    |__ class netChannel
+         |__ class netBufferChannel
+         |    |__ class netChat
+         |    |__ class netMessageChannel
+         |
+         |__ class netMonitorServer
+
+
+

class netBuffer

+A simple buffer class. +
+class netBuffer
+{
+public:
+  netBuffer ( int max_length ) ;
+  ~netBuffer () ;
+
+  int getLength() const ;
+  int getMaxLength() const ;
+  char* getData() ;
+
+  void remove () ;
+  void remove (int pos, int n) ;
+  bool append (const char* s, int n) ;
+  bool append (int n) ;
+} ;
+
+

class netMessage

+A message buffer used to transfer binary data and handle byte swapping. +
+class netMessage : public netBuffer
+{
+public:
+
+  netMessage ( const char* s, int n ) ;
+  netMessage ( int type, int to_id, int from_id ) ;
+
+  int getType () const ;
+  int getToID () const ;
+  int getFromID () const ;
+
+  void geta ( void* a, int n ) const ;  // a=array; array should already be serialized
+  void puta ( const void* a, int n ) ;
+
+  int getch () const ;
+  void putch ( int c ) ;
+
+  bool getb () const ;
+  void putb ( bool b ) ;
+
+  int getw () const ;
+  void putw ( int i ) ;
+
+  int geti () const ;
+  void puti ( int i ) ;
+
+  void gets ( char* s, int n ) const ;
+  void puts ( const char* s ) ;
+
+  void print ( FILE *fd = stderr ) const ;
+};
+
+

class netAddress

+This is the representation of an Internet-style machine address. +
+class netAddress
+{
+public:
+  netAddress () ;
+  netAddress ( const char* host, int port ) ;
+
+  void set ( const char* host, int port ) ;
+
+  const char* getHost () const ;
+  int         getPort () const ;
+
+  static const char* getLocalHost () ;
+
+  bool getBroadcast () const ;
+} ;
+
+
+When reading from the network using one of the higher level classes, +you can use a netAddress with an empty string as the 'host' to mean +"accept data from any host". When writing to the network, the +reserved host name '<broadcast>' (the < and > are literally +there) will cause the message to be broadcast to all machines on your +subnet. +

+Otherwise, use either the machines' network name - or it's IP address +(as an ASCII string). +

class netSocket

+netSocket is the low-level socket class. +
+class netSocket
+{
+public:
+  netSocket () ;
+  virtual ~netSocket () ;
+
+  int getHandle () const ;
+  void setHandle (int handle) ;
+
+  bool  open ( bool stream=true ) ;
+  int   bind ( cchar* host, int port ) ;
+  int   listen ( int backlog ) ;
+  int   accept ( netAddress* addr ) ;
+  int   connect ( cchar* host, int port ) ;
+  int   send ( const void * buffer, int size, int flags = 0 ) ;
+  int   sendto ( const void * buffer, int size, int flags, const netAddress* to ) ;
+  int   recv ( void * buffer, int size, int flags = 0 ) ;
+  int   recvfrom ( void * buffer, int size, int flags, netAddress* from ) ;
+  void  close ( void ) ;
+  void  setBlocking ( bool blocking ) ;
+} ;
+
+

Example:

+This example is stripped of error checking for clarity: + + + + + +
+

Sender:

+
+  netInit () ;
+
+  netSocket *sock = new netSocket () ;
+  sock -> open        ( false ) ;
+  sock -> setBlocking ( false ) ;
+  sock -> connect     ( host, port ) ;
+
+  while ( !done )
+    sock -> send ( msg, len, 0 );
+
+  sock -> close () ;
+
+
+

Reciever:

+
+  netInit () ;
+
+  netSocket *sock = new netSocket () ;
+  sock -> open        ( false ) ;
+  sock -> setBlocking ( false ) ;
+  sock -> bind        ( host, port ) ;
+
+  while ( !done )
+    if ( (len = sock -> recv(msg, maxlen, 0)) >= 0 )
+      ...use the data...
+
+  sock -> close () ;
+
+
+This code produces a 'Datagram' connection - which is fast but unreliable +(using UDP protocol). Passing a 'true' to sock->open() would produce a +'Stream' connection (using TCP). + +

class netChannel

+netChannel adds event-handling to the low-level +netSocket class. Otherwise, it can be treated as +a normal non-blocking socket object. +The direct interface between the poll loop and +the channel object are the handleReadEvent and +handleWriteEvent methods. These are called +whenever a channel object 'fires' that event. +The firing of these low-level events can tell us whether +certain higher-level events have taken place, depending on +the timing and state of the connection. +
+class netChannel : public netSocket
+{
+public:
+
+  netChannel () ;
+  virtual ~netChannel () ;
+
+  void setHandle (int s, bool is_connected = true);
+  bool isConnected () const ;
+  bool isClosed () const ;
+  void shouldDelete () ;
+
+  // --------------------------------------------------
+  // socket methods
+  // --------------------------------------------------
+  
+  bool  open        ( bool stream=true ) ;
+  int   listen	    ( int backlog ) ;
+  int   connect     ( cchar* host, int port ) ;
+  int   send        ( const void * buf, int size, int flags = 0 ) ;
+  int   recv        ( void * buf, int size, int flags = 0 ) ;
+  void  close       ( void ) ;
+
+  // poll() eligibility predicates
+  virtual bool readable (void) ;
+  virtual bool writable (void) ;
+  
+  // --------------------------------------------------
+  // event handlers
+  // --------------------------------------------------
+  
+  void handleReadEvent (void);
+  void handleWriteEvent (void);
+  
+  // These are meant to be overridden.
+  virtual void handleConnect (void) ;
+  virtual void handleRead (void) ;
+  virtual void handleWrite (void) ;
+  virtual void handleClose (void) ;
+  virtual void handleAccept (void) ;
+  virtual void handleError (int error) ;
+
+  static bool poll (u32 timeout = 0 ) ;
+  static void loop (u32 timeout = 0 ) ;
+};
+
+

class netBufferChannel

+netBufferChannel is a netChannel with I/O buffering. +Clients and servers built on top of netBufferChannel +automatically support pipelining where you can send +multiple commands without waiting for the response +to each command before you send the next. +
+class netBufferChannel : public netChannel
+{
+public:
+  netBufferChannel (int in_buffer_size = 512, int out_buffer_size = 4096) ;
+  void closeWhenDone (void) ;
+
+  virtual bool bufferSend (const char* msg, int msg_len) ;
+  virtual void handleBufferRead (netBuffer& buffer) ;
+};
+
+

class netChat

+netChat adds support for 'chat' style protocols - +where one side sends a 'command', and the other sends +a response (examples would be the common internet +protocols - smtp, nntp, ftp, etc..). +

+The handle_buffer_read() method looks at the input +stream for the current 'terminator' (usually '\r\n' +for single-line responses, '\r\n.\r\n' for multi-line +

+class netChat : public netBufferChannel
+{
+public:
+  netChat () ;
+
+  void setTerminator (const char* t);
+  const char* getTerminator (void);
+
+  bool push (const char* s) ;
+
+  virtual void collectIncomingData (const char* s, int n) ;
+  virtual void foundTerminator (void) ;
+};
+
+

class netMessageChannel

+A channel for binary messages. Takes care of packing and unpacking them in/out of the buffers. +
+class netMessageChannel : public netBufferChannel
+{
+public:
+  netMessageChannel () ;
+
+  bool sendMessage ( const netMessage& msg ) ;
+  virtual void handleMessage ( const netMessage& msg ) ;
+};
+
+

class netMonitorServer

+The monitor server gives you remote, 'back-door' access to your server while it is running. netMonitor is a telnet command port with +password authorization. It can be paired with and used to remotely admin another server. +Once connected via any telnet client to the monitor, you can issue commands. The monitor can be hooked up to a python interpreter for added power. Since an ordinary telnet session is not secure, the password could be intercepted, but that level of security is usually not needed for games. +
+class netMonitorServer : public netChannel
+{
+public:
+  netMonitorServer( cchar* _name, int port ) ;
+  ~netMonitorServer() ;
+
+  cchar* getPassword () const ;
+  void setPassword ( cchar* string ) ;
+  void setPrompt ( cchar* string ) ;
+  void setCommandFunc ( void (*func)(cchar*, netMonitorChannel*) ) ;
+} ;
+
+
+ + +
+Valid HTML 4.0! + +
+ +Dave McClurg +<dpm@efn.org> +
+
+ + + diff --git a/doc/plib_logo.png b/doc/plib_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..445fbf8c56f9d1528f97cf742ed53a5ba320372a GIT binary patch literal 29635 zcmXt8V{~2p)4j26+cq29Xl$c#(%810#T z*MA52^_BJYW%>2R@%07xF9HI;fLUKa%P%0u7ZC7YG7I>XmGzZn`IW`-l?C{())M$- zne}C9`DMxRWeNDNl>_+2k@dx4`NhHU#Q^{zeF1=9fUGZoH3$qu8pIM*3gQ7848jIMgRlVqIrm>06oU?jr6u4$BSHE=V?hxI zhb0F9Og;v0D5{vdb?xF7$#B$aiI& zUxR96CZKk%5Bmsq6#wX%r)r__r2qCdCJgq+cA?_tEmk$8+3z<}&vNaj#(Fu-tp=z4 zZ0kj3MRk$gSy9EAtDaW=;_=G{;T1wmqphgz26pIsmDzTjFV5d(Y;Mwj4@`0t@fKsx4~5dIdhCLkFu#_6v_uz z`;D~pc*8pzm|?XXG@mA7hh>)~^?>d+hU562t-W}c{+b%|Hnr62=Md8YJjt8+Ax%6ZK{KQZ@5X{YJt!j%ta_U;v-HmpA$n>su7h2OV!;4Sio zKYfKd@erX0cc;eiSdQDaRIr7GZ=h%jgRlM)S+U~nuo>~+8Q@-tLf@JvT|5Q#zH?{p zaNW|KwZvN7dg&l2a~})JQ|Q=O?bs2%Z)dUa_*05ACScW*mZUF2GVq3Uyn8g=?^7nC zrAo%&bRB2qTdHD)=j2|8Ft)Kkpt7fH6cYYi38X;xl02En`B)ibhfRuq4L)6*oZLXl zJ-Bw9bXo2lX+Fqtz?5^ES(>$-m5=vt8Txa zsPwTuJ0_idPN#G$71JLCyJTn@UeiJDzK?A-L4*?iQjB-rfrlzlZdEov9u-9m0qVz! zNnl8#mh}z?3_8)p5KG^X$!<4PlaiV@#qjp$hGk(0d zFe|`}pv9x2kq1|WqGEuhQ|ol)2}CvIo=u_%MeP zO0YK<>lY@8PP8wHUcWbFDhjR|+(NHzu(PApcMWiZmybQ>3q5n7?3ttmmc29%&HV=ktf>j5CI3k{1sJdfM)MjA6vE zDLUCFvO~%-DeSIPAT{n{V+*l&dxIrn@E?T-G`C~URq>2RgX1CAEf=1z-lZ7q+AD}SM#D6JNW$e?u;ew4OLnwr{G^A!BvP%r zbzj|lDAz|7fUD}dIDqsJ<~BmAE-3hp%10LcXQvg_6cbf|YDp3j%aAld`F&0dxCf4j zNv_Fy#qKyMCMj7Ze$;U2orY$R$mvp;eI&_nD@{#5Asq3XbmjF$Hnip##!eZJC|XxH zUqyz!_V@0OoDN#Eg^p`=1|L!k=jNoX#p*ioB)Vw*!M_MO9mKGr227u?-zPCwh(yE)geIwN!fEK&{QxpZ*aYbdj+MjV=MN zCkgIy7My;ILxvrNR{d4{bx~;m1oE2b?*av3rPjMB0VHsv$FdbPF$5IT(WiVhR2ua& zq+|@YKB&;CO4167WDz^U#&2aVa9XiU%MZcU*pQ-4$R;L|yFc#J9YlXq0C`Z8s7Yl~ z{X`V%h7k2)u(X4+Rx&MAP4Fa}qWrK$2cjbl>Qi>`)VMRn zmg=!xsPJMV4~!q)Yhp-K#%?L_h+HmWnTq9<5Lsu5V~lyzGyY25O1)6ud!{oW&R;a+ z{%l-nYb!COOzgtq!HZ6gd{u6$I^k%l-r0ZuC<%);4oJR{vCmdZ@ApRF94v(3CI}#U zZu?8#?>Hpqi@Q0=6^0x0Po<{Nm@|I(o+zO)H4$@U%Q_kZj?_inbNQx>B0CUbH$2J5 zW52UFNWlLW@WYAmJXK+IJa2>viYL9%Mn0Al{e?up)fU#uiQ`20s0jOLV3uH2|C**D znr0P<`Dd!*ThOQl!SI$ErEr}Iv2DB<5hF|koFs#ZUg^f4G&zHpD#_7OQ>7u3uEM-* zlp$c(zMf5N^irHzm+tlU zxw>#wB=KYR1y#Gqy3ih>O4MxdZaBYcV==gW6tRj0}41PD;4P?Y z{?CO%TF*F!?))u#gc^x$e(>-H7D)>|1ws8r#$}a$q||Ww%xpK+gh7#J4jY)w-WrAB zDGIP>+Bq=`tfdQx`y8gWQ5h7$MeL4DK1SMRc;W`8KdlQ`yJk=6F-?u5!c>06^BE?Y zLDKn&@sMeZZQax4C%>Hi ze&0{~eO=w+U3hS$!S-3^QSav09Ov8E9~&YQ#iZ;eYb2hO6`r3azs7#~aPJ}~o<%l< zs<$4J%EYQ2(ckZLO)p7REl4CwODHFV*I!E0UvM^47Ix5t^3v_b#X&k?)T_kSaF!#H zJ4SGbH&aI=qf*J5YH3(Ig$ni)dIp96Dl;khwkOm6V@z8WGl~%-fMqDL#xK<3FGHUbNyLFdQf(m9uhC zGA2MlYi{Rm6SK%5hBdm|ccfT}wa6j@?swE(LOh?eVywGBZtSRvrD!K&Af#~W)1`Y6 z(&2?`gts#tB-$=B^@t%+8NJqOm8D?}0;St71)K9>9AQ^TX7-82EE6uc-Dsk-5LOe@ zYE+#;si;H?ha_243{17!_fB4C?Z^&9V`AYbaooZBIfMm7Ec{_L_&a+vaZGgTA05HC zdagKYe&pDHeJ`ckr}0aM6d!Gw^Fv`X#iq0&7-I!L)Pbp-$q0<-L+=v3EWs^jQ4cNd zACwPfoq+TlQa#*Sm2cnzm|&BXNQ9nmpSjwwMmUCmNs=<#;1gqWLh(*>avOpQ~Fs64<93JFX zPi6gfP~u#&O9Qe!=gdfgWVmXxeLLiqszoEVO-SgCKXKIK$(&3o>nyxeAz?y zb>4a1GHpS3{Yi%D>!0rRt=#s9*|M6qZ6L~xzHZd*2b{ZKVtjAC=2Kftgrt)IHhJAO zTm0Pd@oeqbigO80g)7o_cZS>jf^L44Jtm}Cmzf(22VT8CqR9Extb0z~NJ68&am}8F z9TJvJBBiVXaZo+2Be4nceq-kO{y$UxLHyXu;Url{b+)I7{h=}KZV`%+&JE$%p=o4n z1V`*xhu!^Ho6AXC(m=x4VO=BPZEBaWbsRfE=oA|o|6o`hV8pZhfJd?Pjk~6}GhTRc zL`2w(>d2s1Y|7%bp;Tqwx@rptpXvc%GYkqlpv!4qnLVLA+!(Rciy%iECsSBcuDOyzhnfyN({=*(a0x-`mYc&K zacM&7r!ABb59V~7rB}C-;KqsC(lydZ2uHGXwn#+$-WmD!oc6)D@i@DR`pdngo8wO> zN@RKNp7PPu<=8!u8Xoh%;^?n=nGcs!7b-;Y<5L%&o)pY0#_WVqyBBfN;*Pxj<@l!wlhYAmCNRSLM=_L*3Wl7~%T>D4W?Lsvq;n?1GGiNST$L ziG7oDLgw83>t$p%g7+7Or(@2hlqVKLVy3yyB0hQEaFZk~6tfgvc>l;v1_7 z^mJQ?RIWIxQ$02bxQW%-V-$*pe^>PS@ymc*eUvt)eAZnXnAIgL*s$YczHx~Y#>`c% zjfu`vZ(>^r(RRW1Fn*LX7+zolvagc)^w17kDJ{D555#M;O~t}mu?|2v+64t$)ZV$W zb8tqUzQ|(+1>$QTshSjDGWosAyax|>>9iq_m+ay_HK7-50JM%TKi1$~r5Gh!&f6T-$VC>dRHVM<(aEGevb?PI z5|MwH$B}SLn?o}$h>;=^Uf2meXr(_>L;4G&1P$n37qAB}eCL&Q#g=AH%zqaOW_qKG z!m4RfP)QVZokANLeblq5kmiv{hIxS67=s}^`M5zu(#``BR0)R@qeBkb+$+;P;9tvP zl2d>6h~XrvrHT?q)+>CHd86{8D~?&OsZ56$Z2KfU8Tr|muGFsb=z--Byv%l@jhxAi z-c=+rgMEOuYJv$AN%(ur;ViTH3?24ej}tN^jU;Dw&15>K<^OwRVbN!qS1{Lk=JnynR21?7N-! zbqGfX;iSADTf)MJNBFXB8QtEn4+rdfzP znX_BP)lxaonnC&23hkH={2AXZWiJvD9_8u5q_h%Z`X`EjZi}zTbU@^KDizWy*K&4&J7$=wFQu~+3LEJWe~)bJIj|p?yQ^w* zPdJO^<%9^!N!oN{5Z^Xhna^AfH2Gq1fTXO`Xt;=Q;q9?iXq>y@>6}TM_V2 zB5Vjg+-2=&xO04g?Sg&-nE3Nfxq2hj%mQg#48OE07dA7Y=*pQgTyUXHBH}E{Owv z5h33szQnD{S>yC5(#zKSFDv9VWXhXP8E3n-BQrdnjGcF=*pG9zbLQPa*L4hu{IIQ; zp)ekk8pvHzE@%#U2WXKZ*xf4`5;z=)4k8Jl+5v&F`E0b(A0i4{YUxZ7v%nUUAgQcG z`q@Mg>wL_h? zRR!Cb5tRZH!$M;rP06^ZkQrNUb)0TS>XiC6B-jJAYB~`+1QP(;DDs=1d6rB5#S^#l zyOcQc>J)sT0fwC9ef+AeE;dq0-T9`bS%v;2&zuMh8kOZpreMz-8%8FsIN__UA6a9F zHc^lHCu#N!n;esJoEDZ(|7fL%GErH<@b(fPk(fdDrz3n*V%2R zs0D3up>mT>RF*^&V)D${=&n`6_aJ=`79mp)fDL!ZfP!^ z*eqkP?@Q78NoI@Y&E5&ZH^||qo5$K>m=sSLomV58F`~2OOR)>y$r)6Mn5t6b)-tsz@uA@aaFjt z(+8LozvfbH$dv)u6lNl={EyEt-~I)KgfbKQZUJ=9Yz#r>kgXD=U0-&vj zPRI#Xi-c)c6A79w>p*}W2Hf3y8ST1>^UBDhya5aDB$+JIX(W)kCFpd!5|H-*LV za4SctLnTK|_eJf2Y}edxj-E}dKYp;SKF?V5!ULPk)jsY!kAyM*DQe=-OVbVeDznGM z1DHV*o4fjJ<|SB1JZe@zYYF>fJ0PQ1Dq*|V(Qi)7;oWaI!|!YV#cS+qD0uD# zRPe2dCY7Q8BXifW#?^^|Co2fBzy4;-4-qo498E~hnKWrvEQ4+0nS&mfMpb;1$!KDV z?1UA4w9#=~%j;5#7F>uA#_E_H+4dx{1RXPbfm>+ZkOh1bfMZRj>l6#z*%h^EMh8bs z<85*t-M>nW9Cvd2N+y+0J>=dJJ86j1WeAcW?{}^!QI-* zkf~F!0GP>zWYNIe?wZwx0#-WO?nSX1S;A4(RJS3xU?f!Sq8NpDI^kLN;Kd!kPW%_+0qx6oqtP2N;3iIpYP1 zI1TJN=~kXTZ|AVG_hkvJ@*?lPz5+_IlLeJ4RV`rUBjCKd+vvBxyhDfq5dvO<-)8rc z7C*IwQ?CNyn?~yV6e3tqM+fY0S+4p~D9A)QW3UQ1x`_OhGh6A9f&s>}#A1+*g>yAv z`>9P(?y!sO!OTU5b=dcJXqU_NMh3fe^+tpdn0Q;;o15d4QeQ3l{;tNYF_IlMe@S}iV{oyU&u(V9(et=f)7BNL!;*a*7oD- zJgx0v_YVi+qC9$$2He`x(%PbSQW#oJtC`HP4cT1u6OqQ#9WZZ&m>4?lf|s}9jrTHn zTm9N-K^_w8IzS(*;5$iU`0JA#7&2f6apjO(sT|_jZXM&nxlGXdz1~qM8Jlp3P{w8p z*OT{da%WN_I2;e92ZH+5f#MgTq%6*`%%Y^(#yL7I=N8+5C?YGR4_=UGa1cp!8VWwJ zcYJv$*9p>9!L{I}l);?1&z}7|j<^aAc)XpjO0o8RUrsI2L{R04X__Str!EELAQVAT zUZ$tG$w?&&^z|>-khlGe%RG$a-~i=B(|)JpJwZN#&RTz<3^cj?_%Nw}8b&rhd`q-R z<}x*9B0FE6GwvxIh3j6s>xLI{25cpnB>6;b2JpKgwOdjT5Uc6W>V^Kfc?+{SBuOrl z3nVM_Y!ULj1v#ZgX*>DIIK{(QQikvCu?#s=uOerZq?R2y;jzLA5l|EooVMXM<;A6K zow4!FRJe~z%+A&fA&tsD=vKmYVPJmYTR^#oj5#XBLo5^=(hgkIzBnb@?mhD?H3rPs zM+d)E_`@OA0OK^ZZ?uQLUkfcpEg_qjil2(7{-XFr2ZE zhLb`uM3r=MJ@VKt))?`!e)pW0_!w4?=cf_TJSRFgXH}bnH-YWtb&|;WIY2s6HTtO` zn999A`Gr1qX(NZEi zyZi4g-##*J{Mi_;!F)YdFeMPB-bcakyp@$xySX;eSb37reZZGJ;O9FP`y)OTS`m;3 zH{&>r__x;BYK$m_L*_)!Z-m`bWMhlq5h#CEsaoLYL%^4h8YzUztL|e&r&##7p#{-K zBv=z~aV2Em>=#_d#+k}DPo)#)P66uM-u-t4+8>z4!|ZGjk>^&q#;Hp(eGZ@NCb2cw zw|{(dIyxelUg+O<%-I7aC{uFGy?HvWz@#7s)fzTUb(@kJE4SJAPjT~cG_~Py6=Z)I z3*VR@1TtWvE^Wtnc_ZGT*X&GqtR@z+4pPDen9_;S07IFEvB&~Qry+pta6tQ&MwMW5B(^H5o_G^b2;IywF3Bk6+EE8#F1#8sp$ z>P^K)lSVKwhw@8IJ(D7Y)z|^2+H!Fs9It;-O*ZM<7o$xF6Et>1-~1?xpG)aO@pO2a zPevsW@cS%cWrA19iGs3Ibx=-XHUMfkG*UJRu}Q7aUR&!>K)gnJ?(E*4Pae&EEVL>J zu{pBVva;<-`#drJzP{MoxdG9CpA17}n*@{V64KT9l_7-VOB=9-&2|0togxas9uyw| zO8@^afK<(}vUwM!?5F8S0i*r-5!3Ru$wL3RLVTYD_8fDB*_<|(Njr6brHd$+=@4L2 z^MM5-u~s_=P;@ehvD5$41OujAK26C~tTMZrEyT=oc7IEtlH@3RaNlhl@`fzx`41^c2+XCV|Af+eAf(FHfhq z-cs-)wUvIZZEzZ%i8MJ@bx0>AHK0l^rXTS|fxY16ym-{d`r(NF=Z0?evsR>+a9SK0 zox)r4pQT=vfVKDeH!pj|WkaJSbDOu-vrmBAz*T;}l`X@R0mrB%39Z11aArD{n>Oxe zijCXbOf~}^?I~i-t&P7`h$u_i=1JGVqWvJH_k+%fAN}6YO%{aF=51c&?eA=_g1~-L zrx~fuK?wUAoQ1C5-hesuw=gwJfP`_(s0^R#8A-l^3wwwm*exA)&@)s;wgJJ%%N84f;BmIFNgXKvSA#LJ*2pWW9da+>xBNs=^ zcII|9*C5o7JIcAoRGiH{&_nooP6^ws8!)-8w*g62hru!8Ki zBs=*omza+Z4@a9~@(ujJ14z~huP8Al6K~*N)4ua<>+GqhGNAHS1s?Xet2ckx%&T7Z z4PRDD(Fdb7$Ok(coh5n?2QL&6ARdr=l8)85Ji}&PH>3G;DY~TUZ(JN2S8DdLoC$@l znq2NolUo@tm~fG(1H{Q3HTov5+#r0D?CBD2-w;b;NHwx(KY8k&epWQDUK~_7)7CcV zn@hCiV)!JNoyyAEUwNrlIb4E;9Lu;g3{DxCF`lIH++ zSX{{DT1f|6z~pvSh<8+`gUSFStvGL?ZgE<^Iu^77VR2b_TYaJ! zlV0LAtkcqsXU)X+WO4{BE4$&hhZ`Vj3E|P z@+L^+NK{qE@@1nfN+(P$mpll?@-b(#=qUpWR3?z{l$H3lbQE!&yoG{tF&tojvAWN2 z)fu=n6G!gnd2~M8c#0s@s1Pn@CsE}@Jgo`ysxbE0%aE&?B*+Wgs!NST#3eUf?Y>(B zu-^^b`LTJMx!i$$4O5dsRwrESi&D@c$*V-F>z)cGf?^E%Iqi;p(`tv=NH=d%y*bTy-PC2%d9ok6bdi!tfw_>@ zaQib=u0KqzIxI18M<}94+GX~}N-FwM>tqzy9<(@G4tg+y`tY!&rZ|Uv3^*0%kpRK} zVE6AlHR3YejfwSnR|mKv)gFQ#p~X{hpJ+vSf{{l|8#tgb7goQQ4*%>~CQ6Kg2)u;4 zk1?%}FxjhVxIXa4Y-E7aE-Xvb@keVh?Smhv9>*$(5~KZ!66h)Z% zL@1=%NJe7~iZ-c=*$#KLj6DwQfoR~G(&JOeQBl^?Cg9gXRYj(Hd@>W=-Q5NbD3m4E z<4Xmnu*_8PRkL6s>a%4KmQCxgT_UX(m%=FoJxyK^M-6+dST z67emd^!LnWJ#6`(2yMp_^Ct3lx*+xZ5**3IdeC+*+3*Qo3hgC=s1f_VC^!i*?o!WD zB5#V=6t^O9h#6W?hcz2@8%??B2~(g8O#?%R8??@i9GoDd%~P_VW<~qWX%i&6lKF^+ z5yPM-!bFYnRwQZIwPY0Yf(6bhRAbaGhTykiqEf5gmRHkBBseJbEboF4A-d5p@AePT z+lvkfg#}T9)C#J_AQ{cJ%i!p8X;Iq%lx4PcM32ZS2k2JtUJ`(NYpUm zPdKH0IPKeEDjssM>@()(COp^Tq!2|>r4WH}=8TP8p{1)FVEH)s8W1kEPfLE{Ss>V}r=RZ&-!T zp8~AulZ3L+^*7^Wd@5Huw)e2U=tXf z*}V$A>7!JD4`C_XP079_+js^m>es#|0za69gV||-hmPb|WO3joT#qM!1p6LqoNyZX z8>qm?`O#i1HARQSjLC{MYvJIMk=liKS2wZO6BEsv=vRy?Hgra`yredjo2$4O*2y8Z zbXkto#?b}(!F zPau0iG(0Mqtm!9nuD>QlEvZD0=Gh53oX%V>xHTkUZlhB>{20k=9Z)rND_IQn&>R^* zV8g2}qH(H&!Gj>cEEGeoekO2Iztm{wZAhz|K3tO0O1sv_w=aU zar}lNY*9`^WmA+Z4Ug+b#~3tBK|XG#^g_aqJvq$GBOsTDxd0ZcbzOhyV-bSxVX_2L zAgo?Qt}nnM`dI%Q?)9;N0d)cZ!`zex!2>IpAN9KM(Ou5Q zs&J0Ggwk3U`*kL6IRGL%HxbG>oJQ*R8SD{;aHM(qr+K^^)t`_;OWl!!Kc76ph)Y3d zjsvCABE+hF@NH!%^BhVl?IJ>)!)n^G$%(%0JU$_!kPxa<)Mq~xvi~s40)|4zm$x^- ztT~~IT;t=phm6Ln^RT9^e}l9B)LDAiX1HT^a@7G3)-4P*hRMDU8A}|pYCQ1%2vv9> zESMv8Klbo2y2@Nd?2|jHLphUrqC+ zc7QX|rML>GK1KO#r}u*_Iww|p28}Fa&7H0v@Esf@#lNAF&QyHarNc@!(rAN@O>E%# zGFKcEj7$Q7v?7#ard|%4P7XYPXr306uZJ3$E~fRRrMh5A6^Fs$El&m0%Gx-)C_#g| zStgi^(JafW;o1@<4@HH6AA4*tcX}pw93oTkNcfH}XgNwWm0H=^G{sg5pS^2RAK#>V zgk!Fi8rmv3s|NzkQ+J7^V5CLguEm|ivgRRg;yVrKU7!&xB}6RU330eBpoqF^R+9Ze zRWP;|d>Nrxd(e$O95FrvAs_7_5SfJi4Pdk)UmcjMN)n25(DtWZ;(G^*I!gOe8rUpa zC}E8_iFwN66CHY-$aL(i9-4W*3U*86iod*h;21NvaFS1jor@4$ztvdbN0 z1lHW9w#8s$p`FONW4ADieR{!W`qgRSf9SU7(ANA$T!AwSrBe}XG;*@$O-(@qxFL(~ zEh9h@V&XE|IxWm~H)zW35GT@x-?wRX*X)nnu&DiNgc$6(vVYC$&gTEgs4&1>>$EIk zC?|^p`TtGBr2G8_SK+lN4qF6&%7}uPj|fL$j%1}c`ZI_n?5K6Z)NhkoId$rO3M*QS z(N+kz2oBUr_cF~5YVQS4LI;b(kHkd|g}g-cb182^jtM&+w{3^iI@`c9M(pVq`sA4U z3N_NZ04D6Q=E*6Q=Xf6H%Cn<2x0`Y zPKfU#Pa`Itxtl7orRZ^jvhIPSvdQf$4aY{t6@;vHHo0kfw576!-0IO~Pg2GQ&%>8| zf=B-N9R0X}tyqK_>bK=09(UdTqLwy(yp8BO{d_xxVikDzcprUwySZQ;bF=c81)r5J zuj1-P%zj+JO1A*8V;C@bv!Ww4w=$Au?Ee*Z7!}WYxesbsyLk%3@l=6{c0dU)-bK(d z`-9{l&5&52k2LFgsb(JZ=@t3Qoh(~fF{xRfDMYJqokh;Hqg$B{wZ>ueDTsX8!f+sv zL+5GNlhOw)2Q^MhtJyd$-eFYq9+6wdadclSG#f}@9u#&X`gD-lX6lQ}1^|e!W4nJA zECg-O@}gz#k=|l)2oj^WF(U{9s zRu%WDXff$*ohzE)Y{dEz@R^ml#+(nUE7A&jQ)(>gCWseDqPo(UOw~T>Fl1|-Va=1G z@f!<6iWl3-7EmP<{8nS+Jj8C2!-nP)S&)E9#M^AWodw>6`{Vm%gRd!P57(_f7vYu= zeouRtKB}5?>faN0&lvZx{-qt;;S#<+MxumEy!P=LkGVDLZ$v2`&2TQ4{LN5dV86%4 zcUB_ySLKFlfp(>Rd$Z4Y>3tA!=(fSLo|tq^_}T|JNOm-X>o*e;_lh>&Hg{_!A{H^a zQ=ii32wIq6`#w#h0FsQH=$>PC6zMjW8hX4q2Me&^2xD&b$X>+DbhD&fdE%7Tpr)AhhDgz(>1kT1og_|!F_ zd?F>2CDze+Ni=17F_CAuF)Jn0eqyp(@~!={I8Ca6ja($tj*yEUzIH!QHi$Q_U}zvm zKt1~A>bD+p?Ebumrb8?kv(pDC8@zdwBb^u zD{?MFxx9YND+4@zdhEQ1EZ)0+?@+%UL-a8jiH=Zh;zpC-ea9NwxiE7G{wPBWvkYYp z`zvzC#{C2>s(|ry8UKh(a|L&3uzK_4xz~^5@`)y>(9#o}>l#QeEkJdN6+MYMNyF(w zR(0bn*ws5!pY+9|dgK;#L#mmOWY2XO6PO51<=fs4`ShlaCN#J!Q`hTV2qwqIspiDD zCZb59^0OH??lSJcFlI+(nXx-Ugtglot%1K4(v9nzHp`k{Bkt&ZI2;ebmrdIs)KX#u zN>c(kz@|5n=#^DGXwr_20BP3t-UNQ06cFJ(e`7t2p7k?OaffEj*F}aea$vBx#zePm z`6B7z0L-mFlmj>7&(zUq0#cPN1ZU-w#iJV}=);qW1!lp+f*YqU9hg2zju>W1g9tws zxGEwN372lFi15af9E&{V?KwQF$40?C)`zpRT$46}?Q8|DznEnG2}P25t8~I!ngtcY zD(?ORj2X15f)N=)Z?jdjraenn3C^|SJP}on6oUxZ% zWR~LeJ4ij?#XACAU=~UYYq=2uRh6=*unY0fGYMYUO*#VpRXyFfC8AEzP9XVRdwn`1 zo23F%yFPI2+rA%c|A>GlAIujU@rS+W!CvAFR?>%G!2X2wRDl8)^ zON)qj`cA8wg=61g>rM;bx)LR|tjX#6E=F+=IPFI%jUe@Hsn%woj}S1FJ^?pg&G>#% zBQ8oNoJKR1QJ0X!l;4t~NyH(>E?zt6w5mshg0u87O-c^(>FWkU#`gQ{v)Erlx_+E{ zfk*Rb7Ng#5F5F-u67&3E0JD$IW?g7Z0iv+f;5yDQz}AeOKm39#@gI>jSQRp+7YQ=^ zW3E)OV8Fjla=AMGbEVMpV!e*DO{5t8@M6q$h{ti?%R(s`%%y$wY8(r438Tp8^#LxG z`H*!L6OFO~vDW?w`%)(nu`!;)lMdEw1$V*or<&K-ha;(yNhewaCs&GW@7S;;EZl7& zpz1FQadbS!y`VH%>VyTIuYN_4Xq!e;HttBPPl2{(+jD|zF%fel^4&S5rV@s zi)3%i%6>*4{hi4#xBjK23AfAgY1Xme32Gs&h-mJLI&J_BrGnIvD2g%Szc@CL*6o8* z&r0=@H^Sd$=<+uc6#*nZCvb87Nv6$FCCPvabQ|w5B*Q8@p$A{|>vO~E?!deyC7o&W z;D(LRLrkO2w-0V`n79<0Y2QAzdy>$Oh_YfVLX00sW5gozr$@FpnZ|4e7)-xS@BJ=; zs{(54c6{CcK7{~8VDh&}Xo)6K_F6k9bH9Q$xcLG0V6&_WIVlbeDwVqHxU<;O zSb|mbCjeV7P+Un@$iYB`OL&Dn4D1%wPCaK_+85O`nKh&|F_r&Jq}5^WWg{bX<-p=f zAvm7nTsPTF;j0TX0C39_v`65U8zo8_~4AheKj zwQx(v%ldT9aHT}FA=Uz184cSz6yC+^&Z27;)5GJc=10x1b+xt;x5E*4T7@2& zLjKcVv#hHHZa>7ZjizbVyvzo&CINz_P+?f?e6G6nJHvv8sj?humyZu{v#SaJnjhBzsx!e~9gG`q~g zrLC<=2%&QCudz2fBhI2oVM+wqR^(fhQ(qnJTx>dS!6AK549TL6fH-J{ zN;87&ro+Mhgr>o~%w&X}t1+?Wwcs}aQuRS~IXjwEzwy_0^xV(HXyeM|>3?$F+9ChW zdy*JdCtT|O0XH|$%#^AfNg*ltA+nk2bVMQBDLZv~d9<+6R0OGi7+I3Z3nIUN;!Bb2 zT3$x$-=zT*Ol+6^-;Z2hGJsa>F|}!ig>-*rdHv>oPVwg@3OcM4uWd30@oCVRm9GDw z4Nns^XX_655&(um=a8hr{yFreY0D2`3JByo>|ilhn@&$ zcbMbaJ9qld{Di{|m+K)`W6OOB9oeInHZcteNy;An5Nq7vl-6x2yeRW{TiKA!7U1b8mo#YQf6oLwO^?O8T#-1d%Wam_UwF_dX}YnLf8- zJRcMB?e)Gu7qd&@q0E*eSevl~#_nItiES-$=;W?6%;uz3{GePI%bKk zk&unIzUhWZ#{ujdJ~qalvmfH*30|&VOx_hV&=J?FmffE}sWlc1xr5xNnW8aZueS+U z{&p+oW#(O?!kez{XW#sdx_jk#+}B#E-cpPG+MKrf<9aZSR3+D_5c_0B{hD_Mhftmj z1t)F7l}=!&wTfx|6lKP&uPsGF{3Bce1!KxjiU0-|?XPebIH!1B;8VLejlNTlZox_r z`^U`6ZbuP-Y(EN>%=#A~udI6wiahd(t*Y}o_BkB6aVOQ{#6Q|Ep4cDMB^g2l&1weV zO``Ad+-K3)y4JDgHe!roTlFboSXy;sa7!c)uXe48Lc#%`j5_?JtsxlRhUtpNY7vGf z7Ew4*j1wqE;$Km3!E?l5p){z%bKEx8)%1G8YfbZD21#?~u)p8r)~RZ1`BhNm!Ef&F zXk2t~0ntK|pJR(hoCDpL`KD%3?byvayDqe}@i(tH27y z(tW0Fb@`iZ0M0JY5qerdx}1Edw^&Y`U~?UM?y761L~!4orsegbtP1 zqiSjwvm|&u%hJM{JIU!y`eZjF6S}gS+E3;?m~zFUw4@JR`y4Z2t;z$J?>E8;`W(y` zWtJDGTgueGcHr?_W^tJ1R0&$6%69WtiGDGrx?3mnZB{T5j2C_CKq$;mCA7NA;EL1i z9NQKH1A&@W|G1NWA8g6(mM|GabgKCyU)3^^-dKhpbk`Z;u+B2ci&FFgOPx8oHYfo> z`I2rSnlM7X0Hg=jq6tJ1wxm=FN5_u*a_VMAa_@SG$a_}N&tTLsp~*U1W;==K1_xrE zF<*nth2^y6P}T*e*h~neUuYfr`c;Z(yM7^oTj+gEoMmPEfX6k(Oqe-J-G}GasriH8 zDRgjl4Ap04bxXJnK@>99^0(uW)9l28_3x_ z$-1Df@PlgWb|Fk(!(8CacLLYBwwZX>{`C5nz7b}Pzs|Z!_*d)hgalLWP}12?&}6N} zaq9Pbu{f;gt!8LMrdZk02FueoBPCz;}!WP2LP{h5#-6;RJJsjm7o zdKgl?8k;=YsE(I%KJB3UpFX-rpr;qxEUv_eOMZ<-gUu?pSAWbFt44ok^GghP5Z`fEby6uVQOozn2Q9p=$x7kh|G)$MnjDieJ1sl)MO@pndidWpCOqboRh=KPypT96rp3IF z&vM-anjiYGP`JNj0*~gvi)pgWkJ=CLV#aj>Ohu3`wR0Id((owcH+}BxRZn%Alsy?b zWfTItn4CP_>Us;%Uf`5(ja%gCRBZy#FCXw7mokp!dN0K<%kPSQgJ=9>;w!u4BzU&&hE<_FQRZa6 z%DQ6zi|SZm{wMuSo{cb;AgiD~fS&PP zaf^}t3N8V-l!W0A87vm-BHsRSgAA_M9_KTVtIyb>XFZJo0ssq8w07nBajLOoGDJhMKRL$v_SK#L@@obyZOueAeF%AxIj%=)}A}2=Tcz3!PRYUzoTD2+5NKctyxl* zo$|D4kN{>)VKvvuRN&>>H^qJxteVC~0iOufaNIIjh>KhsIbpU&(f0p_q&9TI;SFM3 zymK#z<0*!bk*F7oz$jw-J4JYP*1e>XR<~8nEubO|+Z%sf~{aU zp`*zYmr9;)x`in~WZY6;%0etoAISIOi23JAO_<0yO%gPLsHw<(mLXU+bfsyy16bJ$ zS^104001BWNkl9*>+POGZrYWqkQQ178&C9F$^}@7HRwmJkee^bm`B&4AJEBUGLP9r_km_XE zqv|$>SsSApxLYZ{D{f`3Wq~E-z7QV&tgj9aH3mYZG6S6`$N;Tg?ec*mFWGnmMslTl z6Dxl#d6qcMBrY}!rjL2o6u93X;PxClzi>Rw&Mwoh2jS+PCb$BZDZ8lubEq zuNK?6D$ck^i#b7oX*KI$Q`}Z>B_4kR?2SvTIiQuT1ddJ&g`kq|3HKb zUz`e6Boze%B+bc6RMZEvLlg#OtnR*KY`b3@)fL9DduNQi>BR(8FLoxjen9_JoIN=W zdq5YyHoaiXu#O$K8=2R&Bl*yX3g(}7K-!G)n{9lA<+apozSEM~Yi)s}lh2KJToxt) zaJq6(b3KO=P4zfOmXu@~S79J6$@N2@HtGjLk{~kw{>XJll%~-_cJceKc8B4Hn%&+?9vp@-Q9aeUne}ht5EKvvZ&;uw z1v@)H&7k-8LIUfmAfk%-(`1JZKB*1Uqrl0Gw2KL2P9;~^Og!D|Pu=|TfphDCro(sp z&yGg=HWKrrFA|P*Ariye$yks#TQbG$L;{0s9kXB zY1YwVss5o6Qn$Aex8yugS*kN)cl0+NsK~?}d4Hem(L-mSvWZ2;(}#p8NoEgfBJn6R zvo!135_*K!(PaN~+pGw@N#_bB`xX7Es;3gHR98QmeofPh$aHZ4x_vY=l>~CBlUaH7 zS7_;8&L>Z8Jn$mK60_NpE$etvJLf1f{aWm)OJsk%Dgt@5hdk?AJ7y>|&YlUU?9$w+ z?8JgS+{$dtlEdUyg-*P-i!pDSi25s7qUoKbr=M*@+vjB7WSzS@@7%be*RC_|Y4HrJ zm1Y!QoHW}DYa5!3JyA4~lI!dN1>n<2v%G1RA;GE7neqpZ3jY(0MGZOoeIH8;LhVu@ z3?tKT0KaMrR-+M$F1S+Fzd22P5V84U=*{pAO>JKHRCHY~^Gl~S6C+kQM8=K+<~w2Y zI&ZKzj^osfQlz=0h^e(?&uklUTFMvvx8nC?PvH3Aj-fuZEH3+}K4fU7OIz7UDYF5f z7wn8-0>dtB?Gx`b3HuyiIuhC-1H7IirO6SCPq*~MALY+-(qT)@{go5@ko?k=v@qS$4ELApF#{t$H>N>1 z`(8B5Mq3A$cnHF6ZEalq?xW~!IJ56%_Lw^QN9+8B*E7m7Gk-26W(Ukoj8ryK{0X2u zuQ(EhzvCc^Fk*+27yu7>-yw?krjwGD7A7~%_G_I|y&{11yNry|;2J_nwMLTiw!TF0 z$m1rwrc$ua0XPBEgr9Pqz1m&*H3hagXV0{-O#UrJu4jiCeN_08G1tL zYWtXVgf1%bjsA3tOv59xJCGK>4LA6fEY45-eEPP1QZ5ff))c!HiH z-6lE<7Y-J`SqO^GqGgv<&>qqjG$}(BG16B_^ zTIPclM)Mm0=@M-zj(%bI?vo6mc1<$p60+I1Pz$WU!;_MOvO*^dQWy=QLt#H#CXjF2 zo{@~Ei6s>`T%}rrld9RPtEgFHjiEs=RaLbn&db0Ph5xA^*0Py+++U=8hGQ)gi_=hX z7H8b)GVg;BAOPF@u{qk!R|-67rzk@aLcdIJc|c(zU=%?BHoA`v3O^%n8HEaaA?+-r zt(|`d4CLS$I4tygb^I-8Tr)0o6%sSKI7V9-gsg0U$MQcR2y|`UI09f(2)5R0P{5XO zzZwLz1(|RZv za6C9RLJ$ZWo@Ho*B{*p0o`HF8u-iEK+`zB z*7POf-i21_cxc%q%mB*l z{2khvZTNN|AkBy9%>!(Ph|La-rG&HIVQ10~vzy?ko~dFgs-wxv)cTqK>Lk+=x&mpS z9VpB{az5DLSoR$9EVq{aR@J;Y%nXiZn9hJJFme_v2f?YQ1X^FAJ&BdXsHbao86G zJ;Y*n47aAZC;?yrU$`HxSwdJ0N|*U-g4T#Z#tzGV z$n>-BhsAaNXp|`gsBFZiG&`TnUup-24m1b~1hOh(}t5&-PH40qVs#8tDbVs6@_ znUhH8-%uk4#Q!%(Tj<$Vc02uL8Oz(**U+A%8u=su8YY<5qcpR(#`m;G6PkblgVWYS zqDZHS3=6l5_O(TOjndJ;sYV-%UN_c@o6k^3qTUX>fx>=?p4j9|wVKE2drE=b#w;s( za&4S>M*viMINw;5q0RH0jxWso(d2IDi(J5NP{M|)Bx7P^qsW}AoSSo~vebrDPbQ(D zDLSLg)H$e%P9KmB)E^vlkQw*^ntCcay^rgbX&1;fCvvEnD&q)M*M~f*1+vbaW4ja7 zuQiPzz+f)Vqtw7v3OMYd#>zKW8H2Lm+hl3B18O006wHs9$Je2s73`--?IfQvM~ehg zEz&Y>^ObR2sc~2-FA$P&r3u6z>}>%aCBYNfO?SF(|EK0SaiKLlAIM*PgI|Zfh%j_8 zdruZ$M49in-*C{cmNS{%?M&=JPZRD3J(ZSqKPPjW{ByE^x+JUwQ*keE0S_QgE-!W8ZcR^K{u;@h#z*yv~`aT9We`+0moQI;e zInHN+xklL^dcyRcBYJ(VCl~qOJXhtw68pg)=z^mHjXMF;M@jnJb)AjSjN8UxYfZ5W zu;{`}B1xCRPbgCepEkP7JkQq_mpIkT`U!6^$kka+@?Vcsrx_31j@cC0L>Ig8v%XS; zjmp5HEV{K%tq5T7PJ^8}I};`uySD%@RJO1{OF(S}Pa@ zN$hilwVv4-xIP*bCA5qj&!2zAin9jPG4n?yAWoUoZiSxff>~TPRP_U%T}uk6UVZX| z2#4}ohmzJ&9PeYRlNmhYR#hj-DDjGk#a{A&2n(A)>(!2&&B*{_2I<)wIH)MM2_1$o z6I1UlEb&)PCm8XKgK6Lqp&5%-UXu!6@7SG3o({3ROZO|<1F!;8s}Na^ik=DVcmxnS-)^q5RsCF3P?6JvOGLO50_5eT3BShrvs_z{hlxE54d-3wwl5*Z60$?yv)F~+fYtYo zOkY-Q>hTzEv|~j7iTT$Fn|5jq^5EtRdRcmbY2<{{{#;0m&|i3bD~@l*=3pcti1mbW zZK`QKM)-_-GC{I8rkc@Lsg(rgU=>En;1_iqP=Eg747G@ZXpXPc1Se@g5ojooM7BU9 z)gw7+|2~CmjF+y_qSKWnzHvOBd+t4mlmi9O7t@8@cfQI$CzmcIiwAj~H-jjcv3({u zVde!h&;`fCFN~Npg~?#G{Ld^=b$}k(p{W^*R0bG#?JentjS1i>iE23#&PKr5d;1Go z8Zz+M1N4}wTL(a8E_R~!-%o->yTU!mSUI?Lnyj#r>kXO03ofn(8E*{Xc3R0Bkb3+&)K!gQYC&>rclg@NQ(GWv_$^s)btm}U z{Edg0QXHyX+_Q)f+|b+>SU~N8iyd^)0!W@!7yp+57d>r+(m%FknJV<*pg!@}ATZq# z11bZOAmVZFQ`cX(3s?5Rlt+DQCmaz3H_pwM2^;f(|Aki|E^PPG1y2YMx^E(0Q*CF8 zo36E&rP6>chUp$f#z900-B!ql#DW&3%{y5IklC{)*o83p zEBmnBQH26f^;~q0kBb$px6cWiPogWNC+v+bSR(7e*6bDPei0f771)>O0SzoM{|8UV z<;}FWpZ&n~|<``|ItGgKqoTE0hlL4-pq~l zoFM>&2>I+HfM{W^HZR;C#=0{$ls0KA^hn2`R)Ok3*#zW>G}j4c!v5Y2r<_l* z!e~NeY2ZQtiimswh0wwiEnT&Nug)Du(+6_(1Z_k`;y$mQVF`f-O6^qJ*roDBtiEBS zZfdHDw50DCF{fUaj1m`JnE3N59)OA+kj4N#tOICLw^0_V>7C*%OhW^RrBzO*{vJa-7tMULn`Op+lbRUTU zJqH<}^PfIw1K$<;49hn*9yA0=WDh*A)?4(qe~RL@nk1s_)4ve5p)z2+bgwys%$#at zv23Nrxfv8W7Pv{eI1$?i!dPtegeEHe z9x(-t_)cseV6zcs5P}cW1@}5hU>`FdG+01bKo-~?kgU2Z$d#XsV0Z>&h`o6sKl1T^ z2?72R2Q5=!#WbR|h(9c*MuF;@DPv(vQJ?E80yIVmQV$Zol}205#=z^0?cqqmYYb2` zeFwuuI0E~gL>T_VV>Fyn9L{Lr38$l1_U|uSJ-8GKvz4$9=e_#4D!j2joH>gru!P*5xTH3+p)sQ7X@|m;;a}p*7KH!!-~Se0OFn|F2!iJ{c^uu2ZpY)7k|Qds@3@KFMUhx}(!-QkDR5#vL(R~TRe~3Y z47b3^-HjxnP z2q_spObkH1pAeqg%wJHqcNP$LE;!)j(XOEivmx3-j?pfib7aR}EZF#OXukmd4>Tkj zh_ih<<*7&mG>jB-CNu+~`f*qp`b&>)|JT0=wlQbql}W~al;ghvFjEfx;QWsU%?wYE zt}vrP9WrWQYb9_FK!@fh2Eg`<=xP7d*nqKqViSP*7gn;bA~3+{@tNaSiH6CLV~774 zEI^q5!bDe?|L9*dgSCZA%Jei{$4XXLGU@(K7Le2Sl_B(+6* zFy)ecx9a@S?qo=T+yA5mFk8(Q@U5s^)GT{WhMfmB7=O9m>+F^b{ zBNX6v5%vjwmDLZ~l9@4^pzBaB3=pV1H$L4;Ia!3(0^G0v0TYiNR}b`g=?DbA&|qe` z;91Q8%8Bo5^iU=js3820F^Q*^FoUyLBhLS^Flr#biVPleTb)MSXxm&W*eyzo=`wpL zVFyT3=u7xSyqli+g~-raW43{Kvr}68RBf2`kkXlrsCzPivmJ~077ECV{tNwzNfC9C zu!V#Io>&X8iUPF<*DE6@Q#ytS3$N82A7cAFnnM6Z;G>z2KKC7_K$>;<(%(?E?FA&- zX@SOggBlXFhr&V#XwV%{Mk3u3q%4BiS3g*o-dA-1uU8CR^#8_)hvOHXO`<1b;6rEo z_=30yB>iGW9Vo&LltT+^c!^>mEPS+Xo(9$;9`cNbi{ZGUwK3&n{}!rou*Sh2kz0`qYik{98phjXkEqY1ea`~~&_ z<Zl&jIzsIHjQ#(G%2O z{-EdWs53Q3iix^e;RW0}S3Z^T1pI{z7sh1+pzx|R#~(@F{+j6F16 z@O%fQl39HiV`_SGv;RS3Jg;8F2!!o9LbmR3IV+WBU}+20m?Oo>?m&p0+kO#sFE7v* zwgw{EMm(C65nWkZ`S37vZ7Qb+Lw51}-~l;4Yy}?#kcpuWI2%!8fyU$e=NI+-f(PYq z_JO_~{mV`tpEDCKnUaz!Pe!n8UIKd`b+;1|?=d9-jAppUU5De?f-24Oe{_XkXj~(X*lI37#dU2^-W|PC*LJ&n%ej2lP|jI zNzcs9lXP`US0FVVzAU1l2Np04Q1Hn5UJrpmugeQ4pklzB&THHOV%oKH>5mX?G&O1c zM5=IWK4Al>5%e;-^aV%wS3bDad1Ay=!ywoI_kZ8Gzdj7BAaLTU4e+REQ(7hvA;4|6 zdWzXqkSATJ891V0u{V$b%@XxntCboEP!V{Ue-9vm{xe70p-6$43WwSgl0lGy5MUW^ zcFWz<=4tc8oRhmIo5UfloyXv+4ffr@9WVyWT#=!H8u-E=U+=mdpz}v?7#cw+@E>X_ z4(^DHh}98`f?JBfm!%@;UiLrbAu1e&)1OP6jK<#$a6QYm6ui) z4(}g4l?9NWqt(>8b3i+0&&)|0;_@Mhs{K4;p5C6}xDn9HM8!JP*q>cFYn2;}RG$Mb zXz@l(0X-$SZwTl?0QjH@unaNpoHo>0&N7$w+ zT_cTNA0d|6s!N!42f#xHXq}6KS7XS)&2IApQ~;;}nP3i?X4~o?PX@UABGT}SIi>?1 zo;WRECoVIk&R0zmQEv)a@Kfw#=s;QNi8J?rF7Y0aIQAp8cujXv>;R3{Q80`j%ZT|& z3zRU^KeEAEu7z;g-naRk0NT;*SecW;KWu7LDL@8@0o)Bm4*=D?RIC?(US!8W?Y-y8 z*MB@9K`3yCDa%rF4)@Q_oiN5%;qb4*1b1I)HjH1sLNb7&Mg$U23edz{M1lXb;7lLQ z+0>yy3QhId7ft1ES*4lx666{bJK?x^O^WwB>Gb-!wTciaFD33^fJ3(hbp8M}d&GGX z_U8i|C`|evx(RVeBYX0Hx%qt~I5!hc6Rz<{cK=tIIJCr_5aR~6!6R@qU$I-%sd#j? z<^0?NN5B_2&PE%%!aNUU_VU{M@0GEFwYR2cwg9UKulj2>y?TKbTA(6AsLV{2 zmTm$tTJma63KvuGoA=E%s8`Sd1|J+$teL1|G0ve0CSQJ~z>R$&UkU$cIMTmq#mIXr zc@+(OShrB@F!)F8p-ih1+k(svi9e!6Fus3Wy%fxkY5eNX>@Nj&lzAWR&fc!t7V7+~ z4Ctz+YU2--rdm_X=?|5GG0i=~eH0M9+vqU>JQ0Eo_Z>w*4gNol2>E|L)P&%rOecN7 z%5dz%a`|J~J$|ST0%Xi$1mbQB(gK}hWFo^>3?v31p|xL8 zs7Hol7vMaMxj8R;;@ERJmZZU#iJl7K^p3>%{g90Ri2_d7N*`FFFB1!{B)|O-(Q6>s zVvmv^V;19up#pd^+m*mtf&C6Py(*FORWr4VL}Y^42F6W-)D*=a8q6~sa3%{qBD(>I zd>s$~`bAlRP_V0GVJ8272;dkB@F)pfZ!jRbm>6Tl*{k;5i5x~#bv79GLsOndw`FUh zAz3E_?egbZEO*%f)*l%3`ic=Jr#_@@bdZ|5m;|Uut@NV>B6115`EA}Ipe-vn*VCU| z4hQ*_Cnkg6Uj_=y^$3e4*kC?4hbcIw+++hbh=l=mJX$4$c+&N^O&M%{IPEv=sWci| z^$Bp*LPL9Ku>FKV3KJr+Pxm%OiqZ(P3qnA6z2!Gs0-!oUc>zbjh^GoM000oeNklfDoXjc>{Wz<|$z^X35zoclNa!cE)pz{EIkKYo84OJHC`-%B4kN%~nN&lg3BiDVq)`D@0kRZK#)Jw9XX%j(mIe%JPDHxV`QxOT`9Q&8 zH~9bLrq2@-V<*Tjc%@PRuzHqWgz}u09?V%sfodGqQ=!5zQ8PtDciN>RQ}v%-71e4e z;Isa1{#RSzd;!a{1&}tA)&vpdAfjU^Z`9H#dc|1zaUJhh#NBr-;>kbPSq#X~q)1%Uw@8Ac|M{W~$T9EB2t%L!W4 zmv(nxFX%ZkJVQ(OkYfwUwBicNPcFFIppwG>q2Vdi>4sUDbn`d0Lke;28f|d?+V&Ft z@_uVE+LHnAXT?kRIyQk;I5}$2v3s?g5IY67nb@ei!Crgzh%!c9a0*I zNecH!rtr%`h~ebv_ax`9HgGN83B;Zl<%$i*07tvp*p zFkc#Kcp&Ub;ODO6ajFBOzgY|D@IWw1b6Pi(ftrdN5&#E@EmiSZ8;T4w`DWhiDDVHk z*2lR=n{davaxX$8b?S9H71GGk$LR-k?bk)<%4o zKJKCj)V|JatCyGw!|u=xojRjwZa z1QMBv%_I7*m(y{!bd|WN~68K>J}n2LsQKD zXuZIy*TwhcEv&;wi}5Hcc+?4?E4kCHFEO7uR@DrTwBAvf>z;yGUElSW{(^t{vWLcOghAxl z;kTS*1bOv;Xw&eS&fA%CVgI6wf56i{V*)-N1(0k08qYy$?~lPt6}zJ~m*wiD*0xkCfhKy!+6~@!)sz2pf zY3vZ*n7q^W_AmwPp066-j>9*Dh^}l^Vlr~ba$!K{Fer7CGTj1ZCXZMGR98HP)_Ull z9(eoohScwodyH2zm;h}5CbqF)?A5!4J}ZoZ0;_K{0a_TQq0s7Ha3gX-6Nr8S*HQCE z#$V=nWA{Wpv=QR)rNRyG?B(UWRD=PsmrMKH|9k^E1I@p99siq1ER?c@C7!iyhnpwlxMn$m<9NXIb5ae z2({$we8vtDONrCsj6ti{q^OY$j+N}HGt+Nu5hZd0a|0>NT}4#qLdg`;9~Mq$_A^^ zDJ#**8#=sCxI$4OJdA2zyg{-sSBcv5QEePd98UP7%uZofZEqT5fEZAdErk2m@-x6> zmwIX8X+jZzvy-n{1V`?wGiGEtoPyl{GJbTiQ6=>o1f!qLq?az;`_DQS{fDM#2)>Rn& zf93JhmDHP}?_IgTBQ5jl&^%eio(deyb$Qy=e93(+YX=Xo>0V_vvH}wjVHmXxG*eN@ z;tbpldxS}#A#1A?<0{L(4$KUczEp}9v%7!SL~MrhYu3-x6C|eekgHk%6vtu*#PYC2 z^(DH(vH+ZTIVBcDiAW^EU&(te4z3#dW7Y?LuHKb%HYY+C0;F#Klh};;w=;|GuZ^yo%WnP+oUtm3+ z_Lb{T8Of1isk_egxf9D>S{gMkERrwuicQapQn$I-jDo;52Dt$@$Z$`aLak)4Y7NOu z;%b6UQQJQRq}*LG^tUm&6d$QaGp#j!lKKEBQHY38);^kxo&ac`whNzp32e z{ef~*<%_R(3X9#rcPMvYsj;iBFLR`IST0WAwD9RkP=YAoVo_)G+C~gCcP61f#P#$}dVB)3s3o&0O1Pc`; z{0@MD{aN8Gm!k#;o03L*!;2ObN8U9Hpb^9}XGKri1Y?|x?>^SSKZETXy}#S0M==bu zKnqye_;J6QfVe2WiK*O|f0Y*hT;#>eUM_Y8(<)xPf@7BN4kW5JvTL*5XL%Bb9UaBu zL+crq=h-RGcSAEA_SHo`+8uu1?SGeuXJ4_|w9#_L66>5tht=c$X%N=n<~0F8?5zh_I10kjszkJreQ1y`7gSkt^pizX952`i{P8HrK><4v?6AnU98 zdVq;vR)5?TevhRQE{Y$fnGF<4?&&DFktBuTkNCGgj1oC2-T;oDBKf*?=k3S0!uZ8h%rE^fbVSCR83UeDG=+zgr(K5 zS`cq(jHRlxLBLrVzs<(Ip!{_PZtQ?$3-plUqV>IT3eaS{r{ajzjpIZAq3VqkQ+iCR z5}EZL5KB0PDOl7>G`1SO$@@e)*G&V)VI!H;+Cll1w`&B^yRWU@xenfv*sWi3*_?%U z_iksMmZ>D>9(cT~`gw!W?{&yr=G4cbe9VuawrDburjYrAqx+^}g|;H*v|2pX9ewbU zEEVaAs&XtgHb5ymY&6`|3%F;pP8YK!bAVCV@y(mtph(fOQ{AAA_Qf{jHkr|zhB&6l z$L&tJ1Rdwd4j_cNx0vWtIL4^F{MAj6%|Kcz_aXW37!<%@OzJ_~GEhW20;@YM?6m0W znfgYkvE1-o9ck=3e$yJ#%iAU>7PpG-8f;RM;>ICJX@CaxR865<(2SX4_v>TcCi+sL z&9p2my%pVbjcv7gCt{|jl{&%Akett}iTW_qw7Vyzv5yLOUcoKj&2?$@bN{n~{5Ijk zhkPFHq1yPJw>W-pqFN!peOuC-|2Y1~M)Wr6lSl*?w|7PMftt)Q$Xos-+`|m|jpu}^ zh*J6t8JWdb7}k!XMXd{|eDGTVW1Xj~C3l0$p6Zu+GkRj!Q(=(#SF)*FrQ>-eKAJr0 zRApwP&y1~g!**cp&`N+RO8rg#YVjvwh4-MWschEkW_-hvxL7$g`l-PLOa_%7RmS<9 zY9YfSBegWXK30_u>F_s6pH&i6zhs=p=m-4G`1Q-801?f?7{xw=0%{Ou;tQl{;Q6F7 zCAWoRXgoiKmaCLv5Jpj148xgT>CWNj-j!;iKPbXrDF^$QmeR(w2lA1sYd^Mf&S{A( zMAhcU3ZSswF6lywKa}WM>Z!v3%@lXOJ;KqulDZ3L2t})|i+tB+8?B@w$Ax$t6=p*` t&%Y{?H*U<>I?=z9mHr!B_*fZ*{{y`-BuTp3m2x??fAfxgh^Zeh>&GXlY?$3uI5=>hN-cK;S5^ zCsshwX#>A*N+uK8WFi`kCKAzPGLRtv!e+CHM4*p`Kp^MG8?Xno{yhN} zkO4VB1aJnCK_DU#Lj0$X1{NW*K|~M;%?ABj6b(WH=)YMYAbyMeULX+Pgry1ediX@N zBi{3;j)VD6jd)S> zxdALGNSa0oTx#uey>)#v^(SZ_Zm1=|%+H7kJ?zun<%{o<^4j~Pf05iflSlayE}6ph zG;3K@?4iA9)Mg1(OI>W*?L;%;BSTt1-M^0CEryN8b_tAyB!qI8nRRYD2b?Xu1RqvD zpKwDoC}2kJ2I0(#PS4!&9M2yart8?z{_%nAuTt7yx}Qzk=SkxFcC71eOU0)xdttnI zNL!6Yue2%EtA9F_j69CeQvQS=?-6eoQPg+yAb-An(3<)Y8n zdZK^1sN5BBQE$P!O2-DhWTx>s4aS{xH51cklvU8)4^3*iT(-5HVGK+bg}c%c1_udh zsz?_a!&x$jp!hPsNtkfW!q2O$DG1eOb|&&hGtUEg`6|h1JfEU7_wf~oWb?we6Q9e2 zkHwaNFcKe+_u$jh(>+$6&Yc3Eixh)gr-%5+`o;+(#cr9`)f?>57dcch>2mO-ge>7J zF8&=>rS93^R;M*MK1bdw^&FekEi=rs zjUyMlv6DY*(T-{95z7*~W)igVF&4@p6JNegunV6lj#k*Cb)-$!HY< z(NSRDKt7SE^)&ZzGPh>u?A+;cFso$nMS400%MwvBvAnV7uV6FFnep6r1KJDsevVHu zTALj27jTZUi$>LZ8F&{$jG$XOhRKn>qEl0*v}7llD-M-v5jQ`4hZUXZm$e<*zi519 zBnOJl{aA_{$j|6+&%AIviuHHN09%L6^B7H(je9&6S)#tJcuypR-M_2AXMDCT5K$<7 z*B-pOVEgvR7Q(#WlCL0zX8wi3!D^3&w^kOa?!XdbKkZ1(LK}bBJfy~8F9#ULA(k{B z^5Yi34M|@M0+<@NX5|i^JU)ryM+GLlEAg>bF-c&3_Iq!h@0n=afoZ}J1eI@3wMZK~ zg7I6T6GyZgR3BMu=w8|oE~8&Db`-;JjMGc{WS-lLLsg6NI$lc(r>h{aE@ z%2&+C#0L`1*8UDGZfE8*Q4vj*JZr>aC*-GImxG1GNn7?jlHQ!yT!}X%Qn-}@vb#!Cs zUk|sp^Ct5}I5!hSc%^?h)1nIQ?%|-zmwF{{Ia@9rXYa)AMjrpFpSa8M$TgC(aiE^E ze=t>uzg`W}pOoQ-El`&-)hrucM-KK04T}lXM$x;N)($&gHuFT!ysM%YhZ(o`rTmSj zn4qE=#r)Lsfun{)y*%W5ghzt!ZM6eEtEE?AV%B@) z;;|2`RY!U9#vw=fC2knVL!2s?-FmtfS#o!hW)kAO z(B0L@A&(zFvE4;w0c|zV=1_(Yr6ToNYxRO=(vkH$v@LUf?Z#gCdInUfJZx#|wjfhh z|FUahwfxN~7Hw{&P`A+OaFMt55&ll*bRvT}?x$tirSKcBqbw4@dg^nsO>!GH7MW)A z>!r@HO3A1%bsU$y#9L8))l3ITWu30$>YzL@g`HKYm9RhQ_CoVPii*}?*=_Mb|57SS zz`mvuVZ%V{F<+=~9?PS-u(R0ez6Ws}_P%=ZCtWlAnzQrh6SDBEiiRvxy#;@sn8Q4( z94n3#^ga0Cv~Gi-ntS`LJrYJT0TU4^1mTqpL(lrG)r#ml_3e~WEn`-;M7QArH-`_7 z%4I7P^jvfI-f11kL-7W~!lX84@KBiAfsQ!)xsaPq!+*-%#&ad5PQG$Asa8*Zv||#r z5i+xHnfPKW$?9B+qgoi}>ZyU{PVxPZvB5dG`ymeVX(G%b*`ye_Y8h; zgBN=`Ve0&9%G4q_*zmivAETsAD=H*Yu%^pp?V0lsLov64eyX$^8QC&v@GIGr_NKMw zfp_(~=s|G6gSEL&-bLTDdJAkKa>rBR?xeBqo8>$fUM$LMpN{nm58Ia`im#{;lJ+FZ m?0uqpxE3Nkis literal 0 HcmV?d00001 diff --git a/doc/psl/appl_guide.html b/doc/psl/appl_guide.html new file mode 100644 index 0000000..8039723 --- /dev/null +++ b/doc/psl/appl_guide.html @@ -0,0 +1,430 @@ + + + + + + + The PLIB Scripting Language: Applications Guide. + + +  + + + + +
+
+

The PSL Application Programmer's Guide.

+
By Steve Baker
+
+

Introduction

+This document is to help people writing C++ applications to include +scripting abilities using the PSL interpreter. +

+ +To include PSL scripts into your application requires that you: +

+
+   #include <plib/psl.h>
+
+
+...and link with '-lplibpsl' +

+Then, sometime before you call any other PSL functions, you must call: +

+
+   pslInit () ;
+
+
+Each PSL script is represented by an object of class pslProgram +

Example Application

+This is the simplest possible PSL application, it +loads a script called "test.psl", compiles and runs it. +
+
+  #include <plib/psl.h>
+
+  void main ()
+  {
+    pslInit () ;
+    pslExtension extensions [] = { { NULL, 0, NULL } } ;
+    pslProgram *prog = new pslProgram ( extensions, "Program1" ) ;
+    prog -> compile ( "test.psl" ) ;
+    while ( prog -> step () != PSL_PROGRAM_END ) /* Nothing */ ;
+  }
+
+
+Here is a line-by-line explanation: +

+

+ + + + + + +
pslInit () ; +Initialise PSL - this must be the first thing you do.
pslExtension extensions [] ... +Make a list of PSL extension functions.
+This program doesn't have any.
pslProgram *prog = new pslProgram... +Declare a program (called "MyProgram").
prog -> compile ( "test.psl" ) ; +Compile the program to bytecode.
while ( prog -> step () != PSL_PROGRAM_END )... + +Tell the PSL program to execute one 'step' +
Keep doing that until we reach the end of the program. +
+
+

+Easy! +

+Now let's look at class pslProgram in more detail. +

class pslProgram

+Each PSL script is represented by an object of class pslProgram. +
+
+  class pslProgram
+  {
+  public:
+
+    pslProgram ( const pslExtension *extns, const char *name ) ;
+
+    pslProgram ( pslProgram         *prog,  const char *name ) ;
+
+
+    void  setUserData ( void *data ) ;
+    void *getUserData () const ;
+
+    void  setProgName ( const char *name ) ;
+    char *getProgName () const ;
+
+    void      dump  () const ;
+    void      reset () ;
+    pslResult step  () ;
+    pslResult trace () ;
+
+    int compile ( const char *memptr, const char *fname ) ;
+    int compile ( const char *fname ) ;
+    int compile ( FILE *fd ) ;
+  } ;
+
+
+

pslProgram::pslProgram

+There are two constructor functions to choose from. The first +takes an array of 'extension functions' and the name by which +this program will be known (for error messages and such). The +second constructor takes an existing, compiled PSL program and +makes a copy of it - it too needs a program name. +

+The second version of the constructor is especially efficient +because this enables the script to be compiled just once - and +run multiple times in parallel. The second and subsequent +copies of the program consume much less RAM than the first +copy because they share the 'code' part of the script. +

+For example, you can run two copies of a script in parallel +like this: +

+
+  pslInit () ;
+ 
+  pslProgram *prog_1 = new pslProgram ( extensions, "code1" ) ;
+  prog_1 -> compile ( "data/test.psl" ) ;
+ 
+  pslProgram *prog_2 = new pslProgram ( prog_1, "code2" ) ;
+ 
+  while ( prog_1 -> step () != PSL_PROGRAM_END &&
+          prog_2 -> step () != PSL_PROGRAM_END )
+    /* NOTHING */ ;
+
+
+

pslProgram::compile

+When you compile a PSL program, any errors or warnings are reported +to stderr (by default) and the number of fatal compilation errors +is returned as the result of the 'compile' function. Programs may +choose to ignore any compilation errors - but executing the resulting +program will immediately produce a PSL_PROGRAM_END. +

+You can pass to the compiler either: +

    +
  1. The filename of the file containing the PSL source code...or... +
  2. A 'FILE *' descriptor for the file containing the PSL source...or... +
  3. The address of a null terminated string containing the + program source PLUS a name to use for the program when reporting + errors, etc. +
+If you pass the filename (1) or the address of the source with a name (3), +then error messages from PSL will refer to that name. But if you pass a +file destriptor then the program name that you passed to the constructor +function will be reported. +

+Applications that would like to report scripting errors in a more elegant +way, may register a callback function that will be called whenever there +is a problem within PSL: +

+
+  void pslSetErrorCallback ( void (*CB) ( pslProgram *, int, char *,
+                                                        int, char * ) ) ;         
+
+Your function will be called with five parameters: +
+
+  void myErrorCB ( pslProgram *prog, int severity,
+                   char *progname, int lineno, char *message ) ) ;         
+
+
    +
  • The 'prog' parameter is a pointer to the pslProgram that had the + problem. +
  • The 'severity' parameter is the error type and severity: +
      +
    • PSL_COMPILETIME_WARNING - A problem was found while + compiling the PSL program - but it may not be serious enough + to prevent it from running. +
    • PSL_COMPILETIME_ERROR - A serious problem was found while + compiling the PSL program - it won't run correctly. +
    • PSL_RUNTIME_WARNING - A problem was found while running + the PSL program - but it wasn't serious enough to prevent + the program from continuing to execute. +
    • PSL_RUNTIME_ERROR - The PSL program 'crashed' while being + executed. +
    +
  • The 'progname' parameter is the name of the program. +
  • The 'lineno' parameter is the line number at which the problem + occurred (if compiling) or the byte-code address at which it + failed (if at runtime). The latter isn't much use unless you + are a developer of the PSL interpreter. +
  • The 'message' parameter is the actual text of the error message. +
+Notice that at compiletime, the 'progname' parameter is the name of the +file or string being compiled (if that's known to the compiler). Since it's +possible for one PSL source file to '#include' another, you should +always use the 'progName' parameter in your error messages in preference +to prog->getProgName() member function. +

pslExtension

+We have not yet talked about this mysterious 'extensions' array that's passed +into the pslProgram constructor function. +

+It's important that your PSL scripts are able to interact with your C++ +program - and this is done by creating a number of C++ functions that +can be called by the PSL program as it executes. These are called +'extensions' because they extend the functionality of PSL. +

+The extension parameter to the pslProgram constructor is an array of +pslExtension structures: +

+
+  class pslExtension
+  {
+  public:
+
+    const char *symbol ;
+    int   argc ;
+    pslValue (*func) ( int, pslValue *, pslProgram *p ) ;
+  } ;
+                                                                               
+
+
    +
  • The 'symbol' field is the name of the extension function AS IT APPEARS INSIDE +PSL PROGRAMS. This doesn't have to be the same as the actual name of +your C++ function. +
  • The 'argc' field is the number of parameters the function expects. You can +set 'argc' to -1 to allow any number of parameters to be passed - so that you +can create functions that work like 'printf' does. +
  • The 'func' field is a pointer to your C++ function. +
+The list of pslExtensions is terminated by a { NULL, 0, NULL} +entry. +

+The C++ function has to look like this: +

+
+  pslValue my_func ( int argc, pslValue *argv, pslProgram *p )
+
+
+
    +
  • The 'argc' parameter to the C++ function is the number of +PSL parameters that were passed to it. It's useful to know this +number when your pslExtension had 'argc' set to -1. +

    +

  • The 'argv' parameter is an array containing the parameters +that PSL is passing to this function. Each element is a 'pslValue' +class which could contain any of the PSL variable types. +

    +

  • The 'p' parameter is a pointer to the pslProgram that was +running at the time this function was called. Notice that pslProgram's +can have user data associated with them - so this gives you quite a +bit of scope for accessing script-specific data structures. +

    +

  • When your script has finished doing it's job, it has to return +some kind of result. This is another one of those 'pslValue' +class objects. +
+

Example Extension Function

+This C++ function prints it's arguments to stdout and returns the +value 123.456. +
+ 
+  pslValue print ( int argc, pslValue *argv, pslProgram *p )
+  {
+    for ( int i = 0 ; i < argc ; i++ )
+    {
+      switch ( argv[i].getType () )
+      {
+	case PSL_INT    : printf ( "%d ", argv[i].getInt    () ) ; break ;
+	case PSL_FLOAT  : printf ( "%f ", argv[i].getFloat  () ) ; break ;
+	case PSL_STRING : printf ( "%s ", argv[i].getString () ) ; break ;
+	case PSL_VOID   : printf ( "(void) " ) ; break ;
+	default : printf ( "Illegal parameter passed to 'print'." ) ; break ;
+      }
+    }
+   
+    pslValue ret ;
+    ret.set ( 123.456f ) ;
+    return ret ;
+  }
+
+
+Adding this to the example program above, requires only that you +change the declaration of 'extensions' as follows: +
+
+  pslExtension extensions [] =
+  {
+    { "print", -1, print },
+    { NULL, 0, NULL }
+  } ;
+
+
+Now, now you can write "Hello World" in PSL script: +
+
+  int main ()
+  {
+    print ( "Hello World.\n" ) ;
+  }
+
+
+...and your C++ function 'print' will be called with one parameter +that'll contain the string value "Hello World.\n". +

pslValue

+The 'pslValue' class is used to pass numbers into and out of extension +functions. It contains the type and value of a number or string in PSL +and it looks like this: +
+
+  class pslValue
+  {
+  public:
+
+    pslType getType   () const ;
+
+    int     getInt    () const ;
+    float   getFloat  () const ;
+    char   *getString () const ;
+
+    void set () ;
+    void set ( int   v ) ;
+    void set ( float v ) ;
+    void set ( const char      *v ) ;
+    void set ( const pslNumber *v ) ;
+  } ;                                                                            
+
+
+Setting nothing into your pslValue ("my_value->set()") is used to +return a 'void' result from your extension function - that is the default +type for a pslValue. +

+The 'getType' call returns the type of this value - currently, +it can be: PSL_INT, PSL_FLOAT, PSL_STRING or PSL_VOID. If you 'set' the +pslValue, it automatically changes it's 'getType' result to match. +

+Doing a 'get' for a type that DOESN'T match the 'getType' of the pslValue +causes it to try to convert to that type. However, a 'getString' on a +non-string pslValue will return NULL. Doing a 'getInt' or 'getFloat' +on a PSL_STRING will perform an atoi() or atof() (respectively) in an +attempt to get a number from the string. +

Running, Tracing, Debugging.

+You run the PSL program one byte-code instruction at a time by calling the +pslProgram::step() function. A byte-code +instruction is rather like the 'machine code' of a physical computer and +it typically takes several byte-code instructions to implement each line +of PSL source code. +

+The 'step' function returns one of three possible results: +

    +
  • PSL_PROGRAM_CONTINUE -- The PSL program is running normally. +
  • PSL_PROGRAM_END -- The PSL program has finished running. This + could be because it simply ended normally - but it could also + be because it 'crashed' with a fatal runtime problem of some kind. +
  • PSL_PROGRAM_PAUSE -- There is a special 'pause' statement in PSL + and when that statement is executed, it causes 'step' to return + this value. +
+The PSL_PROGRAM_PAUSE return is intended to cope with the specific +case when PSL is being used in an interactive graphical application. +Typically, such applications will not want to run PSL scripts to +completion every frame - but instead run them up to the next 'pause' +statement. +

+A typical game might have dozens of PSL scripts running in parallel +and wish to run each of them until the script 'pause's. +

+So, your application's main loop might look something like this: +

+
+  read_the_joystick () ;
+
+  for ( int i = 0 ; i < num_scripts ; i++ )
+    while ( program [ i ] -> step () == PSL_PROGRAM_CONTINUE )
+      /* Do Nothing */ ;
+
+  render_the_graphics () ;
+  swap_the_doublebuffer () ;
+
+
+Then, one of those scripts (to move a monster for example) +might look like this: +
+
+   int main ()
+   {
+     int i = getMyMonster () ;
+
+     while ( 1 )
+     {
+       moveMonster ( i ) ;
+       pause ;
+     }
+   }
+
+
+The 'pause' command indicates that this script has completed it's +work for this frame. +

+Alternatively, some applications may wish to run the scripts for +fixed amounts of time, fixed numbers of byte-codes - or until +some other criterion is satisfied. +

Resetting a Script

+There is a pslProgram::reset() function that +restarts the PSL program from the beginning having first +reset all of its internal variables. +

Debugging PSL scripts

+For debugging PSL scripts, you may replace the 'step' call with +'trace' - which causes the byte-code for each instruction to be +printed to stderr as it's executed. +

+You can also call pslProgram::dump() to print out +all of the byte-code and the PSL symbol table for the program. +

Include paths.

+By default, PSL searches for files with relative pathnames in +the current directory - but you can override this by setting: +
+
+
+   pslScriptPath ( "directory" ) ;
+
+
+
+
+Steve J. Baker. <sjbaker1@airmail.net>
+ + diff --git a/doc/psl/impl_guide.html b/doc/psl/impl_guide.html new file mode 100644 index 0000000..001f2cb --- /dev/null +++ b/doc/psl/impl_guide.html @@ -0,0 +1,463 @@ + + + + + + + The PLIB Scripting Language: Implementation Guide. + + +  + + + + +
+
+

The PSL Implementation Guide.

+
By Steve Baker
+
+

Introduction

+This document describes the virtual machine that the PSL byte code +interpreter implements. +

+The machine has 65536 bytes of instruction memory (and hence, 16 bit +code addresses) and 256 variables (and hence 8 bit data addresses). +Each variable can be a 32 bit integer, an IEEE single precision float +or a character string of arbitary length. +

+In addition, there is a 256 element stack - each entry of which can +contain any PSL data type. +

+The machine has just two registers - the Program Counter (PC) and +the Stack Pointer (SP) - neither of which are accessible to running +programs directly. +

The Instruction Set

+Each instruction consists of a one byte opcode and some number of +bytes of data. +

+The hex numbers for these opcodes are listed in plib/src/psl/pslOpcodes.h +

+

OPCODE_BAD

+INTRUCTION BYTES: 1
+ + + + + +
+EFFECT: + Produces a 'Suspicious Opcode' error message and halts the program.
+ Generally, programs + that run amok for some reason (eg an error in the compiler) will + hit a zero byte fairly soon afterwards. Hence instruction 0x00 + is reserved to be the BAD instruction. Other unrecognised + instructions are also flagged as errors - but it's useful to + explicitly reserve opcode 0x00 for this function due to the + high probability of it being executed by broken programs.
+ +

OPCODE_LINE_NUMBER

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + Updates the 'current line number' from the two bytes + embedded in the instruction.
+ +

OPCODE_PUSH_INT_CONSTANT

+ +INTRUCTION BYTES: 5 + + + + + +
+EFFECT: + Takes four bytes from the instruction and pushes them + onto the stack as an integer.
+ +

OPCODE_PUSH_FLOAT_CONSTANT

+INTRUCTION BYTES: 5 + + + + + +
+EFFECT: + Takes four bytes from the instruction and pushes them + onto the stack as a float.
+ +

OPCODE_PUSH_STRING_CONSTANT

+INTRUCTION BYTES: 1 + strlen string + 1 + + + + + +
+EFFECT: + Takes a null-terminated string from the instruction + stream and pushes it onto the stack.
+ +

OPCODE_GET_PARAMETER

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + Fetches the value of a function's parameter from the + depths of the stack and puts it into a local variable. +

+ The second byte of the instruction is the + index of the variable. The third byte is a small + integer offset - which is the number of the parameter + you want. +

+ Look at the number two down from the top of the stack + (which should be + the number of parameters of a recently called + function). +

+ Now copy the stack element at + sp - ( nargs + 2 ) + offset + into the variable.

+ +

OPCODE_POP

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Throws away the top element of the stack.
+ +

OPCODE_CALLEXT

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + The second byte of the instruction is the index of + a PSL 'extension function', the third + is the number of arguments being passed to it. +

+ Pop that number of values off the stack and pass them + to the extension function. +

+ Call the extension function. + Push the result onto the stack.

+ +

OPCODE_CALL

+INTRUCTION BYTES: 6 + + + + + +
+EFFECT: + [The number of aguments will already have been pushed + onto the stack.] +

+ The four bytes after the instruction is the function address. + The fifth byte is the number of arguments. +

+ Push return address.
+ PC = address of function. +

+ popNumber ( &result ) ;
+    pc = popInt () ;
+    nargs = popInt () ;
+    popVoid ( nargs ) ;
+ pushNumber ( &result ) ;

+ +

OPCODE_STACK_DUPLICATE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Duplicate the top element of the stack.
+ +

OPCODE_EXCHANGE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Exchange the top two elements of the stack.
+ + +

OPCODE_LESS

+

OPCODE_LESSEQUAL

+

OPCODE_GREATER

+

OPCODE_GREATEREQUAL

+

OPCODE_NOTEQUAL

+

OPCODE_EQUAL

+

OPCODE_ADD

+

OPCODE_SUB

+

OPCODE_SHIFTLEFT

+

OPCODE_SHIFTRIGHT

+

OPCODE_OROR

+

OPCODE_ANDAND

+

OPCODE_OR

+

OPCODE_AND

+

OPCODE_XOR

+

OPCODE_DIV

+

OPCODE_MOD

+

OPCODE_MULT

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Pop the top element from the stack and operate on it + and the next element down - leaving the result on + the stack in it's place. So (for example) if the + operation was 'SUB' (Subtract), and A is on top of + the stack and B is beneath it - then the result of + this operation would be to leave (B-A) on the stack + with no sign of either A or B. +

+ The 'ADD' operator also works with strings by concatenating + them.

+ +

OPCODE_NOT

+

OPCODE_TWIDDLE

+

OPCODE_NEG

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Perform the C unary '!', '~' or '-' operator on the top + element of the stack.
+ +

OPCODE_PAUSE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Informs application program that the script wishes + to be paused until next frame.
+ +

OPCODE_HALT

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Informs the application that the script wishes to halt. + Even if the application ignores this request, the script + will continue looping at this location for ever.
+ +

OPCODE_PEEK_JUMP_TRUE

+

OPCODE_PEEK_JUMP_FALSE

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + The two bytes at the end of the instruction contain + an address. Inspect the number off the top of the stack + (without popping it) - and branch to the specified + instruction if the value is TRUE (or FALSE as applicable).
+ +

OPCODE_JUMP_TRUE

+

OPCODE_JUMP_FALSE

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + The two bytes at the end of the instruction contain + an address. POP the number off the top of the stack + (without popping it) - and branch to the specified + instruction if the value is TRUE (or FALSE as applicable).
+ +

OPCODE_JUMP

+INTRUCTION BYTES: 3 + + + + + +
+EFFECT: + The two bytes at the end of the instruction contain + an address. Jump to that address.
+ +

OPCODE_PUSH_VARIABLE

+INTRUCTION BYTES: 2 + + + + + +
+EFFECT: + The second byte of the instruction is the index of + a variable. The value of that variable is pushed onto + the stack.
+ +

OPCODE_POP_ADD_VARIABLE

+

OPCODE_POP_SUB_VARIABLE

+

OPCODE_POP_MUL_VARIABLE

+

OPCODE_POP_MOD_VARIABLE

+

OPCODE_POP_DIV_VARIABLE

+

OPCODE_POP_AND_VARIABLE

+

OPCODE_POP_OR_VARIABLE

+

OPCODE_POP_XOR_VARIABLE

+

OPCODE_POP_SHL_VARIABLE

+

OPCODE_POP_SHR_VARIABLE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Three things are on the stack when this function is called.
+ The thing on top is the result of an expression evaluation.
+ The thing beneath that is the index of a variable.
+ The thing beneath that is the dimension of the variable.
+ The value is added to/subtracted from/multiplied by/etc + the variable - and stored back into the variable.
+ The new value of the variable is left on the stack.
+ +

OPCODE_POP_VARIABLE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + Three things are on the stack when this function is called.
+ The thing on top is the result of an expression evaluation.
+ The thing beneath that is the index of a variable.
+ The thing beneath that is the dimension of the variable.
+ The value is stored into the variable.
+ The new value of the variable is left on the stack.
+ +

OPCODE_SET_INT_ARRAY

+

OPCODE_SET_FLOAT_ARRAY

+

OPCODE_SET_STRING_ARRAY

+INTRUCTION BYTES: 2 + + + + + +
+EFFECT: + The second byte of the instruction is the index of an array + variable.
+ On the top of the stack is an integer.
+ This instruction allocates that number of elements of storage to + the array.
+ +

OPCODE_SET_INT_VARIABLE

+

OPCODE_SET_FLOAT_VARIABLE

+

OPCODE_SET_STRING_VARIABLE

+INTRUCTION BYTES: 2 + + + + + +
+EFFECT: + The second byte of the instruction is the index of a variable.
+ That variable is created, set to the appropriate type and + initialised appropriately.
+ +

OPCODE_FETCH

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + The index of a variable is on top of the stack.
+ The thing beneath that is the dimension of the variable.
+ Replace those with the value of that variable.
+ +

OPCODE_INCREMENT_FETCH

+

OPCODE_DECREMENT_FETCH

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + The index of a variable is on top of the stack.
+ The thing beneath that is the dimension of the variable.
+ Replace those + with the value of that variable. Post-increment/decrement the + variable.
+ +

OPCODE_INCREMENT_LVALUE

+

OPCODE_DECREMENT_LVALUE

+INTRUCTION BYTES: 1 + + + + + +
+EFFECT: + The index and dimension of a variable is on top of the stack.
+ Increment/decrement the variable leaving the stack + contents undisturbed.
+ +
+
+Steve J. Baker. <sjbaker1@airmail.net>
+ + diff --git a/doc/psl/index.html b/doc/psl/index.html new file mode 100644 index 0000000..37ed840 --- /dev/null +++ b/doc/psl/index.html @@ -0,0 +1,55 @@ + + + + + + + The PLIB Scripting Language. + + +  + + + + +
+
+

PSL: PLIB's Scripting Language.

+
By Steve Baker
+
+ +

Introduction

+Whilst applications can use any suitable scripting +language (Python, PERL, Lua, etc) in conjunction with PLIB, there are +attractions to using PLIB's own scripting language: PSL. +

+In particular, in using PSL, you do not add any dependancies on +your application. If the end user has the correct version of PLIB +installed - then the scripting language is already there - for sure. +

+PSL is a very light-weight C-like language with some features borrowed +from C++. It's also 'stackless' and very friendly to games applications. +

+There are two PSL documents: +

+

+Each application can extend PSL by providing additional built-in +functions - so if you are a PSL programer then you'll probably need to +read the documentation for whatever PSL-enabled application +you are writing for. If you are writing a PSL-enabled application, then +you'd better sit down and WRITE that document! +


+
+Steve J. Baker. <sjbaker1@airmail.net>
+ + diff --git a/doc/psl/prog_guide.html b/doc/psl/prog_guide.html new file mode 100644 index 0000000..41615d7 --- /dev/null +++ b/doc/psl/prog_guide.html @@ -0,0 +1,209 @@ + + + + + + + The PLIB Scripting Language: Programming Guide. + + +  + + + + +
+
+

The PLIB Scripting Language Programming Guide

+
By Steve Baker
+
+

Introduction

+ +This document explains how to write PSL programs. + +

+ +How you actually run those programs depends on which PSL-enabled +application you are running. There is a stand-alone PSL interpreter +in plib/examples/src/psl/psl_demo - but it doesn't include any +application-specific language extensions - so a PSL program +that's written for a specific application may not run +on psl_demo. + +

+ +However, psl_demo is great for learning to write PSL scripts. + +

The PSL Language

+ +PSL is designed to be as close to the C Programming +Language - although we +cut a few corners - we extended the language in a few places - and +we consciously omitted other features that are dangerous in a +scripting language. + +

+ +The following C features are implemented much as you'd expect: +

    +
  • Types 'void', 'int' and 'float' (and arrays of those things). +
  • Function definitions. +
  • Global variable definitions. +
  • Statement types: +
      +
    • Local variable definitions. +
    • Static variable definitions. +
    • "return" +
    • "break" +
    • "continue" +
    • "for" +
    • "do/while" +
    • "switch/case/default" +
    • "while" +
    • "if" +
    • "if/else" +
    • "{}" compound statements. +
    • Assignment statements. +
    • Procedure calls. +
    +
  • Most Arithmetic operators. +
  • Comments '/*' '*/'. +
  • C preprocessor directives: +
      +
    • "#include" +
    • "#define" without parameters. +
    • "#undef" +
    • "#ifdef/#endif" +
    • "#ifdef/#else/#endif" +
    • "#ifndef/#endif" +
    • "#ifndef/#else/#endif" +
    +
  • Special characters in strings: +
      +
    • "\n" +
    • "\r" +
    • "\a" +
    • "\b" +
    • "\f" +
    • "\t" +
    • "\\" +
    • "\"" +
    +
  • Recursion, etc. +
+ +Some new features have been added that are not part of C: +
    +
  • "pause" -- Pause the program until next frame. +
  • "string" data type (and arrays of strings). +
  • Casts are unnecessary between compatible types. +
  • C++ style '//' comments. +
  • C++ style local variable declarations. +
+Some features of C are NOT IMPLEMENTED in PSL: +
    +
  • Pointers. +
  • Casts. +
  • Dynamic Memory Allocation. +
  • ',' and '?:' operators in expressions. +
  • "static", "auto" and "register" storage class reserved words. +
  • "char", "short", "unsigned", "signed", "long", "double". +
  • "typedef" +
  • "enum", "union", bitfields. +
  • "goto". +
  • "#if" +
  • "#pragma" +
  • "#define" with parameters. +
  • 'f' and 'l' suffixes for float and long numbers. +
  • All preprocessor directives must have the '#' as the + very first character on the line - they cannot be + preceded with whitespace. +
+ +The following features are "NOT IMPLEMENTED YET" - but will +hopefully arrive soon: +
    +
  • Multiple variable definitions like 'int i, j, k ;' +
  • Structs. +
  • '\' to escape the end-of-line character in strings and macro's. +
  • Parameter passing by name. +
  • Many of C's standard library functions are needed. +
+ +

Compatibility Notes:

+

C++ style local variables.

+With PSL's C++ style locals, you can say things like this: +
+
+    for ( int i = 0 ; i < 10 ; i++ )  /* Do something */ ;
+
+
+In standard C++, the scope of the variable 'i' is from it's +declaration to the end of the 'for' loop. However, Microsoft's +Visual C++ uses an obsolete version of the C++ standard that +allows the scope of 'i' to continue to the end of the block +that contains the for loop. So: +

+WINDOWS USERS BEWARE: PSL IMPLEMENTS THIS CORRECTLY - *NOT* LIKE +MSVC. + +

Hard Limits

+Currently there are hard limits in many places - the number of +variables, the size of the program, the depth of nesting, etc. +These limits will gradually be removed as PSL is developed. + +

Debugging PSL Programs

+ +Specific PSL-enabled applications may have their own +special features to assist with debugging - but all +PSL-enabled applications support several 'shell variables' +that enable certain debugging features. +

+When using a command line shell, you can set these +variables using one of the following commands before +you run your application: +

+    setenv VARIABLE value     -- csh or tcsh
+    export VARIABLE=value     -- bash or sh
+    set VARIABLE=value        -- DOS shell
+
+

Byte-Code Dump

+It's possible to view the byte code that PSL generated +by setting the shell variable 'PSL_DUMP' to either: +
    +
  • never (the default) -- Never produce a dump unless + the application demands it. +
  • on_error -- Produce a dump whenever the PSL program + fails to compile for whatever reason. +
  • always -- Always produce a dump after the PSL program + finishes compiling. +
+

Byte-Code Execution Trace

+It's possible to view the byte code as it's executed +by setting the shell variable 'PSL_TRACE' to either: +
    +
  • never (the default) -- Never produce a trace unless + the application demands it. +
  • always -- Always produce a trace. +
+When the execution trace is enabled, extra instructions will be +inserted into the byte code to enable the PSL interpreter to +produce debug indicating which lines of the source code are +being traced. +

Byte-Code Stack Display

+If you have PSL_TRACE turned on (either via the config variable or +by the application program), then the shell variable 'PSL_STACK' +can be set to display the contents of the top eight stack locations +as the program is traced. If PSL_TRACE is disabled then PSL_STACK +has no effect. +
    +
  • never (the default) -- Never produce stack dumps + within a trace. +
  • always -- Always produce a stack dump within trace. +
+
+
+Steve J. Baker. <sjbaker1@airmail.net>
+ + + diff --git a/doc/puAux.png b/doc/puAux.png new file mode 100644 index 0000000000000000000000000000000000000000..749fb9464dd74a24796982c8d3c10e2107285799 GIT binary patch literal 1803 zcmV+m2lV)fP)000>X0{{R3PSztN0005MP)t-s3K|M7 zmM#`X7NVv?9%>$*qCzT)Dw?7~DuOB&QWie8J_;fVy0%J=o<4GpHbQnPCVD1@nmQU< z8eWDnzP3s}u0C3VG9q>&4lWK}iZUKv9&(m8K5{Bff-X90CL(eo!n#V%!cH=tG9F?c zKFU5?iY^j95=w$DI(8;LdMXx97A}%5B5oo+!ag>pHa2o5Hew1E`BZ+LKY%c z9-gK`hMqd6szO?ZE>3W2}OCxK`GSo0ddlVM~oP*M^BKm4Aim!Cp9G4bsFL_G$gpBWI|f#bnsNkZ>?yJMq}-0p^gF|MWf!RFDxw7>kFBV47k>gj=&D4Oa}nmnYtve zNh;1D>ve_ACKBbLa=APQVyK*$D&SCt8;;*^|hCX|f|!^h(XI zRyQJSSGTvT8`TIK8`a2B-LC(Q)@?);Ios&R8NR1aM@KiR8{H+UxVts4wU${xV9}r~ zat1)bq--?eYmLZ><&*;}yVi*KnM|e;_kF75VmZ$s-Lvt{*?7EEDs67Y<2d3lW>MYT zETK6rC3rSVXoAJtw{N8$pY86}d`%-tK34Pg4Koa3<@+Ff5I!z6RM8M3eE-|6t#J_1 z647dftx$5c(DXG^aVs^?VvG`EnkGe{V!{L#0-z+C&DTO4%atV2h)d+Y)_EAtoA8b~ zjK#pgIcJPZg<^D682oSzcqv4R^EMBuNXAn*HCtxr6Ipwf4PXrrVT5(-I`%ooD&%e0I~Rh znmjrCTD`cYsslU?6D3_j&_VGC^C|>EK`=g*N*$)gv3%!bSkvZ*m73!kL4Z}1v1D2K zha_xIC)Gg=bEZBR;ABJ;I6oFE4DSfBuw;kgA(V@fR%pst`CZpC4EZ9IM^8iPzymc+ zepHj!l4_c!Q^_=^FN8!t%fcObLBQ4eg}5%yCc~{zlRc3)SRgwgdT9*0m`tW2pX8vL z99X@XQWq)gJ}mS+5(TK2U{i2mJ|$j4>0u)YH3EGGy$W}Pys%GXVzPr4)zrz^iF)z# zg?e(ca66eMkXBb0%?h!SJ%sd9g5B>CFCL22Lr7wHo`%>Ho}D)WMxyS-$|KV1?TfAH zpZ-{VKi&Cm_+nRk`q@gM@S?xKS9%KHoA0*9|BDque^qqDGE509KVufl zo^;b7;{POuTZ})oi&|MT;7~9BfVNFbJ9{*%i(H+m?3{!&swhYh9=l26> zgAZ8%x0n%l`};xGvN&APVgV@GY#01fjKIitMKGaWVMrCPs2jM+g4cvddOnxW>$k34 zxn)>w7_O&73qok1SZL15Gd)jIFd!i^FU$4}j&XFimdoXexniYQ^X}gLEeCYGzOK8j ztAi?!k{@`GnaiQ-8R$93SO(;!_quLxu~Mlx&Tb5TmEDR{sTA#Au!F}g_7-ckUfTxE zwow65ELI#FI5pttZqaK&?G4AR#hSKl$70RquMtpbHe)d-<^VuQG|>b`C)RAQqv1HV z-E8I|mTo&UGc#k4$Hoq34i0`ekZ22Z%t(NUEC+3Urfra{vBxmyO(phK-;$y%^(_Gu tz*2iCuw07Jhaa$z7cKvXJ~Z`>{R>R5>S|k=T7Uom002ovPDHLkV1fgL1egE- literal 0 HcmV?d00001 diff --git a/doc/puAux/index.html b/doc/puAux/index.html new file mode 100644 index 0000000..a8373ea --- /dev/null +++ b/doc/puAux/index.html @@ -0,0 +1,54 @@ + + + + + SSG Auxiliary Libraries. + + + + + + + + + + +
+

PUI Auxiliary Libraries.

+
+ by Steve Baker +
+

Introduction

+ + + + +
+PLIB/puAux is a suite of auxiliary libraries that build +higher level classes on top of the basic PUI classes. +

+

+

+

Conventions

+puAux is installed and named with the same +conventions as the base PLIB libraries. +
+ie: +
+   /usr/include/plib/puAux.h
+   /usr/lib/libplibpuaux.a
+
+

+PLIB/puAux functions, classes and constants are all +named with an 'pua' or 'PUA' prefix. +
+

+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + + + diff --git a/doc/pui/BasicPUI.html b/doc/pui/BasicPUI.html new file mode 100644 index 0000000..f7cd4ab --- /dev/null +++ b/doc/pui/BasicPUI.html @@ -0,0 +1,636 @@ + + + + + + + + + BasicPUI + + + +
+

+Introduction to Using the Picoscopic User Interface

+ +
+

+John F. Fay

+ +
+

October 18, 2000

+(Updated by Sebastian Ude on October 02, 2001) +
+ +

+Contents

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1.Introduction:  +Graphical User Interfaces and Windowed Programs
2.Elementary PUI
2.1 Creating User Interface +Widgets
2.2 Invoking PUI
2.3 Widget Callbacks
3.Conclusion
+ +

+1.  Introduction:  +Graphical User Interfaces and Windowed Programs

+The Picoscopic User Interface (PUI) is designed to be a simple, easy-to-use +graphical user interface.  It is based on the OpenGL graphics language +and requires a windowing system, either the OpenGL Utility Toolkit (GLUT) +or another similar one.  The gentle reader is assumed either to be +familiar with OpenGL and his windowing system or at least to have their +manuals at hand. +

Basic to a GUI is the idea of a widget.  A widget is something +that appears on the computer screen and which transfers data from the user +to the program or from the program to the user.  The user can activate +a widget by pressing a key, clicking a mouse button, or by using some other +input device.  This lets the user send information to the program.  +A widget can also display text or a graphic, allowing the program to transfer +information to the user. +

A windowing system requires a specific style of program which differs +from a text-based program in several fundamental ways.  First, instead +of starting at the beginning of a problem and stopping at the end, a windowed +program starts by creating the windows and then enters an infinite loop +in which it responds to user inputs.  Second, instead of having a +self-contained hierarchy of functions which call each other, a windowed +program is controlled by the windowing system which calls functions as +it sees fit.  The following diagram shows the structure of a typical +windowed program. +

+
+        +------------------+
+        |   Main Program   |  The application developer
+        | Set up window(s) |  supplies this.
+        |   and widget(s)  |
+        +------------------+
+                 |
+                 V
+         +----------------+
+         | Window Manager |   The window manager library
+         |  (like GLUT)   |   supplies this.
+         +----------------+
+                 |
+          +------+--------+---------------+---------------+----> etc.
+          |               |               |               |
+          V               V               V               V
+     +----------+    +----------+    +----------+    +----------+
+     |  Window  |    |  Window  |    |  Window  |    |  Window  |  The application
+     |  Mouse   |    | Keyboard |    |   Idle   |    | Display  |  developer
+     | Callback |    | Callback |    | Callback |    | Callback |  supplies these.
+     +----------+    +----------+    +----------+    +----------+
+          |               |               |               |
+          V               V               V               V
+     +----------+    +----------+     May call       +----------+
+     |   PUI    |    |   PUI    |   PUI functions    |   PUI    |  PUI
+     |  Mouse   |    | Keyboard |    for default     | Display  |  supplies
+     | Function |    | Function |     behaviors      | Function |  these.
+     +----------+    +----------+                    +----------+
+          |               |
+          +-------+-------+
+                  |
+          +-------+-------+---------------+------> etc.
+          |               |               |
+          V               V               V
+     +----------+    +----------+    +----------+
+     | Widget 1 |    | Widget 2 |    | Widget 3 |  The application developer
+     | Callback |    | Callback |    | Callback |  supplies these.
+     +----------+    +----------+    +----------+
+
+
+

The functions that the application developer supplies are called "callbacks" +because the windowing system or user interface system "calls back" into +the application. +

A windowed program with a user interface, then, consists of a main program +and a set of callbacks.  The callbacks may invoke other function in +turn, but these are all arranged in hierarchical trees underneath the callbacks. +

A windowed program executes differently from a typical hierarchical +program as well.  It begins with the main program setting up the window(s) +and the user interface.  The main program then calls the window manager, +which contains an infinite loop and never returns control to the main program.  +(GLUT in particular has this behavior; I cannot speak for all window managers.)  +When the window manager needs to redraw the display that is shown on the +screen, it calls the application's Window Display Callback.  When +the user moves the mouse or clicks a mouse button, the window manager calls +the application's Window Mouse Callback.  When the user presses a +key on the keyboard, the window manager calls the application's Window +Keyboard Callback.  Other input devices cause the window manager to +call other callbacks in the application.  Finally, if the user hasn't +done anything for a while, the window manager will call the application's +Window Idle Callback. +

In building a window callback, the application developer must be sure +to call the appropriate PUI functions.  The applications's Window +Mouse Callback, for example, must call the PUI mouse function "puMouse" +if the user interface is to process a mouse click.  The application's +Window Display Callback, besides rendering any graphics in the window, +must also call the PUI display function "puDisplay" in order to display +the widgets in the user interface. +

When the application developer's window callback calls the appropriate +PUI function, PUI determines which (if any) widget the user has just activated +with his mouse, keyboard, or other input device.  PUI then calls that +widget's callback function which the application developer must supply +and which provides the actual heart of the interface.  The callback +for an input widget will probably read the text that the user has typed +there and pass it on to the application.  The callback for a button +widget may turn something in the program on or off.  What a callback +does is limited only by the application developer's imagination. +

+2.  Elementary PUI

+Using the Picoscopic User Interface on an elementary level requires three +things:  setting up the user interface widgets, invoking PUI from +the window callbacks, and supplying the widget callbacks. +

PUI was written in C++, designed to be invoked from C++ programs.  +The following text and examples will assume a program written in C++. +

+2.1 Creating User Interface +Widgets

+Most (or all) widgets have certain properties in common.  These are: +
    +
  • +position - the x- and y-coordinates, in pixels, of the lower left-hand +corner of the widget relative to the lower left-hand corner of the window
  • + +
  • +size - the width and height of the widget, in pixels
  • + +
  • +callback - the application program's function which is called when the +widget is activated
  • + +
  • +value - a number or character string that is associated with the widget
  • + +
  • +label - text that is printed next to the widget
  • + +
  • +legend - text that is printed inside the widget (some widgets only)
  • +
+PUI supports other widget properties, but these are enough to allow the +application developer to create a simple user interface. +

Each widget in the Picoscopic User Interface is an object in C++.  +The application developer creates a widget by defining a variable to point +to the object and creating a new object: +

+
+  puButton *button = new puButton ( 10, 10, 100, 30 ) ;
+
+
+

To set the properties of a widget, the application developer calls methods +in the object: +

+
+  button->setPosition ( x, y ) ;                  // Sets the position of the widget to
+                                                         (x, y)
+  button->setSize ( w, h ) ;                      // Sets the size of the widget to (w, h)
+  button->setCallback ( widget_cb ) ;             // Sets the callback to
+                                                         "void widget_cb ( puObject *ob )"
+  button->setLegend ( "Press Me" ) ;              // Sets the legend
+  button->setLabel ( "A Button:" ) ;              // Sets the label
+  button->setLabelPlace ( PUPLACE_LOWER_LEFT ) ;  // Makes the label print on the button's
+                                                          left
+
+
+

Other allowed values for the label position are: + +

    +
  • PUPLACE_ABOVE_LEFT +
  • PUPLACE_ABOVE_RIGHT +
  • PUPLACE_TOP_LEFT +
  • PUPLACE_TOP_CENTERED +
  • PUPLACE_TOP_RIGHT +
  • PUPLACE_UPPER_LEFT +
  • PUPLACE_UPPER_RIGHT +
  • PUPLACE_CENTERED_LEFT +
  • PUPLACE_CENTERED_RIGHT +
  • PUPLACE_LOWER_LEFT +
  • PUPLACE_LOWER_RIGHT (default) +
  • PUPLACE_BOTTOM_LEFT +
  • PUPLACE_BOTTOM_CENTERED +
  • PUPLACE_BOTTOM_RIGHT +
  • PUPLACE_BELOW_LEFT +
  • PUPLACE_BELOW_RIGHT +
+

For a detailed description of these have a look at the +PUI Programmers Guide. +

All of these calls are optional.  If the application developer +does not want to label a widget, he simply does not call "setLabel" or +"setLabelPlace".  Similarly, if he has set the widget's position and +size while creating the widget, there is no need to call "setPosition" +or "setSize". +

The following subsections describe some of the widget types that PUI +supports. +

+2.1.1 Button

+The button ("puButton") is a rectangle, usually containing a legend, which +is activated when the user clicks the mouse on it.  It has two possible +values, off and on; its actual value changes each time the user clicks +on it. +

The button has two possible methods of construction: +

    +
  • +puButton ( x1, y1, x2, y2 ) - creates a button with lower left-hand coordinates +of (x1, y1) and with upper right-hand coordinates of (x2, y2)
  • + +
  • +puButton ( x1, y1, "text" ) - creates a button with lower left-hand coordinates +of (x1, y1) and containing the text "text".  The size is set by the +size of the text.
  • +
+ +

+2.1.2 Button Box

+The button box ("puButtonBox") is a large rectangle containing a set of +buttons, each with text next to it, which the user can select.  It +is sometimes called a radio button.  It is created by creating a null-terminated +list of labels and then by calling the constructor function: +
+
+  char **labels = { "Label1", "Label2", "This is Label3", "Four", NULL } ;
+  puButtonBox my_box = new puButtonBox ( x1, x2, y1, y2, labels, <0|1> ) ;
+
+
+

This creates a button box with lower left-hand coordinates of (x1, y1) +and upper right-hand coordinates (x2, y2).  The number of buttons +is specified by the number of entries in the "labels" array; each string +before the NULL gets a button.  The final argument in the constructor +specifies whether the user can select multiple buttons (0) or whether selecting +a new button will deactivate the previously-selected button (1). +

The button box should not be given a legend. +

+2.1.3 Frame

+The frame ("puFrame") is a rectangular area that provides a background +for other widgets.  It should be defined before any other widgets +that go inside it or else it will cover them up.  It is constructed +by the following function call: +
    +
  • +puFrame ( x1, y1, x2, y2 ) - creates a frame with lower left-hand coordinates +of (x1, y1) and with upper right-hand coordinates of (x2, y2)
  • +
+The frame should not be given a legend.  It does not support a callback +either. +

+2.1.4 Input Box

+The input box ("puInput") is a rectangular area into which the user can +type text.  The user must first click the mouse inside the area and +then type the text.  He concludes by pressing <Enter> or <Tab> +or by clicking the mouse outside the input box. +

An input box is constructed by the following function call: +

    +
  • +puInput ( x1, y1, x2, y2 ) - creates an input box with lower left-hand +coordinates of (x1, y1) and with upper right-hand coordinates of (x2, y2)
  • +
+The input box should not be given a legend. +

+2.1.5 Menu Bar

+The menu bar ("puMenuBar") is easily the most complicated widget written +up here.  It consists of a horizontal bar containing buttons which +the user can click on.  The bar is always situated on the left-hand +edge of the window and is usually in the upper left-hand corner.  +It is constructed by the following function call: +
+
    +
  • +puMenuBar *menu = puMenuBar ( y ) - creates a menu bar with the lower left-hand +corner "y" pixels above the lower left-hand corner of the window.  +A value of -1 for "y" will put the menu bar in the upper left-hand corner +of the window.
  • +
+ +


Once the menu bar has been constructed, it must be loaded with its +entries and submenus.  A submenu is the list that drops down when +the user clicks on an entry in the menu.  A typical menu bar will +have the entries "File", "Edit", "Search", and "Help".  Under the +"File" entry is a submenu with entries "New", "Open", "Save", "Save As", +a separator, "Page Setup", "Print", another separator, and "Exit".  +Each entry in the submenu has its own widget callback function.  To +create a submenu, the application developer must first create two null-terminated +lists of character strings and callback functions: +

+
+  char      *file_submenu   [] = { "Exit", "-----",  "Print",  "Page Setup", "-----",
+                            "Save As",  "Save",  "Open",  "New", NULL } ;
+  puCallback file_submenu_cb[] = { exit_cb,    NULL, print_cb, page_setup_cb,    NULL,
+                           save_as_cb, save_cb, open_cb, new_cb, NULL } ;
+
+
+

Notice here that the submenu entries are placed in the list from bottom +to top.  After the last character string is a NULL entry; this tells +PUI that there aren't any more entries in the submenu.  The callbacks +for the separators are also null, indicating that there is no callback +for the separator.  The submenu is added to the menu bar with a call +to the "add_submenu" function: +

+
+  menu->add_submenu ( "File", file_submenu, file_submenu_cb ) ;
+
+
+

Further calls to "add_submenu" add the other submenus: +

+
+  char      *edit_submenu   [] = {  "Select All", "-----",  "Paste",  "Copy",  "Cut",
+                            "-----",  "Undo", NULL } ;
+  puCallback edit_submenu_cb[] = { select_all_cb,    NULL, paste_cb, copy_cb, cut_cb,
+                               NULL, undo_cb, NULL } ;
+  menu->add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ;
+
+  char      *search_submenu   [] = {  "Find Next",  "Find", NULL } ;
+  puCallback search_submenu_cb[] = { find_next_cb, find_cb, NULL } ;
+  menu->add_submenu ( "Search", search_submenu, search_submenu_cb ) ;
+
+  char      *help_submenu   [] = {  "About",  "Help", NULL } ;
+  puCallback help_submenu_cb[] = { about_cb, help_cb, NULL } ;
+  menu->add_submenu ( "Help", help_submenu, help_submenu_cb ) ;
+
+
+

When the last submenu has been added, the application developer must +close the menu bar: +

+
+  menu->close() ;
+
+
+

This is absolutely necessary or else PUI will think that any widgets +the application defines later are supposed to go into the menu bar. +

The menu bar does not support a legend.  It does not support a +callback itself, although all the entries in the submenus certainly do +support callbacks.  Repositioning and resizing a menu bar will be +problematic as well.  There may be an occasion when a menu bar can +take a label, but this would be rare. +

+2.1.6 One-Shot Button

+The one-shot button ("puOneShot") is a button whose value is always reset +immediately to zero after it has been activated.  In all other respects +it is the same as the regular button.  It is created in the same way +as the button: +
    +
  • +puOneShot ( x1, y1, x2, y2 ) - creates a one-shot button with lower left-hand +coordinates of (x1, y1) and with upper right-hand coordinates of (x2, y2)
  • + +
  • +puOneShot ( x1, y1, "text" ) - creates a one-shot button with lower left-hand +coordinates of (x1, y1) and containing the text "text".  The size +is set by the size of the text.
  • +
+ +

+2.1.7 Text Box

+The text box ("puText") is an output-only widget which displays a character +string.  It is created by the following constructor: +
    +
  • +puText ( x, y ) - creates a text box at the point (x, y) in the window
  • +
+The application actually adds the text by assigning a label and a label +place to the text box.  The label place will determine where relative +to the position of the text box the text itself will appear. +

+2.2 Invoking PUI

+The summary of invoking PUI is very simple: +
    +
  • +At the beginning of the program, before creating any widgets, the application +must call the function "puInit ()".
  • + +
  • +If the application developer wants PUI to process a mouse click, he must +have the application call the function "puMouse ( button, updown, x, y +)".
  • + +
  • +If the application developer wants PUI to process a mouse motion, he must +have the application call the function "puMouse ( x, y )".
  • + +
  • +If the application developer wants PUI to process a key from the keyboard, +he must have the application call the function "puKeyboard ( key, updown +)".
  • + +
  • +If the application developer wants PUI to display the user interface, he +must have the application call the function "puDisplay ()".
  • +
+The application developer must create window callbacks for the window manager +to call when the user interacts with the computer.  The following +are taken from GLUT, but another window manager will have similar callback +requirements. +

+2.2.1 Main Program Code

+In the main program, the application developer usually places the following +or similar statements: +
+
+  // Create the GLUT window and the OpenGL context
+
+  glutCreateWindow       ( "PUI Application"  ) ;
+
+  // Set up the GLUT window callbacks
+
+  glutDisplayFunc        ( displayfn  ) ;  // display the window
+  glutMouseFunc          ( mousefn    ) ;  // process mouse clicks
+  glutMotionFunc         ( motionfn   ) ;  // process mouse moves
+  glutPassiveMotionFunc  ( motionfn   ) ;
+  glutKeyboardFunc       ( keyboardfn ) ;  // process key presses
+  glutIdleFunc           ( displayfn  ) ;  // what to do when nothing's happening
+
+  // Initialize PUI
+
+  puInit () ;
+
+  // Set up the widgets here
+
+

+2.2.2 Window Callbacks

+The application's window callbacks should generally look like this.  +Again, these are written assuming GLUT; other window managers should be +similar. +
+
+  void displayfn ( void )
+  {
+    // stuff to display any non-PUI drawings
+
+    // update any PUI widgets that update by themselves ... like a timer display
+
+    // redisplay PUI
+
+    puDisplay () ;
+
+    // Finish up
+
+    glutSwapBuffers () ;
+    glutPostRedisplay () ;
+  }
+
+  void mousefn ( int button, int updown, int x, int y )
+  {
+    // Invoke the PUI mouse function
+
+    puMouse ( button, updown, x, y ) ;
+    glutPostRedisplay () ;
+  }
+
+  void motionfn ( int x, int y )
+  {
+    // Invoke the PUI mouse motion function
+
+    puMouse ( x, y ) ;
+    glutPostRedisplay () ;
+  }
+
+  void keyboardfn ( unsigned char key, int, int )
+  {
+    // Invoke the PUI keyboard function
+
+    puKeyboard ( key, PU_DOWN ) ;
+    glutPostRedisplay () ;
+  }
+
+
+

+2.2.3 Miscellaneous Other Tidbits

+If the application developer wishes to delete a widget, he should not delete +the object directly.  Instead he should call the PUI function "puDeleteObject +( ob )" with his widget pointer as the argument. +

+2.3 Widget Callbacks

+The final pieces to the puzzle are the widget callbacks.  These are +of the form +
+
+  void widget_cb ( puObject *ob )
+  {
+    // code goes here
+  }
+
+
+

where "puObject" is the C++ type for a generic widget.  When PUI +calls a widget callback, it passes to it the address of the widget whose +activation caused the callback.  This is useful because it allows +a single callback for multiple widgets: +

+
+  void widget_cb ( puObject *ob )
+  {
+    if ( ob == button )  // the button from section 2.1
+    {
+      ...
+    }
+    else if ( ob == my_box )  // the button box from section 2.1.2
+    {
+      ...
+    }
+    else
+    {
+      printf ( "Error - unknown widget" ) ;
+    }
+  }
+
+
+

A widget callback needs to be either a regular function (global +or defined in the same file that contains the widget's definition) or a +static member function of a class.  It cannot be a regular member +function of a class. +

+2.3.1 Widget Values

+It is frequently very useful in a callback to know the value of a widget.  +A PUI widget has an integer value, a floating-point value, and a character +string value.  These values are kept synchronized as much as possible.  +For example, if the application assigns an integer value to a widget, the +floating-point value is set equal to the integer and the integer is printed +into the character string. +

An application program can use the following function to retrieve the +value of a PUI widget ("ob" is the pointer to the object): +

+
+  int ivalue1 = ob->getValue () ;  // No argument, return the integer value
+
+  int ivalue2 ;
+  ob->getValue ( &ivalue2 ) ;  // Place the integer value in the argument
+
+  float fvalue ;
+  ob->getValue ( &fvalue ) ;  // Place the floating-point value in the argument
+
+  char svalue[PUSTRING_MAX] ;  // Create a string
+  ob->getValue ( svalue ) ;    // Copy the string value into the argument
+                               // Note that the argument is of type "char *"
+
+  char *sptr ;
+  ob->getValue ( &sptr ) ;  // Have the pointer point to the string value
+                            // Note that the argument is of type "char **"
+
+
+

The values of the various widgets have the following meanings: +

    +
  • +Button:  0 - turned off; 1 - turned on
  • + +
  • +Button Box:  If only one button can be activated at a time, the value +is the number of the button which is activated.  If multiple buttons +can be activated at once, then the bits in the integer value corresponding +to the activated buttons are one and the bits corresponding to the inactive +buttons are zero.
  • + +
  • +Frame:  no value
  • + +
  • +Input:  the string value is the character string that the user has +typed in
  • + +
  • +Menu Bar:  no value
  • + +
  • +One Shot Button:  value is always zero
  • + +
  • +Text:  no value
  • +
+ +

+3.  Conclusion

+This has been offered as an introduction to the Picoscopic User Interface.  +It makes no warranty as to its accuracy although efforts have been made +to make it correct.  It is offered in the hope that the gentle reader +will find it useful. +

The PUI has considerably greater capabilities than have been listed +here.  There are well over a dozen additional types of widgets.  +Additionally, the application developer can do many more things with this +widgets than have been described here. +

"As to more than these, my son, beware.  Of the making of many +books there is no end, and in much study there is weariness for the flesh." +- Ecclesiastes 12:12 + + diff --git a/doc/pui/FAQ.html b/doc/pui/FAQ.html new file mode 100644 index 0000000..1892769 --- /dev/null +++ b/doc/pui/FAQ.html @@ -0,0 +1,171 @@ + + + + + + + The Picoscopic User Interface: FAQ list. + + + + + + + + +
+Imagine Pooh Here + +

PUI: A Picoscopic User Interface

+

Frequently Asked Questions List.

+By Steve Baker +
+Here are some questions that have been asked about PUI: +

+

+


+

+

Q1: Can I build a Windoze DLL for PUI?

+ + Not from the present sources. Some Windoze compilers + require an '_export' directive on functions or classes + that are to be exported from the DLL. This is ugly and + I refuse to add non-standard syntax to the library just + to pander to a disgusting Windoze-ism. PUI is a VERY + small library and there is really no need for it to + be a DLL anyway. + +

Q2: Which versions of OpenGL and GLUT will support PUI?

+ + In principal, any version of GLUT since 2.0 and any + reasonably compliant OpenGL (including Mesa) should + be happy to support PUI. + +

Q3: Which machines/operating systems can run PUI?

+ + AFAIK, any machine with C++, OpenGL (or Mesa) and + GLUT will run PUI programs without problems. + +

Q4: Is PUI stable? Are there known bugs?

+ + So far, there have only been a couple of bugs reported + in PUI - the most significant is that the menu bar doesn't + work properly after the GLUT window has been resized. +

+ The 'complex' demo program seems to fail when a dialog + box is dismissed. This may be because the dialog object + is deleted inside a callback within a member function of + that object. This problem only appears under Microsoft + operating systems and so is hard for me to track down. + The simplest thing may be for me to change the demo + program so it just doesn't work that way. + +

Q5: What changes do you expect to make in future releases?

+ + I am generally unhappy about the somewhat inconsistant + set of constructor functions for the various widgets. + Some auto-size their widgets, some take the size directly, + others have both. This needs to be made much more consistant + across all the widget classes. +

+ I want to write a cute interactive GUI designer program to + write C++ source code for the PUI constructor functions + automatically. Something along the lines of the XForms + 'fdesign' or the FLTK 'fluid' programs. +

+ If GLUT ever changes to allow applications to run without + calling 'glutMainLoop', I will greatly improve the Dialog + box API which sucks right now. It would also spur me on + to write a File Selector widget. The problem is that + you'd really like to be able to write things like: +

+
+    if ( ( fd = fopen ( "babble", "w" ) ) == NULL )
+    {
+      if ( puYesNoDialog ( "Can't open babble file",
+                           "Do you want to continue?" ) )
+        return ;
+      else
+        exit ( 1 ) ;
+    }
+
+
+ However, the 'puYesNoDialog' function can't get it's + graphics up on the screen without returning to GLUT's + main loop - and it can't do that since the call to + puYesNoDialog is in a GLUT callback function. + Look at the ugly code in 'complex' to see how to do + this kind of thing without this facility. +

+ Finally, the appearance of PUI's widgets is somehow + not as neat as some other GUI's such as MUI and Motif. + This should be pretty easy to fix - and it's something + on the 'to do' list. + +

Q6: Where can I ask questions?

+ + Since PUI is now a part of PLIB, + all user level questions, development issues and announcements + are handled from the PLIB mailing list. + +

Q7: What's the difference between PUI and MUI?

+ + The main reason I wrote PUI was my disappointment + with MUI. +

+ MUI has now been 'abandoned' by it's author (Tom Davis) + who none-the-less retains copyright over it. MUI has + several known bugs - and the only known documentation + was written by me - and I have never actually written + a real application using it! +

+ The MUI library (distributed with GLUT) takes + over all of the GLUT callbacks - this makes it + very hard to write general purpose programs with + MUI. +

+ In contrast, it is very easy to add PUI widgets to + an existing OpenGL/GLUT application with only a + couple of lines of code. +

+ PUI also works quite well with 3D hardware such + as 3Dfx Voodoo that 'take over' the screen. Since + MUI and GLUT both create their menu's using the + underlying windowing system's GUI, these menu's + won't be visible on that kind of hardware. PUI + uses only OpenGL to do all kinds of widgets, so + portability onto 3Dfx hardware is assured. + +

Q8: Are there restrictions on using PUI ?

+ + PUI is OpenSource under the terms of LGPL. If for some reason, + you find LGPL too restrictive and if you wish to use it in some + major project then please talk to me about it - I can be flexible. + +

Q9: Do I have to write in C++ to use PUI?

+ + Yes - there will never be a {insert favorite non-C++ language here} + version of PUI. +
+ + +
+Valid HTML 4.0! + +
+Steve J. Baker. <sjbaker1@airmail.net> +
+
+ + diff --git a/doc/pui/complex.png b/doc/pui/complex.png new file mode 100644 index 0000000000000000000000000000000000000000..6ddc95b1d66694d730a9375cc837990b46732466 GIT binary patch literal 28076 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 LNkvXXu0mjfaCo@; literal 0 HcmV?d00001 diff --git a/doc/pui/index.html b/doc/pui/index.html new file mode 100644 index 0000000..da0878a --- /dev/null +++ b/doc/pui/index.html @@ -0,0 +1,2210 @@ + + + + + + + + The Picoscopic User Interface: Programmers Guide. + + +  + + + + + + +
+
Imagine Pooh Here
+
+
+

+PUI: A Picoscopic User Interface

+ +
+

+Programmers Guide.

+ +
By Steve Baker
+
+ +

+Introduction

+The Picoscopic User Interface (PUI - pronounced 'poohey') +is a simple set of C++ classes that allow programs written in GLUT and +OpenGL to create buttons, sliders, menus etc. +

Because PUI's widget rendering is performed using OpenGL calls, it is +well suited to the job of adding a few simple GUI elements to a game or +other interactive program where the GUI is required to overlay (often transparently) +some 3D rendering. By using OpenGL calls, PUI is much faster than most +other GUI libraries on systems with 3D rendering hardware. +

PUI is now a part of PLIB. +

The PUI FAQ list is here. There is also a +short +introduction to the basics of PUI from John F. Fay. +

Like most similar GUI's (MUI, Xforms, FLTK, GLUI, etc), PUI is comprised +of a base class (puObject) from which all the interesting +widgets are derived. Most of the packages functionality is concentrated +in the puObject class. +

+Contributions

+Mark Danks contributed code +for making PUI run without GLUT under Win32, +

AbdulWajid Mohamed added the 'PUSTYLE_SHADED' +and 'PUSTYLE_SMALL_SHADED' code. He also fixed a number of bug fixes. +

Dave McClurg added the puListBox and puFilePicker +widgets. +

John F. Fay added a few widgets and other things and reworked this document. +

+A Truly PUI API:

+Here are the elements that make up the PUI API: + + + + + + + + + + + + + + + + +
+

+Classes:

+
+

+Non-class functions:

+
+ + + + + +
+ +

+Note:

+In the description of each widget class below, there is a small screenshot +of one example of that widget. You can see the code used to generate those +screenshots in the PLIB example program +widget_list.cxx. However, +by changing the rendering style, you can make them look considerably different +from this. +

+PUI Terminology.

+Some terminology: +
    +
  • +WIDGET : A single graphic user interface entity such as a button, slider, +menu or some kind of decorative entity like a border or information string.
  • + +
  • +LEGEND : This is a text string that resides inside the widget's active +area.
  • + +
  • +LABEL : This is a text string that resides outside the widget's active +area.
  • + +
  • +VALUE : All puObjects have a current 'value' which +can be read or written as an integer, float or string. The exact meaning +of this value may vary from widget to widget. For some widget types, the +value has no meaning at all (eg a puFrame) - although +it is always available to be set and read by the application. The manner +in which the value of a widget is displayed (if at all) varies from widget +to widget.
  • + +
  • +ABOX : The Activity Box - A rectangular region that is the 'active' area +into which mouse clicks must be directed in order to make a widget do whatever +it does.
  • + +
  • +BBOX : The Bounding Box - A rectangular region that surrounds the pixels +drawn by the widget. This is typically the ABOX - extended to include the +LABEL string.
  • + +
  • +STRING : PUI recognizes only the printable ASCII set plus space and newline.
  • +
+ +

+Hello World in PUI.

+ + + + + + + +
Here is a simple example program. It pops up a window with a single +button widget that prints 'Hello World.' to stdout when you click it. (This +program is in the PUI examples directory as 'simple.cxx').Simple PUI example
+ +
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <math.h>
+#include <GL/glut.h>
+#include <plib/pu.h>
+
+void motionfn ( int x, int y )
+{
+  puMouse ( x, y ) ;
+  glutPostRedisplay () ;
+}
+
+void mousefn ( int button, int updown, int x, int y )
+{
+  puMouse ( button, updown, x, y ) ;
+  glutPostRedisplay () ;
+}
+
+void displayfn ()
+{
+  glClearColor ( 0.1, 0.4, 0.1, 1.0 ) ;
+  glClear      ( GL_COLOR_BUFFER_BIT ) ;
+
+  puDisplay () ;
+
+  glutSwapBuffers   () ;
+  glutPostRedisplay () ;
+}
+
+void button_cb ( puObject * )
+{
+  fprintf ( stderr, "Hello World.\n" ) ;
+}
+ 
+
+int main ( int argc, char **argv )
+{
+  glutInitWindowSize  ( 240, 120 ) ;
+  glutInit            ( &argc, argv ) ;
+  glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ;
+  glutCreateWindow    ( "PUI Application"  ) ;
+  glutDisplayFunc     ( displayfn ) ;
+  glutMouseFunc       ( mousefn   ) ;
+  glutMotionFunc      ( motionfn  ) ;
+
+  puInit () ;
+
+  puOneShot *b = new puOneShot ( 50, 50, 200, 80 ) ;
+
+  b -> setLegend   ( "Say Hello" ) ;
+  b -> setCallback ( button_cb ) ;
+
+  glutMainLoop () ;
+
+  return 0 ;
+}
+Notice that the program uses a pretty conventional GLUT startup sequence, +with the usual callbacks for mouse events and redisplay. You can even continue +to use GLUT popup menus which will work quite happily in conjunction with +PUI menus. +

The needs of PUI are small - as a minimum, call the puMouse +function whenever the mouse is clicked or dragged, call the puKeyboard +function whenever a key is pressed, call the puDisplay +function frequently enough to maintain smooth interactions. +

The creation of the user interface is similarly simple, calling the +constructor function for the objects you need (with the dimensions of the +active area of each object) - then add labels, legends, callback functions, +etc. +

Whenever the puMouse function detects that the +mouse coordinate lies over an active widget, it calls the user-supplied +callback function and takes the necessary actions to update the graphics +on the next call to puDisplay. +

+Multiple Windows in PUI

+When you create a new widget, PUI places it in the current GLUT window. +If the your application opens multiple windows, you can place user interface +widgets in any window you please. The widget coordinates are calculated +from the lower left-hand corner of the window. There are a few recommended +practices when using PUI in connection with multiple windows: +
    +
  • +Make the first widget in a new window a puGroup +widget and place all of that window's widgets in that group. This is simply +good organization.
  • + +
  • +Do not place widgets that are displayed in one window under a puGroup +widget that is displayed in another window. That only works with recent +PLIB versions and is simply not recommended.
  • + +
  • +If the user will be able to reshape the window, include a window reshape +function that adjusts the positions and sizes of the widgets. If the user +should not resize the window then the window reshape function should reset +the window to its original size.
  • +
+ + + + + + + +
This is a more complicated example in the PUI example directory called +'complex.cxx', it produces a menu and a button that float in front of a +tumbling cube that is rendered in OpenGL. In a second window are two sliders +which allow the user to control the speed and direction of the cube's rotation. +A third window contains a bi-slider and a tri-slider which truncate the +cube in two directions.Complex PUI example
+ +

+Object Picking

+The most recent widget to be activated is called the "active widget." Usually +if you click the mouse on another widget the active widget immediately +deactivates and the new widget becomes the active widget. There are times, +however, when you will want to click the mouse somewhere in a window and +have the active widget operate on where you clicked the mouse. You do this +by setting and invoking the widget's "active callback." A simple GLUT mouse +function that does this is as follows: +
  static void mousefn ( int button, int updown, int x, int y )
+  {
+    if ( !puMouse ( button, updown, x, y ) )
+    {
+      // PUI didn't take the mouseclick, try the main window
+      mouse_x = x ;
+      mouse_y = puGetWindowHeight () - y ;
+      // Check for an active widget.
+      //  If there is one, call its active callback
+      if ( puActiveWidget () )
+        puActiveWidget () -> invokeActiveCallback () ;
+   }
+
+   glutPostRedisplay () ;
+ }
+First the mouse function calls puMouse to see if +the user clicked on a new PUI widget. If the user did not, puMouse +returns FALSE and the function executes the contents of the if-block. It +saves the mouse coordinates in a pair of global variables and, if there +is an active widget, it invokes the active widget's active callback. This +active callback will invoke the OpenGL select mode, create the pick matrix, +and redraw the window to get the selected objects. The PUI examples directory +has a program, "PointPicker", which does this. +

+Transparent GUI's

+One very trendy idea is to use translucent GUI widgets. This is handy because +the GUI doesn't intrude quite so badly into the available screen area. +Translucent menus have always been possible in PUI - it's just that nobody +ever actually wanted to do it. Here's what you do: +
    +
  • +Pick a default colour with an alpha component of about 0.4f.
  • + +
  • +Be sure to glEnable(GL_BLEND) and set up the appropriate blend functions. +I use glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) and glAlphaFunc(GL_GREATER,0.1f) +- call these just before puDisplay().
  • + +
  • +Now, is that cool or what?
  • +
+ +

+Class Descriptions

+ +

+puFont

+A puFont is a simple class which can be constructed in one of two ways: +

+1.) Using a GLUT bitmap font:

+ +
  puFont::puFont ( void *glut_font ) ;
+  puFont::puFont () ;
+
+
+Where 'glut_font' is a GLUT_BITMAP font. +

In case of the second constructor which does not take any arguments +at all, a puFont is created from the GLUT_BITMAP_8_BY_13 fixed font, which +can be considered as the default PUI font. +

Since there are only seven GLUT_BITMAP fonts, these have all been pre-declared +within PUI (mainly for backwards compatability with PLIB versions before +1.0.7). +

  extern puFont PUFONT_8_BY_13 ;        - 8x13 Fixed width
+  extern puFont PUFONT_9_BY_15 ;        - 9x15 Fixed width
+  extern puFont PUFONT_TIMES_ROMAN_10 ; - 10-point Proportional
+  extern puFont PUFONT_TIMES_ROMAN_24 ; - 24-point Proportional
+  extern puFont PUFONT_HELVETICA_10 ;   - 10-point Proportional
+  extern puFont PUFONT_HELVETICA_12 ;   - 12-point Proportional
+  extern puFont PUFONT_HELVETICA_18 ;   - 18-point Proportional
+
+
+These are light on storage - but slow to render on most hardware-based +OpenGL implementations because GLUT uses glBitMap to render its glyphs. +If you wish to use PLIB on a software-only OpenGL implementation (Good +Luck!) then you'll want to use these GLUT fonts since glBitMap is usually +faster than texture-mapped text. +

+2.) Using a pre-built FNT font:

+ +
  puFont::puFont ( fntTexFont *tex_font,
+                   float pointsize, float slant = 0 ) ;
+
+
+Where 'tex_font' is texture-based font created using the +FNT +library and setting the pointsize and optional italic slant (both measured +in pixels). +
  +

After the puFont has been constructed, you can retrieve the height or +width of a certain amount of text: +

  int puFont::getStringHeight ( void ) const ;
+  int puFont::getStringHeight ( const char *str ) const ;
+  int puFont::getStringWidth  ( const char *str ) const ;
+
+
+The getStringHeight routine that does not take any parameters returns the +height of the "K" character. +

Note that always the height excluding the descender is returned. +There is another function which allows you to determine the font descender: +

  int puFont::getStringDescender ( void ) const ;
+
+
+The following function can be used to draw some text at a specific position: +
  void puFont::drawString ( const char *str, int x, int y ) ;
+
+
+However, this is not the recommended way of positioning text on the user +interface. Instead, have a look at the puText widget. +

+puObject

+All PUI widgets are derived from the puObject abstract base class. User +programs should never declare objects of this type. puObjects are in turn +derived from an internally defined puValue class - but this is never visible +to the user and is not documented here. +

When you declare most widgets, you must define the size of ABOX for +the widget. (This is actually the pixel coordinates of the rectangle around +the active area of the widget - with the origin at bottom-left of the window). +

   puObject::puObject ( int minx, int miny, int maxx, int maxy ) ;
+
+
+(Some widgets need more or fewer arguments to their constructor - but most +follow this scheme). +

You can always find the type of an object derived from puObject by calling: +

  int puObject::getType ( void ) const ;
+
+
+This returns a bitmask showing the inheritance of any object derived from +the puObject base class: +
  #define PUCLASS_VALUE
+  #define PUCLASS_OBJECT
+  #define PUCLASS_GROUP
+  #define PUCLASS_INTERFACE
+  #define PUCLASS_FRAME
+  #define PUCLASS_TEXT
+  #define PUCLASS_BUTTON
+  #define PUCLASS_ONESHOT
+  #define PUCLASS_POPUP
+  #define PUCLASS_POPUPMENU
+  #define PUCLASS_MENUBAR
+  #define PUCLASS_INPUT
+  #define PUCLASS_BUTTONBOX
+  #define PUCLASS_SLIDER
+  #define PUCLASS_DIALOGBOX
+  #define PUCLASS_ARROW
+  #define PUCLASS_LISTBOX
+  #define PUCLASS_DIAL
+  #define PUCLASS_FILEPICKER
+  #define PUCLASS_FILESELECTOR
+  #define PUCLASS_BISLIDER
+  #define PUCLASS_TRISLIDER
+  #define PUCLASS_VERTMENU
+  #define PUCLASS_LARGEINPUT
+  #define PUCLASS_COMBOBOX
+  #define PUCLASS_SELECTBOX
+
+
+Hence, if you declare an object of (say) class puOneShot, +then calling getType() for that object would return +
   PUCLASS_VALUE | PUCLASS_OBJECT | PUCLASS_BUTTON | PUCLASS_ONESHOT
+(since a puOneShot is a kind of puButton +which is a kind of puObject which is [although not documented that way +here] a kind of puValue). +

For the purposes of printing diagnostics, etc there is also: +

  const char *puObject::getTypeString ( void ) const ;
+
+
+...which returns a pointer to a statically allocated string which is the +name of the top-level class to which that object belongs. +
   struct puBox { int min[2] ; int max[2] ; }
+
+   puBox *puObject::getABox ( void ) ;
+   puBox *puObject::getBBox ( void ) ;
+
+
+get the ABOX (Activity Box) and BBOX (Bounding Box) of the object. +

You can also move or resize the ABOX of a widget after it has been declared +using: +

  void puObject::setPosition ( int x, int y ) ;
+  void puObject::setSize     ( int w, int h ) ;
+
+  void puObject::getPosition ( int *x, int *y ) const ;
+  void puObject::getSize     ( int *w, int *h ) const ;
+The bounding box is adjusted to fit around the activity box and the label, +if any. +

The following functions allow you to manipulate +the value of the widget: +

  void  puObject::clrValue ( void ) ;
+  void  puObject::setValue ( int   i ) ;
+  void  puObject::setValue ( float f ) ;
+  void  puObject::setValue ( const char *s ) ;
+
+  void  puObject::getValue ( int   *i ) ;
+  void  puObject::getValue ( float *f ) ;
+  void  puObject::getValue ( char  *s ) ;  /* copies the string value into the string that is passed in */
+  void  puObject::getValue ( char **s ) ;  /* sets the argument to point to the string value */
+  int   puObject::getValue ( void ) ;      /* Obsolete ! Use getIntegerValue instead. */
+
+  int   puObject::getIntegerValue ( void ) ;
+  float puObject::getFloatValue   ( void ) ;
+  char  puObject::getCharValue    ( void ) ;     /* returns the first character in the string value */
+  char *puObject::getStringValue  ( void ) ;
+
+
+The clrvalue() function has the special function of setting the string +value of the widget to the empty string - and the numeric parts to zero. +

Some kinds of widget have a default value that they can easily be reset +to. +

  void puObject::defaultValue ( void ) ;
+
+
+...allows you to return the widget to some known default value. The default +value can also be set and read: +
  void  puObject::setDefaultValue ( int    i ) ;
+  void  puObject::setDefaultValue ( float  f ) ;
+  void  puObject::setDefaultValue ( const char *s ) ;
+
+  void  puObject::getDefaultValue ( int   *i ) ;
+  void  puObject::getDefaultValue ( float *f ) ;
+  void  puObject::getDefaultValue ( char  *s ) ;
+  void  puObject::getDefaultValue ( char **s ) ;
+  int   puObject::getDefaultValue ( void ) ;  /* Obsolete ! Use getDefaultIntegerValue instead. */
+
+  int   puObject::getDefaultIntegerValue ( void ) ;
+  float puObject::getDefaultFloatValue   ( void ) ;
+  char *puObject::getDefaultStringValue  ( void ) ;
+
+
+There are many occasions when you'd really like to have the PUI widget +directly drive and/or reflect the value of some memory location in the +application code. These calls let you do that: +
  void puObject::setValuator ( int   *i ) ;
+  void puObject::setValuator ( float *f ) ;
+  void puObject::setValuator ( char  *s ) ;
+
+
+Once you make one of these calls, PUI will automatically update the memory +location indicated with the current value of the widget whenever it changes +- and also update the appearance of the widget to reflect the value stored +in that memory location whenever the widget is redrawn. This is often a +lot more convenient than using a callback function to register changes +in the widget's value. +

IMPORTANT NOTE: When you pass a 'char*' to setValuator, it is your responsibility +to ensure that this pointer is pointing at enough memory to store the longest +possible string that this widget can possibly need. The string in the puValue +class is defined as being PUSTRING_MAX characters long; currently this +is 80. +

Passing NULL to setValuator causes PUI to revert to using an internal +variable to store the widget's state. +

Each widget can also retain a pointer to user-supplied data: +

  void  puObject::setUserData ( void *data ) ;
+  void *puObject::getUserData ( void ) const ;
+When the widget is drawn, the application has control of the drawing style +and the colours in which the widget is drawn. Reasonable defaults are provided +by PUI if you don't set them: +
  void puObject::setStyle ( int style ) ;
+  int  puObject::getStyle ( void ) const ;
+
+  'style' is one of:
+      PUSTYLE_NONE
+      PUSTYLE_PLAIN
+      PUSTYLE_SHADED          -- This is the Default.
+      PUSTYLE_SMALL_SHADED
+      PUSTYLE_BEVELLED
+      PUSTYLE_SMALL_BEVELLED
+      PUSTYLE_BOXED
+      PUSTYLE_SPECIAL_UNDERLINED
+      PUSTYLE_DROPSHADOW
+      PUSTYLE_RADIO
+The various styles are interpreted as follows: +
    +
  • +PUSTYLE_NONE - No background is drawn for the widget - just the legend +and the label.
  • + +
  • +PUSTYLE_PLAIN - Just a plain solid-coloured rectangle.
  • + +
  • +PUSTYLE_BEVELLED - The widget is drawn with a three-dimensional-looking +bevelled edge around it - with the 'light' shining on it from the top left.
  • + +
  • +PUSTYLE_SMALL_BEVELLED - Same as for PUSTYLE_BEVELLED but with a smaller +border.
  • + +
  • +PUSTYLE_SHADED - Similar to PUSTYLE_BEVELLED - but with the main rectangle +counter-shaded.
  • + +
  • +PUSTYLE_SMALL_SHADED - Same as for PUSTYLE_SHADED but with a smaller border.
  • + +
  • +PUSTYLE_BOXED - A simple rectangle with a line around the outside.
  • + +
  • +PUSTYLE_SPECIAL_UNDERLINED - A simple rectangle drawn with a thin line +at the bottom, giving an "underlined" effect like you can see in the +puMenuBar +and puVerticalMenu widgets.
  • + +
  • +PUSTYLE_DROPSHADOW - A simple rectangle - but drawn with a shadow below +and to the right to make it look like it has been raised off of the page.
  • + +
  • +PUSTYLE_RADIO - The bounding rectangle and the legend text isn't drawn +at all - the widget is drawn as a small diamond shape which is filled in +when the object has a non-zero value. This is only useful for puButton +widgets.
  • +
+In case of PUSTYLE_BOXED and PUSTYLE_SPECIAL_UNDERLINED, you can alter +the thickness of the border around the widget / the width of the line at +the bottom of the widget or retrieve the current value: +
  void puObject::setBorderThickness ( int t ) ;
+  int  puObject::getBorderThickness ( void ) const ;
+You should be careful to call the "setBorderThickness" routine after you +have specified the widget style.  The "setStyle" function sets the +border thickness automatically and will overwrite any previous border thickness +you may have set. +

By default, a border thickness of two pixels is used in PUSTYLE_BOXED. +For PUSTYLE_SPECIAL_UNDERLINED, a one pixel wide line is drawn. +

In addition, you can use the negation of the style to swap the appearance +of the selected and deselected versions of an object. Hence, using a style +of -PUSTYLE_BEVELLED will produce a widget that appears to be pressed in +when its value is zero and popped out when it's value is non-zero. +

While most widgets default to a style of PUSTYLE_SHADED, some of the +more complex types such as sliders and menus pick more complex defaults +in order to look 'reasonable'. You can still override those defaults - +but the results can often be less than desirable. +

 void puObject::setColour ( int which, float  r, float g, float  b, float  a = 1.0f ) ;
+ void puObject::getColour ( int which, float *r, float *g, float *b, float *a = NULL ) const ;
+
+  'which' is one of:
+      PUCOL_FOREGROUND
+      PUCOL_BACKGROUND
+      PUCOL_HIGHLIGHT
+      PUCOL_LABEL
+      PUCOL_LEGEND
+      PUCOL_MISC
+
+
+Here is a description of each colour's meaning: +
    +
  • +PUCOL_FOREGROUND, PUCOL_BACKGROUND, PUCOL_HIGHLIGHT - Depending on the +style which you have set, these colours are used for different purposes +while drawing the widget's ABOX.
  • + +
  • +PUCOL_LABEL - Obviously, this is the colour which is used when drawing +the label of a widget.
  • + +
  • +PUCOL_LEGEND - The colour that is used when the legend string is drawn. +For the +puInput, puLargeInput +and puListBox classes, which don't have a legend, +PUCOL_LEGEND is the colour of other kind of text inside the widget.
  • + +
  • +PUCOL_MISC - This colour is used for miscellaneous things in some classes. +Currently, it indicates the colour of the arrow inside a +puArrowButton, +and it is used while drawing the circle that is inscribed in a puDial +or the text cursor in the puInput and puLargeInput +widgets.
  • +
+Picking all of the individual colours for each widget can be tedious, so +there is a handy function that sets a 'theme' colour for the widget and +then picks suitable colours near to that theme for the other colours of +the widget. This function works well enough that you will almost never +need to set the colours individually. +
  void puObject::setColourScheme ( float r, float g, float b, float a = 1.0f ) ;
+
+
+Please note that these routines only affect the appearance of the widget +itself in case of classes that contain subwidgets and are not menu widgets +(currently puFileSelector, puLargeInput, +puComboBox, +puSelectBox +and the obsolete puFilePicker class). +

However, these classes are all derived directly or indirectly from +puGroup, +and thus you can use the appropriate puGroup functions +in order to change the styles, colours or border thicknesses of the subwidgets. +

In addition to the pre-defined styles, PUI allows you to create your +own drawing function and save the related drawing data. This is done by +means of the "render data" and "render callback." A render callback is +a user-defined function that has the following definition: +

  typedef void (*puRenderCallback)(class puObject *, int dx, int dy, void *) ;
+
+
+The function takes four parameters: a pointer to the object whose render +callback this is, the x- and y-coordinates of the lower left-hand of the +widget, and a pointer to your render data. The user tells PUI to use his +rendering callback instead of the usual drawing function by invoking the +following function: +
  void puObject::setRenderCallback ( puRenderCallback *c, void *d = NULL ) ;
+The two arguments are the name of the render callback and an optional pointer +to the user-defined render data. If a render callback exists, the widget's +draw function renders only the activity box (unless PUSTYLE_NONE is used) +and the label (if you specified one) and calls the render callback afterwards +instead of executing the code which would normally be used to draw the +widget. +

PUI also has functions to allow the user to retrieve and invoke the +render callback: +

  puRenderCallback puObject::getRenderCallback ( void ) const ;
+  void      *puObject::getRenderCallbackData ( void ) const ;
+  void       puObject::invokeRenderCallback ( int dx, int dy ) ;
+
+
+When a widget is activated, its user-supplied callback function is called +(if it exists): +
  typedef void (*puCallback)(puObject *) ;
+ 
+  void       puObject::setCallback ( puCallback c ) ;
+  puCallback puObject::getCallback ( void ) const ;
+  void       puObject::invokeCallback ( void ) ;
+
+
+The callback is invoked (typically) when the user releases the left mouse +button when the cursor lies within the widget's active area. The user-supplied +function is called with the address of the widget as a parameter so that +the same callback can often be used with a variety of similar widgets. +It is also possible to invoke an object's callback explicitly using invokeCallback +- bear in mind that this does not change the value of the object - unless +the callback itself does so. +

There are occasionally times when you'd like the widget to be activated +when the user PRESSES the mouse button instead of when he RELEASES it (which +is the default). PUI lets you make widgets that are activated on both button-down +and +button up. You can even tell PUI to call your callback continually all +the time the left button is pushed while the cursor is inside the widget: +

  void puObject::setActiveDirn ( int dirn ) ;
+  int  puObject::getActiveDirn ( void ) const ;
+
+  where:
+    'dirn' is either PU_UP, PU_DOWN, PU_CONTINUAL or PU_UP_AND_DOWN.
+
+
+As discussed earlier, PUI tracks the most recently invoked widget and calls +it the "active widget". PUI allows you to define a callback to be invoked +for the active widget when the user clicks the mouse outside any user interface +widgets. You can also define a callback for PUI to call when the widget +is being deactivated, when the user is activating another widget. The following +functions do these things: +
  void       puObject::setActiveCallback ( puCallback c ) ;
+  puCallback puObject::getActiveCallback ( void ) const ;
+  void       puObject::invokeActiveCallback ( void ) ;
+
+  void       puObject::setDownCallback ( puCallback c ) ;
+  puCallback puObject::getDownCallback ( void ) const ;
+  virtual void puObject::invokeDownCallback ( void ) ;
+
+
+Most widgets can have a LEGEND (text inside the active area of the widget), +and also a LABEL (text outside the active area). The application gets to +choose where the LABEL is placed relative to the active area of the widget. +You can also move the LEGEND text around inside the widget. +
  void   puObject::setLegend ( const char *str ) ;
+  void   puObject::setLabel  ( const char *str ) ;
+
+  const char *puObject::getLegend ( void ) const ;
+  const char *puObject::getLabel  ( void ) const ;
+
+  void   puObject::setLegendFont ( puFont font ) ;
+  void   puObject::setLabelFont  ( puFont font ) ;
+
+  puFont puObject::getLegendFont ( void ) const ;
+  puFont puObject::getLabelFont  ( void ) const ;
+
+  void  puObject::setLabelPlace ( int place ) ;
+  int   puObject::getLabelPlace ( void ) const ;
+
+  where 'place' is one of:
+ + + + + + + + + +
+
      PUPLACE_ABOVE_LEFT
+      PUPLACE_ABOVE_RIGHT
+
+      PUPLACE_TOP_LEFT
+      PUPLACE_TOP_CENTERED
+      PUPLACE_TOP_RIGHT
+
+      PUPLACE_UPPER_LEFT
+      PUPLACE_UPPER_RIGHT
+
+      PUPLACE_CENTERED_LEFT
+      PUPLACE_CENTERED_RIGHT
+
+      PUPLACE_LOWER_LEFT
+      PUPLACE_LOWER_RIGHT    -- The default LABEL place
+
+      PUPLACE_BOTTOM_LEFT
+      PUPLACE_BOTTOM_CENTERED
+      PUPLACE_BOTTOM_RIGHT
+
+      PUPLACE_BELOW_LEFT
+      PUPLACE_BELOW_RIGHT
+
Illustration of label places
+ +
  void  puObject::setLegendPlace ( int place ) ;
+  int   puObject::getLegendPlace ( void ) const ;
+
+  where 'place' is one of:
+ + + + + + + + + +
+
      PUPLACE_TOP_LEFT
+      PUPLACE_TOP_CENTERED
+      PUPLACE_TOP_RIGHT
+
+      PUPLACE_CENTERED_LEFT
+      PUPLACE_CENTERED_CENTERED    -- The default LEGEND place.
+      PUPLACE_CENTERED_RIGHT
+
+      PUPLACE_BOTTOM_LEFT
+      PUPLACE_BOTTOM_CENTERED
+      PUPLACE_BOTTOM_RIGHT
+
Illustration of legend places
+ +

In previous PLIB versions, a different (and smaller) set of label and +legend place names was used. The old names are kept for backwards compatibility, +but should be considered as deprecated. They might be removed in the future. +

Here is how the old names translate into the new ones: +

  LABEL places:
+
+      PUPLACE_ABOVE          => PUPLACE_TOP_LEFT
+      PUPLACE_BELOW          => PUPLACE_BOTTOM_LEFT
+      PUPLACE_LEFT           => PUPLACE_LOWER_LEFT
+      PUPLACE_RIGHT          => PUPLACE_LOWER_RIGHT
+      PUPLACE_TOP_CENTER     => PUPLACE_TOP_CENTERED
+      PUPLACE_BOTTOM_CENTER  => PUPLACE_BOTTOM_CENTERED
+      PUPLACE_LEFT_CENTER    => PUPLACE_CENTERED_LEFT
+      PUPLACE_RIGHT_CENTER   => PUPLACE_CENTERED_RIGHT
+
+  LEGEND places:
+
+      PUPLACE_LEFT           => PUPLACE_CENTERED_LEFT
+      PUPLACE_CENTERED       => PUPLACE_CENTERED_CENTERED
+      PUPLACE_RIGHT          => PUPLACE_CENTERED_RIGHT
+
+
+Each widget can be hidden (so it isn't drawn - and can't be clicked on), +or simply 'greyed out' (so it can't be clicked on even though it's drawn +- but in a style that makes it clear that this is the case). +
  void puObject::greyOut    ( void ) ;
+  void puObject::activate   ( void ) ;  -- Undo the 'greyout' effect
+  int  puObject::isActive   ( void ) const ;
+
+  void puObject::hide       ( void ) ;
+  void puObject::reveal     ( void ) ;  -- Undo the 'hide' effect
+  int  puObject::isVisible  ( void ) const ;
+
+
+If you want to see or change which window a widget is drawn in, you use +the following functions: +
  void  puObject::setWindow ( int w ) ;
+  int   puObject::getWindow ( void ) const ;
+
+
+If you have some generic data that your widget needs but that doesn't fit +anywhere else, you can save it in the widget's "user data" entry: +
  void  puObject::setUserData ( void *data ) ;
+  void *puObject::getUserData ( void ) const ;
+
+
+Finally, an object can be made to react to the 'return' key on the keyboard +just as if it had been clicked with the mouse. +
   void puObject::makeReturnDefault ( int boolean ) ;
+   int  puObject::isReturnDefault   ( void ) const ;
+
+
+In general, it is very confusing to the user to have multiple objects set +up with ReturnDefault enabled (although PUI allows this). Typically, this +option is only used on buttons in simple Yes/No dialog boxes. +

+puButton

+View of puButton widgetThe +puButton class is derived from puObject. It implements +a simple push-button widget. When clicked its value alternates from '0' +to '1' and is highlighted graphically when in the '1' state. By default, +buttons 'latch' down when clicked. The application could change this to +a one-shot behavior by resetting the value to '0' in the buttons callback +(although it is a lot more convenient to simply use the puOneShot +class for this). +
  puButton::puButton ( int minx, int miny, const char *legend ) ;
+  puButton::puButton ( int minx, int miny, int maxx, int maxy ) ;
+where the first constructor sets the button's size automatically according +to the size of the 'legend' string. +

Apart from these convenient short-hand constructor functions, puButtons +have no special API - everything they need is in the puObject +API. +

+puArrowButton

+View of puArrowButton widgetThe +puArrowButton class is derived from puButton. The +only difference is that puArrowButton renders as an arrow rather than as +a rectangular button. +
  puArrowButton::puArrowButton ( int minx, int miny, int maxx, int maxy,
+                                 int arrow_type ) :
+
+where 'arrow_type' is one of:
+Single arrows:
+
+  PUARROW_UP
+  PUARROW_DOWN
+  PUARROW_LEFT
+  PUARROW_RIGHT
+
+Double arrows:
+
+  PUARROW_FASTUP
+  PUARROW_FASTDOWN
+  PUARROW_FASTLEFT
+  PUARROW_FASTRIGHT
+
+
+In addition, you can get/set the direction of the arrow in mid-run: +
  int  puArrowButton::getArrowType ( void  ) const ;
+  void puArrowButton::setArrowType ( int i ) ;
+
+
+A puArrowButton widget does not have a legend. +

+puOneShot

+View of puOneShot widgetThe +puOneShot class is derived from puButton. It implements +a simple push-button widget which automatically pops back out again as +soon as the mouse is released. This means that its value is always '1' +inside the callback function and '0' at all other times. +

puOneShot's have no special API - everything they need is in the puButton +API. +

+puRange

+There is a group of widgets whose values are restricted to be between a +minimum and a maximum value.  An abstract "puRange" widget class was +created to provide the common code for these widgets.  The "puRange" +object class is derived from the "puObject" class and includes methods +to get and set the allowed minimum and maximum values: +
  float puRange::getMinValue ( void ) const ;
+  virtual void puRange::setMinValue ( float f ) ;
+ +
  float puRange::getMaxValue ( void ) const ;
+  virtual void puRange::setMaxValue ( float f ) ;
+Default values are 0.0 for the minimum and 1.0 for the maximum.  If +you want to specify an increment (for example, if you want values to be +whole numbers) you can: +
  float puRange::getStepSize ( void ) const ;
+  void puRange::setStepSize ( float f ) ;
+The default step size is 0.0, meaning that the value changes continuously. +

+puSlider

+View of puSlider widgetThe +puSlider class is derived from puRange. It implements a slider widget. +When clicked, dragged or unclicked its value changes in proportion to where +it is clicked. For the value ( as returned by puObject::getValue(float +*f) ) ranges from 0.0 to 1.0 from the left to the right (or from the bottom +to the top). The application can change the position of the slider using +puObject::setValue(float f) with a number in the +range 0.0 to 1.0. +
  puSlider::puSlider ( int minx, int miny, int sz ) :
+  puSlider::puSlider ( int minx, int miny, int sz, TRUE ) :
+  puSlider::puSlider ( int minx, int miny, int sz, {TRUE|FALSE}, int width ) ;
+
+
+The first version produces a HORIZONTAL slider, the second version produces +a VERTICAL slider. The third version produces a slider with a non-default +width. +
  void  puSlider::setSliderFraction ( float f ) ;
+  float puSlider::getSliderFraction ( void ) const ;
+
+
+The 'slider fraction' is the proportion of the total width of the slider +widget that is taken up with the sliders' "handle". It defaults to 0.1 +(ie one tenth of the width of the entire widget). + +

  void  puSlider::setPageStepSize ( float f ) ;
+  float puSlider::getPageStepSize ( void ) const ;
+
+The 'page step size' defines how much the slider handle jumps +if one clicks on the slider background. It defaults to 0, which makes +the slider handle jump immediately to the clicked upon position. + +

There are several choices to be made relating to when (or how often) +you wish your callback function to be called: +

  void  puSlider::setCBMode ( int mode ) ;
+  float puSlider::getCBMode ( void ) const ;
+  
+   where 'mode' is one of:
+       PUSLIDER_CLICK  - Only invoke the callback when
+                         the mouse goes in the active direction.
+       PUSLIDER_DELTA  - Invoke the callback only when the value
+                         of the slider changes by more than a
+                         certain amount.
+       PUSLIDER_ALWAYS - Invoke the callback all the time that
+                         the mouse is in the widget with the mouse
+                         button pushed down. (This is the default).
+
+
+In the PUSLIDER_DELTA case, the amount of change required before the callback +is called is set by: +
  void  puSlider::setDelta ( float f ) ;
+  float puSlider::getDelta ( void ) const ;
+
+
+The parameter is expressed as a fraction of the total slider width (ie +0.0f to 1.0f). +
  int  puSlider::isVertical ( void ) const ;
+
+
+This returns TRUE for Vertical sliders - FALSE for Horizontal ones. +

+puDial

+View of puDial widgetThe +puDial class is derived from puRange and implements a square widget with +a circle inscribed in it. A radius of the circle is also drawn. The value +of the widget is proportional to the angle the radius makes with the vertical +with straight down being zero - then winding it clockwise, straight up +is 0.5 and straight down again being 1.0. The constructor takes three arguments: +the coordinates of the lower left-hand corner of the widget and the widget +size: +
  puDial::puDial ( int minx, int miny, int sz ) ;
+
+
+By default, you can move the pointer directly from 0.0 to 1.0 or 1.0 to +0.0 without winding it all the way around the dial. This is called 'wrapping' +the pointer. If you need to change the wrap mode, you can call: +
  void puDial::setWrap ( int wrapmode ) ;
+  int  puDial::getWrap ( void ) const ;
+
+  where:
+    'wrapmode' is either TRUE (default) or FALSE.
+
+
+Everything defined in the puSlider API can also +be applied to puDial widgets. +

+puBiSlider

+View of puBiSlider widgetThe +puBiSlider class implements a slider with two sliding boxes inside the +slider bar. It is based on the puSlider class, +meaning that anything you can do with a puSlider +you can also do with a puBiSlider. You define it with the following call: +
  puBiSlider::puBiSlider ( int minx, int miny, int sz, int vertical = FALSE ) ;
+
+Since there are two sliding boxes, they are considered to be a current minimum and current maximum value. These are both integers and neither one is stored in the "puValue" as the widget's value. You set and retrieve them with the following functions:
+ +
  void puBiSlider::setCurrentMax ( float f ) ;
+  void puBiSlider::setCurrentMin ( float f ) ;
+
+  float  puBiSlider::getCurrentMax ( void ) const ;
+  float  puBiSlider::getCurrentMin ( void ) const ;
+
+
+If the user clicks on the puBiSlider with his mouse, the box that is closer +to the mouse is activated and moves to the mouse position. The other box +will stay put. If the user drags the upper box over the lower box or vice +versa, the stationary box will move with the other box. +

+

+puTriSlider

+View of puTriSlider widgetThe +puTriSlider class implements a slider with three sliding boxes inside the +slider bar. It is based on the puBiSlider and +includes everything that widget supports. You define it with the following +call: +
  puTriSlider::puTriSlider ( int minx, int miny, int sz, int vertical = FALSE ) ; 
+
+
+The third box in the puTriSlider stores its value in the widget's value. +You can set and retrieve the absolute minimum and maximum values that the +widget boxes can take, and you can set and retrieve the current minimum +and maximum values in the same way that you do with the puBiSlider. +You can set and retrieve the value of the middle box by using the "setValue" +and "getValue" functions. +

The puTriSlider has an additional option which allows you to freeze +the boxes on the ends of the slider bar. The following functions let you +do this: +

   void puTriSlider::setFreezeEnds ( int val ) ;
+   int  puTriSlider::getFreezeEnds ( void ) const ;
+
+   where:
+     'val' is either TRUE (the default) or FALSE.
+
+
+ +

+puFrame

+View of puFrame widgetThe +puFrame class is derived from puObject. It is designed +to provide some aesthetic layout and labelling to your user interface. +As such, the user can never do anything to the frame by clicking on it +- there is no point in creating a callback for this kind of object since +it will never be called. The frame renders as a large PUCOL_FOREGROUND +coloured rectangle (with legend and label) in the appropriate style. +

puFrames have no special API - everything they need is in the puObject +API. +

+

+puText

+View of puText widgetThe +puText class is derived from puObject. It's function +is simply to allow text to be positioned on the user interface. Since puText +has no active area (it cannot be clicked), it has no legend text and the +text you want it to draw should be in the label string. +

The only special API for puText is its constructor function: +

  puText::puText ( int x, int y ) ;
+
+
+By default, this is the bottom-left corner of the label - but you can of +course change that with puObject::setLabelPlace(). +

Everything else that puText widgets need is in the puObject +API. +

+

+puInput

+View of puInput widgetThe +puInput class is derived from puObject. It's designed +for the specific purpose of allowing the user to input a string (or an +integer or floating point number). +

First, create the puInput object: +

   puInput::puInput ( int minx, int miny, int maxx, int maxy ) ;
+
+
+Note that puInput objects do not display their 'legend' string - the center +of the widget being used to draw the value as a string. +

A puInput object can be in two states - accepting keystrokes - or ignoring +them. Use these functions to toggle between the two states: +

  void puInput::acceptInput ( void ) ;
+  void puInput::rejectInput ( void ) ;
+  int  puInput::isAcceptingInput ( void ) const ;
+
+
+When the user hits the 'Return' key, the puInput is automatically set to +reject further input. When the user clicks the mouse onto the puInput, +it is automatically set to accept input - and the I-bar cursor is moved +next to the character nearest to where the mouse was clicked. +

The text area contains an 'I' bar cursor and a highlighted 'select' +area. You can get and set the character positions (not pixel coordinates) +of those entities: +

  void puInput::setCursor ( int pos ) ;
+  int  puInput::getCursor ( void ) const ;
+
+  void puInput::setSelectRegion ( int  start, int  end ) ;
+  void puInput::getSelectRegion ( int *start, int *end ) const ;
+
+
+Since a puInput uses the normal value getting and setting functions of +puObject, +you are limited to PUSTRING_MAX characters (currently 80). +

When typing into a puInput box, the Backspace, Delete, Home, End, Left +and Right Arrows, Space, Return, Tab, Esc and ^U keys have the expected +functions. All printable characters are inserted into the text. Everything +else is not accepted by the puInput and is available to other functions. +

If you want to restrict the characters that the input widget will accept, +you use the following functions: +

  void  puInput::setValidData ( const char *data ) ;  /* Sets list of valid characters to 'data' */
+  void  puInput::addValidData ( const char *data ) ;  /* Appends 'data' to list of valid characters */
+  char *puInput::getValidData ( void ) const ;        /* Returns pointer to list of valid characters */
+  int   puInput::isValidCharacter ( char c ) const ;  /* Returns 1 if character is valid and 0 if not */
+
+
+This will avoid, for example, the user typing his name into an input box +that is supposed to accept his birthdate. +

Additionally, you can let a puLargeInput widget reject all input that +would modify the text in the puInput box (everything except the Home, End, +Left and Right keys): +

  void puLargeInput::enableInput ( void ) ;
+  void puLargeInput::disableInput ( void ) ;
+  int  puLargeInput::inputDisabled ( void ) const ;
+
+  where:
+    inputDisabled() returns FALSE if the text can be modified and TRUE if it can not.
+
+
+If the text is too long to fit in the widget, only the rightmost characters +will be displayed. One space is always left to the right of the text to +give the user the impression that there is still room to type. +

+

+puLargeInput

+View of puLargeInput widgetThe +puLargeInput widget resembles the puInput widget +but it is actually based on the puGroup widget. +The reason for this is that the widget contains sub-widgets within it: +there is puFrame widget to display the text area; +two puSlider widgets to allow the user to scroll +vertically and horizontally; and there are up to four puArrowButton +widgets to allow rapid vertical scrolling. The puLargeInput widget allows +the user to enter an arbitrarily large amount of text, including multiple +carriage returns. This text is stored in a separate text buffer and is +not connected to the widget value. You create a puLargeInput widget with +the following call: +
  puLargeInput::puLargeInput ( int x, int y, int w, int h,
+                               int arrows, int sl_width ) ;
+
+
+Which creates a puLargeInput at location (x, y), with size (w, h), with +sliders of "sl_width" pixels wide, and with "arrows" pairs of up- and down-arrows +on the right-hand slider. Allowed values for "arrows" are 0, 1, and 2. +

The following functionality from the puInput +widget is also implemented in the puLargeInput widget: +

  void puLargeInput::acceptInput ( void ) ;
+  void puLargeInput::rejectInput ( void ) ;
+  int  puLargeInput::isAcceptingInput ( void ) const ;
+
+  void puLargeInput::setCursor ( int pos ) ;
+  int  puLargeInput::getCursor ( void ) const ;
+
+  void puLargeInput::setSelectRegion ( int  start, int  end ) ;
+  void puLargeInput::getSelectRegion ( int *start, int *end ) const ;
+
+  void  puLargeInput::setValidData ( const char *data ) ;  /* Sets list of valid characters to 'data' */
+  void  puLargeInput::addValidData ( const char *data ) ;  /* Appends 'data' to list of valid characters */
+  char *puLargeInput::getValidData ( void ) const ;        /* Returns pointer to list of valid characters */
+  int   puLargeInput::isValidCharacter ( char c ) const ;  /* Returns 1 if character is valid and 0 if not */
+
+  void puLargeInput::enableInput  ( void ) ;
+  void puLargeInput::disableInput ( void ) ;
+  int  puLargeInput::inputDisabled ( void ) const ;
+
+  where:
+    inputDisabled() returns FALSE if the text can be modified and TRUE if it can not.
+
+
+Besides the obvious methods of typing or erasing, you can add text to and +remove text from the widget using the following calls: +
  void  puLargeInput::setText ( const char *l ) ;  /* Erases current text and copies input string to text */
+  char *puLargeInput::getText ( void ) const ;     /* Returns the pointer to the text string */
+  void  puLargeInput::addNewLine ( const char *l ) ;  /* Inserts input string at start of next line after cursor */
+  void  puLargeInput::addText ( const char *l ) ;     /* Inserts input string at cursor position */
+  void  puLargeInput::appendText ( const char *l ) ;  /* Adds input string to the end of text */
+  void  puLargeInput::removeText ( int start, int end ) ;  /* Removes the text between character positions */
+
+
+The puLargeInput widget always stores a carriage return at the end of its +text. Even if you delete all the text in the widget, it will create an +empty carriage return. +

The puLargeInput widget also has a few other functions which may be +handy: +

  int  puLargeInput::getNumLines ( void ) const ;  /* Returns the number of lines in the text */
+  void puLargeInput::selectEntireLine ( void ) ;   /* Expands the select region to include full lines */
+  void puLargeInput::setTopLineInWindow ( int val ) ;  /* Sets the top line to be displayed in the widget window */
+
+
+puLargeInput widgets do not have a legend. +

+puGroup

+The puGroup class is derived from puObject. Its +purpose is to allow you to group together other puObjects +and to operate on them en masse. +

When you want to create a group of puObjects +using a puGroup, you construct the puGroup itself, then construct all the +widgets that you want to have inside it. When you have finished adding +widgets to the puGroup, you call: +

  void puGroup::close ( void ) ;
+
+
+puGroup widgets can be placed inside other puGroup widgets to an arbitrary +depth. When a puGroup is delete'ed, it will automatically delete everything +that it contains. +

It is also possible to retrieve the number of childs, and there is a +function which returns the first child inside a puGroup (or NULL if the +group is empty): +

  int puGroup::getNumChildren ( void ) const ;
+  puObject * puGroup::getFirstChild ( void ) const ;
+
+
+Doing a puObject::hide() on a puGroup will hide +all the objects inside the puGroup. Doing a puObject::greyout() +on a puGroup prevents anything inside that group from being clicked. +

A puGroup can be put into "floating" state, meaning that the user can +drag it around with the right mouse button pressed: +

  void puGroup::setFloating ( int value ) ;
+  int  puGroup::getFloating ( void ) const ;
+
+  where:
+    'value' is either TRUE or FALSE.
+
+
+By default, floating is not enabled. +

Colours, styles, etc are not propagated from puGroup to its children, +although there are six functions which allow you to change the appearances +of the child widgets on a per-child-class basis: +

  void puGroup::setChildStyle ( int childs, int style, int recursive = FALSE ) ;
+  void puGroup::setChildBorderThickness ( int childs, int t, int recursive = FALSE ) ;
+
+  void puGroup::setChildColour ( int childs, int which,
+                                 float r, float g, float b, float a = 1.0f,
+                                 int recursive = FALSE ) ;
+  void puGroup::setChildColourScheme ( int childs,
+                                       float r, float g, float b, float a = 1.0f,
+                                       int recursive = FALSE ) ;
+
+  void puGroup::setChildLegendFont ( int childs, puFont f, int recursive = FALSE ) ;
+  void puGroup::setChildLabelFont ( int childs, puFont f, int recursive = FALSE ) ;
+
+
+These routines all take a bitmask specifying which childs to apply the +specified value(s) for by or-ing the appropriate PUCLASS_ constants as +their first argument. +

The last parameter specifies whether the function should operate recursively, +meaning that if a child of the puGroup is another puGroup object or a widget +derived from puGroup, the attributes of the widgets in this group are also +changed. +

Have a look at the following example (assume inputbox to be a pointer +to a puLargeInput widget, which is derived +from puGroup and contains a puFrame, two puSlider +and up to four puArrowButton widgets): +

  inputbox -> setChildStyle ( PUCLASS_ARROW, PUSTYLE_BOXED ) ;
+  inputbox -> setChildColour ( PUCLASS_ARROW | PUCLASS_FRAME,
+                             PUCOL_HIGHLIGHT, 1.0, 1.0, 1.0 ) ;
+
+
+In this case, we would have set the style of all arrow button subwidgets +(if there were any) to PUSTYLE_BOXED and specified the "highlight" colour +of both the frame and the arrow button subwidgets to be white. +

Note that when you specify a child class in the bitmask, all childs +derived from that class are also affected. Hence, using PUCLASS_OBJECT +as the first argument of these functions is an easy way to change the parameters +of all child widgets. +

The coordinates of puGroup child objects are always specified relative +to the bottom-left corner of the puGroup rather than in absolute screen +coordinates. +

The puGroup widget is not drawn. It is commonly a good thing to do, +but certainly not essential, to make the first widget inside a puGroup +to be a puFrame of the group's desired size. The +frame will provide a background for all the other widgets in the group. +

+puInterface

+The puInterface class is derived from puGroup. This +class is another abstract class - application programs should not declare +puInterface objects. +

The difference between a simple puGroup and a +puInterface is that when an interface is enabled, it takes priority over +other widgets so that they cannot be clicked upon. Widgets such as popup +menus are typically puInterfaces which group a number of +puButtons. +

When declaring objects derived from puInterface, it is essential that +these objects are deleted in the reverse order to their creation (except +when your program is about to exit anyway). This is because PUI keeps a +list of puInterfaces on a stack, and attempting to delete a puInterface +from the middle of the stack will cause an error. +

The PUI system itself maintains a private global puInterface which is +used to group together all of the puObjects that +the application generates. +

+

+puPopup

+The puPopup class is derived from puInterface. +Its function is to pop up a bunch of other widgets on command. +

One significant difference between puPopups and other puObjects +is that it starts off hidden (puObject::isVisible()==FALSE) +- and the application (or some other widget) has to make it visible in +order for it to DO something. +

It's possible to use a puPopup to create a popup menu, as a part of +a drop-down menu and to implement dialog boxes and alert boxes. +

+

+puDialogBox

+View of example puDialogBox widget groupThe +puDialogBox class is derived from puPopup. While +there is a puDialogBox in existence (ie Constructed and not yet destroyed) +nothing that isn't contained within that puDialogBox will be activated +by either keyboard or mouse (Although they will still be re-drawn). +
   puDialogBox::puDialogBox ( int x, int y ) ;
+
+
+Here is a simple example where a callback function wants to tell the user +that its code hasn't been written yet... +
puDialogBox *dialog_box = NULL ;
+
+void go_away_callback ( puObject * )
+{
+  delete dialog_box ;
+  dialog_box = NULL ;
+}
+
+void make_dialog ( const char *txt )
+{
+  if ( dialog_box != NULL )
+    return ;
+
+  dialog_box = new puDialogBox ( 50, 50 ) ;
+  {
+    new puFrame ( 0, 0, 400, 100 ) ;
+    new puText  ( 10, 70 ) -> setLabel ( txt ) ;
+
+    puOneShot *ok = new puOneShot ( 180, 10, "OK" ) ;
+    ok -> makeReturnDefault ( TRUE ) ;
+    ok -> setCallback       ( go_away_callback ) ;
+  }
+  dialog_box -> close  () ;
+  dialog_box -> reveal () ;
+}
+
+void not_implemented_yet_callback ( puObject * )
+{
+  make_dialog ( "Sorry, that function isn't implemented yet" ) ;
+}
+
+
+When 'not_implemented_yet_callback' is called (presumably from another +puObject), +it calls 'make_dialog' to construct and activate a suitable dialog box +object. +

This causes the dialog box to pop up onto the screen. When the 'OK' +button is pressed (or the RETURN key is hit - since the button has +puObject::makeReturnDefault(TRUE) +set), the 'go_away_callback' is called - which deletes the dialog box (which +in turn causes the destruction of all the contents of that box). +

+

+puPopupMenu

+View of puPopupMenu widgetThe +puPopupMenu class is derived from puPopup. It's +designed for the specific purpose of building a simple popup menu - which +is actually implemented as a stack of buttons (puOneShot's +actually) with callbacks. +

First, create the puPopupMenu object: +

   puPopupMenu::puPopupMenu ( int x, int y ) ;
+
+
+Afterwards, each item is added: +
   puObject *puPopupMenu::add_item ( const char *str, puCallback cb ) ;
+
+
+Where 'str' is the string that will appear in the menu and 'cb' is the +corresponding callback that will be made if that menu item is clicked, +or NULL in order to indicate items that should not have an action when +clicked upon. +

Each item that you add will be translated into an appropriately sized, +styled and coloured puOneShot. The add_item() +function returns a pointer to the newly created puOneShot +casted into a puObject. +

Since puPopupMenu is derived from puPopup and +hence is a puGroup, don't forget to call +

  puPopupMenu::close () ;
+
+
+after you have finished adding the individual menu entries. +

A puPopupMenu does not have a label or a legend. +

+

+puMenuBar

+View of puMenuBar widgetThe +puMenuBar class is derived from puInterface. +It's designed for the specific purpose of building a horizontal strip menu +(typically at the top of the screen). Each entry in that menu is a +puOneShot +that pops up a puPopupMenu when clicked upon. +

First, create the puMenuBar object: +

   puMenuBar::puMenuBar ( int height = -1 ) ;
+
+
+Which creates a menu bar with height 'height' at the top of the window. +If you omit 'height', the menu bar height will be auto-determined through +the height of the current default legend font. +

Afterwards, PUI will always attempt to remain the menu bar at the top +of the screen, even if the screen is resized. +

Next, set up the colours for the menu - just as with any other puObject, +then add the individual items: +

  void *puMenuBar::add_submenu ( const char *name, char *legends[], puCallback cb[] );
+
+
+'name' is the name that appears on the menu bar, 'legends' is the NULL-terminated +list of strings that appear inside the popup menu and 'cb' is the list +of corresponding callback functions. (Both arrays should be NULL terminated +but the puCallback array can have other NULL pointers indicating PopupMenu +items that have no action when clicked upon). +

Each submenu that you add will be translated into a puOneShot +and an appropriately sized, styled and coloured puPopupMenu +which is hidden and revealed appropriately as it's button in the menu bar +is clicked upon. +

Don't forget (since this is a kind of puInterface) +that you need to call: +

  puMenuBar::close() ;
+
+
+...when you have finished creating the menu items. +

Example: +

  char      *file_submenu    [] = { "Exit" , "Close", "--------", "Save" , NULL};
+  puCallback file_submenu_cb [] = { exit_cb, cull_cb,       NULL, save_cb, NULL};
+  char      *help_submenu    [] = { "About...", "Help" , NULL } ;
+  puCallback help_submenu_cb [] = { about_cb  , help_cb, NULL } ;
+
+  puMenuBar *menu = new puMenuBar ( -1 ) ;
+  menu->add_submenu ( "File", file_submenu, file_submenu_cb ) ;
+  menu->add_submenu ( "Help", help_submenu, help_submenu_cb ) ;
+  menu->close () ;
+
+
+A puMenuBar does not have a label or a legend. +

+puVerticalMenu

+View of puVerticalMenu widgetThe +puVerticalMenu is derived from the puGroup class +and is designed to create a menu with its entries one above the other. +You create it with the following call: +
  puVerticalMenu::puVerticalMenu ( int x = -1, int y = -1 ) ;
+
+
+where using a default value for "x" will place the menu on the left-hand +side of its window and using a default value for "y" will place it on the +top of its window. Its behavior is essentially the same as that of the +puMenuBar +widget. +

A puVerticalMenu does not have a label or a legend. +

+

+puListBox

+View of puListBox widgetThe +puListBox widget is a multiple-line text widget which was designed to support +the puFileSelector widget. It is derived +from the puButton widget and supports that widget's +entire API. You create a puListBox by invoking the constructor method: +
  puListBox::puListBox  ( int minx, int miny, int maxx, int maxy,
+                          char **list = NULL) ;
+
+
+In this case "list" is a null-terminated array of character strings that +contain the items that go into the list box. You are responsible for supplying +this array and for making sure that it is still around the entire time +the puListBox widget is using it. The widget does not make its own +copy of the array. You can change which array the listbox operates on by +calling: +
  puListBox::newList ( char **list ) ;
+
+
+The puListBox widget supports three additional function calls in its API. +They are as follows: +
  int  puListBox::getNumItems ( void ) const ;  /* Return the number of items in the list */
+  int  puListBox::getTopItem  ( void ) const ;  /* Return the index of the top item that is displayed in the list */
+  void puListBox::setTopItem  ( int item_index ) ;  /* Set the index of the top item to be displayed */
+
+
+If the text is too wide for the box, it'll be truncated with "..." displayed +to the right of it to show the user that there is some missing text. +

A puListBox widget does not have a legend. +

+puButtonBox

+View of puButtonBox widgetThe +puButtonBox class is derived from puObject. It +automatically generates a number of 'radio' buttons in it's draw function +and has single callback function. It can optionally manage the problem +of ensuring that exactly one of the buttons is depressed at all times, +or it can allow multiple buttons to be active at the same time. +
  puButtonBox::puButtonBox ( int minx, int miny, int maxx, int maxy,
+                             char **labels, int one_button ) ;
+
+
+The 'labels' parameter is a NULL-terminated array of pointers-to-strings +containing the labels for each of the radio-buttons. These are drawn in +order from top to bottom of the Button Box. +

As with the puListBox widget, you can change +the list of labels in mid-run or retrieve the number of buttons: +

  void puButtonBox::newList ( char **list ) ;
+  int  puButtonBox::getNumItems ( void ) const ;
+
+
+Like most other puObjects, puButtonBox has a 'value' +that can be set using puObject::setValue(int i) +and read using puObject::getValue(). If 'one_button' +is set TRUE then the puButtonBox will use its value as the index of the +radio-button that is currently pressed. If 'one_button' is FALSE then the +puButtonBox will limit the number of buttons to 32 and use its value as +a mask indicating which buttons are set ('1'==button pressed, '0'==button +not pressed). +
  int puButtonBox::isOneButton ( void ) const ;
+
+
+This function returns TRUE for a one_button box, FALSE otherwise. +

By default, if one_button==TRUE then the first button is highlighted +since the 'value' of the puButtonBox is zero. If one_button==FALSE then +none of the buttons are pressed by default since the value is 0x00000000 +and that indicates no buttons pushed. +

In the one_button==TRUE case, setting the value to something out of +range will result in none of the radio buttons being highlighted. +

The puButtonBox widget does not have a legend. +

+puComboBox

+View of puComboBox widgetThe +puComboBox class is derived from puGroup. It implements +yet another widget that allows the user to select one option of an amount +of possible ones. +

The widget contains a puInput widget that displays +the currently selected item and even allows the user to type in an arbitrary +string if you allow him to do so (see below). There is also a +puPopupMenu +that contains all the entries - it is invisible until the user clicks on +the puArrowButton on the right of the combo +box, and it is hidden again after the user selects an item from it or if +he clicks somewhere else on the screen. +

You create a a puComboBox with the following call: +

  puComboBox::puComboBox ( int minx, int miny, int maxx, int maxy,
+                           char **list, int editable = TRUE ) ;
+
+
+Like the constructors of the previously described widgets, the puComboBox +constructor takes a NULL-terminated array of strings specifying the items +that should go into the widget as it's fourth parameter. If 'editable' +is TRUE (default), the input box is made editable for allowing the user +to enter a string in addition to selecting one of the entries from the +popup menu. +

Depending on the position of the puComboBox inside the window, the widget +automatically determines whether the popup menu should be shown above or +below the widget when the user clicks on the arrow. The puArrowButton +is either pointing in up or down direction to indicate where the menu will +pop up. +

Regardless of whether you passed TRUE or FALSE to the 'editable' parameter +when constructing the widget, the user can click inside the input box and +use the Up and Down keys to browse through the items. The Home and End +keys switch to the first or last item as you would expect, and the PgUp +/ PgDown can be used to toggle the popup menu. +

Just like all other classes which store a pointer to an array of strings +that indicates the widget's elements, the puComboBox class provides a routine +that allows you to change the list of elements in mid-run - and you can +also retrieve the number of items: +

  void puComboBox::newList ( char **list ) ;
+  int  puComboBox::getNumItems ( void ) const ;
+
+
+The puComboBox stores the currently selected item or the text that was +typed in by the user (if the combo box is 'editable' - see above) as the +widget's string value which can be retrieved using puObject::getValue(char +*s), puObject::getValue(char **s) or puObject::getStringValue(). +

There is also a function that allows you to retrieve the number of the +currently selected item: +

  int puComboBox::getCurrentItem ( void ) ;
+
+
+If the combo box is 'editable' and if the user typed in some text into +the input box instead of selecting an item from the popup menu or using +the appropriate keys, the function automatically compares the entered string +against each element in the list of items. If there is a match, the number +of the corresponding item is returned just as if it had been selected through +one of the more common methods. If there is no match, a negative number +is returned - it's up to the programmer to decide what the application +should do then (for example, show up a message box saying that an invalid +value was typed in). +

Last but not least, the number of the current item can also be set: +

  void puComboBox::setCurrentItem ( int number ) ;
+
+
+Whenever the current item is changed through the user or through the programmer +using one of the methods described above, the widget invokes its callback. +

A puComboBox widget does not have a legend. +

+puSelectBox

+View of puSelectBox widgetThe +puSelectBox class is derived from puGroup. It is +one more widget that is designed for the purpose of allowing the user to +select one element from a list of items. +

The widget contains a puInput widget that displays +the currently selected item. Unlike in the puComboBox +class, it can not be made editable. There are two puArrowButtons +that allow the user to select an item. If the currently selected item is +the first item, the 'down' arrow is automatically disabled and greyed out +- if the currently selected item is the last item, the same thing happens +to the 'up' arrow. +

You create a a puSelectBox with the following call: +

  puSelectBox::puSelectBox ( int minx, int miny, int maxx, int maxy,
+                             char **list ) ;
+
+
+Again, 'list' is a NULL-terminated array of strings specifying the items +that should appear in the widget. +

The remainder of the puSelectBox API is the same as the API of the puComboBox +class described above - you change the item list that in mid-run, retrieve +the number of items and set or retrieve the currently selected item: +

  void puSelectBox::newList ( char ** list ) ;
+
+  int  puSelectBox::getNumItems ( void ) const ;
+
+  int  puSelectBox::getCurrentItem ( void ) const ;
+  void puSelectBox::setCurrentItem ( int number ) ;
+
+
+Like the puComboBox widget, a puSelectBox additionally +stores the currently selected item as the widget's string value. +

The puSelectBox widget allows the user to select an item using the Up, +Down, PgUp, PgDown, Home and End keys after clicking into the input box +just as a puComboBox does. The only difference +is that the PgUp and PgDown keys don't toggle a popup menu (a puSelectBox +has none). Instead, they have the same meaning as the Up and Down keys. +

Whenever the current item is changed by the user (through clicking upon +one of the arrow buttons or using the appropriate keys) or by the programmer, +the widget invokes it's callback. +

A puSelectBox widget does not have a legend. +

+puFilePicker

+puFilePicker is now obsolete. Please use puFileSelector +instead. +

+puFileSelector

+View of puFileSelector widgetThe +puFileSelector class is derived from puDialogBox. +FileSelector creates a dialog box for selecting a file. The puFileSelector +widget contains: +
    +
  • +A puFrame widget of the puFileSelector's desired +size.
  • + +
  • +A puListBox widget for listing the files. It's +label is placed at the top of it (PUPLACE_TOP_LEFT) and can be set through +the 'title' argument of the puFileSelector constructor.
  • + +
  • +A puSlider widget with zero, two, or four +puArrowButton +widgets (depending on what you ask for) to scroll the file list.
  • + +
  • +Two puOneShot button widgets for the OK and Cancel +buttons.
  • + +
  • +A puInput to allow the user to type in the filename +by hand.
  • +
+The API for puFileSelector: +
  puFileSelector::puFileSelector ( int x, int y,
+                                   const char *dir,
+                                   const char *title = "Pick a file" ) ;
+  puFileSelector::puFileSelector ( int x, int y, int arrows,
+                                   const char *dir,
+                                   const char *title = "Pick a file" ) ;
+  puFileSelector::puFileSelector ( int x, int y, int w, int h,
+                                   const char *dir,
+                                   const char *title = "Pick a file" ) ;
+  puFileSelector::puFileSelector ( int x, int y, int w, int h, int arrows,
+                                   const char *dir,
+                                   const char *title = "Pick a file" ) ;
+
+
+The first constructor sets the position but uses a default value for the +size of the dialog box, currently defaulting to 170,220. The second is +like the first but also allows you to specify up- and down-arrows (0 for +none, 1 for single arrows, 2 for single and double arrows) next to the +list box. The third and fourth constructors allow you to also select the +size of the dialog box as well. +

The "dir" argument is the name of the directory whose files are to be +listed as a starting point - it can be either absolute or relative to the +current working directory. +

"title" is a title that is displayed in the puFileSelector widget. +

When a file has been picked by the user, the string value of the widget +will be set to the absolute pathname of the file. +

  puFileSelector::setSize ( int w, int h) ;
+
+
+This method is used to set the current width and height of the dialog box. +
  puFileSelector::setInitialValue ( const char *pathname ) ;
+
+
+Sets the initial value within the type-in area of the widget. This is generally +a simple file name - but it could be a full path either relative to the +'dir' you specified in the constructor or or an absolute path. +

The puFileSelector widget does not have a legend or a label. Instead, +you can specify the title which is displayed at the top of the widget when +constructing it. +

+Non-Class Functions.

+The following functions are not a part of any classes: +

+

+Setting Defaults

+It can be pretty tedious coding in the colours, fonts and style for every +puObject. +There are a number of routines that alter the defaults that will be assumed +for all subsequently constructed puObjects... +
  void puSetDefaultStyle ( int style ) ;
+  int  puGetDefaultStyle ( void ) ;
+
+  void   puSetDefaultFonts ( puFont  legendFont, puFont  labelFont ) ;
+  void   puGetDefaultFonts ( puFont *legendFont, puFont *labelFont ) ;
+  puFont puGetDefaultLabelFont  ( void ) ;
+  puFont puGetDefaultLegendFont ( void ) ;
+
+  void puSetDefaultColourScheme ( float  r, float  g, float  b, float  a = 1.0 );
+  void puGetDefaultColourScheme ( float *r, float *g, float *b, float *a = NULL);
+
+
+These take the same arguments as the corresponding puObject +class members. +

+

+puInit

+This must be the first PUI function that you call. It must be called after +GLUT window setup but before glutMainLoop(). +
   void puInit ( void ) ;
+
+
+ +

+puDisplay

+Causes PUI to redraw all of the currently created widgets. +
   void puDisplay ( void ) ;
+
+
+It sets up the GL transforms as follows: +
  int w = puGetWindowWidth  () ;
+  int h = puGetWindowHeight () ;
+
+  glPushAttrib   ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT ) ;
+
+  glDisable      ( GL_LIGHTING   ) ;
+  glDisable      ( GL_FOG        ) ;
+  glDisable      ( GL_TEXTURE_2D ) ;
+  glDisable      ( GL_DEPTH_TEST ) ;
+
+  glViewport     ( 0, 0, w, h ) ;
+  glMatrixMode   ( GL_PROJECTION ) ;
+  glPushMatrix   () ;
+  glLoadIdentity () ;
+  gluOrtho2D     ( 0, w, 0, h ) ;
+  glMatrixMode   ( GL_MODELVIEW ) ;
+  glPushMatrix   () ;
+  glLoadIdentity () ;
+...and after it finishes rendering the GUI and the puCursor (see below), +it restores state like this: +
  glMatrixMode   ( GL_PROJECTION ) ;
+  glPopMatrix    () ;
+  glMatrixMode   ( GL_MODELVIEW ) ;
+  glPopMatrix    () ;
+  glPopAttrib    () ;
+
+
+All drawing code is done with whatever glMaterial/glTexture/glEnabled facilities +are currently set. All PUI rendering is done with simple colours and 2D +drawing functions such as glRect and glDrawPixels. +

This function is typically called from the glutDisplayFunc callback. +

Example: +

  void my_display_func ()
+  {
+    glClearColor ( 0.1, 0.4, 0.1, 1.0 ) ;
+    glClear      ( GL_COLOR_BUFFER_BIT ) ;
+    ...do your own OpenGL rendering...
+    puDisplay () ;
+    glutSwapBuffers   () ;
+    glutPostRedisplay () ;
+  }
+ 
+  .
+  .
+  puInit () ;
+  glutDisplayFunc ( my_display_func ) ;
+  .
+  .
+  glutMainLoop() ;
+
+
+ +

+puDeleteObject

+You should use this routine instead of deleting widgets directly. +
   void puDeleteObject ( puObject *ob ) ;
+
+
+ +

+puKeyboard

+This routine takes keystroke events (presumably generated by GLUT) and +uses them to decide which (if any) widgets changed state as a result. puKeyboard +doesn't do any actual graphics - so you have to call puDisplay +if you want the display to be updated. +
   int puKeyboard ( int key, int state ) ;
+
+   'key' is either an ASCII character, or one of the
+       PU_KEY_xxx symbols. These are named similarly
+       to the GLUT_KEY_xxx symbols but are numerically
+       set such that:
+
+           PU_KEY_xxx == GLUT_KEY_xxx + PU_KEY_GLUT_SPECIAL_OFFSET
+   'state' is one of
+       PU_DOWN           (which is the same as GLUT_DOWN)
+       PU_UP             (which is the same as GLUT_UP)
+       (At present, GLUT can only generate PU_DOWN events for the
+       keyboard - and PUI won't do anything with PU_UP events.
+
+
+Notice that this function will accept either an ASCII character or a special +keycode. +

The return result is TRUE if one of the widgets actually used the keystroke. +This can be used to determine if the keyboard event was 'consumed' by the +user interface - or whether it should be used in some other application-specific +way. It is also true to say that in a single-buffered application, the +puDisplay +function doesn't need to be called until puKeyboard() returns TRUE (unless +of course the application chooses to change a colour or a label or something). +

Example: +

   void my_keyboard_func ( unsigned char key, int x, int y )
+   {
+     puKeyboard ( key, PU_DOWN ) ;
+     glutPostRedisplay () ;
+   }
+ 
+   void my_special_func ( int special_key, int x, int y )
+   {
+     puKeyboard ( special_key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ;
+     glutPostRedisplay () ;
+   }
+ 
+   .
+   .
+   puInit () ;
+   glutKeyboardFunc ( my_keyboard_func ) ;
+   glutSpecialFunc  ( my_special_func  ) ;
+   .
+   .
+   glutMainLoop() ;
+
+
+Note: PU_KEY_GLUT_SPECIAL_OFFSET is required since the definitions of the +GLUT_KEY_xxx symbols overlap the ASCII character range but the PU_KEY_xxx +symbols don't. Hence PU_KEY_GLUT_SPECIAL_OFFSET is currently defined to +be 256. +

These are the code symbols for the special keys: +

    PU_KEY_F1       PU_KEY_F2         PU_KEY_F3     PU_KEY_F4
+    PU_KEY_F5       PU_KEY_F6         PU_KEY_F7     PU_KEY_F8
+    PU_KEY_F9       PU_KEY_F10        PU_KEY_F11    PU_KEY_F12
+
+    PU_KEY_LEFT     PU_KEY_UP         PU_KEY_RIGHT  PU_KEY_DOWN
+    PU_KEY_PAGE_UP  PU_KEY_PAGE_DOWN  PU_KEY_HOME   PU_KEY_END
+    PU_KEY_INSERT
+
+
+ +

+puMouse

+This routine take mouse events (presumably generated by GLUT) and uses +them to decide which (if any) widgets changed state as a result. puMouse +doesn't do any actual graphics - so you have to call puDisplay +if you want the display to be updated. +
   int puMouse ( int buttons, int state, int x, int y ) ;
+   int puMouse ( int x, int y ) ;
+
+   'buttons' is one of
+       PU_LEFT_BUTTON    (which is the same as GLUT_LEFT_BUTTON)
+       PU_MIDDLE_BUTTON  (which is the same as GLUT_MIDDLE_BUTTON)
+       PU_RIGHT_BUTTON   (which is the same as GLUT_RIGHT_BUTTON)
+   'state' is one of
+       PU_DOWN           (which is the same as GLUT_DOWN)
+       PU_UP             (which is the same as GLUT_UP)
+
+
+Notice that this function will accept exactly the arguments that GLUT passes +to the glutMouseFunc, glutMotionFunc and glutPassiveMotionFunc callbacks. +This means that the 'y' coordinate is reversed compared to those used in +OpenGL. The coordinate is flipped back inside the function before comparing +it to the active areas of all the widgets. +

Since GLUT doesn't tell you which mouse buttons are held while the mouse +is being 'dragged', the second form of the 'puMouse' function (which is +usually called from the glutMotionFunc and glutPassiveMotionFunc callbacks), +assumes that the mouse buttons are the same as for the last call to the +first form of the puMouse() call (which does have button information). +

The return result is TRUE if one of the widgets was actually hit by +the mouse event. This can be used to determine if the mouse event was 'consumed' +by the user interface - or whether it should be used in some other application-specific +way. It is also true to say that in a single-buffered application, the +puDisplay +function doesn't need to be called until puMouse() returns TRUE (unless +of course the application chooses to change a colour or a label or something). +

+The PUI 'soft' Cursor

+PUI can take motion events (presumably generated by GLUT - and passed on +to puMouse) and use them to draw a mouse cursor. +Typically, the underlying window manager will draw a perfectly good cursor +for you - but there are a few (rare) cases where such facilities are not +available and a cursor drawn using OpenGL is needed. +

It is also possible to show and hide the PUI cursor (note that it is +hidden by default): +

   void puShowCursor     ( void ) ;
+   void puHideCursor     ( void ) ;
+   int  puCursorIsHidden ( void ) ;
+
+
+The cursor is always drawn in black, with a white border - it is about +18 pixels wide and 18 pixels high and is drawn as an arrow pointing north-west. +At present, there is no way to change the cursor shape or colour. +

If you enable this function on a machine that does have a hardware (or +at least operating-system generated) cursor, then you will probably notice +that the PUI cursor lags behind the 'real' cursor. This is because the +PUI cursor can only be drawn at the end of the frame, after all the other +OpenGL drawing functions are complete. Also, if you are using a double-buffered +rendering context, the cursor won't appear in it's new position until the +buffers are swapped at the end of the frame. +

Still, for all its faults, if there is no other cursor provided for +you - PUI's cursor is a lot better than nothing. +

+Example of puMouse and puShowCursor usage:

+Example: +
   void my_mouse_func ( int button, int updown, int x, int y )
+   {
+     puMouse ( button, updown, x, y ) ;
+     glutPostRedisplay () ;
+   }
+ 
+   void my_motion_func ( int x, int y )
+   {
+     puMouse ( x, y ) ;
+     glutPostRedisplay () ;
+   }
+  .
+  . 
+  puInit () ;
+
+  glutMouseFunc         ( my_mouse_func  ) ;
+  glutMotionFunc        ( my_motion_func ) ;
+  glutPassiveMotionFunc ( my_motion_func ) ;
+
+  if ( my_hardware_doesnt_have_a_cursor )
+    puShowCursor () ;
+  .
+  .
+  glutMainLoop () ;
+
+
+ +

+Active Widget Functions

+PUI has three functions which you can use to manipulate the active widget. +Here they are: +
  void puDeactivateWidget ( void ) ;  /* Sets the Active Widget to NULL */
+  void puSetActiveWidget ( puObject *w ) ;  /* Sets the Active Widget */
+  puObject *puActiveWidget () ;  /* Returns the active widget */
+
+
+ +

+Cut and Paste

+You can cut and paste text between different puInput +widgets and puLargeInput widgets. The cut, copy, +and paste commands are the standard CTRL-X, CTRL-C, and CTRL-V. +

Interfacing with Windowing Libraries.

+PUI works with many windowing libraries (Currently: GLUT, freeglut, SDL, +FLTK and PLIB's own 'PW' library). Prior to version 1.8.1, PLIB required +GLUT or freeglut - and that library is +used in most of our example programs and demo's. +

+You have a choice about how to tell PUI which windowing library you are using: +

+EITHER: +

    +
  1. #define PU_USE_GLUT or PU_USE_PW or PU_USE_FLTK or PU_USE_SDL ...to + explicitly tell PUI which windowing library you want it to use. + Make sure you define this symbol BEFORE you '#include <plib/pu.h>'. + I would generally do this with a '-D' directive on your compile line. +
    +   eg:
    +      #define PU_USE_FLTK  1
    +      #include <plib/pu.h>
    +
    +

    +...OR... +

  2. Include the header file for whatever windowing library you are + using (PW, SDL, FLTK, GLUT, etc) BEFORE you '#include <plib/pu.h>'. + pu.h will then automatically detect which windowing library you are + using and set PU_USE_ appropriately. +
    +   eg:
    +      #include <FL/Fl_Gl_Window.H>
    +      #include <plib/pu.h>
    +
    +

    +...OR... +

  3. #define PU_USE_NONE before you include pu.h - and PUI will assume + nothing about your windowing library. This is an OK thing to do for + every source file EXCEPT the one that calls puInit() which must use one + of the other mechanisms. Use this technique if you are doing something + like writing a library of your own that uses PLIB but which also + needs to be independent of any windowing library. +

    +...OR... +

  4. #define PU_USE_NATIVE before you include pu.h - and PUI will try to + figure out what it needs to know using native commands such as those + in the wgl/glX/agl library - in the absence of a windowing library. + This may result in some problems if you want to do certain + operations for which PUI needs to query the windowing library. + Use of PU_USE_NATIVE is not recommended in systems that allow you + to resize the window or open multiple windows. +

    +...OR... +

  5. Do none of the above - in which case PUI will assume you are using + GLUT/freeglut. It has to work like this for reverse-compatibility + with applications written for PLIB 1.7.x and earlier. +
+Check out the example programs for PU - notably fltk_demo and sdl_example +in the examples/src/pui area and pw_pui in the examples/src/pw area. +

+If you have your own windowing library (ie not PW, GLUT, SDL or FLTK), +then you can make it work properly with PUI by defining four inline +functions and passing them into 'puSetWindowFuncs'. Look at (for +example) puGLUT.h to see how this is done. +


+ + + + + + +
Valid HTML 4.0! +
+Steve J. Baker. <sjbaker1@airmail.net>
+
+ + + diff --git a/doc/pui/labelplaces.png b/doc/pui/labelplaces.png new file mode 100644 index 0000000000000000000000000000000000000000..02f1d6d6bb378ff97829095726d3ec59b197a2c1 GIT binary patch literal 13349 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 ZoY->!tCIKXAOH7(kdaiBs1h>@{6AXyL8Jfx literal 0 HcmV?d00001 diff --git a/doc/pui/legendplaces.png b/doc/pui/legendplaces.png new file mode 100644 index 0000000000000000000000000000000000000000..bf691b92a982273bee8789b599d52863ad75a827 GIT binary patch literal 10769 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 V_yXrYY`*{i002ovPDHLkV1m!)omv0@ literal 0 HcmV?d00001 diff --git a/doc/pui/puArrowButton.png b/doc/pui/puArrowButton.png new file mode 100644 index 0000000000000000000000000000000000000000..2e22cef3401e1efac458d191bacb5719dc0169ee GIT binary patch literal 2280 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 literal 0 HcmV?d00001 diff --git a/doc/pui/puFrame.png b/doc/pui/puFrame.png new file mode 100644 index 0000000000000000000000000000000000000000..20c7c0deaccf5d53514c032e374c43fffa896581 GIT binary patch literal 3164 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 literal 0 HcmV?d00001 diff --git a/doc/pui/puLargeInput.png b/doc/pui/puLargeInput.png new file mode 100644 index 0000000000000000000000000000000000000000..0b87362980931bee89b957a8d2cc91c07432f83b GIT binary patch literal 7149 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 literal 0 HcmV?d00001 diff --git a/doc/pui/puListBox.png b/doc/pui/puListBox.png new file mode 100644 index 0000000000000000000000000000000000000000..0f096ab5f1047b3476dee29b89aa1b31a4916eea GIT binary patch literal 4873 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 KMNUMnLSTXsffmpJ literal 0 HcmV?d00001 diff --git a/doc/pui/puSelectBox.png b/doc/pui/puSelectBox.png new file mode 100644 index 0000000000000000000000000000000000000000..77d58deeecf10d21011e382b14b851f14672847a GIT binary patch literal 2790 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 literal 0 HcmV?d00001 diff --git a/doc/pui/puSlider.png b/doc/pui/puSlider.png new file mode 100644 index 0000000000000000000000000000000000000000..04da0b1ff2effe122d1f7b27b941d574d92dc61b GIT binary patch literal 2470 zcmV;X30d}uP)+Ie|-Q&UsY($Z#TX0NZWU|?XszrPF&3>+LB z6ciMys;WCXJDQrBa&mHkfq`jhX|l4iV`F2&!oo&IMkXdErKP3gS0e0*PDU%tM+3kwTOOiakg$Qv6Q*x1;3cz6>N z6VTAmD=RChsi|CCTsk^BySuxYnVB;)GjVZoBO@c>;o(C=L!zRhXlQ7$v9SmU2uMgs z#>U2{r>EA|*0{L1c6N49P*Bd!&M+`AZ*OlvKtP|LpMHLRdU|?PR8&GrB)XQM?Ei35O)9$AXe_@+(Rw_4Z~RQG|Mq(qp@Ws0nB=u7kw* z_|}%$Y+m~`WzAY~(Wzu|Qee}X|%h}>;PE*-bNk&)qK8=UN7Cx_r=5f}nx<}1BV{|@7Wtg{bEPWJC@ z8pw5AF(+-VkRMhc?V;-C0UOG# z*ohkUgGLfG0-%v9O3*dQG=iX^lK_!o4;2mND4QVqHI4{~fW-V+jRQ2O$bL?OY7miJ zap4)tQMR1qXH(_nriy6X+9n$E)64S$q9dg5$_g4q>tR$(+#t1pl1g!u@mNb5CpT_aps`LzRHdhcyP$8mQbNz5Ze+ zdOX>QI=64r%}bM`Y}u&4#Paf~(s3{eMTE8=w$nwURIdN_{5Ya(Di4JV9ic*qOO0BZ}6@sVUbA1P zk^WhQ%E=Wg{1U;rNQjy*;VvT5HuouBeVErNNm^v3i3v(~QQ;Y5zrs2>fSX8Hs_^0P zU~}y7z$!jGh<_MZMb49hQ|+I9Ql^xZvJ*Gw zE|n=wOgJW@s@z=LNpuN)i%ufcqmu);Xxga4D=Be0LBo?e3vqjVAs%44nxY>`Le55P zk=e)wOK*XMi)F&)x|^UGWI1Cp!6%|5*W4lZ8vP)SW=v25%k{#Iub4gYRHTqFxdqw6 zfD|ME8crrXMIgI;UARF6!iV9_In#5@v&j8KmBM#T7y z2w6AWa8mV6cq3bC6rF+;;gGDJO6hIGFB{)+|o+S&Pm&CfHjt1n)6r-adqm*4*Vl)xO> zY+mfKS1!70NHnvO^fFsNoaUSLC4t*<^B!5+W)9t(Jnrligsz3;sYxu{YrE=vAmFY^ zVQ%Pa5jW+;Dfvhh9i6w*zcj{i74&b{n-JeVQs=;Z#k&9V4hYXtc$kG>r zu;Lelu%ET<`^ZDC6TSvT*Y&UX;|81aWl001;Ln>~)y`Agd6u{7ySjFhLc{z`^9gZy5jY~3?kRI~fHwJSI_v2Y5h{7Am=<&oj`InkcY?;dG) z&Yn8?wbpFT`o9}Dhiwq6n)m0O=e^Y@i~FxU(-T(lm;luTVN7P{K3h~{6fY^np?>;e zU;9P3rn8qD)vj%z+Kf1_VLdu12MzJlF- Z<+4J#s~0tX4gjVZ22WQ%mvv4FO#r~=-{$}T literal 0 HcmV?d00001 diff --git a/doc/pui/puTriSlider.png b/doc/pui/puTriSlider.png new file mode 100644 index 0000000000000000000000000000000000000000..27471020e2c218150f42e3b7ddbb86d469ff4238 GIT binary patch literal 3275 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 literal 0 HcmV?d00001 diff --git a/doc/pui/puVerticalMenu.png b/doc/pui/puVerticalMenu.png new file mode 100644 index 0000000000000000000000000000000000000000..3d6eb293f792163d47ca7115aefacbf9ec5f7280 GIT binary patch literal 1370 zcmV-g1*Q6lP)ARr)MU|^h_od5s-5D*YhP*5;1FmZ8lj*gD1s;USG2uMgsC@3gtX=xZ3 z7+6?XI5;?Yd3lG2hohsTm6esTv9SaM1Vu$fWMpJiR8)0!bqov)OiWBIEiD`z99&#n zJv}{zg@q+0C843AHa0erl9H~ju5E2?eSLk5jEtwJrNm(GBPrBbaauCk*%$*3JMBKOG_#$Dr{_Q8X6ipIy!uOe5IwOv$L}Y2M1!{KcXxLW4-ZaGP9Gm1US3`j5)xWkT8W8?mzS4EM@KI&FF!v&l$4aPu&{4$Z-0M( zsi~=(o13?{w}F9y%gf7QVPTz}ol#LytE;P7Sy_mPh@_;XmX?;Xva$sQ1x7|jc6N3R z4Gm3AO)f4j9UUD$K0bzqh9)K^qN1X&udi-yZhn4#jg5_(nwk|A6$1kUgM))ZLqngR zpEENv3kwS?D=Qlt8#_BYrlzK}w6teuXIEEOczAeEPfuT8Ux2j26951MfJsC_RCwCm zmkU=CQ4q&h29qEb3^B+`sj#w#RF*|V8J4CI_JAJDR$2lHDOp4&6{+#Lo4dR0vMi6q ztUS#*-nqkne|PS^GiO2WtYlo(<8%R$dD|7y_b_AeJH$AWPavk47mfUe>`CWQT}N5Z zjnaCE+V#RSW%2W5z@zAFoviG?nQU2gJ!Lt?|5w|AHMKDkb3Q9=#TuLdbmhGup=fp85eUljIm9J8j22TS8JTa zP&rjEM%QT=&L=}?GbK98*;H;T(r)93fk22hjnR5BVP}0yyPff1s*`0q6l=Q9?JigQ zR4=A0=K%TtBwJ(n#ZA8M?ZeHa4u&y8JLYK-$lr2ZnVVNZLTRZZF&@4L%q=1HP#pIbXBF zHsRp+wWs{)ZWnzr2r!}J=#Nh59fYTTxEHL`%6@>OUJH!)VC+Oa_l6A?@cLjtr@<7Q zhr2(#P8bXB$Qe4*Wc4?+ts}wZ%vNJT#}H>MZw=A=H+L^K6@f8B93Gu?YOdEoVtgdOC{p+CdU^NQs%2IIY;uH#1)fLzR9JpZxGi~ zZSt)$_vJEia}u{GbFW))7o$b%;mslQ8qEdg(52E!3?o+aEyQD%!I8R2}XEw`BF*gx~W;zrFwVcrPWXGXtur<-+vMpJ%p3jSN3#zpY!JAT=HD!b@=|t cwxjj?1+wAn1xbF&p#T5?07*qoM6N<$f;7%~RsaA1 literal 0 HcmV?d00001 diff --git a/doc/pui/simple.png b/doc/pui/simple.png new file mode 100644 index 0000000000000000000000000000000000000000..49da69ab5f8f9c0b0609ffbde68bd509ef822068 GIT binary patch literal 3109 zcmV+=4BGRFP)>sxK`#BdV8lT#ltZnEFlF%8s>z< zK!o!F>0{CdCK2FRNQ8jzLPDJfz`WleubNXR%7IKN@K*O4|E(sS=UvJ}8}e)^X8 z?s{B-h3IUHX&QLj6tkw?Hr4!`X!%{g%6&_*i-_I^;2^q5uF3Znf;axWE5+={fDQ&a zgCXpQwp)=MF+&*`&n2yjaM~83DrD3LDfG}aan^-PHTdeWz2WVgiY2eM; zI~}kkNL0R2V1RY&3bGJd-~_gELRSz=2FV!}gi(wA$wExabiNR}pb)&E5M`LpBe|jw z!deQ!yO8{DNhLYi*~jor!P!DqkxbD*Zps%@sLm9=cNhu3`8p?Yd-A)l+m$cmwYQKf zTDJEkE!#ECBep_fyVE$y7eW^ma;o!ay3`>08+-dFW;7T&&#segJLV)i>wM0}OumHP zGKBN42-~Y(D8qg=6rz#%5rSPlAY30Ipc@UaLVO2(h?4KwJ6Hqq{(ivjCyT_lvF`D!=A7hAR6G}4Tz(y)&>MVyL44Pm;0`5v_KvwoV>Z57&X^Oy6!>U={nomTRHjnP%@mkFH5hLr(^oDMI=9$q}OMW zOox!@amjHclf3c7=Ad?SauCU+mIl>-(=>_G{?bo$>4~RvTu$_8w1DJwRa&+?n~;32 zr&I@$dU{H=^d7~?t09Sy%;TNskaS&pI&~wdOpA%?K~kCBd2;U2+8yGTqIMT2y|=fppKM zQ383aVO94w>N|^%Iv78fqJ>-o1U-;jWkeL&jbVMM+XTd};`sF?xvB!$&l`cbFLEHb z1jIw&D@kIp3xbcmTLn7~1RapDv@!ZUG4BM3hrw66itAjaNQ<)sleA7l6E1fskrhHIRrAh}I!J835`1p|J3`fsp{DtTpgHxbU|wBjk2%?E(VtJAlwW zAeXfxObmPzZ-Kn;wkS&%?4!{@=HusVO(z-Yo*HGj{s@|m(UU-&u+A2^1|Ys;)POV~ zD?pwXR_hg20gwUfXh+nJo=SkIW!M^8Rj1J}Yn+qQ&y+kG;RLlAh~L|AP-~{Ew1!WCKdQbC zNJ&_4^}}-3cZ1P;-4A4w^?lU>KvGkfq=R6w>m zd)(tp5`a|U2o9;QcH{ugqU4GQA?#W|r-J)6bKiAd$1+P6h>O^p!@+~@G9a`a$gm28 zaxUW1PdN&jLIO$2A3Xw+B7vMEJ<&2B-mjf#k7xWoAVe#HaN;;JvG~$?* zgKj%Rk`qx#2}(|gaVjvY^9xTUuwr>2{%nFg_CkKBBr~%9U`l~6PfUlwums9)e(|Ne zJ=d<+{wm?OJrN@-VD~YOdo3) z(~bm2{H7?FH#kr6-Sj%`gYUgs`G03ta_B_OW1q)vR}FeBn81a``Z znVs09H5{sMVg(^?Lu06OR98m=#EO&Xu;6m==_LX)5HH8Bl%t5TV?~)*q2U@eQV_703*_$^*kd#6{XiXMtbQR@a>Gyr(|TB zGC#<)8e7E&I*}jlW8~^~d)xIG=8(;832l#4<&twzndosnpN%|o_ks>;^*eJ-chYmPBeHa?$a zgzdR8ZA$BpaKHEqMtZO6(xKG}>#JzIYtAt8di{My>>fAm&)T>7k>F!Q&JV3I684o+ zlM(4-bV1Ad|C5ohm$S{YGDe#Jh~K4+jS)GA7Voe5>h&7F2dl{l-^|X{0OigF>muDnYaN>YaAnfy{0h|%SdZSAjGRA8)u?15^PP+;oirZH~$^*C;Bir4PS zkcr~h#f$Y^;-L7>W9IRMSDy*1KlGx0X$)X?&&+em#B3hY$k`E|q+f~6jnO@p^~UcZ z&c>{k&#~Y1qKr)R*|taM1B + + + + + + A PLIB Windowing Library. + + + + + + + + + + +
+

A PLIB Windowing Library.

+
+ by Steve Baker +
+

Introduction

+PW is now just one component of PLIB. +

+The PLIB minimal Windowing Library (PW) is a portable interface that makes +it as simple as possible to make an OpenGL application that can open a +single window and read the mouse and keyboard. It is a wrapper to make the +various underlying OS mechanisms look the same to application code. +

+You should include the PW header file '/usr/include/plib/pw.h' +and link to the PW library '/usr/lib/libplibpw.a'. +

+Before using any other PW functions, you should initialise the +library by calling either: +

+
+void pwInit ( int multisample, int num_samples ) ;
+
+
+...or: +
+
+void pwInit ( int x, int y, int w, int h,
+              int multisample,
+              char *title, int border, int num_samples ) ;
+
+
+The first version creates a full-screen window with no borders, +titles, etc (or at least as close to that as your OS can manage). +The second version creates a window with it's origin at (x,y) of +dimensions w x h, with the specified title string, and optionally +with or without a border. +

+The window that's created will have the maximum number of colours +that your system supports - double-buffered plus a Z buffer. +

+Both forms allow you to specify whether you'd like multisampling +to be enabled or not - and if so, with how many samples. If your +system cannot produce the number of samples requested, pwInit will +fall back to the best it can do. +

+Then, initialise other PLIB components such as PUI. When initialisation +is complete, call this function: +

+
+void pwCallbacks ( pwKeybdFunc    *kb = NULL, pwMouseFunc *ms = NULL,
+                   pwMousePosFunc *mp = NULL, pwResizeCB *rcb = NULL,
+                   pwExitCB      *ecb = NULL ) ;
+
+
+These parameters are callback functions for (repectively) +incoming keyboard keystrokes, incoming mouse button press or +release events, mouse movements, window resize events and finally, +a window close callback. (That's invoked by PW when the user attempts +to close the window by pressing the exit button on the window border.) +PW also calls the exit function when it detects a fatal error that +occurs after the window has been successfully opened. +

+Your application has to be prepared for these callbacks to be invoked +at any time after pwCallbacks. PW allows you to issue OpenGL calls +any time after pwInit(). +

+The specifications of these various callbacks are: +

+
+typedef void pwResizeCB ( int w, int h ) ;
+typedef void pwExitCB   () ;
+typedef void pwKeybdFunc    ( int key, int updown, int x, int y ) ;
+typedef void pwMouseFunc    ( int button, int updown, int x, int y ) ;
+typedef void pwMousePosFunc ( int x, int y ) ;
+
+
+The 'updown' parameter is set to PW_DOWN when a key or mouse button +is pressed or to PW_UP when it's released. The 'key' parameter is +either an ASCII character or one of: +
+
+ PW_KEY_F1       PW_KEY_F2       PW_KEY_F3       PW_KEY_F4       
+ PW_KEY_F5       PW_KEY_F6       PW_KEY_F7       PW_KEY_F8       
+ PW_KEY_F9       PW_KEY_F10      PW_KEY_F11      PW_KEY_F12      
+
+ PW_KEY_LEFT     PW_KEY_UP       PW_KEY_RIGHT    PW_KEY_DOWN     
+
+ PW_KEY_PAGE_UP  PW_KEY_PAGE_DOWN
+
+ PW_KEY_HOME     PW_KEY_END      PW_KEY_INSERT   
+
+
+The 'button' parameter is one of: +
+
+ PW_LEFT_BUTTON  PW_MIDDLE_BUTTON  PW_RIGHT_BUTTON
+
+
+These constants are chosen to be identical to the similarly named +constants used in the PLIB PUI library so that it's easy to connect +a PUI GUI to a PW window. +

+Once the window is initialised, you may call other PLIB graphics +initialisations (puInit, ssgInit, etc). +

+So now you have your window open, you only need to do one thing - +call this function once per frame: +

+
+void pwSwapBuffers () ;
+
+
+This function swaps the double-buffers, collects keystrokes, mouse events +and resize events and calls whatever application-defined callbacks are +needed. It is NOT LEGAL to call pwSwapBuffers() from inside a PW callback +function. +

+When you wish to close the PW window, you should call: +

+
+void pwCleanup () ;
+
+
+Some applications need to know when the shift/control/alt keys are being +held down by the user: +
+
+int pwGetModifiers () ;
+
+
+This returns the state of those three keys as the OR of the three constants: +
+
+ PW_SHIFT  PW_CTRL  PW_ALT   
+
+
+Note that the results are only valid inside of a callback called from +pwSwapBuffers(). +

+By default, PW disallows auto-repeat of keyboard keys. This is generally what +you want for games. However, it's possible that you may want auto-repeat to +be enabled: +

+
+   void pwSetAutoRepeatKey ( bool enable ) ;
+
+
+If the user has told the windowing system that auto repeat should be off as +some kind of a global preference then pwSetAutoRepeatKey(true) won't turn +it back on again. +

+Finally, there are a number of functions for setting and getting the window +position and dimensions - also to set the current cursor shape: +

+
+void pwGetSize       ( int *w, int *h ) ;
+void pwSetSizeOrigin ( int x, int y, int w, int h ) ;
+void pwSetSize       ( int x, int y ) ;
+void pwSetOrigin     ( int w, int h ) ;
+void pwSetCursor     ( int c ) ;
+
+
+The available cursor shapes for pwSetCursor are: +
+
+   PW_CURSOR_NONE    0
+   PW_CURSOR_RIGHT   1
+   PW_CURSOR_LEFT    2
+   PW_CURSOR_QUERY   3
+   PW_CURSOR_AIM     4
+   PW_CURSOR_CIRCLE  5
+   PW_CURSOR_WAIT    6
+   PW_CURSOR_CROSS   7
+   PW_CURSOR_CUSTOM  8
+
+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + + + diff --git a/doc/requirements.html b/doc/requirements.html new file mode 100644 index 0000000..eb88a0f --- /dev/null +++ b/doc/requirements.html @@ -0,0 +1,147 @@ + + + + + PLIB: Requirements & Portability. + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+

Hardware Requirements for Running PLIB

+To do good 3D graphics, you'll need an OpenGL-compatible 3D graphics +accellerator that your operating system supports. Most systems can +run OpenGL in software only - but performance is so poor as to be +unusable in the kinds of application for which PLIB is intended. +

+If you want to hear sound effects and music, you'll obviously +need a sound card and some speakers or headphones. + +

Software Requirements for Running PLIB

+The PLIB team works hard to minimise our dependance on other +libraries to make it easy for end-users to install application +software without having to visit dozens of web sites to collect +the libraries they need. +
    +
  • 3D/2D Graphics: OpenGL. If you aren't already set up to + run OpenGL, you should check: +
      +
    • The OpenGL ARB site. +
    • Or download Mesa + (which is an excellent OpenGL clone by Brian Paul). +
    • If you are a Linux user, you may wish to check the + Linux3D Site. +
    • If you are a Windoze or Macintosh user, you should have + had a copy of OpenGL delivered with your operating + system. +
    +
  • Windowing: From version 1.8.0, PLIB itself is completely independent of + any other windowing library. However, some of the demo and example + programs use freeglut (or at a + pinch: + the original + GLUT (GL Utility Toolkit by Mark Kilgard). + freeglut + works very well with PLIB applications and is maintained by + many of the same people - so there is good continuity. + It's perfectly possible to write PLIB programs that don't use + either GLUT or freeglut - for example, you can use PLIB with + SDL). +
+ +

How Portable is PLIB?

+VERY! +
    +
  • All flavors of Linux are supported - on any CPU platform that + supports Xfree86 and OpenGL. +
  • All flavors of Win32 are + supported, using either MSVC++ or CygWin for compilation. + Theoretically you could use Borland C++ also, but since there is little + user base these days, the necessary 'project' files to build PLIB + under Borland's IDE tend to get outdated. +
  • Most other UNIX-like systems (at least BSD, IRIX, Solaris, OS-X) providing + they support X-windows. +
  • MacOS-X is also reasonably well supported. +
  • MacOS-9 runs everything except the joystick library. However, it's getting + hard to find people to test this port - so it may have problems by the time + you come to use it. +
  • Support for BeOS is problematic - for some reason, people who are prepared + to help out with porting PLIB to BeOS are hard to find. However, since + BeOS (I'm told) has compatibility with X, OpenGL and OSS, it should + be easy to port to and may just work 'out of the box'. +
+ +
+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/requirements.png b/doc/requirements.png new file mode 100644 index 0000000000000000000000000000000000000000..7958ee3e49fd1137ce1b528e54252e52833695f0 GIT binary patch literal 5343 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 + + + + + + A Simple Geometry library for OpenGL. + + + + + + + + + + +
+

A Simple Geometry library for OpenGL

+
+ by Steve Baker +
+

Introduction

+Simple Geometry (SG) is simple set of matrix, quaternion and vector +math functions that were originally written to support the SSG +(Simple Scene Graph) API - but which are rather useful +for all sorts of OpenGL applications. +

+SG is now a part of PLIB. +

+Since SG is designed to work with OpenGL, it uses similar +naming conventions - all SG functions and other tokens +that work with 'float' precision math begin with 'sg' or 'SG'. +There are a complete duplicate set that work with double-precision +math that begin with 'sgd' or 'SGD'. +

+Most SG functions are implemented as 'inline' C++ +functions - for speed. The underlying data types are +mostly just arrays of 'float' - which is generally the most +efficient type for OpenGL programs. +

+SGD functions are absolutely identical to their SG +eqivelents - except for working at double precision. +This document doesn't describe the SGD functions, types +and constants. That would just be a waste of space - they +are just identical to the SG versions. +

Usage.

+You need to #include "sg.h" and link to the SG binary. +

Basic SG Types.

+Most SG functions operate on SGfloat data elements - which +are normally '#define'd as 'float'. Common data types are: +
+
+  SGfloat    -- A floating point number.
+  sgVec2     -- A two   element array of SGfloat
+  sgVec3     -- A three element array of SGfloat
+  sgVec4     -- A four  element array of SGfloat
+
+  sgMat4     -- A 4x4 element array of SGfloat
+
+  sgCoord    -- A simple structure used to hold
+                euler rotation angles and a 3D
+                translation:
+
+       struct sgCoord
+       {
+         sgVec3 xyz ;
+         sgVec3 hpr ;
+       } ;
+
+  sgLine3    -- An infinite line defined by a point
+                somewhere on the line and a normalized
+                direction vector.
+
+       struct sgLine3
+       {
+	 sgVec3 point_on_line ;
+	 sgVec3 direction_vector ;  /* Should be a unit vector */
+       } ;
+
+  sgLineSegment3 -- A finite line segment defined by it's
+                end-points.
+
+       struct sgLineSegment3
+       {
+	 sgVec3 a ;
+	 sgVec3 b ;
+       } ;
+
+  sgQuat     -- An sgVec4 holding the values (x, y, z, w),
+                representing the quaternion w + xi + yj + zk.
+
+  class sgSphere   -- A sphere
+  class sgBox      -- An axially aligned box.
+  class sgFrustum  -- An OpenGL-style view frustum
+
+  ...and the same again but in 'double' precision:
+
+    SGDfloat, sgdVec2, sgdVec3, sgdVec4,
+    sgdMat4, sgdCoord, sgdQuat, sgdSphere,
+    sgdBox, sgdFrustum
+
+
+Note 1: All angles used by or produced by SG are in degrees. +

+Note 2: Some functions refer to 'angle+axis' notation for rotations, +this is the same format that glRotate uses. +

sgVec*

+The sgVec* types are just simple arrays - designed especially +to hold values for OpenGL vertices, normals, texture coordinates +and colours with the minimum of fuss. These arrays come in 2, 3 and +four element versions - and almost all SG functions that can +operate on an sgVec are provided in three appropriately named +versions, one for each length varient. By convention, the +elements are in the order: +
    +
  • (X,Y), (X,Y,Z), (X,Y,Z,W) for vertices +
  • (S,T), (S,T,R), (S,T,R,Q) for texture coordinates +
  • (R,G,B), (R,G,B,A) for colours (two element arrays don't + make much sense as colours). +
  • (H,P,R) for heading/pitch/roll euler angle triplets. +
  • (nX,nY,nZ) for normals. +
  • (A,B,C,D) for plane equations. +
+

Lines

+A couple of SG functions take or produce data for a line in space. +There are generally two ways to specify a 3D line, either as +two points that lie on the line - or as one point and a direction +vector. +

+SG calls the first form a "line segment" (sgLineSegment3) and the +second form an "infinite line" (sgLine3). +

+You can find the infinite line that is the extension of a line segment +using: +

+
+  void sgLineSegment3ToLine3 ( sgLine3 *line,
+                         const sgLineSegment3 lineseg ) ;
+
+
+You can also find the distance from a point to either a line +or a line segment using: +
+
+  SGfloat sgDistToLineVec3               ( const sgLine3 line,
+					   const sgVec3  pnt )
+  SGfloat sgDistToLineSegmentVec3        ( const sgLineSegment3 line,
+					   const sgVec3  pnt )
+  SGfloat sgDistSquaredToLineVec3        ( const sgLine3 line,
+					   const sgVec3  pnt ) ;
+  SGfloat sgDistSquaredToLineSegmentVec3 ( const sgLineSegment3 line,
+					   const sgVec3  pnt ) ;
+
+
+Try to use the 'Squared' versions if you can because you +save a costly sqrt() function. +

sgMat4

+sgMat4 arrays are SM_float[4][4] arrays for convenience - but notice +that the order of the elements is such that you can take the +address of an sgMat4, cast that into a 'float *' ('double *' for +SGD) and pass the result to any OpenGL matrix function. eg: +
+
+   sgMat4 m ;
+   sgMakeIdentMat4 ( m ) ;
+   glLoadMatrixf ( (GMfloat *) m ) ;
+
+...or...
+
+   sgdMat4 m ;
+   sgdMakeIdentMat4 ( m ) ;
+   glLoadMatrixd ( (GMdouble *) m ) ;
+
+
+Another way to look at this is to treat an sgMat4 as an array +of four sgVec4's. This is convenient for quite a few reasons. +For example, you can extract the sgVec3 that represents the +translation part of an sgMat4 by just using mat[3] anywhere +where an sgMat3 would be appropriate. +

sgTriangleSolver functions

+Remember all those high-school trig problems (eg "Given two +sides and included angle, find length of remaining side")... +nope - neither do I. That's why these functions are useful. +

+ + + + + +
+NOTES: +
    +
  • lenA, lenB, lenC = The lengths of sides A, B and C. +
  • angA, angB, angC = The angles opposite sides A, B and C. +
  • SSS = Three sides. +
  • SAS = Two sides and the angle between them. +
  • ASA = Two angles and the side between them. +
  • SAA = One side and two angles that don't include it. +
  • ASS = Two sides and one angle that's not between them. +
+
+
Triangle
+
+A triangle can be specified completely using the 'SSS', 'SAS', +'SSA' and 'ASA' methods - but if you only know two sides and an +angle that's NOT between those two sides (the 'ASS' method) then +there are often two possible triangles that fit that description. +

+Hence (in the ASS case), SG needs to know whether one of the +unknown vertices is greater than 90 degrees (Obtuse) or less +than 90 degrees (Acute). Hence the two routines below that use +ASS notation have to be told whether angA is Obtuse or not. +

+
+void sgTriangleSolver_SSStoAAA ( SGfloat  lenA, SGfloat  lenB, SGfloat  lenC,
+                                 SGfloat *angA, SGfloat *angB, SGfloat *angC ) ;
+void sgTriangleSolver_SAStoASA ( SGfloat  lenA, SGfloat  angB, SGfloat  lenC,
+                                 SGfloat *angA, SGfloat *lenB, SGfloat *angC ) ;
+void sgTriangleSolver_ASAtoSAS ( SGfloat  angA, SGfloat  lenB, SGfloat  angC,
+                                 SGfloat *lenA, SGfloat *angB, SGfloat *lenC ) ;
+void sgTriangleSolver_SAAtoASS ( SGfloat  lenA, SGfloat  angB, SGfloat  angA,
+                                 SGfloat *angC, SGfloat *lenB, SGfloat *lenC ) ;
+void sgTriangleSolver_ASStoSAA ( SGfloat  angB, SGfloat  lenA, SGfloat  lenB,
+                                 bool angA_is_obtuse,
+                                 SGfloat *lenC, SGfloat *angA, SGfloat *angC ) ;
+
+
+These functions take three parameters which are a mixture of lengths and angles +for an arbitary triangle. They return the missing three parameters. +In all cases of 'degenerate' triangles (eg zero angles, zero lengths), the +package comes up with a plausible result - although it may not be a unique +solution. This should be robust and more roundoff-error-tolerant than +producing an error return. +

+Impossible triangles will produce all-zero results. +

+If one or more of the results are not needed, pass a NULL pointer for it +and the package can avoid doing the unnecessary trig and perhaps run a +little faster. +

+
+SGfloat sgTriangleSolver_ASAtoArea ( SGfloat angA, SGfloat lenB, SGfloat angC ) ;
+SGfloat sgTriangleSolver_SAStoArea ( SGfloat lenA, SGfloat angB, SGfloat lenC ) ;
+SGfloat sgTriangleSolver_SSStoArea ( SGfloat lenA, SGfloat lenB, SGfloat lenC ) ;
+SGfloat sgTriangleSolver_SAAtoArea ( SGfloat lenA, SGfloat angB, SGfloat angA ) ;
+SGfloat sgTriangleSolver_ASStoArea ( SGfloat angB, SGfloat lenA, SGfloat lenB,
+                                     bool angA_is_obtuse ) ;
+
+
+These compute the area of a triangle from three parameters. Impossible +triangles (eg lenA > lenB+lenC) return zero areas. +

sgCoord

+This simple structure contains two members - each of type +'sgVec3' - the first is 'xyz', the second 'hpr' - and together, +they represent a rotation followed by a translation. +

+There is a rich set of functions to interconvert sgCoord's and +sgMat4's. +

sgQuat

+The sgQuat is an array of four floats, (x, y, z, w), respresenting +the quaternion q = w + xi + yj + zk. +

+It can be used to encode rotations. +The quaternion (sin(a) * v, cos(a)) rotates 2*a about the vector v. +

+There is a rich set of functions to interconvert sgQuat's and +other rotation structures. +

sgSphere

+An sgSphere contains a sgVec3 for the center of the sphere and +an SGfloat for the radius. +
+
+class sgSphere
+{
+public:
+  sgVec3  center ;
+  SGfloat radius ;
+
+  SGfloat *getCenter (void) ;
+  SGfloat  getRadius (void) ;
+
+  void setCenter ( SGfloat x, SGfloat y, SGfloat z ) ;
+  void setRadius ( SGfloat r ) ;
+
+  int isEmpty (void) ;
+  void empty  (void) ;
+
+  void orthoXform ( sgMat4 m ) ;
+
+  void extend ( sgSphere *s ) ;
+  void extend ( sgBox    *b ) ;
+  void extend ( sgVec3    v ) ;
+
+  int intersects ( sgSphere *s ) ;
+  int intersects ( sgVec4 plane ) ;
+  int intersects ( sgBox *b ) ;
+} ;
+
+
+Since sgSphere's are frequently used as simple bounding shapes +for field-of-view culling, it's quite useful to be able to represent +an 'empty' sphere. This is represented using a sphere of +Negative radius. +

+The 'isEmpty()' function tests to see if a sphere is empty and the +'empty()' function forces the sphere to be empty. +

+The 'orthoXform(m)' function transforms the sphere using the matrix +'m' - which must be orthographic (since transforming a sphere +by a non-ortho matrix yields something that's no longer spherical). +

+Three 'extend' functions allow you to expand the sphere to +encompass other shapes. These functions attempt to minimise +the radius of the resulting sphere: +

+
+  void extend ( sgSphere *s ) ;
+  void extend ( sgBox    *b ) ;
+  void extend ( sgVec3    v ) ;
+
+
+There are also three intersection tests that return TRUE if the +volume contained by the sphere intersects the volume contained +by the other object: +
+
+  int intersects ( sgSphere *s ) ;
+  int intersects ( sgBox *b ) ;
+  int intersects ( sgVec4 plane ) ;
+
+
+('plane' in this context means the semi-infinite volume bounded +by that plane). +

sgBox

+An sgBox contains an sgVec3 for the vertex of the cube +at the minimum (X,Y,Z) and another at the maximum (X,Y,Z). +
+
+class sgBox
+{
+public:
+  sgVec3 min ;
+  sgVec3 max ;
+
+  SGfloat *getMin (void) ;
+  SGfloat *getMax (void) ;
+
+  void setMin ( SGfloat x, SGfloat y, SGfloat z ) ;
+  void setMax ( SGfloat x, SGfloat y, SGfloat z ) ;
+
+  int isEmpty(void) ;
+  void empty (void) ;
+
+  void extend  ( sgSphere *s ) ;
+  void extend  ( sgBox    *b ) ;
+  void extend  ( sgVec3    v ) ;
+
+  int intersects ( sgSphere *s ) ;
+  int intersects ( sgBox    *b ) ;
+  int intersects ( sgVec4   plane ) ;
+} ;
+
+
+
+Since sgBox'es are frequently used as simple bounding shapes +for field-of-view culling, it's quite useful to be able to represent +an 'empty' box. This is represented using a box whose minumum vertex +has larger X, Y or Z than its maximum vertex. +

+The 'isEmpty()' function tests to see if a box is empty and the +'empty()' function forces the box to be empty. +

+Three 'extend' functions allow you to expand the box to +encompass other shapes. These functions attempt to minimise +the size of the resulting box: +

+
+  void extend ( sgSphere *s ) ;
+  void extend ( sgBox    *b ) ;
+  void extend ( sgVec3    v ) ;
+
+
+There are also three intersection tests that return TRUE if the +volume contained by the box intersects the volume contained +by the other object: +
+
+  int intersects ( sgSphere *s ) ;
+  int intersects ( sgBox *b ) ;
+  int intersects ( sgVec4 plane ) ;
+
+
+('plane' in this context means the semi-infinite volume bounded +by that plane). +

sgFrustum

+An sgFrustum corresponds to the set of parameters that is +used to describe an OpenGL view frustum. +
+
+class sgFrustum
+{
+public:
+
+  sgFrustum (void) ;
+
+  void setFrustum ( SGfloat left  , SGfloat right,
+                    SGfloat bottom, SGfloat top,
+                    SGfloat near  , SGfloat far ) ;
+  void setFOV     ( SGfloat h, SGfloat v ) ;
+  void setNearFar ( SGfloat n, SGfloat f ) ;
+ 
+  SGfloat getHFOV (void) ;
+  SGfloat getVFOV (void) ;
+  SGfloat getNear (void) ;
+  SGfloat getFar  (void) ;
+  SGfloat getLeft (void) ;
+  SGfloat getRight(void) ;
+  SGfloat getTop  (void) ;
+  SGfloat getBot  (void) ;
+
+  void getFOV     ( SGfloat *h, SGfloat *v ) ;
+  void getNearFar ( SGfloat *n, SGfloat *f ) ;
+
+  int  contains ( sgVec3    p ) ;
+  int  contains ( sgSphere *s ) ;
+} ;
+
+
+There are two ways to use an sgFrustum: +
    +
  • You can call setNearFar() and setFOV() to set the near/far + clip planes and the field of view (in degrees - horizontally + and vertically). You can use all the get*() functions to + query the frustum. +
  • You can call setFrustum - with the same parameters as you'd + pass to a glFrustum command. You can subsequently update the + near/far planes with setNearFar() and use all the get*() + functions EXCEPT getFOV(). Since glFrustum allows one to + define all kinds of skewed frustums, it is impossible to + return meaningful FOV figures. +
+The constructor for an sgFrustum produces a simple +45 degree by 45 degree frustum with the near clip +at one unit and the far clip at one million units. +When using setFOV, the application programmer should be +aware that aspect ratios are not enforced, thus these +ratios can deviate from the window aspect ratio. +If this happens, a sphere will look like an ellipse. +
+
+  int  contains ( sgVec3    p ) ;
+  int  contains ( sgSphere *s ) ;
+
+
+...returns SG_OUTSIDE if the point or sphere lies entirely outside +the volume contained by the frustum, SG_INSIDE if it lies entirely +inside the frustum - or SG_STRADDLE if they only partially overlap. +

Constants.

+Since finding a portable definition for PI is impossible (thanks Mr Gates), +SG supplies a suitable definition - and some convenience multipliers +to convert degrees to radians and back again. +
+
+#define SG_PI
+#define SG_DEGREES_TO_RADIANS
+#define SG_RADIANS_TO_DEGREES
+
+
+

Vector functions:

+Pretty much all the low level vector functions are available for +sgVec2, sgVec3 and sgVec4. The order of parameters is always +destination first, operands next - in the order that you'd +write them in a C-style assignment statement. Hence (for example): +
+
+  sgAddVec3 ( dst, a, b ) ;   /* dst = a + b ; */
+  sgAddVec3 ( dst, a ) ;      /* dst += a ; */
+
+
+The following functions exist: +
    +
  • Zero - set all elements to zero. +
  • Set - set each element individually. +
  • Add - add vectors element by element. +
  • Sub - subtract vectors element by element. +
  • Scale - multiply each element of a vector by a variable. +
  • AddScaled - dst+=src*scale or dst = a + b * scale. +
  • Negate - negate each element of a vector. +
  • Compare - compare vectors element by element with optional tolerance. + (return TRUE if vectors are equal - within tolerances) +
  • Equal - return TRUE if vectors are exactly equal. +
  • Length - compute length of a vector. +
  • Distance - compute distance between two points. +
  • DistanceSquared - compute the square of the distance between two points. +
  • ScalarProduct - scalar (dot) product. +
  • VectorProduct - vector (cross) product (3-element vectors ONLY!). +
  • Normalise/Normalize - make vector be one unit long. +
  • Lerp - linear interpolate between a and b by fraction 'f'. +
  • LerpAngle - Lerp allowing for the modulo-360 problem. +
+Here are all the function prototypes - as you can see, the names +are 100% consistent and the paramter order is easy to remember. +
+  void sgZeroVec2 ( sgVec2 dst ) ;
+  void sgZeroVec3 ( sgVec3 dst ) ;
+  void sgZeroVec4 ( sgVec4 dst ) ;
+
+  void sgSetVec2  ( sgVec2 dst, SGfloat x, SGfloat y ) ;
+  void sgSetVec3  ( sgVec3 dst, SGfloat x, SGfloat y, SGfloat z ) ;
+  void sgSetVec4  ( sgVec4 dst, SGfloat x, SGfloat y, SGfloat z, SGfloat w ) ;
+
+  void sgCopyVec2 ( sgVec2 dst, sgVec2 src )
+  void sgCopyVec3 ( sgVec3 dst, sgVec3 src )
+  void sgCopyVec4 ( sgVec4 dst, sgVec4 src )
+
+  void sgAddVec2  ( sgVec2 dst, sgVec2 src )
+  void sgAddVec3  ( sgVec3 dst, sgVec3 src )
+  void sgAddVec4  ( sgVec4 dst, sgVec4 src )
+
+  void sgAddVec2  ( sgVec2 dst, sgVec2 src1, sgVec2 src2 )
+  void sgAddVec3  ( sgVec3 dst, sgVec3 src1, sgVec3 src2 )
+  void sgAddVec4  ( sgVec4 dst, sgVec4 src1, sgVec4 src2 )
+
+  void sgSubVec2  ( sgVec2 dst, sgVec2 src )
+  void sgSubVec3  ( sgVec3 dst, sgVec3 src )
+  void sgSubVec4  ( sgVec4 dst, sgVec4 src )
+
+  void sgSubVec2  ( sgVec2 dst, sgVec2 src1, sgVec2 src2 )
+  void sgSubVec3  ( sgVec3 dst, sgVec3 src1, sgVec3 src2 )
+  void sgSubVec4  ( sgVec4 dst, sgVec4 src1, sgVec4 src2 )
+
+  void sgNegateVec2 ( sgVec2 dst )
+  void sgNegateVec3 ( sgVec3 dst )
+  void sgNegateVec4 ( sgVec4 dst )
+
+  void sgNegateVec2 ( sgVec2 dst, sgVec2 src )
+  void sgNegateVec3 ( sgVec3 dst, sgVec3 src )
+  void sgNegateVec4 ( sgVec4 dst, sgVec4 src )
+
+  void sgScaleVec2  ( sgVec2 dst, SGfloat s )
+  void sgScaleVec3  ( sgVec3 dst, SGfloat s )
+  void sgScaleVec4  ( sgVec4 dst, SGfloat s )
+
+  void sgScaleVec2  ( sgVec2 dst, sgVec2 src, SGfloat s )
+  void sgScaleVec3  ( sgVec3 dst, sgVec3 src, SGfloat s )
+  void sgScaleVec4  ( sgVec4 dst, sgVec4 src, SGfloat s )
+
+  void sgAddScaledVec2  ( sgVec2 dst, svVec2 src, SGfloat s )
+  void sgAddScaledVec3  ( sgVec3 dst, svVec3 src, SGfloat s )
+  void sgAddScaledVec4  ( sgVec4 dst, svVec4 src, SGfloat s )
+
+  void sgAddScaledVec2  ( sgVec2 dst, sgVec2 a, svVec2 b, SGfloat s )
+  void sgAddScaledVec3  ( sgVec3 dst, sgVec3 a, svVec3 b, SGfloat s )
+  void sgAddScaledVec4  ( sgVec4 dst, sgVec4 a, svVec4 b, SGfloat s )
+
+  int sgCompareVec2 ( sgVec2 a, sgVec2 b, SGfloat tol )
+  int sgCompareVec3 ( sgVec3 a, sgVec3 b, SGfloat tol )
+  int sgCompareVec4 ( sgVec4 a, sgVec4 b, SGfloat tol )
+
+  int sgEqualVec2   ( sgVec2 a, sgVec2 b )
+  int sgEqualVec3   ( sgVec3 a, sgVec3 b )
+  int sgEqualVec4   ( sgVec4 a, sgVec4 b )
+
+  SGfloat sgScalarProductVec2 ( sgVec2 a, sgVec2 b )
+  SGfloat sgScalarProductVec3 ( sgVec3 a, sgVec3 b )
+  SGfloat sgScalarProductVec4 ( sgVec4 a, sgVec4 b )
+
+  void sgVectorProductVec3 ( sgVec3 dst, sgVec3 a, sgVec3 b )
+
+  SGfloat sgLerp ( SGfloat a, SGfloat b, SGfloat f )
+
+  void sgLerpVec2 ( sgVec2 dst, sgVec2 a, sgVec2 b, SGfloat f )
+  void sgLerpVec3 ( sgVec3 dst, sgVec3 a, sgVec3 b, SGfloat f )
+  void sgLerpVec4 ( sgVec4 dst, sgVec4 a, sgVec4 b, SGfloat f )
+
+  SGfloat sgDistanceSquaredVec2 ( sgVec2 a, sgVec2 b )
+  SGfloat sgDistanceSquaredVec3 ( sgVec3 a, sgVec3 b )
+  SGfloat sgDistanceSquaredVec4 ( sgVec4 a, sgVec4 b )
+
+  SGfloat sgDistanceVec2 ( sgVec2 a, sgVec2 b )
+  SGfloat sgDistanceVec3 ( sgVec3 a, sgVec3 b )
+  SGfloat sgDistanceVec4 ( sgVec4 a, sgVec4 b )
+
+  SGfloat sgLengthVec2 ( sgVec2 src )
+  SGfloat sgLengthVec3 ( sgVec3 src )
+  SGfloat sgLengthVec4 ( sgVec4 src )
+
+  /* Anglo-US spelling issues.  <sigh> */
+
+  #define sgNormalizeVec2 sgNormaliseVec2
+  #define sgNormalizeVec3 sgNormaliseVec3
+  #define sgNormalizeVec4 sgNormaliseVec4
+
+  void sgNormaliseVec2 ( sgVec2 dst )
+  void sgNormaliseVec3 ( sgVec3 dst )
+  void sgNormaliseVec4 ( sgVec4 dst )
+
+  void sgNormaliseVec2 ( sgVec2 dst, sgVec2 src )
+  void sgNormaliseVec3 ( sgVec3 dst, sgVec3 src )
+  void sgNormaliseVec4 ( sgVec4 dst, sgVec4 src )
+
+
+

Matrix Functions.

+The following functions let you construct an sgMat4 from a variety of +input data formats: +
+
+  void sgCopyMat4      ( sgMat4 dst, sgMat4 src ) ;
+
+  void sgMakeIdentMat4 ( sgMat4 dst ) ;
+
+  void sgMakeCoordMat4 ( sgMat4 dst, SGfloat x, SGfloat y, SGfloat z,
+                                     SGfloat h, SGfloat p, SGfloat r ) ;
+  void sgMakeCoordMat4 ( sgMat4 dst, sgVec3 xyz, sgVec3 hpr )
+  void sgMakeCoordMat4 ( sgMat4 dst, sgCoord *src )
+
+  void sgMakeRotMat4   ( sgMat4 dst, sgVec3 hpr )
+  void sgMakeRotMat4   ( sgMat4 dst, SGfloat h, SGfloat p, SGfloat r )
+  void sgMakeRotMat4   ( sgMat4 dst, SGfloat angle, sgVec3 axis ) ;
+
+  void sgMakeTransMat4 ( sgMat4 dst, sgVec3 xyz ) ;
+  void sgMakeTransMat4 ( sgMat4 dst, SGfloat x, SGfloat y, SGfloat z ) ;
+
+  void sgQuatToMatrix  ( sgMat4 dst, sgQuat quat ) ;
+
+
+
+You can multiply matrices: +
+
+  void sgMultMat4      ( sgMat4 dst, sgMat4 a, sgMat4 b ) ;
+  void sgPostMultMat4  ( sgMat4 dst, sgMat4 a ) ;
+  void sgPreMultMat4   ( sgMat4 dst, sgMat4 a ) ;
+
+
+You can do a cheap matrix inversion (for simple rotate/translate +matrices): +
+
+  void sgTransposeNegateMat4 ( sgMat4 dst ) ;
+  void sgTransposeNegateMat4 ( sgMat4 dst, sgMat4 src ) ;
+
+
+...or a (more costly) full matrix inversion (for more complex matrices): +
+
+  void sgInvertMat4 ( sgMat4 dst ) ;
+  void sgInvertMat4 ( sgMat4 dst, sgMat4 src ) ;
+
+
+You can transform vertices and points using a matrix. The 'Vec*' forms +assume that the vector merely need to be rotated (like you would want +for a surface normal for example) - whilst the 'Pnt*' forms perform +a rotate and translate. If your matrix is more complex and contains +scaling, shearing, perspective and such like, then use the 'FullXform' +versions - which are quite a bit slower than the other forms: +
+
+  void sgXformVec3     ( sgVec3 dst, sgMat4 mat ) ;
+  void sgXformVec3     ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ;
+
+  void sgXformPnt3     ( sgVec3 dst, sgMat4 mat ) ;
+  void sgXformPnt3     ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ;
+
+  void sgFullXformPnt3 ( sgVec3 dst, sgMat4 mat ) ;
+  void sgFullXformPnt3 ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ;
+
+  void sgXformVec4     ( sgVec4 dst, sgMat4 mat ) ;
+  void sgXformVec4     ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ;
+
+  void sgXformPnt4     ( sgVec4 dst, sgMat4 mat ) ;
+  void sgXformPnt4     ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ;
+
+  void sgFullXformPnt4 ( sgVec4 dst, sgMat4 mat ) ;
+  void sgFullXformPnt4 ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ;
+
+
+The properties of a matrix can be tested with: +
+  int sgClassifyMat4 ( const sgMat4 mat ) ;
+
+which returns a bitmask with zero or more of the following bits set: +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
SG_ROTATIONUpper 3x3 includes a rotational component.
SG_MIRRORChanges handedness.
SG_SCALEUniform scaling going on.
SG_NONORTHOUpper 3x3 not orthogonal (including non-uniform scaling).
SG_TRANSLATIONTranslates.
SG_PROJECTIONFourth column not 0,0,0,1.
+ +

Coord routines:

+These routines operate on 'sgCoord' structures: +
+
+  void sgZeroCoord ( sgCoord *dst ) ;
+  void sgSetCoord  ( sgCoord *dst, SGfloat x, SGfloat y, SGfloat z,
+                                   SGfloat h, SGfloat p, SGfloat r ) ;
+  void sgSetCoord  ( sgCoord *dst, sgVec3 xyz, sgVec3 hpr ) ;
+  void sgSetCoord  ( sgCoord *coord, sgMat4 src ) ;
+  void sgCopyCoord ( sgCoord *dst, sgCoord *src ) ;
+
+
+The varient of sgSetCoord that takes a matrix is somewhat tricky since +there is no way to convert matrices that are not simple rotate/translates +into sgCoord's - and even when the matrix is a simple rotate/translate, +there are a number of possible sets of Euler rotations that could +be appropriate to reproduce the behavior of the input matrix. sgSetCoord +picks a valid rotation - but just beware of this behaviour. +

Quaternion routines:

+Quaternions are convenient alternatives to Matrices for storing +rotations. +
+  void sgMakeIdentQuat ( sgQuat dst ) ;
+
+Creates the identity quaternion. (0,0,0,1) +
+  void sgSetQuat ( sgQuat dst, SGfloat w,
+                               SGfloat x,
+                               SGfloat y,
+                               SGfloat z ) ;
+
+Sets the four components of the quaternion. +
+  void sgCopyQuat ( sgQuat dst, sgQuat src ) ;
+
+Copies one quaternion into another. +
+  void sgNormalizeQuat ( sgQuat dst ) ;
+  void sgNormalizeQuat ( sgQuat dst, sgQuat src ) ;
+
+For most operations, quaternions need to be normalized, +these functions ensure that they are. +
+  void sgInvertQuat ( sgQuat dst ) ;
+  void sgInvertQuat ( sgQuat dst, sgQuat src ) ;
+
+Computes the inverse of a quaternion. +
+  void sgQuatToAngleAxis ( SGfloat *angle, sgVec3 axis, const sgQuat src ) ;
+  void sgQuatToAngleAxis ( SGfloat *angle,
+                           SGfloat *x, SGfloat *y, SGfloat *z,
+                           const sgQuat src ) ;
+
+Converts a quaternion into an angle+axis format - just the thing +to pass to glRotate for example. +
+  void sgAngleAxisToQuat ( sgQuat dst, SGfloat angle, const sgVec3 axis ) ;
+  void sgAngleAxisToQuat ( sgQuat dst,
+                           SGfloat angle,
+                           SGfloat x, SGfloat y, SGfloat z ) ;
+
+Converts an angle+axis rotation into a quaternion. +
+  void sgMultQuat     ( sgQuat dst, sgQuat a, sgQuat b ) ;
+  void sgPostMultQuat ( sgQuat dst, sgQuat q ) ;
+  void sgPreMultQuat  ( sgQuat dst, sgQuat q ) ;
+
+Concatenates quaternion rotations. +
+  void sgQuatToMatrix ( sgMat4 mat, sgQuat quat ) ;
+  void sgMatrixToQuat ( sgQuat quat, sgMat4 mat ) ;
+
+Converts between quaternions and rotation matrices. +
+  void sgPostRotQuat ( sgQuat dst, SGfloat angle, sgVec3 axis )
+  void sgPostRotQuat ( sgQuat dst, SGfloat angle,
+                       SGfloat x, SGfloat y, SGfloat z )
+  void sgPreRotQuat  ( sgQuat dst, SGfloat angle,
+                       SGfloat x, SGfloat y, SGfloat z )
+
+Rotates a quaternion by an angle/axis rotation. +
+extern void sgSlerpQuat ( sgQuat dst,
+                          const sgQuat from, const sgQuat to,
+                          const SGfloat t ) ;
+
+Interpolates between two quaternions. +

Miscellaneous Functions.

+
+  void sgHPRfromVec3 ( sgVec3 hpr, sgVec3 src ) ;
+
+This function takes a vector representing a direction and computes +a suitable heading and pitch angle to look along that vector. Since +the roll angle is indeterminate, it is set to zero. +
+  void sgMakeNormal ( sgVec3 dst, sgVec3 a, sgVec3 b, sgVec3 c ) ;
+
+This finds the surface normal of a triangle given three vertices. +

Plane Equation Functions:

+
+  SGfloat sgDistToPlaneVec3 ( sgVec4 plane, sgVec3 pnt ) ;
+
+This returns the distance from the point to the plane. +
+  SGfloat sgHeightAbovePlaneVec3 ( sgVec4 plane, sgVec3 pnt ) ;
+
+This returns the vertical (Z) distance from the point to the +plane (my applications tend to use Z-is-up conventions). +
+  void sgMakePlane ( sgVec4 dst, sgVec3 a, sgVec3 b, sgVec3 c ) ;
+
+This finds the plane equation of a triangle given three vertices. +
+  void sgMakePlane ( sgVec4 dst, sgVec3 normal, sgVec3 pnt ) ;
+
+This finds the plane equation of a plane given its normal and +a point on the plane. +

Intersection Calculations:

+These routines compute the intersections of various geometric +entities: +
+int sgIsectPlanePlane       ( sgVec3 point, sgVec3 dir,
+                              sgVec4 plane1, sgVec4 plane2 ) ;
+
+Two planes intersect along an infinite line. Given two planes, +this routine routines a point and direction for the intersection +line. It returns FALSE if presented with two parallel planes, +TRUE otherwise. +
+int sgIsectInfLinePlane     ( sgVec3 dst,
+                              sgVec3 l_org, sgVec3 l_vec,
+                              sgVec4 plane ) ;
+
+Given a plane, and an infinite line, +this routine routines the point at which they intersect. +It returns FALSE if presented with a line which is parallel +to the plane, TRUE otherwise. +
+int sgIsectInfLineInfLine   ( sgVec3 dst,
+                              sgVec3 l1_org, sgVec3 l1_vec,
+                              sgVec3 l2_org, sgVec3 l2_vec ) ;
+
+Two infinite lines in 3D are unlikely to intersect - even +if you actually expect them to (because of roundoff error). +Hence, this routine sets dst to the closest point on the +second line which is closest to the first line. Return +FALSE if the lines are parallel, TRUE otherwise. +
+SGfloat sgIsectLinesegPlane ( sgVec3 dst,
+                              sgVec3 v1, sgVec3 v2,
+                              sgVec4 plane ) ;
+
+Intersect a line segment with a plane. The return +result is in the range 0..1 if the intersection lies +between v1 and v2, >1 if beyond v2 and <0 if before v1. +FLT_MAX is returned if the vector does not intersect +the plane. +

Interconversion between sg and sgd types:

+The following routines interconvert between double precision +'sgd' types and their single precision 'sg' counterparts: +
+  void sgdSetVec2 ( sgdVec2 dst, sgVec2 src ) ;
+  void sgdSetVec3 ( sgdVec3 dst, sgVec3 src ) ;
+  void sgdSetVec4 ( sgdVec4 dst, sgVec4 src ) ;
+  void sgdSetMat4 ( sgdMat4 dst, sgMat4 src ) ;
+  void sgdSetCoord ( sgdCoord *dst, sgCoord *src ) ;
+
+  void sgSetVec2 ( sgVec2 dst, sgdVec2 src ) ;
+  void sgSetVec3 ( sgVec3 dst, sgdVec3 src ) ;
+  void sgSetVec4 ( sgVec4 dst, sgdVec4 src ) ;
+  void sgSetMat4 ( sgMat4 dst, sgdMat4 src ) ;
+  void sgSetCoord ( sgCoord *dst, sgdCoord *src ) ;
+
+

Perlin Noise.

+There are three classes that implement Perlin Noise functions +(in one, two and three dimensions). +
+
+class sgPerlinNoise_1D
+{
+  sgPerlinNoise_1D () ;
+  void regenerate  () ;
+  SGfloat getNoise ( SGfloat x ) ;
+} ;
+ 
+class sgPerlinNoise_2D
+{
+  sgPerlinNoise_2D () ;
+  void regenerate  () ;
+  SGfloat getNoise ( sgVec2 pos ) ;
+  SGfloat getNoise ( SGfloat x, SGfloat y ) ;
+} ;
+ 
+class sgPerlinNoise_3D
+{
+  sgPerlinNoise_3D () ;
+  void regenerate  () ;
+  SGfloat getNoise ( sgVec3 pos ) ;
+  SGfloat getNoise ( SGfloat x, SGfloat y, SGfloat z ) ;
+} ;
+
+
+In each case, you simply construct the class, then call 'getNoise' +with the coordinate (either one, two or three-dimensional) at which +you need the value for the noise function. Perlin noise is both +repeatable (if you make the same request twice, you get the same +result) and continuous (there are no large jumps between results +for points that are close to one-another). +

+Calling 'regenerate' will cause the function to subsequently +generate a different noise pattern (presuming you didn't re-seed +the 'rand()' function to the same value before each call). +

Spring/Mass/Damper model.

+Many applications can greatly benefit from some simple dynamics +modelling. SG includes two classes that taken together implement +a standard Spring-Mass-Damper model with Euler integration. +
+
+  class sgParticle ;
+  class sgSpringDamper ;
+
+
+The idea is that you connect up a bunch of 'particles' (which each have +mass, position and velocity) using a collection of springs (actually, these +are combinations springs and dampers). Then, each frame, you can apply +forces to some or all of the particles - and perhaps forcably reposition +some of them - then loop around updating all of the spring-damper models +to see what forces they apply to the particles as a result. Finally, you +should call the 'upadate' function for each particle to see where it ends +up some short time later. +
+
+class sgParticle
+{
+  sgParticle ( float mass ) ;
+  sgParticle ( float mass, float x, float y, float z ) ;
+  sgParticle ( float mass, sgVec3 position ) ;
+ 
+  float *getPos         () ;
+  float *getVel         () ;
+  float *getForce       () ;
+  float  getMass        () ;
+ 
+  void   setPos      ( sgVec3 p ) ;
+  void   setVel      ( sgVec3 v ) ;
+  void   setForce    ( sgVec3 f ) ;
+
+  void   setPos      ( float x, float y, float z ) ;
+  void   setVel      ( float x, float y, float z ) ;
+  void   setForce    ( float x, float y, float z ) ;
+
+  void   setMass     ( float  m ) ;
+ 
+  void zeroForce   ()           ;
+  void addForce    ( sgVec3 f ) ;
+  void gravityOnly ()           ;
+
+  void bounce ( sgVec3 normal, float coefRestitution ) ; 
+  void update ( float dt ) ;
+} ;
+
+
+Aside from the obvious set/get routines that allow you to set or get the +particles mass, position, velocity and the total force that's applied to it, +you can also zero the force (a usual thing to do at the start of each cycle) +or set the force equal to normal gravity, or you can add a force to whatever +is currently being applied. The constructor function requires that you +pass in the mass and (optionally) the initial position. The mass of a +particle cannot ever be zero because you'll get divide-by-zero errors +all over the place if you do. +
+
+class sgSpringDamper
+{
+  sgSpringDamper () ;
+  sgSpringDamper ( sgParticle *_p0, sgParticle *_p1,
+                   float _stiffness, float _damping,
+                   float _restLength = -1.0f ) ;
+ 
+  float       getRestLength () ;
+  float       getStiffness  () ;
+  float       getDamping    () ;
+  sgParticle *getParticle   ( int which ) ;
+ 
+  void setParticles ( sgParticle *_p0, sgParticle *_p1 ) ;
+  void setParticle  ( int which, sgParticle *p ) ;
+ 
+  void setRestLength () ;
+ 
+  void setRestLength ( float l ) ;
+  void setStiffness  ( float s ) ;
+  void setDamping    ( float d ) ;
+ 
+  void update () ;
+ 
+} ;
+
+
+Each spring/damper is connected to two sgParticle's. It has a stiffness +and a damping factor plus a 'rest' length (ie how long it is when no +forces are applied to the system). If you omit the rest length, it will be +calculated from the current positions of the two particles. +

+Apart from the usual set/get functions, there is a single 'update' routine +which calculates the forces on the two particles and calls 'addForce' to +each. +

+A typical application will look like this: +

+Initialisation: +

    +
  • Construct all the sgParticle's +
  • Construct all the sgSpringDamper's and connect them to the particles. +
+Initialisation: +Each frame... +
    +
  • For each particle: Set up all the EXTERNAL forces that apply to it: +
      +
    • particle->zeroForce () ; ...or... +
    • particle->gravityOnly () ; ...or... +
    • particle->setForce ( something ) ; +
    + Don't ever just allow the previous frame's forces to continue to be + applied. + +
  • For each spring/damper add in the forces it applies to + the particles it's connected to: spring -> update () ; + +
  • For each particle: Update it's velocity and position: particle -> update ( dt ) + (where 'dt' is the elapsed time since the previous frame). +
  • If the particle collided with something, then you can adjust + it's velocity using: + sgParticle::bounce ( sgVec3 normal, float coefRestitution ) + (Where 'normal' is the surface normal of the plane we hit and + 'coefRestitution' is 1.0 for perfectly elastic surfaces, 0.0 for + ultra-sticky surfaces...or somewhere in between). +
+As always with these systems, if you set the spring stiffness too high, or the +damping coefficient too low, these equations will tend to explode - generating +unreasonably large forces and velocities. The only way to fix this is to back +off the numbers - or call both sets of 'update' functions very frequently. +

+You have been warned! +

+By default, gravity is set to -9.8 meters/sec/sec in the Z direction - but this +assumes you are on earth, using the metric (mks) system and have Z-is-up coordinates. +

+If any of those are not true, you can change the force that 'particle->gravityOnly()' +applies using: +

+
+  void   sgSetGravity     ( float  g ) ;
+  void   sgSetGravityVec3 ( sgVec3 g ) ;
+  float *sgGetGravityVec3 () ;
+  float  sgGetGravity     () ;
+
+
+Note that 'sgSetGravity' and 'sgGetGravity' use positive numbers for the +accelleration due to gravity - and assume Z-is-up. But the 'Vec3' versions +make neither of those assumptions - so make sure the vector you give points +DOWNWARDS! +

+eg +

+
+    sgSetGravity ( 1.6 ) ;   /* On the Moon! */
+
+
+...but... +
+
+    sgSetVec3 ( lunar_gravity, 0.0, 0.0, -1.6 ) ;   /* Note minus sign! */
+    sgSetGravityVec3 ( lunar_gravity ) ;
+
+
+
+

Credits.

+Thanks to Kevin Thompson +who kindly donated the original Quaternion code. +
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/sg/triangle_params.png b/doc/sg/triangle_params.png new file mode 100644 index 0000000000000000000000000000000000000000..476a6296601e8d91e652f768bcc0551dae361ae8 GIT binary patch literal 6195 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 literal 0 HcmV?d00001 diff --git a/doc/sl/index.html b/doc/sl/index.html new file mode 100644 index 0000000..313f9ff --- /dev/null +++ b/doc/sl/index.html @@ -0,0 +1,71 @@ + + + + + + + The SL Sound and Mixer Libraries. + + + +

The SL Sound and Mixer Libraries.

+By Steve Baker +

Introduction.

+The 'SL' sound library is primarily targetted towards producing sound +effects for games, and isn't really intended for playing music (although +you could certainly use it for doing that). The emphasis is on low +CPU impact and low latency rather than high quality and fancy MIDI/MOD +facilities. +

+SL is a part of PLIB. +

+The 'SL' sound library will eventually become a fully portable freeware +sound library that should run on all major platforms. Right now, SL +runs under: +

    +
  • Linux on systems with OSS (the Open Sound System) installed +(it's usually a standard part of the kernel). +
  • Windows NT, 95 and 98. +
  • OpenBSD. +
  • SGI's IRIX. +
  • Other UNIX systems using OSS (which should include Solaris, FreeBSD, +BSD/OS, SCO and others) should be able to run the Linux version of +SL by adding '-DSL_USING_OSS_AUDIO' to the compile command line. +
+At present, there is no support for Apple's MacOS sound systems +- although it is hoped that these will be added in the near future. +
    +
  • The SL Library +deals with all aspects of sample replay for games and +other realtime applications - it is designed such that it can be +ported to other operating systems by replacing just the slDSP +class. +

    +

  • The SM Library +controls the audio mixer on a PC sound card and +probably only works 100% correctly within OSS - and then probably +only on fairly 'vanilla' Soundblaster-compatible sound cards. +Portability of SM is patchy - regard it as a bonus. +
+It is due to the relative non-portability of the SM routines +that these are in a separate library. For non-portable +applications, SL and SM can be considered as a single library. +

+Download the Open Sound System +drivers (previously known as VoxWare). +


+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/sl/mixer.png b/doc/sl/mixer.png new file mode 100644 index 0000000000000000000000000000000000000000..3a4a701b161e9827ded94c072ea5e2d6cd994b10 GIT binary patch literal 39578 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 + + + + + + The SL Sound Library. + + + + + + + + +
Imagine clown here

The SL Sound Library.

+By Steve Baker +
+

Introduction.

+This library allows one to play, mix and modify sound samples +in realtime - with a special view towards sound for interactive +systems such as games and simulation. +

+At present, SL can drive PC-style sound cards from +within Windows NT, 95 and 98, Linux and several +other UNIX systems. When not running under Windows, +SL relies on the +Open Sound System (OSS) drivers +(formerly known as `VoxWare') - which are generally a +standard part of the Linux Kernel and are available on +most other UNIX implementations. Notably, the SGI IRIX +operating system does not support OSS - so IRIX is +supported via the proprietary SGI Audio Library. +A future port to the Mac operating system is anticipated. +

+For PC-based Linux systems using relatively 'standard' SoundBlaster +compatible sound cards, there is a companion library 'SM' that allows +one to drive the Audio mixer on your sound card. Since the SM library +is not easily portable to other systems, it is not considered a part +of SL. SM may also be ported to Windows - but it's unlikely that it +will be more generally available so it's use is deprecated. +SM does not exist for IRIX. +

Credits.

+Steve Baker <sjbaker1@airmail.net> wrote the first cut of +the library for Intel-based Linux machines and maintains the +system and its documentation. +

+Tom Knienieder ported SL onto the Windows family of operating +systems, onto OpenBSD and also onto IRIX as a part of the FlightGear project. +

+Curt Olson is the leader of the FlightGear project and provided +useful testing and helpful comments in appropriate quantities. +

Overview

+The SL library allows one to operate at several levels. +
    +
  • class slDSP: Allows you to play raw audio samples, + (one at a time) into the DSP channel + of a typical sound card. This is designed to + be simple enough that it should be portable + onto a wide range of non-OSS systems - and it + is the only SL class that you would need to + port. +

    +

  • class slSample: Allows you to read sound samples from + disk in a range of standard formats, + and to shift their frequency, volume, + etc. +

    +

  • class slScheduler: This layer allows you to play a number + of 8 bit monophonic samples in parallel, + up to three of them are be mixed together + with a simple prioritization scheme to + allow for cases when the application + wants to play more than that. This + class only makes calls to slSample and slDSP + interfaces, and should therefore be quite + portable. +

    + slScheduler will be extended to support 16 bit + samples and stereo in the near future. +

+

Using the Library.

+To use this library, you must '#include "sl.h"' and link to +libsl.a or libsl.so. +

class slDSP.

+Hardly any programs will ever create more than one slDSP object - in fact, +most programs won't explicitly create any slDSP's at all since they'll +be more likely to use slScheduler - which is derived from slDSP. +

+Here are the member functions: +

+
+  class slDSP
+  {
+  public:
+
+    slDSP ( int rate, int stereo, int bps ) ;
+    slDSP ( char *device, int rate, int stereo, int bps ) ;
+   ~slDSP () ;
+    
+    void play ( void *buffer, size_t length ) ;
+
+    int working     () ;  /* For optimists  */
+    int not_working () ;  /* For pessimists */
+
+    int getBps   () ;
+    int getRate  () ;
+    int getStereo() ;
+
+    void sync () ;
+    void stop () ;
+  } ;
+
+
+By default, "/dev/dsp" (or just plain "dsp" under Windows) +is the device used by this class, but +an alternative device may be accessed if it's name is passed +into the constructor. The constructor function also needs to +know the desired sample replay rate, whether the sounds should +be played in stereo (TRUE) or mono (FALSE) and how many bits +per pixel of audio are to be provided (typically 8 or 16). +

+The constructor function cannot guarantee to provide the +exact settings you require since hardware sample replay +units vary considerably in capabilities from one manufacturer +to another. Hence, all applications should call +the slDSP::getBps/getStereo/getRate() commands after the +object has been created. These routines tell the application +the actual number of bits per sample, the stereo setup and +the sample rate that this channel is currently set up to support. +The getRate command gets the sampling rate in Hz. +

+The 'stop' function aborts any sounds that are currently being played +and the 'sync' function blocks until the current audio stream is +flushed. +

+The 'slDSP::not_working()' function returns TRUE if there is any kind of +problem with the driver. Mostly it returns TRUE if your system isn't +set up for audio. 'slDSP::working()' is provided by popular request +and is just the opposite. +

+The driver will not fail if you call it's member functions when +'slDSP::not_working()' returns TRUE - so programs written for audio +should work OK even on hardware with no audio support. +

+The play command takes raw audio data from a buffer in memory +(typically from the buffer of an slSample) and plays it with the +current settings. In 8bps mono mode, the data is a simple +unsigned byte stream. In 16bps mode, the data is a stream +of unsigned shorts and in stereo mode, samples alternate +between left and right channels - left first, then right. +

+Hence, each sound sample can be 1, 2 or 4 bytes long. +

+
+ 8bps Mono  :      L      |      L      |...
+16bps Mono  :    H   L    |    H   L    |...
+ 8bps Stereo:    LL LR    |    LL LR    |...
+16bps Stereo: HL LL HR LR | HL LL HR LR |...
+
+Where: L is a low  order byte (monophonic),
+       H is a high order byte (monophonic),
+      LL is low-order, left channel,
+      LR is low-order, right channel,
+      HL is high-order, left channel,
+      HR is high-order, right channel.
+
+
+

class slSample

+This class is intended to hold all the relevent information that +goes along with a digital sound sample. Most programs will create +a separate slSample for each sound effect or musical note: +
+
+class slSample
+{
+public:
+
+  slSample () ;
+  slSample ( Uchar *buff, int leng ) ;
+  slSample ( char *fname ) ;
+  slSample ( char *fname, slDSP *match ) ;
+ ~slSample () ;
+  char *getComment () ;
+  void   setComment ( char *nc ) ;
+
+  Uchar *getBuffer () ;
+  int    getLength () ;
+  void   setBuffer ( Uchar *buff, int leng ) ;
+
+  void setRate   ( int r ) ;
+  void setBps    ( int b ) ;
+  void setStereo ( int s ) ;
+
+  int  getRate   ()        ;
+  int  getBps    ()        ;
+  int  getStereo ()        ;
+
+  int  getPlayCount ()     ;
+
+  float getDuration ()     ;
+
+  int loadFile    ( char *fname ) ;
+  int loadRawFile ( char *fname ) ;
+  int loadAUFile  ( char *fname ) ;
+  int loadWavFile ( char *fname ) ;
+
+  void changeRate   ( int r ) ;
+  void changeBps    ( int b ) ;
+  void changeStereo ( int s ) ;
+
+  void autoMatch ( slDSP *player ) ;
+  void adjustVolume ( float vol ) ;
+
+  void print ( FILE *fd ) ;
+} ;
+
+
+There are three ways to construct a slSample - first, you can create +an (initially) empty sample: +
+
+  slSample::slSample () ;
+
+
+Secondly, you can provide a buffer of audio data (with either 1, 2 or 4 +bytes per sample) - along with the length of that buffer (in bytes - +NOT in samples). +
+
+  slSample::slSample ( Uchar *buff, int leng ) ;
+
+
+You can also load a file from disk: +
+
+  slSample::slSample ( char *fname ) ;
+
+
+In that case, slSample will look at the extension of the filename and +choose an appropriate loader for that file type. +

+Since sample files from disk may well not match the settings on +the slDSP that will be replaying it, there is an option to pass +the address of the slDSP that will be doing the replaying and have +the slSample automatically match it's settings to that of the +replay unit. +

+You can load a sample file into a slSample at any time using: +

+
+  int slSample::loadFile    ( char *fname ) ;
+  int slSample::loadRawFile ( char *fname ) ;
+  int slSample::loadAUFile  ( char *fname ) ;
+  int slSample::loadWavFile ( char *fname ) ;
+
+...and...
+
+  void slSample::autoMatch ( slDSP *player ) ;
+
+
+The basic 'slSample::loadFile' call will auto-detect the file type - the remaining +three calls presume the file to be of the type indicated irrespective +of the extension. +
+
+  Raw - A simple file containing just the samples in the same
+        format as required by the slDSP class (see above). These
+        often have the file extension '.ub' (unsigned byte).
+        Since this format has no header, it is always assumed to be
+        mono, 8000Hz and 8bps. If you know better - then use
+        setRate/setStereo/setBsp to sort out the resulting mess!
+  AU  - (Sun Microsystems 'audio' format), this file type typically
+        has the '.au' extension.
+  Wav - (Microsoft 'WAVE' format), this file type typically
+        has the '.wav' extension.
+
+
+All flavours of 'slSample::loadFile' return FALSE if the file cannot be read, +TRUE otherwise. slSample::autoMatch allows the sample to be tweaked to +work correctly with the slDSP that will be used to replay the sample +subsequently. +
+
+  char *slSample::getComment () ;
+  void  slSample::setComment ( char *nc ) ;
+
+
+Each sound sample can have a comment string associated with it. +Both '.wav' and '.au' files support such strings. In a 'wav' file, +there may be multiple strings - only the first is read. +
+
+  Uchar *slSample::getBuffer () ;
+  int    slSample::getLength () ;
+  void   slSample::setBuffer ( Uchar *buff, int leng ) ;
+
+
+These calls allow low level access to the sample buffer contained +in an slSample. slSample::setBuffer makes a private copy of the buffer you +pass to it, so you may delete your copy afterwards. slSample::getBuffer +returns a pointer to the local buffer inside the slSample - you +must not free it up. If you want to free up the memory in a slSample +without deleting it then you should call slSample::setBuffer(NULL,0). +

+The following calls get and set the internal parameters of an slSample: +

+
+  void slSample::setRate   ( int r ) ;
+  void slSample::setBps    ( int b ) ;
+  void slSample::setStereo ( int s ) ;
+
+  int  slSample::getRate   ()        ;
+  int  slSample::getBps    ()        ;
+  int  slSample::getStereo ()        ;
+
+
+Note that setting a parameter does not affect the audio data in +the buffer. If a sample was recorded at (say) 8KHz and you call +slSample::setRate(16000), then the sample will replay at 16KHz but will be +only half the usual duration and twice the frequency. The effects +of slSample::setBps and slSample::setStereo are even more destructive if used inappropriately. +

+The main use for the slSample::setRate/setBps/setStereo are for when the audio +data was created inside the program or read from a 'Raw' file format +and there was no other indication of these settings. By default, +such samples are assumed to be 8KHz, monophonic 8bps recordings. This +is supported by almost all audio cards. +

+If you need to manipulate the sample so that it sounds right when +replayed with settings that differ from those when it was recorded +then you need: +

+
+  void slSample::changeRate   ( int r ) ;
+  void slSample::changeBps    ( int b ) ;
+  void slSample::changeStereo ( int s ) ;
+
+
+It is sometimes useful to permenantly change the volume of a +sound sample: +
+
+  void slSample::adjustVolume ( float vol ) ;
+
+
+Setting vol to 2.0 would double the volume, setting it to 0.5 +would halve it. +

+Bear in mind though that these change/adjust routines make +permenant changes to the data in the sound buffer. The more +changes you make, the more noise you'll introduce into the +sample. If possible, record 16 bit samples and play with them +in a proper sample studio package - then reduce them to 8 bits +before you load them into an slSample. +

+These routines are also relatively slow - they create a new +sound buffer, process the old data to fit the new setup and +then free up the old buffer. The length and address of the +internal slSound buffer will often change as a result. +

+
+  float slSample::getDuration () ;
+
+
+This returns the duration of the sound sample in seconds. +
+
+  int slSample::getPlayCount () ;
+
+
+Returns a count of the number of simultaneous instances of this +sample are currently being played by slSchedulers (see below). +
+
+  void slSample::print ( FILE *fd ) ;
+
+
+This prints out all the parameters of the sound sample to the +specified file descriptor in human-readable form. +

class slScheduler.

+This class is where it all comes together. Most programs will +only create a single slScheduler. Since the slScheduler needs +dedicated access to the DSP, this class is inherited from an +slDSP - so most programs that use slScheduler should not declare +an slDSP as well. +

+IMPORTANT NOTE: Since slScheduler is derived from slDSP, all +slDSP member functions are available as slScheduler calls. +

+
+class slScheduler : public slDSP
+{
+public:
+  slScheduler ( int rate ) ;
+  slScheduler ( char *device, int rate ) ;
+ ~slScheduler () ;
+
+  float setSafetyMargin ( float seconds ) ;
+
+  void update () ;
+  void dumpUpdate () ;
+
+  void stopSample   ( slSample *s, int magic ) ;
+  void pauseSample  ( slSample *s, int magic ) ;
+  void resumeSample ( slSample *s, int magic ) ;
+
+  int loopSample ( slSample *s, int pri = 0,
+                   slPreemptMode mode = SL_SAMPLE_MUTE,
+                   int handle = 0, slCallBack cb = NULL ) ;
+  int playSample ( slSample *s, int pri = 1,
+                   slPreemptMode mode = SL_SAMPLE_ABORT,
+                   int handle = 0, slCallBack cb = NULL ) ;
+
+  void stopMusic   ( int magic ) ;
+  void pauseMusic  ( int magic ) ;
+  void resumeMusic ( int magic ) ;
+
+  int loopMusic  ( char *fname, int pri = 0,
+                   slPreemptMode mode = SL_SAMPLE_MUTE,
+                   int handle = 0, slCallBack cb = NULL ) ;
+  int playMusic  ( char *fname, int pri = 1,
+                   slPreemptMode mode = SL_SAMPLE_ABORT,
+                   int handle = 0, slCallBack cb = NULL ) ;
+} ;
+
+
+The idea behind slScheduler is that it manages all your music and +sound sample replay needs. You construct it at the start of your program run, load in your +slSamples and then call the 'slScheduler::update' function every iteration of your code. +

+The constructor function accepts a sample rate argument - note the +caveats in the slDSP contructor function - you need to call getRate() +to determine if your hardware was actually able to support the rate +you asked for - or whether you actually obtained some slightly +different rate. If you don't get a rate that matches your sound +samples then you'll need to call slSample::changeRate() or even +slSample::autoMatch for each one in turn to change it's sampling rate +to something that suits the hardware. +

+Notice that at present, slScheduler only supports 8bps monophonic +playback, this may change in the future. +

+Once everything is up and running, you can very easily play either +slSamples or music. Simply call: +

+
+  int slScheduler::loopSample ( slSample *s ) ;
+or
+  int slScheduler::playSample ( slSample *s ) ;
+
+  int slScheduler::loopMusic ( char *fname ) ;
+or
+  int slScheduler::playMusic ( char *fname ) ;
+
+
+There are additional arguments to these calls - but they are all +optional. slScheduler::loopSample() tells the scheduler to play +that sample in a loop forever, the slScheduler::playSample() tells +it to play the sample as a single one-shot effect. +

+Similarly, you can read and play music (currently only in 'MOD' +format). slScheduler::loopMusic() tells the scheduler to play +the music in a loop forever, the slScheduler::playMusic() tells +it to play the file as a single one-shot playback. +

+You can only play back one music track at a time. +

Preempting

+The slScheduler can (at present) play only three sounds at the +same time. You can ask it to play more than that - but only three +will actually sound at any given instant. If too many sounds try +to play at once, all but three of them will be 'pre-empted'. +

+You can tag each sample with a priority and a 'pre-empt' mode. The +priority indicates which sounds have priority over which others and +the 'pre-empt' mode tells it what to do if a sound of higher +priority wants to play in the meantime. +

+The full form of the slScheduler::loopSample/Music and +slScheduler::playSample/Music calls is: +

+
+  int slScheduler::loopSample ( slSample *s, int priority, slPreemptMode mode ) ;
+  int slScheduler::playSample ( slSample *s, int priority, slPreemptMode mode ) ;
+  int slScheduler::loopMusic  ( char *fname, int priority, slPreemptMode mode ) ;
+  int slScheduler::playMusic  ( char *fname, int priority, slPreemptMode mode ) ;
+
+
+The 'priority' is a simple integer. Zero is the least important +sound, SL_MAX_PRIORITY (currently set at 16) is the most +important. +

+The 'mode' parameter lets you choose what happens to a sound when +it is pre-empted. 'mode' is one of: +

    +
  • SL_SAMPLE_ABORT - If this sound is pre-empted, it simply +stops playing completely and is forgotten about by the scheduler. +
  • SL_SAMPLE_RESTART - If this sound is pre-empted, it will +stop playing - and restart from the beginning when the sound +system is free enough. Beware that on a busy sound system, the +same sound may try to start over and over again - producing a +stuttering effect as it is repeatedly stopped and restarted by +higher priority sounds. +
  • SL_SAMPLE_MUTE - The sound simply goes silent until +it either ends or the sound card becomes free enough for +it to carry on playing. The duration of the sound from start +to finish will be no different than if it had not been +pre-empted at all. +
  • SL_SAMPLE_DELAY - If this sound is pre-empted then it +will pause until the sound card becomes free again and continue +from exactly where it left off. +
  • SL_SAMPLE_CONTINUE - Although a higher priority sound +has come along, this sound will continue to play if it has +already been started. This is a useful mode for sounds that +need to be played without interruption - but which could be +delayed in time until the sound card is free. It is just +as if the priority of this sound jumps to SL_MAX_PRIORITY+1 +immediately it starts playing. +
+

Interacting with a Sample that's Playing.

+Also, you can ask the scheduler to tell you when something important +happens to your sound. +
+
+  int slScheduler::loopSample ( slSample *s, int priority, slPreemptMode mode,
+                                int magic, slCallBack cb ) ;
+  int slScheduler::playSample ( slSample *s, int priority, slPreemptMode mode,
+                                int magic, slCallBack cb ) ;
+                                int magic, slCallBack cb ) ;
+  int slScheduler::loopMusic  ( char *fname, int priority, slPreemptMode mode,
+                                int magic, slCallBack cb ) ;
+  int slScheduler::playMusic  ( char *fname, int priority, slPreemptMode mode,
+                                int magic, slCallBack cb ) ;
+
+
+The 'cb' parameter is the address of a callback function that SL +will call whenever something important happens to your sound. +

+Your function much accept three parameters: +

+
+  typedef void (*slCallBack) ( class slSample *sample, slEvent event, int magic ) ;
+
+
+The first parameter is the slSample that was being played when this +even happened, the second parameter tells you what happened: +
+
+  SL_EVENT_COMPLETE  -- The sound finished playing.
+  SL_EVENT_LOOPED    -- The sound looped back to the start.
+  SL_EVENT_PREEMPTED -- The sound was preempted by another sound.
+
+
+Other events may be added in the future, so your callback function +should be prepared to do nothing gracefully if an unknown event +comes along. +

+Since there may be multiple copies of a single sample being played +at the same time, the 'magic' parameter is a means for you to identify the +context that the sound comes from. You pick a magic number that's +meaningful to your application when you call slScheduler::playSample +or slScheduler::loopSample - and that number will be passed back to +your callback function whenever it is called. SL doesn't do anything +else with this magic number - so it's meaning can be anything +you want provided that the magic number must be non-zero. +

+A callback function can start new sounds playing, but if you +wait for the SL_EVENT_COMPLETE event to do that then there +may be a fraction of a second pause between the sound that +just stopped and the new one starting. +

+There are other ways to use the magic number to interact with +playing samples: +

+
+   void slScheduler::stopSample   ( slSample *sample, int magic ) ;
+   void slScheduler::pauseSample  ( slSample *sample, int magic ) ;
+   void slScheduler::resumeSample ( slSample *sample, int magic ) ;
+
+   void slScheduler::stopMusic   ( int magic ) ;
+   void slScheduler::pauseMusic  ( int magic ) ;
+   void slScheduler::resumeMusic ( int magic ) ;
+
+
+This stops/pauses/resumes all instances of the specified sample with +the specified magic number. The 'stopSample' command will even stop +looped samples. If the 'sample' is NULL then all samples with +that magic number are stopped/paused/resumed. If the 'magic' number +is zero then all instances of 'sample' are affected. If sample is +NULL and magic is zero then all currently playing sounds are +affected. +

+IMPORTANT NOTE: When you are playing a sample, it is important +not to delete it until after it has finished playing. SL keeps +count of the number of times each sample is playing - and will +produce a fatal error and exit if you attempt to do this. Note +that even if you tell the sample to stop playing using +slScheduler::stopSample, you must not delete it until *AFTER* +the next call to slScheduler::update(). You can use the +slSample::getPlayCount() function to find out how many playing +instances of a particular slSample there are. +

+

Envelopes.

+Most of the time, a sound can be simply played as-is. This is +highly desirable since doing real-time audio modification is +costly. However, there are times when you really need to +alter the pitch, volume, stereo-postion (pan) or filtering +of a sample in realtime. +

+One classic example of this is in a car racing game when +you want the pitch and volume of the engine to change in +response to the throttle and gear ratio - even though the +sound itself is really a simple looped sample. +

+To cater for these needs, SL has the concept of an 'envelope'. +An envelope is a array of floating point values and and array +of times at which those values are correct. The envelope can +be attached to a playing sound like this: +

+
+   slScheduler::addSampleEnvelope ( slSample *s, int magic,
+              int slot, slEnvelope *e, slEnvelopeType type ) ;
+   slScheduler::addMusicEnvelope ( int magic,
+              int slot, slEnvelope *e, slEnvelopeType type ) ;
+
+
+As with the other sample interaction commands, the sample and +magic number can be used as wild-cards to apply the same command +to a number of sample instances. +

+Each sample instance can has a minimum of four (and a maximum +of SL_MAX_ENVELOPES) slots into which an envelope can be placed. +Each envelope can operate on a different aspect of the sample +and the envelopes are applied to the sound in slotwise order. +

+To remove an envelope from a particular sample/magic/slot +combination, simply use slScheduler::addSampleEnvelope or +slScheduler::addMusicEnvelope with a NULL envelope. +

+Using a lot of envelopes will considerably increase the amount +of CPU time consumed by SL - so beware of trying overly fancy +effects. +

+The 'slEnvelopeType' argument defines which aspect of the sample's +performance will be affected by the envelope: +

+
+  SL_PITCH_ENVELOPE  : The value in the envelope determines the
+                       rate at which the sample is speeded up
+                       or slowed down during replay. A value of
+                       2.0 would double the pitch of the sound and
+                       halve it's duration. A value of 0.5 would
+                       halve the pitch and double the duration.
+
+  SL_VOLUME_ENVELOPE : The value of the envelope multiplies the
+                       amplitude (volume) of the sound. Beware of
+                       large multipliers that would cause the sound
+                       to be clipped and small multipliers that
+                       increase the noise level of the sample.
+
+  SL_FILTER_ENVELOPE : **NOT IMPLEMENTED YET**
+                       The value of this parameter determines the
+                       number of consecutive samples that are
+                       averaged together in a kind of moving average.
+                       Large numbers tend to muffle the sound.
+
+  SL_PAN_ENVELOPE    : **NOT IMPLEMENTED YET**
+                       The stereo position of the sound is moved
+                       to the left (negative numbers) or to the
+                       right (positive numbers). +/-1.0 is full-scale.
+
+  SL_ECHO_ENVELOPE   : **NOT IMPLEMENTED YET**
+                       The sound is added to a half volume copy of
+                       itself which is shifted in time by this number
+                       of seconds.
+
+
+In addition to these, there are a set of SL_INVERSE_PITCH_ENVELOPE, +SL_INVERSE_VOLUME_ENVELOPE, ...etc. These operate in an identical +manner to the standard envelopes - but with the opposite sense. +Hence, if you want to cross-fade two similar sounds then apply the +same envelope as an SL_VOLUME_ENVELOPE to one sample and as an +SL_INVERSE_VOLUME_ENVELOPE to the other and they will neatly +cross-fade. +

+Here is the class slEnvelope: +

+
+class slEnvelope
+{
+  slEnvelope ( int _nsteps = 1, slReplayMode _rm,
+                                float *_times, float *_values ) ;
+  slEnvelope ( int _nsteps = 1, slReplayMode _rm ) ;
+ ~slEnvelope () ;
+
+  int getPlayCount () ;
+
+  void setStep ( int n, float _time, float _value ) ;
+
+  float getStepValue ( int n ) ;
+  float getStepTime  ( int n ) ;
+
+  float getValue ( float _time ) ;
+} ;
+
+The envelope can be constructed either with or without initial data - +and can be either SL_SAMPLE_ONE_SHOT or SL_SAMPLE_LOOP. A one-shot +envelope retains it's final value indefinitely after it finishes +playing - a looped envelope returns to the start and repeats +indefinitely. +

+One very common thing to do is to create an slEnvelope containing +just one value. +

+The slEnvelope::setStep() function allows you to modify the value +of the n'th step of the envelope and to position it in time. +You can use this in realtime to dynamically interact with the +envelope and hence modify whatever samples are using that envelope +at the time. You can read back the time and value for any given +step of the envelope using slEnvelope::getStepTime() and +slEnvelope::getStepValue(). +

+Envelopes interpolate between the steps you provide - so if +a pitch envelope had just three value/time pairs: +

+
+  slEnvelope my_envelope ( 3, SL_SAMPLE_LOOP ) ;
+  my_envelope . setStep ( 0,  0.0, 1.0 ) ;
+  my_envelope . setStep ( 1, 10.0, 2.0 ) ;
+  my_envelope . setStep ( 2, 20.0, 1.0 ) ;
+
+  scheduler -> playSample ( my_sample ) ;
+  scheduler -> addSampleEnvelope ( my_sample, 0,
+                     0, & my_envelope, SL_PITCH_ENVELOPE ) ;
+
+
+...then this could be applied to a sample which would then +gradually increase in pitch over the next 10 seconds - by +which point, the pitch would have doubled - and over the +following 10 seconds, would gradually return to normal. +This behaviour would repeat for as long as the envelope +remained attached to that sample. +

+slEnvelope::getValue() returns the interpolated value at the specified +time. +

+You can remove an envelope from a sample thats playing by passing +a NULL envelope in the same slot. +

Timing the Audio Updates.

+The slScheduler has to pump audio data into the Linux device +driver in realtime. It does this by transferring a chunk +of pre-mixed audio data over to the Linux device driver +every time the it seems to be getting low on data. +

+Note that libsl is not using a separate thread +to do this - although you could place calls to slScheduler::update() +in a separate thread if you wanted to. That means that +if you call the update function too infrequently, the +Linux device driver will run out of data and you will +hear breaks and clicks in the audio. +

+Here is some advice on how to deal with this problem. +slScheduler supports this call: +

+
+  float slScheduler::setSafetyMargin ( float num_seconds ) ;
+
+
+The slScheduler::setSafetyMargin() call allows you to tell the scheduler +to ensure that there are at least 'num_seconds' of audio +queued up in the driver after each call to update. (The +default safety margin is two seconds - which is likely +to be on the long side in many applications). +

+If you make this number very large (say 4 seconds or more) +then Linux won't have enough buffer space to hold that +amount of audio data in it's internal buffer (which is +about 64Kb long on my system) - and slScheduler::update() will block +indefinitely trying to keep it that full. The audio +will be smooth - but your application will get zero +time to run in! +

+If you set the safety margin to a smaller number (but +still much larger than the rate that you call update - +say 1 or 2 seconds) then the system will produce smooth, +continuous audio - but there will be a significant delay +between calling the slScheduler::playSample() function and that sample +actually playing. That is because there is still a number of +seconds of data in the Linux buffer that has to be +played before your new sound will be heard. +

+For music replay, this is fine - all the audio was pre-planned, +so it'll sound great. But if you are creating the audio for +a game or something - then it's probably important that the +"BLAM!!" sound happens very soon after the player presses +the fire button. With safetyMargin set to two seconds, you +could get anywhere up to a two second delay! +

+Here is one (partial) remedy: +

+If you know that a VERY important sound has to be played +RIGHT NOW - then you can call the slScheduler::dumpUpdate() +function instead of the usual slScheduler::update() on that +frame. This will have the effect of telling Linux to +dump all the audio that has been queued but not yet +played - so that your important sound can play RIGHT +NOW. The downside is that a significant amount of +sounds that were sitting in the Linux queue will +be dumped - and a certain amount of CPU time will +be wasted as a result of having to compute those +seconds of audio twice. +

+If you were playing a boring buzzing noise for the sound of +an engine in a flight simulator - then losing a second +or two of it might be acceptable. If you were playing +continuous music in the background then using slScheduler::dumpUpdate() +would sound like your CD player skipping when you jog it +too hard. +

+The final solution is to set safetyMargin to something +quite small (say 0.2 seconds). This means that in theory, +all your sounds will be heard within 0.2 seconds of you +asking for them to play. Of course if you should fail to +call the slScheduler::update() function at LEAST once every 1/5th +second, Linux could run out of audio data and you'd get +gaps and clicks which sound REALLY TERRIBLE. +

+I find that even with the most simple application, I get +bad breakup with a safetyMargin anywhere under 0.13 seconds - +but that's on a fairly ancient 100MHz 486 - I would expect +a modern CPU to do better. +

+The sound library can keep up with an 8KHz mono, 8bps sample +rate consuming only 1ms of CPU time every iteration - also +on my 100MHz 486. +

+There are times when you might want to change safetyMargin +on-the-fly. This is fine - but the effect of the change +won't be noticable until the data that is currently +queued within Linux has been played. +

The Volume Level Conundrum.

+Globally, the volume is controlled by the audio mixer. If +you are using the 'SM' companion library then you can +do this with class slMixer - no +problem there - but the relative volume of the samples +that are played together cannot be controlled so easily. +

+For speed, the scheduler simply adds the sounds together +and clamps any values that would overflow. To do anything +more complex would consume significant amounts of CPU time. +

+(Since sounds are stored as unsigned char's the value +0x80 actually represents zero voltage - so the equation +for adding two sound samples 'a' and 'b' is: +

+
+   a + b - 0x80 
+
+
+it is important to bear this in mind if you do any work +on the slSample buffers yourself.) +

+Clearly if two sounds each range in voltage from utter +minimum (0x00) to absolute maximum (0xFF) then there will +be considerable distortion when they are added. The problem +gets even worse when there are three sounds. Hence, if you +really expect to play two sounds at once then you should +reduce the volume of each sample to the range 0x40 to 0xC0, +and if you expect to play three sounds at once then the +range 0x56 to 0xAA. However, if you do that then you are +in effect losing one or two bits of audio precision - which +raises noise levels quite a bit. +

+It would have been possible to go to 16 bit sound samples - but +not all PC sound cards can do that - and in any case, it would +double the amount of memory and CPU time taken to play the sounds. +

+One notable fact is that it is fairly unlikely that all +your sounds actually fill the range 0x00 to 0xFF exactly. Most +sampled sounds that you find recorded by home equipment are +typically either: +

    +
  • Over-recorded - they fill the range 0x00 to 0xFF - but + were actually recorded a too high a range and are + already pretty noisy. +
  • Under-recorded - they don't fill the range 0x00 to 0xFF. +
+If under-recorded, then further reducing the volume for the +sake of adding several sounds together may be unnecessary. +

+If over-recorded, then you'll need to reduce the volume of +the sample for sure since it's already being clipped. +

+Even in the happy case of perfect recordings, it's pretty +unlikely that the loudest parts of two sounds will happen +to replay at the exact same instant - and even if they +did, it might be in some rare spikes of audio where quality +might not suffer. +

+So, there is no solid mechanism to ensure the best quality. +You might want to spend some time playing with the relative +volumes of the various samples you use in your code. The +slSample::changeVolume() function is too slow to use in +realtime - but it is handy for tuning the samples right +after they have been loaded from disk. +

The Future.

+I keep hearing about compressed '.WAV' files - I havn't found +any yet - or a spec on how to read them. +

+There doesn't seem to be a spec for '.AU' files either - but +the header format seems simple enough from just looking at +a few example files. +

+It's also possible to allow more simultaneous sounds - the +limit of three is a bit arbitary - but again, CPU costs are +something to be concerned about. Fortunately, if I ever +increase the limit, it shouldn't affect existing programs. +

+It would be nice if the scheduler knew how to do stereo +and 16 bps. +

A Note about Reference Counting.

+Instances of the slEnvelope and slSample classes are 'reference +counted' within SL. That means that a counter in each object is +incremented each time you start one playing - and decremented +each time one stops. When you 'delete' an slEnvelope or an slSample, +the reference count will be checked to ensure it is zero - if it +is not then a fatal error will be produced if you then attempt +to call slScheduler::update() since that would play a sound that +has been deleted by the application. +

+The error does not occour at the point when you delete the +sample or envelope since it is OK to do that so long as you +never try to play more sounds later. It is therefore illegal +to delete a sample or envelope and then stop +it playing. +

+This arrangement allows an application to exit with outstanding +sounds queued up. +

Example Program.

+This example creates an 'engine' sound in memory using +some summed sine waves, loads a number of sounds (and +has them automatically matched to the current sound +replayer), then plays the engine sound in a loop, +periodically interrupting it with some other noises. +
+
+#include "sl.h"
+#include "sm.h"
+#include <math.h>
+
+/*
+  Construct a sound scheduler and a mixer.
+*/
+
+slScheduler sched ( 8000 ) ;
+smMixer mixer ;
+
+int main ()
+{
+  mixer . setMasterVolume ( 30 ) ;
+  sched . setSafetyMargin ( 0.128 ) ;
+
+  /* Just for fun, let's make a one second synthetic engine sample... */
+
+  Uchar buffer [ 8000 ] ;
+
+  for ( int i = 0 ; i < 8000 ; i++ )
+  {
+    /* Sum some sin waves and convert to range 0..1 */
+
+    float level = ( sin ( (double) i * 2.0 * M_PI / (8000.0/ 50.0) ) +
+                    sin ( (double) i * 2.0 * M_PI / (8000.0/149.0) ) +
+                    sin ( (double) i * 2.0 * M_PI / (8000.0/152.0) ) +
+                    sin ( (double) i * 2.0 * M_PI / (8000.0/192.0) )
+                  ) / 8.0f + 0.5f ;
+
+    /* Convert to unsigned byte */
+
+    buffer [ i ] = (Uchar) ( level * 255.0 ) ;
+  }
+
+  /* Set up four samples and a loop */
+
+  slSample  *s = new slSample ( buffer, 8000 ) ;
+  slSample *s1 = new slSample ( "scream.ub", & sched ) ;
+  slSample *s2 = new slSample ( "zzap.wav" , & sched ) ;
+  slSample *s3 = new slSample ( "cuckoo.au", & sched ) ;
+  slSample *s4 = new slSample ( "wheeee.ub", & sched ) ;
+
+  /* Mess about with some of the samples... */
+
+  s1 -> adjustVolume ( 2.2  ) ;
+  s2 -> adjustVolume ( 0.5  ) ;
+  s3 -> adjustVolume ( 0.2  ) ;
+
+  /* Play the engine sample continuously. */
+
+  sched . loopSample ( s ) ;
+
+  int tim = 0 ;  /* My periodic event timer. */
+
+  while ( SL_TRUE )
+  {
+    tim++ ;  /* Time passes */
+
+    if ( tim % 200 == 0 ) sched.playSample ( s1 ) ;
+    if ( tim % 180 == 0 ) sched.playSample ( s2 ) ;
+    if ( tim % 150 == 0 ) sched.playSample ( s3 ) ;
+    if ( tim % 120 == 0 ) sched.playSample ( s4 ) ;
+
+    /*
+      For the sake of realism, I'll delay for 1/30th second to
+      simulate a graphics update process.
+    */
+
+#ifdef WIN32
+    Sleep ( 1000 / 30 ) ;  /* 30Hz */
+#else
+    usleep ( 1000000 / 30 ) ;  /* 30Hz */
+#endif
+
+    /*
+      This would normally be called just before the graphics buffer swap
+      - but it could be anywhere where it's guaranteed to get called
+      fairly often.
+    */
+
+    sched . update () ;
+  }
+}
+
+
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/sl/sl.png b/doc/sl/sl.png new file mode 100644 index 0000000000000000000000000000000000000000..381a241a6c356d0a37940366ee143cc2e186027a GIT binary patch literal 5958 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 literal 0 HcmV?d00001 diff --git a/doc/sl/sm.html b/doc/sl/sm.html new file mode 100644 index 0000000..bc64a36 --- /dev/null +++ b/doc/sl/sm.html @@ -0,0 +1,106 @@ + + + + + + + The SL Mixer Library. + + + + + + + + +
Imagine kitchen mixers here +

The SL Mixer Library.

+By Steve Baker +
+

Introduction.

+This library allows one to drive the mixer controls of +most PC-style sound cards. It relies +on the Open Sound +System drivers (formerly known as 'VoxWare') +- which are generally a standard part of the Linux Kernel - and +which is also available on some other UNIX platforms. +

+A port of SM to the Windows family of operating systems +is currently underway. +

+SM does not work under SGI's IRIX. +

Using the Library.

+To use this library, you must '#include "sm.h"' and link to +libsm.a or libsm.so. +

class smMixer.

+This library comprises a single class 'smMixer'. +

+Most programs will only ever create a single smMixer object. +

+Here are the member functions: +

+
+  class smMixer
+  {
+    public:
+
+      smMixer () ;
+      smMixer ( char *device ) ;
+     ~smMixer () ;
+  
+      int not_working () ;
+
+      /* Volume/Treble/Bass controls are in integer percentages */
+
+      void setTreble       ( int treble ) ;
+      void setBass         ( int bass   ) ;
+
+      void setMasterVolume ( int volume ) ;
+      void setSynthVolume  ( int volume ) ;
+      void setPCMVolume    ( int volume ) ;
+      void setSpeakerVolume( int volume ) ;
+      void setLineVolume   ( int volume ) ;
+      void setMicVolume    ( int volume ) ;
+      void setCDVolume     ( int volume ) ;
+
+      void setMasterVolume ( int left, int right ) ;
+      void setSynthVolume  ( int left, int right ) ;
+      void setPCMVolume    ( int left, int right ) ;
+      void setSpeakerVolume( int left, int right ) ;
+      void setLineVolume   ( int left, int right ) ;
+      void setMicVolume    ( int left, int right ) ;
+      void setCDVolume     ( int left, int right ) ;
+  } ;
+
+
+Basically, each of the mixer devices can be driven with either +mono or stereo volume controls, each volume is a expressed as an +integer percentage of maximum. +

+By default, "/dev/mixer" is the device used by this class, but +an alternative device may be accessed if it's name is passed +into the constructor. +

+The 'not_working()' function returns TRUE if there is any kind of +problem with the driver. Mostly it returns TRUE if your system isn't +set up for audio. +

+The driver will not fail if you call it's member functions when +'not_working()' returns TRUE - so programs written for audio +should work OK even on Linux boxes with no audio support. +


+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/spacething-small.png b/doc/spacething-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d1aab0b0c73ffa2df8801b7c0aab9d96a6cbcf7a GIT binary patch literal 1760 zcmV<61|Ru}P)T@p|CyQpnP&fHX3YQ0%*@Qp%$dxYnar7H%x0O) zW@gN0Gt4t)%ri5YnVFfHW|?N0nPz60W;2;HW|=cHW@edYW@ct)GiEbpW-~KpGXQ1) zGiCq)GiGKpW-~K0W-~K0Gcy1)05dZH001*H05bpp05bpp0PS5CMF0Q>3Q0skRCwCW zSc!J(HV`$-YYEi!k&bXBP5Ma29zg&9fxRP{B?;Rhv?t19uqDlY=gL(|8YG<ZI#XpTNC)S2Hm^c^Q5W2QH6D{ z5<$uzRPMe(3+Uz{QGMcWJRxs$WR-m2vl_sjwCX>$oNgLCeF zg?H9oUoa_88~h%(Gk8HLWIsmnY?g)(D(}6aXSDMD5(#HKdd8O3ZgO|?5-(>3I6QGh zosS+9^H=bnoZRq9k4Zch!kEO4(+tvwsd=%`YL)_TZES-0ig$(!~rN(0KN?QNcIXQ62sF{(az{n}7X@0aM+CDMB=u@4@< z<~b;daeH8IP2}#bl|)+fyG@As7{gn^coq@8Nr-FnWx4lUXfKtdRb^Tq-RzQOG^Bx7b~8(L2U9|W*#z(twRE*&FkwstzuRUQ)def# zV{*)|FA4GbWdSi7_#T8X5{%73>EWZm7b=iV1OgU949xmyA}~SVlC-) z9J8i=S0Hqm`@rUqg8pnPvzTnqPf#itk#7(Eyx>XL7xOBnE3kqv$f}eMfQ+v9SFLMR zEp6+j>@s)0Q@@pP4Vvj^L)N(H79m|k*Umb%$8y>NLj_@k9WC);IUR!7$zn1>E3fM! zOaQ+<-t9mS+^vT|8!Xky{*<#?JVjn&6KWhQqs7)n=q45Jj8mFs&Y;vG;(1RBJDdo_ zZzUBt_l`<^B>%n(l&$ebodPS*7G7ny_A_$Xh*Ov5wlrY_}^?z z9)V^m5Da5ks5cJC%&BAbP18`*EPvHhkA_kWSQwF@IT@RXnPyNc(3Y);v%>_vTobm0 zNa_P`n9K$655?}IkrikZEW&Uz)#Di_|1=!eVm3GnSWbA!HNxKS`>_~L09?IoVbcW^ z4*U>IfzPUoPro|MOt91oLd1VvNI;;{B_R(PImE|8bZwP`42b=Y?=K7*FKznPV`qrv za15}+RF-a|Yn0mt1>WHsZpKVx<+~e#;M~$e9%Bc;<3Pd>G(^Pn|K>ViQSG+-Z{D4d zjr@?iB2q{&vIiycn$yb;4`TWC9~d&P9UmF;8M;8FeY~?q3XL$HVg`NwF&pI}bWg*d z&i!*Rq&Wrja(A(x7%262i(Z$D-H!R_&>2p@SR%Fi>6UKEnc-+)lE>9ZD+RUQa(jP` z=Y20nd`~|dn1OM6#5*m*NBgv0`NFRL>4;y-Or#c_$ltEro(PUyfT2oPEI0FPGq=c> z_#8p22$_*rn?iPW#CfuIU6Oh_9!+P{#Q>+*^JF1?Z~l{RGrHe(RwCR~Mt1u|b_Y + + + + A Simple Scene Graph API for OpenGL. + + + +

How to write plib-loaders and writers

+ +

1.1 Introduction

+ +This page is intended for those who wish to write loaders and writers +for plib. If you just use plib (using the included loaders and writers), you don't +really need to read this. + +It is quite easy to adapt 3D-file-loaders and writers to be used by ssg. Plib +already has many loaders and writers that may be used as examples.

+ +A brief note on cross platform compilation: Plib comes with +both Makefiles (for Unix and for the CygWin-system under Windows) +and workspace/project-files for Micro$oft Visual C++. If you +have both, please update, test and commit both. +Otherwise, when committing, please tell the people that something is +missing (for example: you didn't update the workspace files) and that some +kind soul should do so. +

+ +Both loaders and writers convert between ssg's internal geometry +representation and that of the file format. One key difference is that when +loading, you should support all possibile +geometry-representations of the file format. This is to ensure that +plib can handle all the possible variations of a file format that may be generated +by the tools that export to it. On the other hand, when writing a format, you can +pretty safely only write to one geometry format (unless you need features peculiar +to more than one geometry representation), because you have the final say on how the +data is to be written. +

+ +Regarding plib's geometry-representations: there are only two on the highest +level: ssgVtxTable and ssgVtxArray. Actually, there is also +ssgVTable, but that is deprecated. + +ssgVtxArray is newer, is derived +from ssgVtxTable and uses a index-list. Apart from that they are quite +similar, and both have an interface getNumTriangles () ; and +getTriangle ( i, ...); +For this reasons, it is easier to write a writer than a loader. + + +For both ssgVtxTable and ssgVtxArray you need to choose a +GL-type. Currently ssgLoaderWriterMesh (more on this in the +next section) uses GL_TRIANGLES. + + +

1.2 class ssgLoaderWriterMesh - an overview

+ +The two main parts of writing a loader are writing the actual parser (coding +the syntax of the format) and transferring the contents +to ssg. The second task is fairly trivial if your contents +obey the restrictions of ssg (which follow from OpenGL). +

+These are: +

+ +1. Each ssg-node can (currently) have only one texture. +
+2. Only one polygon or strip or fan per node. So you can't have a 3-, a 4- and a 5-sided +poly inside one node (without subdividing the polys into triangles) +
+3. Currently, you may have only one texture coordinate per vertex. +
+4. You may have only one normal per vertex. +

+To modularize the two steps parsing the format and transfering it to ssg +and to reduce redundant work, there is an intermediatory structure, the class +ssgLoaderWriterMesh. For example, this has a member function +
+void ssgLoaderWriterMesh::addFaceFromIntegerArray( int numVertices, int *vertices );
+
+With several calls, you can add several n-sided polys to one mesh ("one node"). +When you are done constructing the mesh from the file, you call +
+void ssgLoaderWriterMesh::addToSSG(
+		class ssgSimpleState *currentState,
+		class ssgLoaderOptions* current_options,
+		class ssgBranch *curr_branch_)
+
+and the class adds the information into the scene graph. It handles +ssgs' restrictions. For example, if the polygons of the mesh use +5 textures then at least 5 nodes will be added to the scene graph.

+ +Unfortunately, this class isn't completely finished. As of this +writing, Wolfram Kuss (w_kuss@rz-online.de) has implemented those +parts that were needed for the loaders he has finished thus far. Hopefully +people will contribute more features as time goes on.

+ +If you are writing a new loader for a file format that doesn't hold to +all restrictions of ssg (and virtually none do), you are urged to use this class. +Your loader will be more consistent and easier to maintain and read.

+ +Further, there are many optimizations that can be done (For example: "If the state is +different, but not the texture, do we need several nodes?" or "When we have multitexturing, +can we use that?" or "Is there an optimal strip length?" or "How do I subdivide polys into +triangles so that the stripifier will work well?"-- the list goes on +forever). Once we have good answers to these questions (and the will +to implement them), it will be easier to do them once in the ssgLoaderWriterMesh than in all the +loaders seperately. It is noteable that most loaders written before +ssgLoaderWriterMesh have had some sort of intermediatory mesh +structure.

+ +In the future, ssgLoaderWriterMesh should also be used for writers, doing the opposite +job: It takes the information from ssg with the restrictions and then looks whether +it can optimize (for example merging nodes) by relaxing the restrictions. + +

1.3 The Class ssgLoaderWriterMesh - A Deeper Look

+At the start of your loader, you create a new ssgLoaderWriterMesh +or do a reInit(). To insert the data into the ssgLoaderWriterMesh, +you have to add vertices, faces, materials, materialindexes (saying what face uses what material) +and, if applicable texture coordinates. For all of these, you can say in advance how many you have. +If you know that you have 3712 vertices, call createVertices(3712) and everything is allocated +at once and addVertex will be very fast. If you don't know in advance how many you have, you +still have to call createVertices(), as this also allocates the vertices. In this case, a +certain amount of vertices will be reserved and the list will dynamically grow as more are added.

+ +Vertices are simply sgVec3s. Faces are simply lists/arrays of vertex indexes. +For adding faces, use addFace if you already have a ssgIndexArray or use +addFaceFromIntegerArray if you have the vertex indexes in a C(++) array. +You need to add at least one material (ssgSimpleState). For each face, you tell +ssg which material to use via addMaterialIndex. Here is code from ssgLoadOFF, which tells +ssg to use the ssgSimpleState ss for all faces: + +
+

+
+	theMesh.createMaterials( 1 );
+	theMesh.addMaterial( &ss );
+	theMesh.createMaterialIndices( _ssgNoFacesToRead ) ;
+	for(i=0;i<_ssgNoFacesToRead ;i++)
+	   theMesh.addMaterialIndex ( 0 ) ;
+
+
+If the file format has texture coordinates, you have to find out whether it has them +per vertex or per vertex and face. If you look at a "straight forward" cube, +having texture coordinates per vertex means you can have 8, but if you have +texture coordinates per vertex and face, you can have 24 (each vertex is part of 3 faces). +The two functions corresponding to these cases are: +
+       void createPerFaceAndVertexTextureCoordinates2( int numReservedTextureCoordinate2Lists = 3 );
+       void addPerFaceAndVertexTextureCoordinate2( ssgTexCoordArray **textureCoordinateArray ); 
+
+or +
+        void createPerVertexTextureCoordinates2( int numReservedTextureCoordinates2 = 3 ); 
+        void addPerVertexTextureCoordinate2( sgVec2 textureCoordinate ); 
+
+ + +

2. Loaders

+ + +

2.1 class ssgLoaderOptions

+ +ssgLoaderOptions is a class that is defined in ssg.h. +It is used to tell the loader some options. +It is NOT used for user-setable options, although +this may be nice to have at some point. For example, +one COULD create a member-variable in it +telling the unit that one wants. The loader would then +be responsible to scale the object in such a way that +the sizes are in that unit (for example: meter, millimeter, etc). + +
+
+Regarding the reason for the callbacks in ssgLoaderOptions, Steve +wrote: +
+
+
+"Whenever a branch node is created. The deal is that most file formats +are +missing important features at the Branch level - but many support +comment +fields - or long ASCII name strings or something. The idea was to allow the artists to attach an ARBITARY comment string +in their modeller - and to have the loader trap these strings and pass +them on to the application.

+ +Hence, if the hook function is defined then when a branch node needs +to +be created, we call the application's callback with the ASCII string +that was embedded in the file and let the application construct the +ssgBranch +node. Hence, you could put the string "~LOD: RANGE=100 meters" +into the comment field in (say) the AC3D modeller. (AC3D calls this a +"Data" +field)...the application could then say to itself: "Any comment that +starts +with a tilde ('~') is a command to the loader" and parse such +'comments' +as commands. In this case, it would construct an ssgRangeSelector and +set +the transition range to 100m and return the application back to the +loader.

+ +Check the Tux Kart sources to see this in action." +

+ +So much for the quote from Steve. +

+ +As of this writing, the ssgLoaderOptions code has been copied from +another loader into ssgLoaderWriterMesh. + + +

2.2 ASCII file formats

+A lexical analyzer for ascii-files is available in ssgParser.cxx and +ssgParser.h. It converts the file into a stream of tokens and +handles comments. In a way, it has two APIs. +One hides the line structure from the loader. The loader just has +to say "getNextToken". +The other API operates in terms of exact line structure. +You do a getLine which reads all the tokens of that line into a +buffer and then you do a parseToken to get each token.

+ +The formats which currently use the parser are .X (which uses the +line-independant API), .ase, .scenery and .off (which use the +line-by-line-API).

+Some functions are used by both APIs. For example:

+ +

+	void openFile( const char* fname, const _ssgParserSpec* spec = 0 );
+

+ +In ssgParserSpec, you give the parser the specification of the format. +You say which characters start a comment, which characters are skipable, +which characters are used for braces (which are used to determine the +parser's level-- useful for parser's which work recursively). +Most important are the delimiters. These determine where one token ends and the +next one begins. For example, the first token of the line + +

+1234,567
+
+ +is 1234 if "," is a delimiter and +1234,567 otherwise. +The parser differentiates between skipable delimiters that +are "swallowed" by the parser and non-skipable ones that are +passed to the loader. So, regarding the example-line there +are three possibilities: +
+"," is not a delimiter => The line contains one token, namely +"1234,567" +
+"," is a skipable delimiter => The line contains two tokens, namely +"1234" and "567" +
+"," is a non-skipable delimiter => The line contains three tokens, +namely "1234", "," and "567" +
+ + +

3. Writers

+For an example how to write out geometry and material, look at +ssgSaveASE. + +For an easy example (geometry only), look at ssgSaveDXF or ssgSaveTRI. +The function + +
+int ssgSaveXYZ ( const char *filename, ssgEntity *ent )
+
+ +normally calls a function + +
+static void save_entities ( ssgEntity *e )
+
+ +which just recursively walks the scene graph. +You should be able to use this function and just +write a + +
+static void save_vtx_table ( ssgVtxTable *vt )
+
+ +which writes a ssgVtxTable.

+ + + +


+ + + + + + +
<= previous =Return to SSG Index
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/branches.html b/doc/ssg/branches.html new file mode 100644 index 0000000..bb21cea --- /dev/null +++ b/doc/ssg/branches.html @@ -0,0 +1,319 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

class ssgBranch - A basic branch node.

+The basic ssgBranch node simply handles a node in the tree, +with zero or more child nodes which can be any kind of ssgEntity +except ssgRoot. +

+There are a rich set of functions for adding, deleting and +replacing child nodes: +

+
+class ssgBranch : public ssgEntity
+{
+  int        getNumKids    (void) ;
+  ssgEntity *getKid        ( int n ) ;
+  ssgEntity *getNextKid    (void) ;
+  int        searchForKid  ( ssgEntity *entity ) ;
+  void       addKid        ( ssgEntity *entity ) ;
+  void       removeKid     ( int n ) ;
+  void       removeKid     ( ssgEntity *entity ) ;
+  void       removeAllKids (void) ;
+  void       replaceKid    ( ssgEntity *old_entity, ssgEntity *new_entity ) ;
+} ;
+
+
+Most of these are pretty self-explanatory. +ssgBranch::getNumKids() returns the number of child nodes +beneath this branch. +ssgBranch::getKid(n) returns the address of the n'th child ssgEntity. +ssgBranch::getNextKid() returns the address of the child entity following +the last one returned by getKid or getNextKid - returning NULL when all child nodes +have been exhausted. +ssgBranch::searchForKid(entity) searches for the specified entity +in the list of child nodes and returns it's index (ie the inverse of getKid). +ssgBranch::addKid(entity) adds the specified entity to the list of +child nodes - the new node is added at the end of the list and will therefore +have the highest numbered index. +ssgBranch::removeKid(n) removes the n'th child node and renumbers +any higher numbered children so there are never any gaps in the number range. +ssgBranch::removeKid(entity) same as removeKid(searchForKid(entity)). +ssgBranch::removeAllKids() remove ALL child entities. +ssgBranch::replaceKid(old, new) replaces old with new. +If the entity removed by any of these commands has a ref count of +zero, it will be deleted. + +

class ssgInvisible - Invisible parts of a Scene Graph.

+It's sometimes useful to have sections of the scene graph that are never +rendered to the screen. These are frequently used for collision detection +and other non-graphical operations. + +

class ssgRoot - The Root of the Scene Graph,

+The node at the root of the scene graph is special. At present, +it resembles an ssgBranch externally. +
+
+class ssgRoot : public ssgBranch
+{
+} ;
+
+
+ +

class ssgTweenController - A morph controller,

+This is essentially identical to an ssgBranch - but adds API to +set the current 'bank' settings of all ssgTween leaf nodes beneath it. +

+An ssgTween is a leaf node that can hold multiple geometric +representations that it smoothly interpolates between. +

+
+  ssgTweenController::selectBank ( float b ) ;
+  float ssgTweenController::getCurrBank () ;
+
+
+This allows you to set which of the banks of it's daughter ssgTweens +will be rendered. That's a "float" quantity - so if you selectBank(2.4) +then the ssgTweens will render their vertices in a position (colour, etc) +that's 40% of the way between the bank 2 and bank 3. +

+You can also set the behaviour when the selected bank number is larger +than the number of banks in the model. This tends to happen when you +simply add some amount of to the bank selector each frame in order +to keep the animation running forever: +

+
+  ssgTweenController::setMode ( int mode ) ;
+
+
+'mode' can either be SSGTWEEN_STOP_AT_END or SSGTWEEN_REPEAT. +The default is STOP_AT_END - where anytime the bank selector +is larger than the actual number of banks in the ssgTween node, +it is clamped to the largest possible number. If you choose +REPEAT, then the bank number will be taken modulo the number of +banks. +

class ssgSelector - A switch point,

+Most ssgBranch nodes represent a collection of objects that +are all present in the scene at the same time. ssgSelector +nodes (and derived classes) typically represent a single +object that can be represented in more than one way. +

+A selector contains up to 32 daughter objects and a +32 bit unsigned integer mask. Where there is a one bit +in the mask, that child object will be drawn, where +there is a zero, it will not. +

+ssgSelector::select(mask) sets the mask, +ssgSelector::getSelect() returns the current +state of the mask, ssgSelector::selectStep(n) +sets the n'th mask bit and zeroes out all the others - effectively +causing only the n'th child object to be displayed. +

+It is quite common to have an ssgSelector with just one +child object that can be enabled with select(1) and disabled +with select(0). + +

class ssgTimedSelector - An animation node,

+This is a selector in which the selection is made as a function +of the amount of time elapsed. +

+SSG will draw each of the child objects in turn for some amount +of time before going onto the next node in the sequence. You +set the time for each child using: +

+
+   ssgTimedSelector::setDuration ( float time, int which_child ) ;
+
+
+...or you can set the same time for each of the child nodes using: +
+
+   ssgTimedSelector::setDuration ( float time ) ;
+
+
+(At present, times are measured in SSG update cycles - ultimately, +there will be an option to set the times in seconds - but until +I have a reasonably accurate portable timer library, I can't +easily implement this). +

+The animation doesn't have to start at the first child node and +end at the last. It is sometimes useful to be able to replay +just a subset of them. +

+
+   ssgTimedSelector::setLimits ( int start_child, int end_child ) ;
+
+
+You can also choose between a number of animation algorithms: +
+
+   ssgTimedSelector::setMode ( ssgAnimDirection mode ) ;
+
+     Where mode is one of:
+
+       SSG_ANIM_ONESHOT, SSG_ANIM_SHUTTLE, SSG_ANIM_SWING
+
+
+
    +
  • In SSG_ANIM_ONESHOT mode, the animation starts at the first child + you specified with setLimits and ends at the last child you + specified. Once the last child has been displayed, the animation + will automatically go into STOP mode and continue to display + that last object until the application intervenes. +
  • In SSG_ANIM_SHUTTLE mode, the animation goes from the start + child to the end child - but unlike SSG_ANIM_ONESHOT, when + the animation has finished displaying the last child object, + it resets to the start child and does it all over again. + SSG_ANIM_SHUTTLE animations don't stop running unless the + application stops them. +
  • In SSG_ANIM_SWING mode, the animation goes from the start + child to the end child - but when the end is reached, the + animation reverses direction and heads back to the start + again. The animation oscillates like a swing until the + application stops it. +
+

+When all this preparation is done, the application must +control the animation: +

+
+    ssgTimedSelector::control ( ssgAnimEnum cntrl ) ;
+
+    Where 'cntrl' is one of:
+
+       SSG_ANIM_START, SSG_ANIM_STOP,
+       SSG_ANIM_PAUSE, SSG_ANIM_RESUME
+
+
+These controls work just like you'd expect. Start, Stop, +Pause, Resume. SSG_ANIM_START resets the animation to +the 'start' child and lets it rip. SSG_ANIM_STOP causes +it to stop wherever it is and continue to display that +child node indefinitely. SSG_ANIM_PAUSE pauses the +animation wherever it is right now and SSG_ANIM_RESUME +allows it to continue from whatever point it was paused. +

+When the animation is in SSG_ANIM_STOP mode, you can +use this node just like a normal ssgSelector node +using the ssgSelector API. +

+If you need to know which child object is currently being +displayed, you cannot call ssgSelector::getSelect() because +SSG computes the animation step only if the node is actually +on-screen. Instead call ssgTimedSelector::getStep() which +returns the currently selected child node. +

class ssgRangeSelector - A level of detail node,

+This is a selector in which the selection is made automatically +based on the range to the object. This is principally used to +allow you to save time by drawing simpler versions of objects +at long ranges and more complex ones close up. +

+Since an ssgRangeSelector is a kind of +ssgSelector, you can find +out which version of the object was most recently drawn using +getSelect(). However, if the object was not drawn recently, that +may not be a very useful thing to know. Mostly, it's useful in +the post-cull callback. +

+You set the ranges at which each daughter object will be drawn +by passing an array of lengths to +ssgRangeSelector::setRanges(float *ranges,int nranges). +The first parameter is an array of ranges and the second is the +number of elements in that array. Note that element N of the +array is the range beyond which child object N will be drawn. +The array should contain one more range than there are child +objects - and beyond the last range, nothing will be drawn. +setRanges takes a copy of your array so you can delete it after +the call. If nranges is less than the number required, the +remaining ranges will be set to infinity. You can query the +current range array with float getRange(int n) which +returns the n'th range in the array. +

+In some cases, you'd like to build several complete versions +of an object such that just one of those versions will be +drawn - and that is the default behaviour. In other cases, +you'd like some basic object to be rendered at long range +and for additional parts to be added to it at shorter ranges. +This is called 'additive' mode and it is set using +setAdditive(int additive) +and queried with isAdditive(). +

class ssgBaseTransform - Nodes with transformations.

+It is common to wish to move objects around in the scene, scale and rotate +them, move their texture maps, etc. All of these operations entail +manipulating a matrix associated with the branch node and the +ssgBaseTransform contains the functionality to store and manipulate +that matrix. Applications use one of the derived classes of ssgBaseTransform +to actually do something with that matrix. +
+
+class ssgBaseTransform : ssgBranch
+{
+  void getTransform ( sgMat4 xform ) ;
+  virtual void setTransform ( sgVec3 xyz ) ;
+  virtual void setTransform ( sgCoord *xform ) ;
+  virtual void setTransform ( sgCoord *xform, float sx, float sy, float sz ) ;
+  virtual void setTransform ( sgMat4 xform ) ;
+} ;
+
+
+You can set up the transformation matrix using ssgBaseTransform::setTransform() +which has versions that allow you to pass either a full-blown +4x4 matrix, a simple translation, an 'sgCoord' (which is an xyz translation +and a hpr rotation) or an sgCoord and scale factors in each of X, Y and Z +directions. +

+ssgBaseTransform::getTransform(matrix) copies the current +transform into the matrix that you provide. +

class ssgTransform - Nodes with spatial transformations.

+An ssgTransform is derived from ssgBaseTransform and uses the base classes' +transform to transform all the spatial vertices and normals of it's +child nodes. This is done by applying the current transform to the +GL_MODELVIEW stack each frame. +

class ssgTexTrans - Nodes with moving texture

+ssgTexTrans nodes are just like ssgTransform nodes except that the +resulting matrix is applied to the GL_TEXTURE stack rather than +the modelview stack. Hence, altering the transform moves the texture +map(s) on the descendent leaf nodes. +

class ssgCutout - turn-to-face-the-viewer nodes.

+Cutout nodes will normally contain only leaf nodes that are +modelled with polygons in the X/Z plane. The ssgCutout will rotate +those polygons such that they turn to continually face the viewer. +

+There are actually two distinct forms of ssgCutout - depending on +what value is passed as a parameter to the constructor function. +ssgCutout(TRUE) produces an object that rotates around it's +origin such as to keep the X/Z plane parallel to the screen and +ssgCutout(FALSE) produces one that tries to stay parallel to the +screen - but which is only allowed to rotate about the Z axis. +The latter form is useful for objects with cylindrical symmetry +and the former for those with spherical symmetry. + +


+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/index.html b/doc/ssg/index.html new file mode 100644 index 0000000..b90657a --- /dev/null +++ b/doc/ssg/index.html @@ -0,0 +1,157 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +
+

SSG: A Simple Scene Graph API

+

for OpenGL

+ by Steve Baker +
+

Introduction

+Simple Scene Graph (SSG) is intended to be a really simple, +low-impact, scene graph API that layers nicely on top +of OpenGL using C++ and which works with or without GLUT. +

+SSG is a part of PLIB. +

+This document assumes a certain degree of knowledge of +OpenGL. +

+SSG includes a subsidiary library of simple matix and vector +math with support for some intersection testing, field-of-view +culling and such like. This is called +'Simple Geometry' (SG). +SG is used extensively by SSG - but is also useful as a +standalone library. +

+A Scene Graph is essentially just a tree-structured database +containing a hierarchy of branches - and a bunch of leaf nodes. +Each leaf node does some OpenGL rendering - the branch nodes +are intended to manage things like: field of view (FOV) culling, +level of detail (LOD) management, transformations, and animation. +

+In addition, each leaf node has a structure tacked on to it +to encapsulate OpenGL state information - and that in turn +may optionally have a texture applied to it. +

+In addition to managing the scene graph, SSG contains code +to manage the positions of cameras, lights and other rendering +aspects of OpenGL. +

Symbol Conventions.

+Both SSG an SG follow conventions for symbols and tokens that +are the conventions used by OpenGL and GLUT. +

+Hence, all SSG symbols for classes and functions start with ssg +and all #define tokens start with SSG. Functions and symbols +that belong to the SG library similarly start with sg or SG. +

+Words within a class or function name are Capitalised and NOT +separated with underscores. Words within #define tokens may +be separated with underscores to make them readable. +

Initialisation.

+The first SSG call in any program must always be ssgInit(). Call +ssgInit only after you have obtained an OpenGL rendering context +(or called glutInit() and created a rendering window if you are +using glut). +

Classes

+The following class hierarchy makes up the core package - which +can be extended to add functionality or to change some underlying +mechanisms. +
+
+   class ssgBase
+    |__ class ssgSimpleList
+    |    |
+    |    |__ class ssgVertexArray
+    |    |__ class ssgNormalArray
+    |    |__ class ssgTexCoordArray
+    |    |__ class ssgColourArray
+    |    |__ class ssgIndexArray
+    |
+    |__ class ssgEntity
+    |    |
+    |    |__ class ssgLeaf
+    |    |    |__ class ssgVTable (deprecated)
+    |    |    |__ class ssgVtxTable
+    |    |          |__ class ssgTween
+    |    |          |__ class ssgVtxArray
+    |    |
+    |    |__ class ssgBranch
+    |         |__ class ssgRoot
+    |         |__ class ssgInvisible
+    |         |__ class ssgSelector
+    |         |    |__ class ssgTimedSelector
+    |         |    |__ class ssgRangeSelector
+    |         |
+    |         |__ class ssgBaseTransform
+    |         |    |__ class ssgTransform
+    |         |    |__ class ssgTexTrans
+    |         |
+    |         |__ class ssgCutout
+    |         |__ class ssgTweenController
+    |
+    |___ class ssgState
+    |     |__ class ssgSimpleState
+    |         |__ class ssgStateSelector
+    |
+    |___ class ssgTexture
+
+
+The general idea is that, all geometry is contained in ssgLeaf classes, +all data heirarchy is in a ssgBranch classes and all OpenGL state information +is in ssgStates. +

+You may not declare instances of ssgBase, ssgEntity, ssgBaseTransform, +ssgLeaf or ssgState since they are all abstract classes. +

+It is presumed that applications will add new kinds of leaves, branches, +states and textures to customise SSG to their needs. +

+Here are the further chapters: +

+The base class +
+An entity of the graph +
+A leaf of the graph +
+A branch of the graph +
+States (material etc) +
+ssgContext +
+Non-class functions: Loading, saving and optimizing databases +
+How to write plib-loaders and writers +

+You can go through all the SSG-documentation by clicking on the "= next =>"-links at the bottom of each page. +

+


+ + + + + + +
= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/non_class.html b/doc/ssg/non_class.html new file mode 100644 index 0000000..f4de9ae --- /dev/null +++ b/doc/ssg/non_class.html @@ -0,0 +1,936 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

Non-class Functions.

+So, with the class functions described above, it is +fairly simple to construct a scene graph. So, now that +you have one, what can you do with it? +
+  void ssgCullAndDraw ( ssgRoot *root ) ;
+
+This call deals with the entire process of rendering the database. +Your application need only call ssgInit(), build a database, +create a current context and call ssgCullAndDraw using the root node +of that database. +

Intersection Testing.

+Most applications need to test the scenery to see if moving +objects have collided with it - there are several ways to do +that - but they all share the same mechanisms: +
+
+int  ssgIsect ( ssgRoot *root, sgSphere *s, sgMat4 m, ssgHit **results ) ;
+int  ssgHOT   ( ssgRoot *root, sgVec3    s, sgMat4 m, ssgHit **results ) ;
+int  ssgLOS   ( ssgRoot *root, sgVec3    s, sgMat4 m, ssgHit **results ) ;
+
+
+These three calls implement various ways to test the database for collisions, +weapon impacts and such like. In each case, the search for a collision +starts at 'root', and the database is transformed by the matrix 'm' +before the test is evaluated - hence, 'm' is ususally the inverse of +the matrix describing the test object's location. +
    +
  • ssgIsect intersects a sphere against the scene. +
  • ssgHOT intersects a vertical line starting at the point 's' (remember, +in SSG, positive Z is 'up'). This is often used to compute +the Height-of-Terrain - hence the name 'HOT'. +
  • ssgLOS intersects an arbitary vector whose direction is defined by 's'. +
+The result in either case is an integer telling +you how many triangles impacted the sphere/vector. If you need to know more +about these intersections, pass the address of a ssgHit * variable as +the last parameter and it will be returned pointing at a STATIC array +of ssgHit structures. Thats a confusing explanation - and an example +will help: +
+
+ie:
+
+   ssgHit *results ;
+
+   int num_hits = ssgIsect ( root, &sphere, mat, &results ) ;
+
+   for ( int i = 0 ; i < num_hits ; i++ )
+   {
+     ssgHit *h = &(results [ i ]) ;
+
+     /* Do something with 'h' */
+   }
+
+
+Remember, you must finish using the results array before you do +another ssgIsect/ssgHOT/ssgLOS because all three functions share +the same results array. +

+By default, these functions only detect collisions with the front face +of polygons. You can make them detect only the back faces using: +

+
+  void ssgSetBackFaceCollisions ( bool b ) ;
+
+
+

+An ssgHit looks like this: +

+
+class ssgHit
+{
+  ssgLeaf *leaf ;
+  int      triangle ;
+  sgVec4   plane ;
+  sgMat4   matrix ;
+
+  ssgHit ()
+  int getNumPathEntries () ;
+  ssgEntity *getPathEntry ( int i ) ;
+} ;
+
+
+The 'leaf' member points at the leaf node that impacted the sphere. +The 'triangle' member tells you which triangle within the leaf +did the impacting. The 'plane' member contains the plane equation +of the impacting triangle and the 'matrix' element tells you +the net result of concatenating all the transform nodes from +the root to the leaf to the matrix you provided in the ssgIsect call. +

+It's possible for there to be multiple paths through the +scene graph to the leaf node. Sometimes you'll need to +look back up the tree to see nodes above the one that we actually +impacted with. Hence, you can read all the ssgEntities that were +traversed on the path from the root down to the leaf. Calling +the 'getNumPathEntries' function to find the number of nodes +along the path - and then 'getPathEntry(n)' to get the n'th entry +in the path. The 'root' node will always be the zeroth entry +in the path - and the leaf node will always be the last. +

Lights.

+SSG supports the eight standard OpenGL light sources as class 'ssgLight'. +Since there are only a finite number of these, they all exist all the +time - you just call: +
+
+  ssgLight *ssgGetLight ( int i ) ;
+
+
+...to get the i'th light should you need to manipulate it. +
+
+class ssgLight
+{
+  int isOn () ;
+  void on  () ;
+  void off () ;
+
+  void setPosition ( const sgVec3 pos ) ;
+  void setPosition ( float x, float y, float z ) ;
+
+  void setColour ( GLenum which , const sgVec4 colour ) ;
+  void setColour ( GLenum which , float r, float g, float b ) ;
+
+  void setHeadlight ( int head ) ;
+  int  isHeadlight () ;
+
+  void setSpotlight ( int spot ) ;
+  int  isSpotlight () ;
+
+  void setSpotDirection ( const sgVec3 dir ) ;
+  void setSpotDirection ( float x, float y, float z ) ;
+  void setSpotDiffusion ( float exponent, float cutoff = 90.0f ) ;
+  void setSpotAttenuation ( float constant, float linear, float quadratic ) ; 
+} ;
+
+
+Each light can be turned on or off - or tested to see if it's on or off. +

+Lights are positioned with 'setPosition()' - which can be relative +to the origin of the world - or relative to the SSG camera (in 'headlight' +mode). +

+If the 'spotlight' mode is enabled, +then the light intensity has a certain distribution and attenuation. +These parameters can be set with the 'setSpotXxx' methods. +Otherwise, the light source is considered to be directional, +or infinitely far away (in the direction of its position). +The 'spotlight' mode is initially disabled. + +

Miscellany.

+It's convenient to find out how much texture memory has been +consumed: +
+
+  int ssgGetNumTexelsLoaded () ;
+
+
+(Bear in mind that a texel could be 16 or 32 bits depending on +the hardware - and with MIPmapping enabled, 25% of the texels +will be in the MIPmaps - so ssgGetNumTexelsLoaded will return +a larger number than the total of the sizes of the input images +might suggest. +

Loading Database Files.

+To load a model file into SSG, you can either call a loader +function that is specific to the format of the file you +wish to load - or you can call 'ssgLoad' - which parses +the filename extension and calls the appropriate format-specific +loader. +

+At time or writing, there are MANY loaders for SSG: +

    +
  • ssgLoadSSG - for '.ssg' files - the 'native' SSG format. +
  • ssgLoadAC - for '.ac' files - a somewhat obscure format produced + by the 'AC3D' modelling tool - reasonably well tried + and tested. +
  • ssgLoad3ds - for '.3ds' files as produced by 3DStudio. +
  • ssgLoadASE - 3DSMAX ASCII EXPORT Version 2.00, well tested +
  • ssgLoadDXF - AutoCADs famous DXF format. well tested +
  • ssgLoadFLT - OpenFlight. Works for OpenFlight files generated by +recent versions of MultiGen - but not for those generated by some other +tools such as Designers' Workbench. +
  • ssgLoadMD2 - Quake MD2 +
  • ssgLoadOBJ - Wavefront, works well +
  • ssgLoadTRI - simple Tri format from "Andy Colbournes Editor". +
  • ssgLoadX - Microsofts DirectX-Format. Most features work. +
  • ssgLoadOFF - Geomview's OFF +
  • ssgLoadM - ??? +
  • ssgLoadATG - Ascii TerraGear. Used by Flight Gear Flight Sim. +
  • ssgLoadVRML1 - VRML1 format, only partially implemented. +
  • ssgLoadIV - Inventor format, only partially implemented. +
  • ssgLoadStrip - The format of a stripifier. +
+ +
+
+  typedef ssgBranch *(*ssgHookFunc)(char *) ;
+
+  ssgEntity *ssgLoad    ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
+  
+  ssgEntity *ssgLoadSSG ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
+  ssgEntity *ssgLoadAC  ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
+  ssgEntity *ssgLoad3ds ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
+  ...etc...
+
+
+Minimally, all you need to do is to call ssgLoadAC/ssgLoad3ds with the +name of the file to load. However, most file formats (AC3D's +and 3Dstudio's included) lack many desirable features, and it is also often +necessary to store application-specific information in the +file. +

+SSG's loaders will decode the comment fields found in the +nodes of many common file formats and pass these onto the +application via 'hookfunc'. This function should decode +the string and construct whatever kind of SSG node it +considers appropriate. +

+Similarly, the application may wish to embellish the +ssgState of a loaded node - and since state information +rarely has a comment field in most file formats, we +pass the texture filename instead and expect the application +to construct the entire ssgState: +

+
+  void ssgSetAppStateCallback ( ssgState *(*cb)(char *) ) ;
+
+
+One common problem with file loaders is that it's +often possible to refer to a second file from inside +the first - but the path to that file is often +not adequately defined by the original file. Hence, +the application can specify a file path to be prepended +to all model or texture file names. +
+
+  void ssgModelPath   ( char *path ) ;
+  void ssgTexturePath ( char *path ) ;
+
+
+You can only supply one path. If you need additional features, use the +function ulFindFile (for more see util-library-doc). + +The last three functions simply set values in the _ssgCurrentOptions +(type ssgLoaderOptions), for example: + +
+inline void ssgModelPath   ( const char *path )
+{
+  _ssgCurrentOptions -> setModelDir ( path ) ;
+}
+
+ +For more on ssgLoaderOptions see also the next page of this doc. + +

+ +Some loaders for file formats that use texture formats not +supported by ssg use this functions to find textures: +

+void ssgFindOptConvertTexture( char * filepath, char * tfname ) 
+
+It finds and optionally (= if necessary) converts the texture. +This is only really implemented for Windo$ :-(, for all others it +should just be the two lines you will find in the comment. I didn't +test it, that's the reason I commented it out. But it will warn you +when you have to convert something, so even for non-Windo$-users the +new function is already a step forward. +For the actual conversion, at first I wanted to use GIMP. So, in Deja, +I looked for "+gimp +convert +batch" and similar strings. I found +several people asking, but almost all the answer were to use +ImageMagick instead. ImageMagick is free as in beer. Also, the docs I +got with the newest gimp is extremely sparse :-(. Therefore, I tried +ImageMagick and this worked straight away. You should find ImageMagick +under +

+http://www.wizards.dupont.com/cristy/ImageMagick.html +

+Plib uses the ImageMagick application "convert". Since there are other +convert.exe-programs on my computer, I had to copy the ImageMagick +stuff into the directory of my plib-application, so that it uses the correct +convert.exe. +

+ + +Most file formats contain considerable numbers of redundant +nodes (because of the way people build using these tools). +This function walks a database sub-tree multiplying +out any ssgTransform nodes and replacing them with +ssgBranch'ed - unless they have userdata associated with them. +Any branch nodes with zero kids are deleted - any with just +one kid are eliminated and the child node pushed up one level. +

+
+  void ssgFlatten ( ssgEntity *ent ) ;
+
+
+It's important for 3D performance to optimise +triangles into triangle strips or fans. Since most +file formats don't record strip/fan information, +it's useful to call: +
+
+  void ssgStripify ( ssgEntity *ent ) ;
+
+
+

Saving Database Files.

+Most SSG programs will simply load a file and display it in +some way - but occasionally, it's useful to be able to write +a file back out again. +

+To write a model file from SSG, you can either call a writer +function that is specific to the format of the file you +wish to save - or you can call 'ssgSave' - which parses +the filename extension and calls the appropriate format-specific +writer. +

+Saving into ssg-format is done with these functions: +

    +
  • ssgSaveSSG - for '.ssg' files - the 'native' SSG format. +
+
+
+  int ssgSave    ( char *fname, ssgEntity *ent ) ;
+  int ssgSaveSSG ( char *fname, ssgEntity *ent ) ;
+
+
+This returns TRUE if the operation worked - FALSE if it failed. +You will also find writers for the 3DS, AC, ASE, ATG, DXF, M, OBJ, OFF, QHI, TRI and +X formats. + +

Features of the file formats and status of the loaders/writers

+ +Here comes a table of the features of all the file formats and loaders and writers. +VRML doesn't really work, so you won't find it here. +The "QHI" (QHull Input - a format used by a toll to create convex hulls) +writer only writes a point cloud and suppoorts no other features, so I also didn't add it to the table. +BTW, if you want to create convex hulls of existing geometry, you just need a few lines. Look +into PPE (prettypoly editor, on SourceForge as well), into the file ppeCoreFuncs.cxx, +function addConvexHull. +The SSG loaders/writers support all +features of the ssg-lib, so missing features mean they miss in the library (normaly because they miss in OpenGL) +The letters mean: +

+A = feature not in file format +
+B = feature in file format and not implemented, not planned. +
+C = feature in file format and not implemented, but planned. +
+D = feature in file format and partly implemented or not tested. +
+E = feature in file format and implemented. +

+And here are the features with some explanaitions: +

+ +
    +
  • Filled polys - Planar polygons. In the ssg-lib, this is the only primitive you can use to make "solid" looking models. This is a 2D primitive; it has no volume but an area. +
  • # sides - No of sides per poly. This is of course equal to number of vertices per poly. Some formats support only triangles, some +also quads and some support n-sided polys, that is they have no restrictions. +
  • Lines - for example used for high voltage lines in a flight simulator. +This is a 1D primitive; it has no area but a length. +
  • Points - for example used for small lights. +This is a "0D" primitive; it has no length. +
  • Sub objects - The complete model may consist of several sub models. +There are several reasons one wants this, for example to make the structure of the model +clearer to humans, to easily enable humans or programs to move, colour, texture, animate etc +one part, for example one wheel of the complete model. +
  • Hierarchie - You not only have sub objects, but there is a parent-kid relation between some, +for example, the upper arm of a human model may be "under" the body and the lower arm "under" the upper arm etc. +
  • DAG - direct acyclic graph means that the hierarchie may not only be a tree where +each node has exactly one parent (apart from the root, which has zero parents), but that nodes may have more than one parent. +Used for example in cars to have the wheel geometry only once. If the one wheel-node has four parents, this means +it will be rendered four times when the scene is drawn. +
  • Colours - Things can have a colour. Uncoloured objects are always white. +
  • Textures - Texture things with 2D bitmaps. +
  • Texture coordinates - The texture mapping is arbitrary. +
  • Texture coord. per face AND vertex - If a vertex is part of three faces, then this feature +allows it to have three texture coordinates. For example, a cube may have 18 texture coordinates, even if it has much fewer vertices. +
  • Texture per face - each face may have another texture bitmap. Formats (and the ssg lib) that +don't have this have to increase the number of nodes to get this effect. +
  • Transparency - Some polys may be partially or completely transparent. +
  • Animation - There may be many different sorts of animation. +
  • Billboards - geometry that automatically rotates so it always faces the viewer. Normaly flat. +As an example, can be used to model trees in a driving simulator. +
  • LOD - stands for "Level of Detail" and means there can be geometries of varying +complexity for the model, so for example, if it is near the viewer you use many polys +and if it is far away you use few polys so that the impact on rendering speed is low. +
  • Other switches - switch means you can switch the visibility of some nodes on and off. +
  • Ascii or Binary - A stands for Ascii, B for Binary files. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Feature 3DS
load
 AC
load/
save
 ASC
save
 ASE
load/
save
 ATG
load/
save
 DXF
load
 DXF
save
 FLT
load
 M
load/
save
 MD2
load
 OBJ
load
 OBJ
save
 OFF
load/
save
 SSG
load/
save
 Strip
load
 TRI
load/
save
 X
load
 IV
load
 VRML1
load
 Filled polys E E E E E E E(5) E E/D ? E E E E E  E E E
 # sides  n/3 3 3 n 3,4(4) 3 n   n? 3 n n(4)  3? n 3,4 3,4
 Lines A D/C C E A(?) E E A A ? E E A? E A  A B B
 Points A A E C A(?) A A B A ? A A A E A  A B B
 Sub objects E E/D E E A(?) A A E A ? C-D C-D A E A  E E E
 Hierarchie A E/D E? E A(?) A A E A ? C C A E A  C E E
 DAG A A A A A(?) A A E A ? A A A E A  A(?) E E
 Colours E E/D E? E ? E E D(1) A ? D C A E A  E B B
 Textures D E/D E E E A A E A ? D C B E A  E E E
 Texture coord. D E E E E A A E A ? E C B E A  E E E
 Texture coord.
per face
AND vertex
 D(?) D(?) A E E A A A A ? E C B(?) A A  C E E
 Texture per face  D(?)/- C A E A A E A ? A A B(?) A A  C B A
 Transparency D D D D A(?) A A E A ? A? A? B(?) E A  E B B
 Animation B A A E A A A B(2) A ? A? A? A E A  B or C B A
 Billboards  A A A A A A E A ? A A A E A  A B A
 LOD  A A A A A A E A ? A A A E A  A B B
 Other switches  A A A A A A D A ? A A A E A  A D D
 A=Ascii or
B=Binary
 B A A A A A A B A B A A A B ? A A+B(3) A A
+footnotes: +

+(1) problematic for ancient files +
+(2) DOFs are not implemented +
+(3) There is an ascii- and a binary X-file-format. +I only implemented the ascii-one and probably wont implement the +binary one. There is a free (as in beer) converter by Microsoft +running under Windo$. +
+(4) It is possible to have polys with an arbitrary number of vertices in ssg. +But then you may only use one poly per node. Also, you can not mix 3 and 4 sided polys in one node. +
+(5) Some OpenGL modes, like for example Quads, are ignored. This means some parts of the model +may dissappear when saving into DXF files. +

+Additional info on some formats: + +

+ATG: +
+ATG stands for ascii TerraGear. Loading of TriStrips and TriFans not implemented yet. +Untextured parts are lost, for example when you save and load +Steve Bakers Tuxedo, you will loose the feet, since they are not +textured. +The files written are not optimal, they use no strips/fans, +only have triangles etc. +Also, one vertex that needs several texture coords is written out several +times. + + +

+3ds-writer: +
+I (Per) have one basicly working, but since there seems to be so much else to do +with plib right now, I'm a bit reluctant to put my energy into it. Also, the +only 3ds-loader I've tested files written by it with is the one in ssg :-) + + +

+OFF loader: +
+Warning: There are two formats called OFF! We support the OFF from GeomView, +not the one from DEC. +We support 2D and 3D, but no higher dimensions. + +

+.SSG file loader/writer: +
+We number .SSG file formats so that for example the loaders knows +what to expect. Until now, there is +
+format "0" - Used by plib 1.2.0, 1.3.x +
+format "1" - Will be used by 1.4.0 +
+Currently, we are between "0" and "1". Normally, we want to keep at least our loader +compatible, but the changes from zero to one would bloat the code so much and it seems +.SSG file version zero were only used by people for temporary files, so we decided to make an incompatible break. +If you need to read or write version zero, either get plib from SVN with the date 14.1.2001, or, if you are a Windo$ person, get +prettypoly.sourceforge.net/download/ppewinbin_ssg_version_zero.zip +This allows you to save and load .ssg version zero. +
+One of the new features in format "1" might be worth mentioning. +It is now possible to load and save *any* class derived from ssgBase (notably +the ssgAux node types). +All that is needed for saving to work is an implementation of the virtual save() method. +Loading requires the corresponing load() method, and also means for creating +an instance of the class, which is accomplished +by a call to ssgRegisterType(). +See the ssgAux implementation for example usage; +the convenience function ssgaInit() registers the ssgAux classes. +

+.X file writer: +
+Planned. +

+.X file loader: +
+The .X-entity "Frame" is not yet implemented. +If you convert files into .X with "3D Exploration", you can check the +option "without frames". + +

+VRML1/IV file loaders: +
+These are only partially implemented, and are useful primarily for +mesh-based models (textured or untextured). It would be useful to add +material support to these loaders, as well as explicit normal definition. + +
+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/ssgBase.html b/doc/ssg/ssgBase.html new file mode 100644 index 0000000..bf7667d --- /dev/null +++ b/doc/ssg/ssgBase.html @@ -0,0 +1,233 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

class ssgBase - The Universal Abstract Base Class.

+All significant SSG classes are derived from ssgBase - which offers a type testing +mechanism and a means to print out the tree hierarchy in human-readable form, +or to save/load it to/from disk. +
+
+class ssgBase 
+{
+  void  ref () ;
+  void  deRef () ;
+  int     getRef () ;
+
+  int   isA ( int ty ) ;
+  int   isAKindOf  ( int ty ) ;
+
+  int   getType (void) ;
+  virtual char *getTypeName(void) ;
+
+  ssgBase *getUserData () ;
+  void  setUserData ( ssgBase *user_data ) ;
+
+  void  setName ( char *nm ) ;
+  char *getName () ;
+  const char *getPrintableName () ;
+
+  virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ;
+  virtual int load ( FILE *fd ) ;
+  virtual int save  ( FILE *fd = stderr ) ;
+
+  ssgBase *clone ( int clone_flags ) ;
+}  ;
+
+
+ +

Reference Counting.

+All SSG classes are reference counted - that means that whenever you +connect a node into the scene graph with ssgBranch::addKid(), we +increment its reference count and each time we remove a node from +the graph with ssgBranch::removeKid(), we decrement the count - +and if it's zero, we'll delete the node to recover memory. +

+Sometimes, you need a node to stay in memory even though it may be +be disconnected from the scene graph. You can achieve that by +calling ssgBase::ref() to increment the reference count. +If you later find you don't need that node anymore then you may +ssgBase::deRef() it. If you ssgBase::deRef() +a node to zero, SSG won't automatically delete it - you still need to use +delete to do that. Since such deletions are recursive, +you may delete an entire sub-branch with a single call. +

+Instead of using ssgBase::deRef() directly, you should use +ssgDeRefDelete() which automatically deletes the node if +the reference count drops to zero. +

+You can read the current ref count for a node +using ssgBase::getRef(). +

Names.

+It's often useful to attach an ASCII name to a node in the +scene graph - this is often derived from a name field in +whatever modelling tool was used to create the object. +ssgBase::setName(s) sets the name, +ssgBase::getName() returns it. + +

User Data

+Although one can derive a new C++ class from an SSG class and thereby customise +it's behavior, it's often more convenient to simply attach application-specific +data structures to a basic entity. Two functions are provided: +ssgBase::getUserData() and ssgBase::setUserData(data). +

+Notice that user data is of class ssgBase - which means that user data +can be named, ref-counted - and can in turn have user data of it's own. +This allows user data to be formed into linked lists when multiple +user data items need to be attached to a single node. + +

Destructor Functions

+When an SSG entity is NOT connected into the scene graph in any +way, then the correct way to get rid of it and free up memory +is to call it's destructor function. However, when the entity +is included into the scene graph, you should disconnect it +from the tree and let the reference count mechanism take care +of the cleanup. +

Type Names

+SSG frequently needs to know what kind of an object an ssgBase is. +Since C++ programs may create new classes that inherit from SSG +classes, we provide several functions to make run time type determination +possible. There is an external function for each type that +returns the type token for that type: +
+
+  int ssgTypeBase       () ;
+  int ssgTypeEntity     () ;
+  int ssgTypeLeaf       () ;
+  int ssgTypeVTable     () ;
+  int ssgTypeVtxTable   () ;
+  int ssgTypeDisplayList() ;
+  int ssgTypeBranch     () ;
+  int ssgTypeBaseTransform ();
+  int ssgTypeTransform  () ;
+  int ssgTypeTexTrans   () ;
+  int ssgTypeSelector   () ;
+  int ssgTypeTimedSelector () ;
+  int ssgTypeRangeSelector () ;
+  int ssgTypeRoot       () ;
+  int ssgTypeCutout     () ;
+
+
+Now, you can use the ssgBase::isA(type) or ssgBase::isAKindOf +to test the type of the node. For example, if you want to test +whether a node is a Leaf node or a Branch node, you can do this: +
+
+  if ( mynode -> isAKindOf ( ssgTypeLeaf() ) )
+    printf ( "Leaf node\n" ) ;
+  else
+  if ( mynode -> isAKindOf ( ssgTypeBranch() ) )
+    printf ( "Branch node\n" ) ;
+  else
+    printf ( "Something else\n" ) ;
+
+
+Notice that if you ran that code on (say) an ssgSelector, then it'll +print "Branch node" since the Selector class is derived from the Branch +class. If you wanted to tell if a node was *exactly* a Branch node - +and not from a derived class, then you could use: +
+
+  if ( mynode -> isA ( ssgTypeBranch() ) )
+    printf ( "Branch node\n" ) ;
+
+
+Finally, you can actually read the type of a node - either as a +token (using ssgBase::getType()) or as an ASCII string +(using ssgBase::getTypeName()). The latter is very useful +for debug routines: +
+
+  printf ( "ERROR - something wrong with my '%s' node.\n",
+                    mynode -> getTypeName () ) ;
+
+
+ +

Printing

+It's sometimes useful during debug to print a section of the +scene graph so you can examine it. ssgBase::print(fd, indent, how_much) +does that for you - it prints out the node itself - and anything +connected beneath it in the scene graph. fd is the +file descriptor to print to (defaults to stderr) and +indent is a string that will prefix all output lines +- and is used internally within SSG to make printout of tree +structures more legible by indenting them. +

+The values for the parameter how_much may be 0,1,2,3 or 4 +and determine how much is printed, with 0 meaning little +and 4 meaning much. +
+For how_much = 0, basic information of the branches is printed +
+For 1, additionally there is: basic leaf info, state pointers +
+For 2, additionally there is: states, user data, number of parents, +bSphere +
+For 3, additionally there is: Reference count +
+For 4, additionally there is: contents of vertex-, normal-, colour-, +texCoord- and index-arrays +
+Experience tells that for how_much = 0 you get very little +out put, with 1, 2 and 3 a manageable amount and with 4 a huge amount, +up to 100 MB for a medium sized model. + + +

+It would be unwise +to attempt to parse the output of ssgBase::print into another +program since it is only intended for human consumption and the +format may change dramatically between revisions of SSG. + +

Cloning:

+All classes derived from ssgBase have a member function +ssgBase *clone(int clone_flags) which new's a +new object of that class as a copy of the calling object. +

+The 'clone_flags' is a set of tokens that you 'OR' together to +specify how 'deep' you want the cloning to go. By default +(with clone_flags==0), only the object itself is cloned and +the clone will simply point to the same child structures as +the original object. However, if you OR in 'SSG_CLONE_RECURSIVE', +then all ssgEntities beneath this one will also be cloned. +ORing in SSG_CLONE_GEOMETRY will cause all the per-vertex data +at the leaf nodes to also be cloned. SSG_CLONE_STATE causes +ssgState objects to be cloned also, SSG_CLONE_STATE_RECURSIVE +also causes states pointed to by other states to be copied, +SSG_CLONE_USERDATA causes user data attached to +the original node to be referenced (NOT copied) by the clone - +otherwise, user data for the clone is set to NULL. Finally, +SSG_CLONE_TEXTURE causes texture maps to be replicated also. +

+Most copy operations will typically use either zero, SSG_CLONE_RECURSIVE +or (SSG_CLONE_RECURSIVE | SSG_CLONE_GEOMETRY) + +


+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/ssgContext.html b/doc/ssg/ssgContext.html new file mode 100644 index 0000000..9ab6498 --- /dev/null +++ b/doc/ssg/ssgContext.html @@ -0,0 +1,183 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

ssgContext

+A 'context' is a set of parameters that relate to rendering +one SSG image. It contains the camera parameters and some +default state information. +
+
+  class ssgContext
+  {
+    ssgContext () ;
+    void forceBasicState () { basicState -> force () ; }
+
+    void makeCurrent () ;
+    int  isCurrent () ;
+
+    void overrideTexture   ( int on_off ) ;
+    void overrideCullface  ( int on_off ) ;
+    int  textureOverridden  () { return ovTexture  ; }
+    int  cullfaceOverridden () { return ovCullface ; }
+    void setCullface ( int on_off ) { cullFace = on_off ; }
+    int  cullfaceIsEnabled () { return cullFace ; }
+
+    sgFrustum *getFrustum () { return frustum ; }
+
+    void getNearFar ( float *n, float *f ) ;
+    void getFOV     ( float *w, float *h ) ;
+    void getOrtho   ( float *w, float *h ) ;
+    void setNearFar ( float  n, float  f ) ;
+    void setOrtho   ( float  w, float  h ) ;
+    void setFOV     ( float  w, float  h ) ;
+
+    int  isOrtho () { return orthographic ; }
+
+    void getCameraPosition ( sgVec3 pos ) ;
+    void setCamera ( sgMat4 mat ) ;
+    void setCamera ( sgCoord *coord ) ;
+
+    void loadProjectionMatrix () ;
+    void loadModelviewMatrix  () ;
+
+    void getProjectionMatrix  ( sgMat4 dst ) ;
+    void getModelviewMatrix   ( sgMat4 dst ) ;
+
+    void  clrClipPlane   ( int i ) ;
+    void  setClipPlane   ( int i, sgVec4 plane ) ;
+    int   isSetClipPlane ( int i ) ;
+    float *getClipPlane  ( int i ) ;
+  } ;
+
+
+In all 3D rendering, you need the concept of a virtual camera - or +eyepoint. This is set up in SSG with the following calls: +
+
+  void ssgContext::setFOV    ( float w, float h ) ;
+  void ssgContext::setNearFar( float n, float f ) ;
+  void ssgContext::setCamera ( sgCoord *coord ) ;
+
+
+The setFOV call sets up the vertical and horizontal fields +of view (in degrees), setNearFar sets the near and far +clip planes (in whatever units your model is built in). +Finally, you can position the virtual camera relative to +the database origin using setCamera. +

+Very often, an SSG program contains other non-SSG routines that +are accessing OpenGL state. Even a menu drawn using PUI or an +overlay in raw OpenGL or using FNT will change the current +state of OpenGL. Since SSG does 'lazy' state changes (only +updating those states that need to be changed) - it will be +fooled into no setting some things that some ssgLeaf nodes +really need. +

+This isn't a problem so long as the application doesn't change states +DURING ssgCullAndDraw (by using derived class member functions, callbacks, +etc). If you need to do that then be sure to call forceBasicState() +before you return to SSG. +

+
+   void ssgContext::forceBasicState ( void ) ;
+
+
+

+During testing, you sometimes need to disable texture rendering, +or backface culling: +

+
+  void ssgContext::overrideTexture  ( int on_off ) ;
+  void ssgContext::overrideCullface ( int on_off ) ;
+
+
+The six standard OpenGL user-defined clipping planes +can be setup and managed automatically using these +calls: +
+
+    void  clrClipPlane   ( int i ) ;
+    void  setClipPlane   ( int i, sgVec4 plane ) ;
+    int   isSetClipPlane ( int i ) ;
+    float *getClipPlane  ( int i ) ;
+
+
+setClipPlane takes the number of the plane (0 through 5) and +a plane equation which will be applied at the next cull/draw +cycle. clrClipPlane removes a clip plane. isSetClipPlane inquires +about whether the i'th plane is turned on and getClipPlane returns +the plane that's currently set as the i'th plane. +

+Once you have your context set up as you'd like, you have to +make it become the 'current' context. +

+
+  void ssgContext::makeCurrent () ;
+
+
+There is a non-class function: +
+
+  ssgContext *ssgGetCurrentContext() ;
+
+
+...which returns the current context. +

+The 'ssgContext' class was not present in PLIB 1.0.xx, so (for +backwards compatibility) there is an initial context assigned +inside ssgInit(). In addition to the member functions of ssgContext, +there are a number of equivelent global functions that are +equivelent to ssgGetCurrentContext()->member_function(). These +functions are deprecated for new code: +

+
+  inline void ssgForceBasicState   () ;
+  inline void ssgGetCameraPosition ( sgVec3 pos ) ;
+  inline void ssgOverrideTexture   ( int on_off ) ;
+  inline void ssgOverrideCullface  ( int on_off ) ;
+  inline void ssgGetNearFar        ( float *n, float *f ) ;
+  inline void ssgGetFOV            ( float *w, float *h ) ;
+  inline void ssgSetFOV            ( float w, float h ) ;
+  inline void ssgSetOrtho          ( float w, float h ) ;
+  inline void ssgSetNearFar        ( float n, float f ) ;
+  inline void ssgSetCamera         ( sgMat4 mat ) ;
+  inline void ssgSetCamera         ( sgCoord *coord ) ;
+  inline void ssgLoadProjectionMatrix () ;
+  inline void ssgLoadProjectionMatrix ( sgFrustum *f ) ;
+  inline void ssgGetProjectionMatrix  ( sgMat4 dst ) ;
+  inline void ssgGetModelviewMatrix   ( sgMat4 dst ) ;
+  inline void ssgLoadModelviewMatrix  () ;
+  inline void ssgLoadModelviewMatrix  ( sgMat4 mat ) ;
+
+
+ + +
+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/ssgEntity.html b/doc/ssg/ssgEntity.html new file mode 100644 index 0000000..51d6ffa --- /dev/null +++ b/doc/ssg/ssgEntity.html @@ -0,0 +1,146 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

class ssgEntity - A Node in the Tree.

+All nodes in the SSG scene graph are ssgEntities. +
+
+clas ssgEntity : public ssgBase
+{
+public:
+ 
+  ssgEntity (void) ;
+  virtual ~ssgEntity (void) ;
+  
+  int  getTraversalMask     () ;
+  void setTraversalMask     ( int t ) ;
+  void setTraversalMaskBits ( int t ) ;
+  void clrTraversalMaskBits ( int t ) ;
+
+  virtual void recalcBSphere (void) ;
+  int  isDirtyBSphere (void) ;
+  void dirtyBSphere  () ;
+  sgSphere *getBSphere () ;
+
+  virtual int getNumKids (void) ;
+  int getNumParents () ;
+  ssgBranch *getParent ( int p ) ;
+  ssgBranch *getNextParent () ;
+
+  virtual void cull  ( sgFrustum *f, sgMat4 m, int test_needed ) ;
+  virtual void isect ( sgSphere  *s, sgMat4 m, int test_needed ) ;
+  virtual void hot   ( sgVec3     s, sgMat4 m, int test_needed ) ;
+} ;
+
+
+ +

The tree structure.

+Every entity has a list of parent entities and (conceptually), a list of +child entities ("kids"). In practice, ssgRoot nodes never have parents +and ssgLeaf nodes never have kids. +

+The structure of the scene graph permits the same node to be inserted +into the graph in multiple locations. This is useful for saving +space when the same object is needed many times in the scene. +Hence, any given node may have more than one parent node. +

+You can traverse the list of parent nodes +using ssgEntity::getNumParents() to find out the number +of parents this node has and +ssgEntity::getParent(n) to locate the n'th parent. +

+Alternatively, after calling getParent, you can call getNextParent +to get the N+1'th parent node - it returns NULL when no more parents +are available. +

+As a convenience for general tree-walking routines, there is a +ssgEntity::getNumKids() call - which will always +return zero on leaf nodes. You cannot actually get kid nodes unless +the node is some kind of ssgBranch. + +

Traversals

+Much of the work done on an SSG scene graph entails 'traversing' +the tree structure. This is done most commonly to display the +scene using OpenGL - but is also done when doing intersection +testing and other operations. +

+It's quite useful to be able to limit the traversal so that +certain nodes do not get tested. This can save time - or +prevent undesirable side-effects. +

+Each entity has a 'traveral mask' - which is a simple integer +with one bit per kind of traversal. At present, there are +three kinds of traversal: +

+
+SSGTRAV_CULL   -- Culling to the field of view.
+SSGTRAV_ISECT  -- General intersection testing.
+SSGTRAV_HOT    -- Height-over-terrain testing.
+
+
+You can directly set or get the traversal mask with ssgEntity::setTraversalMask(m) +ssgEntity::getTraversalMask(). You can set an individual traversal bit using +ssgEntity::setTraversalMaskBits(m) or clear one using +ssgEntity::clrTraversalMaskBits(m). + +

Bounding Sphere

+Quite a few graphics algorithms can be accellerated using a +bounding sphere. The standard ssgEntity uses bounding +spheres to do field-of-view and intersection testing. +

+Clearly one does not want to recompute the bounding sphere +every frame - just some objects do change their size over +time. Hence, the bounding sphere is lazily evaluated. +

+Whenever you do something to change the size or shape of an entity, +you should call ssgEntity::dirtyBSphere(). This will +mark this entity's sphere as invalid ("dirty") and also, walk +backwards up the scene graph tree making all the nodes above +this one dirty too. The next time SSG needs to know the bounding +sphere size, it'll recompute it. +

+If you'd prefer for the bounding sphere recalculation to be +done immediately, then you can call ssgEntity::recalcBSphere() +and it will be done immediately. Branch nodes like ssgTransforms will +automatically dirty their bounding spheres when necessary. Leaf nodes +generally do not. +

+When anyone needs to know the bounding sphere size for a node, +they'll call ssgEntity::getBSphere() - which will +recaclulate the Bsphere if it needs to. + +

Culling and Drawing

+The actual tree-traversal, culling and rendering is handled by +a virtual function ssgEntity::cull() - calling +this on the root node in the scene graph causes the entire +scene to be rendered in an efficient manner. + +
+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/ssgLeaf.html b/doc/ssg/ssgLeaf.html new file mode 100644 index 0000000..9c5f08b --- /dev/null +++ b/doc/ssg/ssgLeaf.html @@ -0,0 +1,305 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

class ssgLeaf - Leaf nodes.

+Leaf nodes are those that actually make OpenGL calls +or take other 'rendering' actions - they contain +all of the geometric information in the scene. +
+
+class ssgLeaf : public ssgEntity
+{
+public:
+  int  getExternalPropertyIndex ()
+  int  isTranslucent ()
+  int       hasState ()
+  ssgState *getState ()
+  void      setState ( ssgState *st )
+
+  virtual float *getVertex   ( int i )
+  virtual float *getNormal   ( int i )
+  virtual float *getColour   ( int i )
+  virtual float *getTexCoord ( int i )
+
+  virtual int  getNumTriangles() ;
+  virtual void getTriangle ( int n, short *v1, short *v2, short *v3 )
+
+  virtual int  getNumLines () ;
+  virtual void getLine ( int n, short *v1, short *v2 ) ;
+
+  int  getNumLines () ;
+  void getLine ( int n, short *v1, short *v2 ) ;
+
+  virtual void transform ( sgMat4 m )
+
+  void setCullFace ( int cf )
+  int  getCullFace ()
+
+  void makeDList () ;
+  void deleteDList () ;
+  GLuint getDListIndex () ;
+} ;
+
+
+ +

Pre- and Post-Draw Callbacks.

+It's frequently useful to have an application function called +just before and/or just after a leaf node is rendered. +
+
+  typedef int (*ssgCallback)( ssgEntity * ) ;
+  ssgCallback getCallback ( int which ) ;
+  void setCallback ( int which, ssgCallback cb ) ;
+
+
+(Where 'which' is either SSG_CALLBACK_PREDRAW for a function that's +to be called before the node is drawn or SSG_CALLBACK_POSTDRAW for +one that's called after rendering.) In both cases, the function will +be passed a pointer to the leaf node that's about to be drawn, and +(in the case of PREDRAW callbacks), may return FALSE to prevent the +node from being drawn, or TRUE to have it draw normally. +

Display Lists

+A leaf node is normally rendered in 'immediate' mode in OpenGL, +so that changes you make to the leaf will be reflected on the +screen on the next occasion that it's drawn. However, on some +graphics hardware, it's more efficient to create an OpenGL +display list for each leaf node. This can be managed by +calling ssgLeaf::makeDList(). If you want to +make changes to the Leaf, you'll have to call makeDList again +since OpenGL does not support the editing of display lists. +

+You can call ssgLeaf::deleteDList() to stop +this leaf from being display listed from now on and to +free up the display list memory. +ssgLeaf::getDListIndex() returns the OpenGL +display list handle - or zero if no display list exists +for this leaf. +

+If you change a leaf node's geometry when it has an +active display list without calling either deleteDList +or makeDList again, then any subsequent operation +involving rendering this node could fail. + +

Face Culling.

+By default, ssgLeaf nodes are back-face culled, you can change +that for any given node using ssgLeaf::setCullFace(cf) +where cf is TRUE to enable backface culling, +FALSE to disable it. You can test the state of face culling +using ssgLeaf::getCullFace(). +

State Management.

+OpenGL supports a wide selection of state information - things like +texture, materials and such. All of this information is held in a +separate SSG class hierarchy: 'ssgState'. Each leaf has a state +which it sets up before drawing the geometry that the leaf contains. +

+Nodes may also be stateless - but that isn't useful for any +of the existing SSG leaf node types. +

+You can set the state for a node using ssgLeaf::setState(state) +and query it using ssgLeaf::getState(). You can ask if +a node has state information attached using ssgLeaf::hasState(). +

+Since OpenGL does not render translucent object well when Z-buffering +is enabled, it's often useful to know if an object is translucent. +ssgLeaf::isTranslucent() handles that test. +

+It is often useful to tag ssgState's with external properties - and +you can retrieve the property of a leaf's state using +ssgLeaf::getExternalPropertyIndex(). +

Querying Geometry

+The actual storage format for geometry in classes derived from +ssgLeaf varies from class to class. However, it's very useful +to be able to query the geometry in an implementation-independent +manner. +

+Although classes derived from ssgLeaf are entitled to store their +geometry in any form, all of them are required to respond to +queries about basic triangle primitives. +

+Firstly, you can get a count of the number of triangles in this +leaf using ssgLeaf::getNumTriangles(). Each triangle +has an index number for each vertex which can be queried using +ssgLeaf::getTriangle(n,&v1,&v2,&v3) which copies +the 'short' indices for the n'th triangle's three vertices into +v1, v2 and v3. +

+Once you know the indices of a triangle's vertices, you can ask for +more information about that vertex using +ssgLeaf::getVertex(i), +ssgLeaf::getNormal(i), +ssgLeaf::getColour(i), and +ssgLeaf::getTexCoord(i). These calls allow you to +retrieve the i'th vertex, normal, colour or texture coordinate as +a short floating point array. (3 elements for Vertex and Normal, +4 elements for Colour (RGBA) and two elements for a texture coordinate. +

+Analogous to getting the triangles, +you may use ssgLeaf::getNumLines () +and ssgLeaf::getLine ( int n, short *v1, short *v2 ) ; +to get the number of lines and to get the n. line. + +

+You can transform all the vertices of a leaf each frame by placing +an ssgTransform node above the leaf in the scene graph - but for +transformations that never change, it's more efficient to pre-transform +the vertices in the leaf node. +ssgLeaf::transform(matrix) permenantly transforms all +the vertices and normals of this leaf by multiplying them by the +matrix. (In the case of the normals, the translation part of the +matrix is ignored). +

+It's inadvisable to repeatedly transform a leaf using transform +since roundoff error will be accumulated with bad consequences (eventually). +In those cases, use an ssgTransform node. + +

class ssgVtxTable - A Vertex-table leaf node.

+This class allows one to represent leaf geometry as arrays of vertex, +normal, texture coordinate and colour data. As a derived class of +ssgLeaf, Vertex Tables add a constructor function to take the +pre-computed vertex data: +
+
+  ssgVtxTable ( GLenum ty, ssgVertexArray   *vl,
+                           ssgNormalArray   *nl,
+                           ssgTexCoordArray *tl,
+                           ssgColourArray   *cl ) ;
+
+
+'ty' is an OpenGL primitive type (such as one might pass to glBegin): +
+
+    GL_POLYGON
+    GL_TRIANGLE_FAN
+    GL_TRIANGLES
+    GL_TRIANGLE_STRIP
+    GL_QUAD_STRIP
+    GL_QUADS
+
+
+the remaining arguments are lists of (x,y,z) vertices, (nx,ny,nz) +normals, (s,t) texture coordinates and (r,g,b,a) colours. +

class ssgVertexArray/ssgNormalArray/ssgTexCoordArray/ssgColourArray - Vertex data storage classes.

+These four classes are used for storing vertex data for ssgVtxTable nodes. +Each class implements an extensible array that grows as you add data to +it. +
+
+  ssg*Array::ssg*Array ( int init = 3 ) ;
+
+
+When you construct the array, you may specify an initial size for it, +the default is three elements. If you don't know how big the array +is, don't worry about it - but if you do know, there are time and +storage space advantages to telling the class the exact number. +
+
+  void ssgVertexArray  ::add ( sgVec3 data ) ;
+  void ssgNormalArray  ::add ( sgVec3 data ) ;
+  void ssgTexCoordArray::add ( sgVec2 data ) ;
+  void ssgColourArray  ::add ( sgVec4 data ) ;
+
+
+These functions tack another data element onto the end of the array. +
+
+  float *ssg*Array::get ( int i ) ;
+
+
+Returns the address of the i'th element of the array. +
+
+  int ssg*Array::getNum () ;
+
+
+Returns the number of data elements currently stored in the array. +
+
+  void ssg*Array::removeAll () ;
+
+
+Empties the array. +

class ssgTween - A Vertex-table morphing node.

+This node is derived from ssgVtxTable. Where VtxTable can retain +a set of arrays, one each for Vertex coordinates, Normals, Texture +Coordinates and Colours, the ssgTween node can store an unlimited +number of 'banks' of these arrays. +

+Hence, one constructs an ssgTween by specifying it's OpenGL primitive +type: +

+
+  ssgTween::ssgTween ( GLenum ty ) ;
+
+
+And then create some number of 'banks' of data arrays by calling: +
+
+  int ssgTween::newBank ( ssgVertexArray   *vl,
+                          ssgNormalArray   *nl,
+                          ssgTexCoordArray *tl,
+                          ssgColourArray   *cl ) ;
+
+
+...once for each bank. All other ssgVtxTable calls operate ONLY +on the 'current bank' - which is either the most recent one you +created with newBank - or you can go back to an older bank by +calling: +
+
+  void ssgTween::setBank ( int bankID ) ;
+  int  ssgTween::getBank () ;
+
+
+(newBank returns the number of the bank it created in case you +lose count!). +

+ssgTween's idea of the 'current bank' is reset to zero after +rendering it...so be sure to always call setBank immediately +before working on it's component arrays. +

+When the ssgTween node is rendered, it works cooperatively +with whichever ssgTweenController node is above it in the +scene graph. See the section on ssgTweenController for more +details. +

+IMPORTANT NOTE: There must be an identical number of vertices, +normals, etc in each bank. If you get that wrong, SSG will +exit with an 'abort'. Note that there is a specific optimisation +in ssgTween that avoids the computational cost of interpolating +between two sets of vertex data if EITHER: +

    +
  • The ssgTweenController's selected bank number happens to +be an integer. +
  • The previous and next banks have the same ssg*Array pointer. +Since one generally doesn't wish to interpolate normals, colours +or texture coordinates, it's important to pay attention to this. +
+
+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssg/state.html b/doc/ssg/state.html new file mode 100644 index 0000000..7205e21 --- /dev/null +++ b/doc/ssg/state.html @@ -0,0 +1,255 @@ + + + + + A Simple Scene Graph API for OpenGL. + + + +

class ssgState - OpenGL state representation.

+Each leaf node will have some kind of ssgState node associated +with it that contains all relevent OpenGL state information. +

+There can (in principal) be a number of different ways to represent +OpenGL state - but all must be derived from an ssgState: +

+
+  class ssgState : public ssgBase
+  {
+    int  getExternalPropertyIndex () ;
+    void setExternalPropertyIndex ( int i ) ;
+
+    ssgStateCallback getStateCallback ( int cb_type ) ;
+    void setStateCallback ( int cb_type, ssgStateCallback cb ) ;
+
+    virtual void force () ;
+    virtual void apply () ;
+  } ;
+
+
+

Callbacks.

+States are 'applied' immediately before the geometry they are attached +to is rendered. Applying the state causes all of it's properties to +be set up in OpenGL. +

+Each state has three optional callback function hooks. The application +program can have a function called before the state is applied, after +it is applied (but before the geometry is drawn) and another after the +geometry is rendered - immediately before the next state is applied. +

+You can set those three callbacks with setStateCallback - passing +either SSG_CALLBACK_PREAPPLY, SSG_CALLBACK_PREDRAW or SSG_CALLBACK_POSTDRAW +as the first parameter and the address of your function as the second. +

External Properties.

+Each state entity can have an external property - which is a simple +integer that can be set using ssgState::setExternalPropertyIndex(i) +or queried using ssgState::getExternalPropertyIndex(). +External properties are of use to certain sorts of applications programs, +for example, a game might want to encode the set of OpenGL state information +that represents Lava as something that is hot and Ice as something that +is cold by encoding the temperature of the material in the External +property field. Most applications will probably use this field as an +index into a table of material properties inside the application itself. +

Applying a State.

+Whilst it's rare for an application to need to deal with an ssgState +once it has been defined, there may be occasions when the application +wishes to draw objects of it's own without using SSG's scene graph. +This often is the case with on-screeen symbology. +

+In such cases, it is important to bear in mind that SSG changes the +OpenGL state as little as possible - in order to save time. Hence, +when a leaf node has just been drawn with one set of state information, +and another leaf node is about to be drawn using another, SSG carefully +compares the two states to see how they differ and arranges to make +only the fewest possible OpenGL state change calls. If the application +goes in "behind SSG's back" and changes state then SSG will be confused. +

+There are two ways to achive this. One is to use SSG state classes to +change the state by calling ssgState::apply(). That call +will ensure that OpenGL's state matches the desired state using the +minimum of calls. However, if your application absolutely MUST make +it's own state calls then you should call ssgState::force() +to force all aspects of a specified state to be set in OpenGL so that +SSG can be certain about how things are set up. +

class ssgSimpleState - Simple State class.

+ssgSimpleState is currently the only concrete class derived from +ssgState. It has so far proved adequate for all state management. +
+  class ssgSimpleState : ssgState
+  {
+    void disable ( GLenum mode ) ;
+    void enable  ( GLenum mode ) ;
+    void set ( GLenum mode, int val ) { val ? enable(mode) : disable(mode) ; }
+  
+    void setTexture ( char *fname, int wrapu = TRUE, int wrapv = TRUE )
+    void setTexture ( ssgTexture *tex )
+    void setTexture ( GLuint tex )
+    void setColourMaterial ( GLenum which )
+    void setMaterial ( GLenum which, float r, float g, float b, float a = 1.0f )
+    void setMaterial   ( GLenum which, sgVec4 rgba )
+    void setShininess ( float sh )
+    void setShadeModel ( GLenum model )
+    void setAlphaClamp ( float clamp )
+  } ;
+
+These calls mostly correspond to similarly named OpenGL functions. +

Enable and Disable calls:

+ssgSimpleState:: disable ( mode ), +ssgSimpleState:: enable ( mode ) and +ssgSimpleState:: set ( mode, val ) provide +the same services as glEnable and glDisable ('set' is a convenience function +that is a 'disable' if 'val' is FALSE, 'enable' otherwise). The 'mode' +parameter uses tokens that are similarly named to those in OpenGL: +
+
+  SSG_GL_TEXTURE_EN
+  SSG_GL_CULL_FACE_EN
+  SSG_GL_COLOR_MATERIAL_EN
+  SSG_GL_BLEND_EN
+  SSG_GL_ALPHA_TEST_EN
+  SSG_GL_LIGHTING_EN
+ 
+
+

Texture states.

+There are three ways to attach a texture to an ssgSimpleState: +ssgSimpleState::setTexture ( fname, wrapu, wrapv ), +ssgSimpleState::setTexture ( ssgtexture ), and +ssgSimpleState::setTexture ( texture_handle ). +In the form that takes a filename, U-axis wrap and V-axis wrap flags, the +texture is loaded from a texture file on disk (see ssgTexture +below for details on how this is done). The map will be MIPmapped and set with +a texture environment that is GL_LINEAR_MIPMAP_LINEAR and GL_MODULATE. +

+If you need something fancier, then declare an 'ssgTexture' class and +pass that to the setTexture function. You can also load your own +texture and pass the OpenGL glBindTexture handle to setTexture. +

If you need the texture file name, first check that +isEnabled ( GL_TEXTURE_2D ) returns true, then you may call +getTextureFilename(). But you should still check the return value for NULL and for "". + +

Materials.

+These calls are all very similar to OpenGL calls - and take the same +parameters: +ssgSimpleState::setColourMaterial(which) +ssgSimpleState::setMaterial(which,r,g,b,a) +ssgSimpleState::setMaterial(which,rgba) +ssgSimpleState::setShininess(sh) +ssgSimpleState::setShadeModel(model) +ssgSimpleState::setAlphaClamp(clamp) +

class ssgTexture - Storing texture maps.

+An ssgTexture loads a texture map for you with the minimum possible +fuss - but offers less flexibility than if you did so yourself. +

+The ssgTexture constructor function +ssgTexture::ssgTexture( char *fname, +int wrapu = TRUE, int wrapv = TRUE ) does all the work, +presuming that you require GL_LINEAR_MIPMAP_LINEAR filtering and a +GL_MODULATE texture environment. +

+You can obtain the OpenGL glBindTexture handle for the texture +using ssgTexture::getHandle(). +

+When ssgTexture loads a map from disk, it uses the filename +extension to determine which image format the file is in. +

+Currently, only SGI format and uncompressed +8 or 24 bit BMP images are supported - but more +formats are planned for the future. Filenames ending with +'.rgb', '.rgba', '.int', '.inta', '.bw' are assumed to be +SGI formatted files, '.bmp' are in Microsoft's BMP format +and '.png' are in Portable Network Graphics format. +

+If for any reason ssgTexture cannot load the requested file, +it creates a 2x2 texel red and white chequerboard map to +enable the program to continue running. This is often very +useful for debugging and to enable program development to +continue when texture maps are not yet painted. +

class ssgStateSelector - Switchable State class.

+There are cases where you would like to be able to switch between a +number of different states for a given leaf node. +
+  class ssgStateSelector : ssgSimpleState
+  {
+    ssgStateSelector ( int nstates ) ;
+
+    void            selectStep ( unsigned int s ) ;
+    int             getSelectStep  (void) ;
+    ssgSimpleState *getCurrentStep (void) ;
+    void            setStep ( int i, ssgSimpleState *step ) ;
+    ssgSimpleState *getStep ( int i ) ;
+  } ;
+
+This class is used to switch between some number of ssgSimpleStates. +You could (for example) draw the ground for your game as either +grass or snow - depending on the season. Construct objects of this +class with the number of alternative representations you will require +as it's parameter, then create a number of ssgSimpleStates - passing +them to ssgStateSelector::setStep(). You can then select which +step you wish to render using ssgStateSelector::selectStep(). +

+ssgStateSelector::getSelectStep() returns the number of the currently +selected step. ssgStateSelector::getCurrentStep() returns a pointer to +the currently selected ssgSimpleState. ssgStateSelector::getStep() +returns a pointer to the i'th ssgSimpleState. +

+Since ssgStateSelector is derived from ssgSimpleState, +all the other ssgSimpleState calls work - and the effect is to +operate on the currently selected ssgSimpleState. +

+Example: +

+
+     ssgStateSelector *s = new ssgStateSelector ( 2 ) ;
+     ssgSimpleState *grass = new ssgSimpleState () ;
+     ssgSimpleState *snow  = new ssgSimpleState () ;
+
+     ...set up grass and snow states appropriately...
+
+     s -> setStep ( 0, grass ) ;
+     s -> setStep ( 1, snow  ) ;
+
+     leafnode1 -> setState ( s ) ;
+     leafnode2 -> setState ( s ) ;
+     leafnode3 -> setState ( s ) ;
+
+     ...choose grass or snow...
+
+     if ( ! winter )
+       s -> selectStep ( 0 ) ;
+     else
+       s -> selectStep ( 1 ) ;
+
+     ...render the scene...
+
+
+Notice that one selectStep call changes the material on all +leaf nodes that use that ssgStateSelector. + + + + + +
+ + + + + + +
<= previous =Return to SSG Index= next =>
+
+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssgAux.png b/doc/ssgAux.png new file mode 100644 index 0000000000000000000000000000000000000000..13d6d5622eb951c9748507d82e5d7b7879233c64 GIT binary patch literal 1671 zcmV;226*|2P)000>X0{{R4Infic00004XF*Lt006O% z3;baP0003vP)t-s3K|MBnlctf7IK<4CVnOwRvMnBLLO!wa*j3%A__jbJ|cD^!n#Uc zhB7XaE~`Y000E~Nkldjo6l0iAsem?0oLh+aiI?R)BBPkSX@~5YoO1@+nW7=mpTw&>YAr8YBk3txR zoiJSQv00PLNpeu5> zGzy_&Euo~|E>~@-giyTBSsI%sNlNg9D3L|wW?95tnW)6AW%=pWa{k6uoCS8e^(_kQ zO2w`O5k4Czh7--j+M->GjDzh3Y4%6`vf>6YH6lq>B~qkA7La}=~X~>TG>!eNJ=Gm+J zI+<>nRRf{xEKRBB!5irmuy#zfJC)MS~2$TUHRx2clnOJ!EF zn>zi1+EP+ZC3u3unJt-bl7P;+UtVH;c!49+yr5~K=xI_!h~yv|q zmQ0eON|Gf;%E|nKVUmZ(>+NWq7ujyHiux*scDZ{I3 zdv;~k>VE4+vnU8KNk_IF1n7xYw}r;A?eiWh&BpUO>O!J?C0)wj1)UetXMYYCMAlq3 zY#z>=@;6pMaIx8FY+P+@Y^*|DZ8TOlG#ZW7{j_kfpLHL z`Sau8@$dpYokB12!_A}1&7-4>i_6O+sFz0uuw24&Va&fJ9vBxFKY#u-`qAd$VHwoH zUC^Heo^Ckqx~{&~IM2GSp6KD~YS%gF#N8TE29k5N;FobW@72$Je+iv?I}3!>lpqFJ zCdKHK=w>A4=mxULRgq{iMKh~6%C+euxJh}CkHi|$NKNPgJUu_pg61x3-Og;=lMsXWM4Cra8n~ z=0LMst|b#{tAw@`zOE3|U|Cp{5i)^AE))H2j?1%kAFalsQrbo=eQpyhN0~hCP?@77 zp@ef}iQ2>w%5juMv}H|T$&vV&r>7$2ipW=XNuhCd?knK5YoL!Qn&?%NbCX)cb2!nw zDvR!ROkxF6?AEo9TVB!^f?`3pVq2E>@4(?g~533eZVtv{{xbzaWHhC RB-j7|002ovPDHLkV1oZD{E7eo literal 0 HcmV?d00001 diff --git a/doc/ssgAux/index.html b/doc/ssgAux/index.html new file mode 100644 index 0000000..c6258ea --- /dev/null +++ b/doc/ssgAux/index.html @@ -0,0 +1,886 @@ + + + + + SSG Auxiliary Libraries. + + + + + + + + + + +
+

SSG Auxiliary Libraries.

+
+ by Steve Baker +
+

Introduction

+ + + + + +
+PLIB/ssgAux is a suite of auxiliary libraries that build +higher level classes on top of the basic SSG classes. +

+

+
+
+ssgAux screenshot +
+This image was created entirely from ssgAux primitives. +
+
+

+

Conventions

+ssgAux is installed and named with the same +conventions as the base PLIB libraries. +
+ie: +
+   /usr/include/plib/ssgAux.h
+   /usr/lib/libplibssgaux.a
+
+

+PLIB/ssgAux functions, classes and constants are all +named with an 'ssga' or 'SSGA' prefix. +
+eg: +

+   class ssgaShape ;
+   SSGA_TYPE_CUBE
+
+ +

ssgaShape

+This is an abstract base class for many of the more +complex classes in the ssgAux 'collection'. The idea +is that ssgaShape's are really ssgBranch nodes that create +their own child nodes - but they behave in such a way that +you are never concerned with the nodes they create beneath +themselves. +
+
+class ssgaShape : public ssgBranch
+{
+  ssgaShape (void) ;
+  ssgaShape ( int numtris ) ;
+ 
+  float *getCenter  () { return center     ; }
+  float *getSize    () { return size       ; }
+  int    getNumTris () { return ntriangles ; }
+ 
+  void setColour  ( sgVec4 c ) { sgCopyVec4 ( colour, c ) ; regenerate () ; }
+  void setCenter  ( sgVec3 c ) { sgCopyVec3 ( center, c ) ; regenerate () ; }
+  void setSize    ( sgVec3 s ) { sgCopyVec3 ( size  , s ) ; regenerate () ; }
+  void setSize    ( float  s ) { sgSetVec3  ( size,s,s,s) ; regenerate () ; }
+  void setNumTris ( int ntri ) { ntriangles = ntri ; regenerate () ; }
+ 
+  void setKidState    ( ssgState *s ) ;
+  void setKidCallback ( int cb_type, ssgCallback cb ) ;
+ 
+  virtual void regenerate () = 0 ;                                              
+}
+
+The constructor allows you to specify the approximate number of +triangles you'd like the shape to consume. For some shapes, this +will be ignored (eg the Cube has 12 triangles - no matter how many +you ask for) - for others, it's honored only approximately (eg the +Sphere can only generate certain specific numbers of triangles) - +and for others, it may be honored exactly. +

+You can also set the colour of all the polygons and the ssgState and +callbacks that are applied to all of the child nodes. +

+setCenter allows you to position the center point of the object in +3D space - and setSize lets you determine the overall dimensions of +the object (this too may also be approximate - the 'size' of a teapot +is hard to specify in any useful way). The version of 'setSize' that +takes an sgVec3 allow you to generate cuboids from cubes and sphereoids +from spheres...but not all shapes allow this. +

+The 'regenerate' call allows the complete child structure of the object +to be deleted and then recreated. This is occasionaly useful if (for +example) you have walked down into the child structures and modified +them - and you'd like the shape back into it's pristine conditions. + +

ssgaCube

+
+
+class ssgaCube : public ssgaShape
+{
+  ssgaCube (void) ;
+  ssgaCube ( int numtris ) ;
+  virtual void regenerate () ;
+} ;
+ 
+
+This is the simplest of ssgaShapes - it defines nothing new over +and above the standard ssgaShape. + +

ssgaPatch

+
 
+ 
+class ssgaPatch : public ssgaShape
+{
+  ssgaPatch (void) ;
+  ssgaPatch ( int numtris ) ;
+ 
+  void setControlPoint ( int s, int t, sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) ;
+  void setControlPoint ( int s, int t,
+                         float x, float y, float z,
+                         float u, float v,
+                         float r, float g, float b, float a ) ;
+  void getControlPoint ( int s, int t, sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) ;
+ 
+  virtual void regenerate () ;
+} ;
+ 
+
+This implements a basic spline patch - with a 4x4 grid of 'control points' +that define its shape. 'setContolPoint' allows you to set the spatial +position, the texture coordinate and the colour at any given (s,t) position. +(s and t each must be in the range 0..3). +

+It's necessary to call the 'regenerate' function whenever you have added +or changed one or more control points. + + +

ssgaTeapot

+
+ 
+class ssgaTeapot : public ssgaShape
+{
+  ssgaTeapot (void) ;
+  ssgaTeapot ( int numtris ) ;
+  virtual void regenerate () ;
+} ;
+ 
+
+This creates the 'classic' Martin Newell teapot model. + +

ssgaSphere

+
+  
+class ssgaSphere : public ssgaShape
+{
+  ssgaSphere (void) ;
+  ssgaSphere ( int numtris ) ;
+ 
+  void setLatLongStyle ( int ll ) ;
+  int  isLatLongStyle  () ;
+  virtual void regenerate () ;
+} ;
+
+
+There are two ways to generate a sphere model - one is to produce a +fairly uniform grid of triangles that are as nearly equilateral +as possible. The alternative is to generate more nearly right-triangles +in cylindrical strips that are stacked on top of each other with a +circular disk at top and bottom. +

+The function 'setLatLongStyle(ll)' allows you to choose which you want, +call regenerate after you change types! + +

ssgaCylinder

+
+ 
+class ssgaCylinder : public ssgaShape
+{
+  ssgaCylinder (void) ;
+  ssgaCylinder ( int numtris ) ;
+ 
+  void makeCapped ( int c ) ;
+  int  isCapped   () ;
+  virtual void regenerate () ;
+} ;                                                                             
+
+
+Cylinders also come in two forms - capped and uncapped. Once again, +if you change the capped versus uncapped status, call regenerate to +make the new version. + +

ssgaWaveSystem

+This class is for handling water waves. +It generates a large polygonal mesh which it distorts in +realtime to simulate water waves. +

+The simulation uses between one and sixteen "Wave Trains" - each +using a class: +

+
+class ssgaWaveTrain
+{
+  ssgaWaveTrain () ;
+ 
+  float getSpeed      () ;
+  float getLength     () ;
+  float getLambda     () ;
+  float getHeading    () ;
+  float getWaveHeight () ;
+
+  void  setSpeed      ( float s ) ;
+  void  setLength     ( float l ) ;
+  void  setLambda     ( float l ) ;
+  void  setHeading    ( float h ) ;
+  void  setWaveHeight ( float h ) ;
+} ;                                                                             
+
+
+So, you create a bunch of these trains (typically, two or three +is plenty to generate 'interesting' motion). +Each wave train has a direction in which the waves are travelling, +a speed, a wave length (which also affects the shape of the wave), +the height of the wave and a 'lambda' term (which controls how much +the tops of the waves bend over). +

+Next, generate the ssgaWaveSystem: +

+ 
+class ssgaWaveSystem : public ssgaShape
+{
+  ssgaWaveSystem ( int ntri ) ;
+ 
+  virtual void        regenerate  () ;
+ 
+  ssgaWSDepthCallback getDepthCallback () ;
+  void                setDepthCallback ( ssgaWSDepthCallback cb ) ;
+ 
+  ssgaWaveTrain *getWaveTrain ( int i ) ;
+  void           setWaveTrain ( int i, ssgaWaveTrain *t ) ;
+ 
+  float getWindSpeed   () ;
+  float getWindDirn    () ;
+  float getEdgeFlatten () ;
+  float getTexScaleU   () ;
+  float getTexScaleV   () ;
+ 
+  void  setWindSpeed   ( float speed      ) ;
+  void  setWindDirn    ( float dirn       ) ;
+  void  setEdgeFlatten ( float dist       ) ;
+  void  setTexScale    ( float u, float v ) ;
+ 
+  void updateAnimation ( float t ) ;
+} ;
+
+
+The appearance of waves varies greatly with the depth of the +water - and this simulation produces the correct effects. There +is an optional user-callback that can be used to feed water depth +values into the simulation. The wave system will likely call this +function many thousands of times per frame - so make sure your +depth function is very efficient. If you don't provide a depth +function then the water will be assumed to be infinitely deep. +

+You can determine the number of polygons used to render +the patch of waves, the size of the wave patch and the amount +of texture repetition. The 'EdgeFlatten' setting determines +to what distance from the edge of the patch the waves are +gradually flattened out. This is useful because it allows +you to keep all those expensive wave polygons close to the +camera - and feather them out over range so that they blend +gently into a larger flat ocean. +

+Designing waves by calling the ssgaWaveSystem API is quite +difficult. You are advised to use the wave designer program +in the PLIB examples package. This program lets you adjust +wave parameters until it looks how you'd like - then hit the +'Write C++ code' to write out a source code snippet that you +can cut and paste into your program. +

+There is a 'README' in the 'water' source directory that +explains how to use the program in a little more detail. +That's all the wave system documentation I have time to write +just now - sorry. + +

ssgaParticleSystem

+This is a very flexible implementation of particle systems - it's not +as efficient as you might want for thousands of particles - this is intended +for high flexibility and relatively fewer numbers of particles. + +

+All of that flexibility is evident in the complex constructor function: +

+
+  class ssgaParticleSystem : public ssgVtxArray
+  {
+    ssgaParticleSystem ( int max ,          /* Max Number of particles      */
+                         int init,          /* Number to launch initially   */
+                       float add ,          /* Max number to create per sec */
+                         int turn_to_face,  /* Turn to face camera?         */
+                       float size,          /* Size of particles            */
+                       float bsphere_rad,   /* Size of bounding sphere      */
+      ssgaParticleCreateFunc create,        /* Initial create fn.           */
+      ssgaParticleUpdateFunc update = NULL, /* Update function              */
+      ssgaParticleDeleteFunc del     = NULL /* Remove function              */
+                      ) ;
+
+    void  setSize ( float sz ) ;
+    float getSize () ;
+
+    update ( float dt ) ;
+
+    int getNumActiveParticles () ;
+  } ;
+
+
+There are three callback functions - with the following types: +
+ 
+  typedef void (* ssgaParticleCreateFunc) ( ssgaParticleSystem *ps,
+					    int index,
+					    ssgaParticle *p ) ;
+ 
+  typedef void (* ssgaParticleUpdateFunc) ( float deltaTime,
+					    ssgaParticleSystem *ps,
+					    int index,
+					    ssgaParticle *p ) ;
+ 
+  typedef void (* ssgaParticleDeleteFunc) ( ssgaParticleSystem *ps,
+					    int index,
+					    ssgaParticle *p ) ;
+
+
+There is also a public class for a single particle: +
+ 
+class ssgaParticle
+{
+public:
+ 
+  sgVec4 col ;
+  sgVec3 pos ;
+  sgVec3 vel ;
+  sgVec3 acc ;
+ 
+  float size ;
+  float time_to_live ;
+  void *userData ;
+ 
+  ssgaParticle () ;
+ 
+} ;                                                                             
+
+
+This is best explained with an example. +Here is an example of a fountain model: +
+
+  fountain = new ssgaParticleSystem ( 2000, /* Max Number of particles */
+                                         2, /* Number to launch initially */
+                                       200, /* Max number to create per sec */
+                                      TRUE, /* Turn to face? */
+                                       0.2, /* Size of particles */
+                                      20.0, /* Size of bounding sphere */
+                           fountain_create, /* Initial create fn. */
+                           fountain_update  /* Update function */ ) ;
+ 
+
+This creates a system that has the capability to draw 2000 particles +(each is a quadrilateral). Two particles are launched initially and 200 +more are added gradually every second. You can choose to have each particle +turn to face the camera - or to be oriented in the X/Z plane, mine are set to +turn-to-face. The particles are 0.2 OpenGL units across. +

+This fountain is a continuous effect - so the inital number of particles +to launch is small - and the 'per second' creation rate determines the +flow. If you wanted something more like an explosion, have a larger number +of initial particles and none launched per-second after that. +

+One messiness is that we don't know how big the fountain may become over +time - and it's VERY inefficient to recompute the bsphere every frame. Hence +you have to tell the class what the maximum bounding sphere of the fountain +is at the outset so we can field-of-view cull it - this could be a near +infinite radius (MAX_FLOAT say) if you truly don't know how big it could +get - but that's inefficient because all 2,000 particles must be sent to +OpenGL even if the fountain is 20 miles away behind the camera - so do your +best to come up with some kind of a reasonable guess. +

+fountain_create and fountain_update are user-defined callback functions, +we could also have defined a 'fountain_delete' function - but many applications +won't need this. +

    +
  • The 'create' callback is called every time the system needs to + create a new particle. +
  • The 'update' callback is called on every update cycle for every + particle that's in flight. +
  • The 'delete' callback is called when the system has decided + that a particle has 'died'. +
+You have to have a 'create' function - but the other two are optional. +

+These callbacks are passed the address of the particle system, the index +number and address of the particle that's being created/updated/deleted - +and (for the 'update' function only) - the amount of elapsed time since +this particle was last updated. +

+The callbacks can set, read or change any or all aspects of the particle... +it's colour, position, velocity, accelleration, size and 'time to live' +(in seconds). +There is also a user data pointer - so you can hang your own data onto +each individual particle. +

+The particle system automatically moves each particle according to the usual +laws of motion and decrements it's time-to-live - so in many cases, +you can just set the initial velocity, the force due to +gravity and a reasonable time to live - and let the particle system code +do the rest. +

+My 'fountain_create' function looks like this: +

+
+void fountain_create ( ssgaParticleSystem *, int, ssgaParticle *p )
+{
+  sgSetVec4 ( p -> col, 1, 1, 1, 1 ) ;  /* initially white */
+  sgSetVec3 ( p -> pos, 0, 0, 0 ) ;     /* start off on the ground */
+  sgSetVec3 ( p -> vel, (rand()%1000-500)/300.0f,
+                        (rand()%1000-500)/300.0f, 50.0f ) ;
+                                        /* Shoot up and out */
+  sgSetVec3 ( p -> acc, 0, 0, -9.8f ) ; /* Gravity */
+  p -> time_to_live = 5 ;               /* Droplets evaporate after 5 seconds */
+}
+
+
+It populates the particle with an initial colour, position, velocity and +accelleration and gives it a 'time to live' of five seconds. The velocity +is randomised a bit to make a nicer looking fountain. +

+You can do all sorts of fancy things to the colour, position, velocity and +accelleration in your 'update' function. +

+
+void fountain_update ( ssgaParticle *p )
+{
+  if ( p -> pos [ 2 ] < 0 )
+    p -> time_to_live = -1 ;
+ 
+  p -> col [ 2 ] = (p -> time_to_live > 2) ? 1 : (p -> time_to_live/2.0f) ;
+}
+
+
+...this one just erases particles that go below zero altitude (by +setting their time-to-live variable to -1) and makes them go a pretty +shade of yellow for the last two seconds before they die. +

+The 'delete' function is principally useful for freeing up any user-data +you allocated in the 'create' function or during 'update's. Don't +delete the particle though - the particle system recycles it to avoid +doing too much dynamic memory allocation. +

+All that remains is to call the ssgaParticleSystem::update function +every frame with a parameter that tells the system how much time has +elapsed since the last call. This allows you to easily speed up, slow +down or pause the particles (or even run them backwards if you want). +

+I call this every frame: +

+
+void updateFountain ()
+{
+  static ulClock ck ;
+
+  ck . update () ;
+ 
+  if ( fountain != NULL )
+    fountain -> update ( ck.getDeltaTime () ) ;
+}
+                                                                                
+
+This function could also check the number of particles currently in flight +and delete the fountain when there are none left - that might be useful +for an explosion or something where you'd want to save CPU time by not +running the explosion particle system when all the pieces have landed +and 'gone away'. +

+The size of each particle is the size field of the individual particle +MULTIPLIED by the number you pass into the size parameter of the +ssgaParticleSystem constructor (or set with setSize()). This allows you +to cheaply set the particle size for all the particles at once - or to +tweak the size of each one in turn. The number in the particle structure +defaults to 1.0 so you can ignore it and just set the size in the +particle system overall. +

+ssgaParticleSystem is derived from ssgVtxArray - so you can apply textures and +other state things using the usual ssgVtxArray::setState(ssgSimpleState*) +call. I applied a texture with a fuzzy alpha-blended circle. +

+While debugging this, I got a bug which caused a picture of Tux to be +applied instead of the droplet texture - it was absolutely hilarious +to see 2000 tiny penguins shooting up in a fountain and falling gently +to earth! + +

ssgaFire

+This class is actually a highly specialised particle system. +You have to construct it quite carefully in order to get a nice +looking fire: +
+
+  class ssgaFire : public ssgaParticleSystem
+  {
+    ssgaFire ( int num_tris,
+               float radius = 1.0f,
+               float height = 5.0f,
+               float speed  = 2.0f ) ;
+ 
+    virtual ~ssgaFire () ;
+ 
+    virtual void update ( float t ) ;
+ 
+    void setUpwardSpeed ( float spd )
+    void setHeight      ( float hgt )
+    void setRadius      ( float rad )
+    void setHotColour   ( sgVec4 col )
+  } ;
+
+
+In the constructor, you set the number of triangles you wish to +generate, the radius and height of the approximately cylindrical +fire and the speed at which the flames head up towards the sky. +

+The number of polygons you use tends to be rather critical - too +many and your fire will look too 'smooth' and will be mostly +white-hot. Too few and it'll look like a number of detached +reddish blobs floating upwards. For the default radius, between +100 and 200 triangles seems to look good - for a 10 meter patch +of fire, you may need as many as 2000 triangles to make it look +effective. All three parameters (speed, height and radius) can +be manipulated in realtime - but since the number of polygons +is fixed, there is a limit to the amount of realtime tweaking +you can do without making it look silly. +

+The 'setHotColour' function allows you to set the colour +of the hottest flames (at the base of the fire). Since many +layers of polygons add up to form the colour, you'll tend to +want to use a primary colour with about 10% of one or two of +the other primaries. The default colour is (1.0, 0.2, 0.1, 1.0) +which produces red flames with yellow and white in the hotter +regions. +

+ +

ssgaLensFlare

+When you view the sun (or some other very bright light source) through +the imperfect lens of a camera, you get a row of bright circles and +rings in a line from the light source, through the center of the lens. +This is called a 'lens flare' and it can be very effective in computer +graphics in depicting a light source that's brighter than the computer +screen can display. It almost makes you want to squint because of +the brightness. +

+The ssgaLensFlare object is a special kind of ssgaShape that can be +positioned beneath the same transform +as the source of the light - and it 'just works'. You don't need +to set any parameters - just add it into the SSG scene graph in the +right place. +

+eg: +

+
+   ssgTransform *myTransform ;
+   ssgBranch    *myLightSource ;
+
+   /* Set up myTransform and myLightsource */
+
+   ...
+
+   myTransform -> addKid ( myLightSource ) ;
+   myTransform -> addKid ( new ssgaLensFlare ) ;
+
+
+It contains a (hard-coded) 256x128 texture map which is compiled +into the code and shared between however many lens flares there +are in the scene. +

+ +

ssgaSky

+ + + + + +
+The ssgaSky class models a blended sky dome, with methods to add celestial +bodies, for example a haloed sun or a textured moon, and methods to add +clouds, stars and planets. +

+WARNING - Solaris C/C++ compilers (from Sun Microsystems Inc) have +some nasty '#define's for the word 'SUN' and 'sun'. If you are writing +portable software, you might want to pick an alternative word! PLIB uses 'Sol', +the formal astronomical name for the star that happens to be our sun. +

+The sky implements various time of day lighting effects, it plays well +with fog and visibility effects, and implements scudded cloud fly-through +effects. Additionally, you can wire in the output of the SimGear +SGEphemeris +class to accurately position all the objects in the sky. +

+This sky dome code was based on the SimGear +sky code, written by Curtis Olson, which is used in +FlightGear. +The code was moved into PLIB for easier access, STL was removed and 'flat +earth' methods added. The main reason the code was moved here was to allow +the PLIB community to add their own extensions (not relevant to FlightGear), +to build for example, 'un-realistic' sky domes, with green skies, purple +clouds or multiple suns. This has not yet fully been achieved, but hopefully +when someone finds the time or has the need, these features will be added. +

+
+ssgaSky screenshots +
+ssgaSky screenshots +
+
+
+class ssgaSky
+{
+public:
+
+  ssgaSky( void );
+  ~ssgaSky( void );
+
+  void build( double h_radius, double v_radius,
+	  int nplanets, sgdVec3 *planet_data,
+	  int nstars, sgdVec3 *star_data);
+
+  ssgaCelestialBody* addBody( const char *body_tex_path, const char *halo_tex_path, double size, double dist, bool sol = false );
+  ssgaCelestialBody* addBody( ssgSimpleState *orb_state, ssgSimpleState *halo_state, double size, double dist, bool sol = false );
+  ssgaCelestialBody* getBody(int i) { return bodies.get(i); }
+  int getBodyCount() { return bodies.getNum(); }
+
+  ssgaCloudLayer* addCloud( const char *cloud_tex_path, float span, float elevation, float thickness, float transition );
+  ssgaCloudLayer* addCloud( ssgSimpleState *cloud_state, float span, float elevation, float thickness, float transition );
+  ssgaCloudLayer* getCloud(int i) { return clouds.get(i); }
+  int getCloudCount() { return clouds.getNum(); }
+
+  bool repositionFlat( sgVec3 view_pos, double spin, double dt );
+  bool reposition( sgVec3 view_pos, sgVec3 zero_elev, sgVec3 view_up,
+                   double lon, double lat, double alt, double spin, double gst, double dt );
+
+  bool repaint( sgVec4 sky_color, sgVec4 fog_color, sgVec4 cloud_color, double sol_angle,
+	  int nplanets, sgdVec3 *planet_data,
+	  int nstars, sgdVec3 *star_data );
+
+  void modifyVisibility( float alt, float time_factor );
+
+  void preDraw();
+  void postDraw( float alt );
+
+  void enable();
+  void disable();
+
+  float getVisibility();
+  void setVisibility( float v );
+} ;
+
+

Building the sky

+Once you have created an instance of ssgaSky you must call the +build() method. The arguments you pass to the build() method +allow you to specify the size of your sky dome, a number of planets, +and a multitude of stars. For the planets and stars you pass in +an array of right ascensions, declinations, magnitudes, and the +distance from the view point. +

Celestial Bodies

+Celestial bodies (eg. sun, moon or even mars if you like) can be +added or modified individually. To add a body use the addBody() +method. The arguments allow you to specify the body texture, the +halo texture, the size of the body, the body distance (usually the +sky dome size) and a boolean flag to indicate if the body should +be used as a reference to 'spin' the dome based on the bodies rotation +for correct sunrise and sunset effects (nb. the sun reference is only used +by repositionFlat() so the user does not need to calculate the spin +parameter). There is an additional form of this method that allows +you to specify your own ssgSimpleState for drawing the body and halo +texture. +

+Body accessor methods are available to: +

    +
  • set body right ascension and declination +
  • get body position and color +
+note: body angle and rotation are calculated internally (by +ssgaSky::repositionFlat() only) for sunrise and sunset effects. +
+class ssgaCelestialBody
+{
+...
+  void getPosition ( sgCoord* p );
+
+  void setAngle ( double angle );
+  double getAngle ();
+
+  void setRotation ( double rotation );
+  double getRotation ();
+
+  void setRightAscension ( double ra );
+  double getRightAscension ();
+
+  void setDeclination ( double decl );
+  double getDeclination ();
+
+  void setDist ( double dist );
+  double getDist ();
+
+  float *getColor();
+} ;
+
+

Cloud Layers

+Cloud layers can be added or modified individually. To add a cloud +layer use the addCloud() method. The arguments allow you to specify +cloud texture, the size of the cloud object, base height above sea +level, layer thickness and a transition zone for entering/leaving +the cloud layer. There is an additional form of this method that +allows you to specify your own ssgSimpleState for drawing the cloud +layer texture. +

+Cloud accessor methods are available to: +

    +
  • enable/disable clouds +
  • set elevation above sea level +
  • set thickness and transition - used to simulate cloud 'puffs' +
  • set speed and direction - used to simulate cloud movement +
+
+class ssgaCloudLayer
+{
+...
+  void enable();
+  void disable();
+  bool isEnabled();
+
+  float getElevation ();
+  void  setElevation ( float elevation );
+
+  float getThickness ();
+  void  setThickness ( float thickness );
+
+  float getTransition ();
+  void  setTransition ( float transition );
+
+  float getSpeed ();
+  void  setSpeed ( float val );
+
+  float getDirection ();
+  void  setDirection ( float val );
+} ;
+
+

Repainting the Sky

+As the sun circles the globe, you can call the repaint() method to +re-color the sky objects to simulate sunrise and sunset effects, +visibility, and other lighting changes. The arguments allow you +to specify a base sky color (for the top of the dome), a fog color +(for the horizon), a cloud color (for blending clouds with the fog +color), the sun angle with the horizon (for sunrise/sunset effects), +and new star and planet data so that we can optionally change the +magnitude of these (for day/night transitions). +

Positioning Sky Objects

+As time progresses and as you move across the surface of the earth, +the apparent position of the objects and the various lighting effects +can change. At this point you may wish to call celestial body +setRightAscension() and setDeclination() methods to update the +position of your bodies (SimGear +SGEphemeris +can be used if you wish to place bodies correctly). +

+Once you have specified the positions of all the sky objects, you must +call the repositionFlat() or reposition() method to allow you to specify +your view position. +

+The repositionFlat() arguments allow you to specify view position for +a 'flat earth' model. A 'spin' angle can be specified for orienting +the sky with the sun position so sunset and sunrise effects look correct. +If you specified a body to be the sun reference on addBody() you do not +need to specify the 'spin' angle. You may specify the amount of elapsed +time since the sky was last updated (used to animate cloud movement). +

+The reposition() arguments allow you to specify your view position in +world Cartesian coordinates, the zero elevation position in world +Cartesian coordinates (your longitude, your latitude, sea level), +the 'up' vector in world Cartesian coordinates, current longitude, +latitude, and altitude. A 'spin' angle can be specified for +orienting the sky with the sun position so sunset and sunrise +effects look correct. You must specify GMT side real time. You may +specify the amount of elapsed time since the sky was last updated +(used to animate cloud movement). +

Rendering the Sky

+The sky is designed to be rendered in two stages. The first stage +renders the parts that form your back drop - the sky dome, the +stars, the planets and the celestial bodies. These should be +rendered before the rest of your scene by calling the preDraw() +method. The second stage renders the clouds which are likely to be +translucent and should be drawn after your scene has been rendered. +Use the postDraw() method to draw the second stage of the sky. +

+A typical application might do the following: +

+thesky->preDraw(); 
+ssgCullAndDraw ( myscene ) ; 
+thesky->postDraw( my_altitude ); 
+
+The current altitude in meters is passed to the postDraw() +method so the clouds layers can be rendered correctly from +most distant to closest. +

Visibility Effects

+Visibility and fog is important for correctly rendering the sky. +You can inform ssgaSky of the current visibility by calling +the setVisibility() method. +

+When transitioning through clouds, it is nice to pull in the +fog as you get close to the cloud layer to hide the fact that +the clouds are drawn as a flat polygon. As you get nearer to +the cloud layer it is also nice to temporarily pull in the +visibility to simulate the effects of flying in and out of +the puffy edge of the cloud. These effects can all be +accomplished by calling the modifyVisibility() method. The +arguments allow you to specify your current altitude +(which is then compared to the altitudes of the various +cloud layers). You can also specify a time factor which +should be the length in seconds since the last time you +called modifyVisibility(). The time_factor value allows the +puffy cloud effect to be calculated correctly. +

+The modifyVisibility() method alters the ssgaSky's internal +idea of visibility, so you should subsequently call +getVisibility() to get the actual modified visibility. +You should then make the appropriate glFog() calls to +setup fog properly for your scene. +

Accessor Methods

+Once an instance of ssgaSky has been successfully +initialized, there are a couple accessor methods you +can use such as getBodyCount() to return the number +of celestial bodies, getBody(i) to return body number +i, getCloudCount() to return the number +of cloud layers, getCloud(i) to return cloud +layer number i, getVisibility() to return the actual +visibility as modified by the sky/cloud model. +

+


+ + +
+Valid HTML 4.0! + +
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+
+ + + diff --git a/doc/ssgAux/snapshot.png b/doc/ssgAux/snapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..aebbc8104525504a45ed186e9fa63cb14991a0b5 GIT binary patch literal 64063 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%0ALjNW0Bod2rJF>UusYCjZ1)-^?*B<5Pqnj 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 literal 0 HcmV?d00001 diff --git a/doc/torcs_logo.png b/doc/torcs_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7f44a4a35652580517d669590da90e6bfd122435 GIT binary patch literal 4563 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 literal 0 HcmV?d00001 diff --git a/doc/tux_aqfh.png b/doc/tux_aqfh.png new file mode 100644 index 0000000000000000000000000000000000000000..4ddec9be23461379caadb4a267319a72f895b1d1 GIT binary patch literal 7704 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 LNkvXXu0mjfHAlWs literal 0 HcmV?d00001 diff --git a/doc/tuxkart_logo.png b/doc/tuxkart_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d3a2c7e90d086450c5aa5e9454ad1a79b3b111 GIT binary patch literal 6536 zcmV;38F%K1P)lK93vvG3cQy3cYBDAh5)yxAzn^t6 z{`QFa;u1hdFhVn#bXvCEty6t-ZAnV|#<~nTK)a;`>H%?XGH$?w!(!Z z{N5gBR=Qj|uY+!iv9F+9R$>wh4~BxCnvTI|WcE`}MJO>Sw0`vd{-plzgq@IRa0?3^ z9rIpQmWF&&c5~@O9#;IZMKm;@`RB{CsN??pkin&CnueZQHlo7D*4dE{JvB?jx_Cc3 z(f-Y2USdQ1+=PB*@BaS7{p6SWkaXL=*!<$F?w$&)p=|3aD*eD_{`!ftn(+SHX#W1T z{{GSa{o;UXM*ii2P*C9h@}jMsy)7)VD<@9=)OK-M_F66m`p|~^!E}yyjI5aH%(v^O6 z=k2!*Nk;lyT>CL6Y`&-dA{81@PyGJ<+WxYF{N0%T{;XeJPvy((0s`=WP)>w@gvNal zv@+mq000&;K@w!d+OVkcJU`>ZrWcbQ5625jW+9-I;R+EmU z&Q=Y*@YrKd4>UJ-`j>Som#yu`$6B!9@L{UTX=&Lc`M`baf_qssz_XIQ+}hJGYlQFC zQUkG=VO5Qpv4tXaLSCU%>NpV<{C=SywvRP>yMI{P6v^s7_vI;T;n5p(VbsW8P--X% zLrX&;tv0i0b0U!#E|AM)L7HY9tda^i6n1;u>RI^Clb0$i?Za)JR(;&%@;O+FEq0mg z@##Hs*1W^ubo;RsMH<8x7IX=lcsIY1Rz z+VPU76)pu>%FnyuF*av47#?MRzXtZA7WZN&b-4<*>Hh`> znjvovDwWGLu;9|uJ0W{oF1=FK35%8FNo-1`63sM!guGHahn{%z&+hUS%$>ZwjR6UF zM7j3;m(JIsjp3D{gKQozY|Gi^`G%`f2iwEG?_VC) zP(ua-T)9r?m41@2Sqp>Fy1v+H_M*R^3I~kFU6s-3RNI$FH#qrxKEy3~G0+EB+(s{5 zS*g`(6AAUX3{{6M`gUoU>Eo2Jb<$w4vQobC^F&1=r$8>31w%CLfETXF! zBBQ-vSY?&zby5cdnd8%0F-SkyWBHW@g5<&7)?*+X3fRy1%GJv&sBgn&r}ot0fNaAn zb7z&vb01zP+TfL9xZ-o2wzpCzj;X0J+r^N>6_*t5R_uCze|%7_wv0p-!&=N>E>P5O z{&(S|NsAZ56|5(?;*$o%D`{iLo*7F~l*K}9>?RXqOy-Z(Gu%Q^`^0DSh&YfwkO=Ws zNQB|!Guusi1$cV)Y}w_@+1XXuKpL9nfo2WlL-T+Vg0EEe5AWuQsM%M-Ve^!LQMm5y>4?lyTxg2$ z)lwpIBbArG;a8NjnRCBrhD>2!Dv%m)~%lDdVZ-wM^BGN zqa5$%u1HvaozMwsPj!YRMx(*t-LUIMOV{-qmC-z{?U{33rE}(tvu5$%mzMG;LiDY9 zZti1=f&H7=k2sxf7V_6i&ndi7>FtY}r)q5058DAY0fxpE@>Xh{PVJ&EiW(D%ij0gzB7NASQpsdaXRtCfxsRb83@r!}s0G&O zprz8FP8XEvWiq)uv$u%w5ZL8FnnMM^(g%Ya zwPDh>Vy;1cntVP7BY~PDlmrwQ4}I@jD`$+$upFayjQRGv?@h!)0~^sO4hd>PT7(>B z8Da!h@z!nm_wL^NP_3@t@$mfPUnFfjqLFChLysWiq3T?wT*?%WQ>k((xjihwf|6Qe#zs52M!#--(Wqpi8i8{}ssti_o_wFmB{;*wB1{}@TxerJf1n;+u5J6@Z*BSK&%rM34BBK;7Q)|c z;=^HGqm2Uj8gYtUsa?SB2B)*`-|S6q7k5qE0c0GLO9y!l^j}uC;A~m;+2n#w47Jsd ze2*=e(0Z+=Hd)t)#nS#~S@vb}0zQB>kgXw&09U72)M8cFrmTPsva71FWjuym1IeQk z3_6wTdv=4-#bu4kZ&;xXRfPmX_@iqLSYSs{N;)Mqk*yM?o9ifRZtmBN7_AkE$xw^4 zB{uZYrk5`b0O1h6kx1NOek&{KnyuIvYD%9q8&>jM|!wJ0V6AkM#1LF-e(12f(=Lt zILaMsL1&h4+U)MS?l!s$H{WV{GEyCi%;Kv9bRe)dx=0e{KJVr(?EPK8Fa>-96tM9c z-rV$x@sl;cA(5+Ib79kT8DAKQlsg=B-9a|MavS@ED)31-*k6OJ7W0C5S>LtGc zruivSA+ph>k8rd3SvrLj^vJe+Zbc8z!cGWx`}?zjJl)8Hv97;+`_|OZbYzO|B7Y=3 z-S)Z37I{*qlQsYkMjLyY{C-Cxsg3vTaFHCm|&zw3{G49LP6LKrxv@Tk>Vn&p!7q$jq35U=`A_*`+wq9?G z*Naj@E1!&1tKRZ0nS61nRz$S+wK5<%H0k1qEOsM@k1mhwm2yr6V_^1uW1vGApbr zvvX&BbptRG8R`O9$mS}fbDdP7K=xx@())*kcaIZ`WYh4p#zkIs6(h$oBR!+FwWN2?vE|z>yQ%jczMYYR z@r;@Pj)P$Ug^(qJ?EA0p-KplL9`CsqLAMwSLB6939;)A)8jGUkOoeB5Umh?SeKRo8SqW{nUeZG`0U~|@{ygi>sLt10S@v1f=;TH5pI2bG`NF*FgIrAZ-`GT1CJ{4T|b zL({EQpZ*9y(w-+h`Ix2MGP+|pCc!wQphb=0^NHcdEAS1~`n#m>TOREE9)hBSHV$%| zN!pM!6Fc&7-QC=kBiTYaGJv>9=f@pGXMAEC7xN!%+J@U7NU&5+noyiU<1Z^aTLu#S zGJKR}XTuvi^I7QX(9WAR(4Vp8Hm4T!LGlOsdoy;nZ0w@RPP#y|06tO;U+npZcVgS$ zDUJbE;>ymxJbNVN!+8X6WP#RZI(1Oj^h z<&U8IF}$s z9fr@HhXDl;pk209eER64Fs5u;wQ0kK4XZNh5{l4l0sM~l;$<8(S>v7zustDbn`hv6`}J%LEx9aRTw zV2uV2QCQZ{*T5ZV91zDH5-cKihzLo|ObxaRzZ2sGvL^;Z5HN6#v!!Jy{OG1l1m)7e zrA`SMqQa7JxTZ#82m}m9|J)_8V_S`Zur0#pormrT^BAnE-+#P+|Jr_#NE(z#;{IuD zwrL-W+ehJcA&MGMV&0(_0Oe<4Nln+hHB+I#OKQx8c^?Soj_MmQmP86?-A7-%)1!RT z#uE$+!h>aNdZHXBa2!|In63Pq&{WbR2;7RbJvPB+vk6xbh9XgM%5&t-7H><;jVPPl zhAN2vR0C|0yDPHw>UOjF`qo7k0i+}pR2}e-ysLpyE>hhQ zjZ{yRB1#j_^GBA}(GKkE3`}74EUvxLXr{7H5T@-{cD@L)`aLMPY}mYPGRgst7bF>I zEQ8kzD8GHh;0=6YUIQpJ9q2LVmD7%+JRgZvmrG4Dt}wzkRYxKnOJAf(D`y-a(P4S> zxoD=p0aT?Q6J6;Cl=6OY4yN4UuzSdDh5>+tsQ)#V{6 zeUXoJtjN-a(i@^e;RIbN&v(q)CJjmzw3H5%e)z#W9%S2v3GN1A9!#(xk^KJUuy2?T zkPOsNnCLzbWq@O78TzDa*P7oO={e>#*O$|vY`XaBgQ1|)*3%{Y_SGecite6e6SDNy zIzZjH1T=W=0L2FxjzVz&($#H*EHxTpApT+-%zt0A z+~{3oF1&uwTNzpcIaU%3HJF84-PgKbN+iy0={`7FtGCbL`Mm0uU?_{@3rj)}BWWMU z*Uz;H*H5!X$WFEsP!3@&p)W9&OAJO9)uPwnja7D=cdfY*@_M_YkvmiNZ@2N!-kC0& zbF1f>7tVEe_q~ufcWpy=VzO4{=>vY*8VbH0Q)Y}}C-jR8r(%^LR# zKe<9G7Er>ZsEq+cL5+DWx~t=F@OMie$m1vrMm-KNfP%g-j2B)s4STk}CKjsn zRss`jKX)aklHf&{fV_8pLGFUv|3n&tl*KoxJf*VA) zUR+kaT;}myslR!5-;A3Rzk2*72+5m|or0l5t!}pzw5t(FRnP`79z3%F#cs6&ir(eY zNnHw`uNYxKjQL?qU?Cl(j$(xi5nOf`D0P59HE;EJte!*r-dVV?e)q~fcS_oa*Z=8H z$0u7XqEKxwfRSLhP33_%$}gA@Fg)?X5)q zplb3Qdg!hHe)qL^GMDZycecMc=G({9Ey>j`YOgjCMU-GfPQzo`O?H#c2UA5cCV>P| z98&PaL=>5*<3VE~%=DA>_4QZYx_x}%^7IcIDXRC6U;kkKXHTa@ETd#K*~$0kDKmOJ2eC9tg_f1au(l(sZ#Ue_a30WrdiualEqseK!H?7kN^sJ zbcUcH3=AbFi8nW4>Um_hT0Q&MbI=ouN)6>-%`MuQQ#dtm`9yn6Hc*bv7<~M{ zL9s4J3Uh>HTFG2WMpL-k@Vee4%0!FkMG9k-DhZRf5)2r2b+}^;_(I~sVwwSp??ILs z1tMcJ8G>;R&dJIep|Ee#aa_z|ES9*-g~v$_?8*Vr7X%?Nfb(zEoP;48L4ph}4U6gf uKsAn13t?Lm_ArwIVqeVApj!eUEdC!K=IHM8w*2A%0000 + + + + + + The Utility Library. + + + +

The PLIB general Utility Library.

+By Sebastian Ude +

Introduction

+The 'UL' utility library is primarily targeted towards hiding common +operating system functions behind a thin layer that makes them portable. +Additionally, it provides some helper classes or routines that were +usually written to support certain parts of the +
PLIB library, but at some point we felt that +they could be useful for user applications as well. +

+UL is a part of PLIB. + +

Contents:

+ + +

Quick reference:

+ + + +

Classes:

+

Non-class functions:

+ +
+ + + + + + + +
+ +

Misc. routines

+ +

ulSleep

+

+  void ulSleep ( int seconds ) ;
+
+Now comes a typical example of what the UL library does. If you need to +'sleep' for, say, 3 seconds, then under Linux/UNIX, you'd need to call: +

+  sleep ( 3 ) ;
+
+But under MS-Windows, you have to say: +

+  Sleep ( 3000 ) ;
+
+In order to avoid writing non-portable code, you can instead call: +

+  ulSleep ( 3 ) ;
+
+...under either operating system. + +

+

ulMilliSecondSleep

+

+  void ulMilliSecondSleep ( int milliseconds ) ;
+
+Same as ulSleep, except that it sleeps (as you +may have guessed) a certain number of milliseconds instead of seconds. + +

File handling

+ +

ulFindFile

+

+  void ulFindFile ( char *filenameOutput, const char *path, const char * tfnameInput, const char *sAPOM ) ;
+
+Basically, this utility function adds tfnameInput to the path and puts this +into the buffer filenameOutput. +

+It handles special chars in path: +

    +
  • ";;" is replaced by ";" +
  • "$$" is replaced by "$" +
  • "$(APOM)" is replaced by sAPOM +
+If there are ";" in path, the path-variable is interpreted as several paths +"segments", delimited by ";". The first file found by this function is +returned. It looks from left to right. A segment may end in $(...). ulFindFile +will then look in in this path and recursively in all the sub-paths. +

+Some examples: +

+To load *.MDl-models, it is very nice to set the texture path to +"$(APOM);$(APOM)/texture;$(APOM)/../texture". This consists of three segments +and tells ulFindFile to look in the path of the model, in a subpath texture +and in a path texture "besides" the path of the model. Some *.mdl-models are +shipped in a directory which contains a "texture"-directory, a +"Model"-directory and others. In this case you find the texture in +"$(APOM)/../texture". +

+Another example: You have all your textures in a directory-structure under +/roomplan: +

+textures --+-- Wallpapers
+           |
+		   +-- Wood --+-- Oak
+           |          |
+		   |          +-- pine
+           ...
+
+Then you should simply use the following texture path: +"/roomplan/$(...)" + +

+

ulFileExists

+

+  bool ulFileExists ( const char *fileName ) ;
+
+Returns "true" if a file with the name 'fileName' exists, or "false" if +it does not. + +

+

ulIsAbsolutePathName

+

+  int ulIsAbsolutePathName ( const char *pathname ) ;
+
+Returns '1' if 'pathname' is an absolute pathname or '0' if it is an +relative one. + +

+

ulGetCWD

+

+  char * ulGetCWD ( char *result, int maxlength ) ;
+
+Stores the current working directory in 'result', which has enough space +for 'maxlength' characters, including the trailing '\0'. On success, +'result' is returned. + +

+

ulMakePath

+

+  char * ulMakePath ( char *path, const char *dir, const char *fname ) ;
+
+Concatenates the strings 'dir' and 'fname', puts the system's slash character +inbetween and stores stores the result in 'path'. Be sure that the buffer +'path' is large enough to store 'strlen ( dir ) + strlen ( fname ) + 2' +(slash and trailing '\0') characters. + +

Directory handling

+ +

struct ulDir

+This structure provides a portable way to read directories. To allocate and +initialize a new ulDir structure, call: +

+  ulDir * ulOpenDir ( const char* dirname ) ;
+
+This function returns a pointer to a newly allocated ulDir structure on +success or a NULL pointer if the specified directory could not be read. +

+After you have constructed an ulDir structure, the directory content can +be read with subsequent calls to: +


+  struct ulDirEnt
+  {
+    char d_name [ UL_NAME_MAX+1 ] ;
+    bool d_isdir ;
+  } ;
+  ulDirEnt * ulReadDir ( ulDir *dir ) ;
+
+This function returns a pointer to a ulDirEnt structure which resides in +the corresponding ulDir structure and which has the above form. The "d_isdir" +flag indicates if a directory entry is another directory. If the end of the +directory has been reached, the function returns NULL. +

+To free an ulDir object and to close the associated directory stream, +please call: +


+  void ulCloseDir ( ulDir *dir ) ;
+
+ +

Data storage

+ +

class ulList

+

+  ulList::ulList ( int init_max = 1 ) ;
+
+This class stores a list of generic (void*) pointers using an +automatically-growing array. Since the process of resizing the internal +array is rather expensive, it is important that one picks a reasonable default +array size (init_max) when constructing an ulList object if performance +matters. Remember that a too large size means that some memory is wasted (but +memory is cheap nowadays), while a too small one means that expensive array +resize operations will be necessary later. +

+If you have absolutely no clue about how many entities will be stored, +you should probably look at the linked list class +below, which has it's own disadvantages, though. +

+Once you have constructed an ulList (hopefully with a good initial size), +you can insert an element using one of the following methods: +


+  void ulList::addEntity ( void *entity ) ;
+  void ulList::addBefore ( int n, void *entity ) ;
+
+While the first one simply adds the new entity to the tail of the list, +the second one lets you specify an exact position (0 is the first element). +Note that due to it's array implementation, inserting an entity at the head +or the middle of the ulList requires that the following entities are all +shifted one array slot to the right, which can be a rather expensive +operation. +

+These functions automatically check whether there is room for one more element +and double the internal array's size if necessary. However, as said +previously, resizing the array is a rather expensive process, so it is not +recommended that you rely too much on this behavior. Instead, pick a good +initial array size. +

+Note that ulList allows you to have multiple entities with the same data +value in the list. Please also note that ulList does not make it's own copy +of the data pointed to by 'entity'. It is up to you to take care of that the +memory region that 'entity' points to holds something useful as long as the +ulList exists. Be especially careful with addresses of variables that have a +limited lifetime. +

+To retrieve an entity, call: +


+  void * ulList::getEntity ( unsigned int n ) ;
+
+Where 'n' is the position of the entity in the list. If 'n' is not a valid +index, NULL is returned. Once you have called this function, you can use +subsequent calls to +

+  void * ulList::getNextEntity ( void ) ;
+
+in order to retrieve the following entities in the list. If there are no more +entities, this function will return NULL. +

+To remove an entity, call one of: +


+  void ulList::removeEntity ( unsigned int n ) ;
+  void ulList::removeEntity ( void *entity ) ;
+
+Where the second function removes the first entity with the specified +data value in case there are multiple ones. +

+To replace the value of an entity, call one of: +


+  void ulList::replaceEntity ( unsigned int n, void *new_entity ) ;
+  void ulList::replaceEntity ( void *old_entity, void *new_entity ) ;
+
+Where the second function will replace the value of the first entity +with the specified old data value in case there are multiple ones. +

+And finally, you can retrieve the number of entities stored in the list +(that's not necessarily the internal array's size), remove all +entities or retrieve the position of an entity by specifying it's data +value: +


+  void ulList::getNumEntities ( void ) const ;
+  void ulList::removeAllEntities () ;
+  int  ulList::searchForEntity ( void *entity ) const ;
+
+Where the latter returns a negative value if no entity with the specified +value was found in the list, and otherwise the position of the first +entity with the specified data value. + +

+

class ulLinkedList

+

+  ulLinkedList::ulLinkedList () ;
+
+The ulLinkedList class stores generic (void*) pointers using a linked list +of nodes where each node maintains a pointer to the next node. +

+This technique has some advantages compared to an array implementation of a +list like ulList. +

    +
  • +No wasted memory. When storing data using arrays (as +ulList does), it is a common practice to choose an array +size that seems "large enough". Most of the time the number of entities stored +will be smaller than the available array slots, so some memory space is wasted. +With a linked list, we allocate single nodes. No memory is wasted (except a +few bytes for each node's pointer to the next node). + +
  • +Resizing an array is an expensive operation, since usually the whole array +content has to be copied from the old to the new memory location. We do not +have this problem with linked lists. + +
  • +In contrast to arrays, it is a cheap operation to insert or remove entities +to / from the head or middle of a linked list. While with an array, the +following entities must all be shifted one slot to the left or right, we +simply need to (re)connect a few pointers with a linked list. +
+There are some disadvantages, though: +
    +
  • +In contrast to an array, there is no cheap way to locate the n-th entity in a +linked list. With an array, you simply have to add the desired position +multiplied with the size of an element to the array's base address and +look at the content of the memory at this location - a very simple operation. +With a linked list, however, we need to iterate over all nodes 'n' times, +so locating the 1000th node is a rather expensive operation. + +
  • +One may argue that insertion operations are more expensive with linked lists +than with arrays, since we allocate single nodes from the heap in contrast +to an array, where the whole list is one huge allocated block of memory. +Although allocation from the heap is indeed rather expensive, this is not +true when it comes to insertions to the middle of a list, these are usually +way more expensive with arrays. Furthermore, a list implemented using +an array such as ulList may make resizes of the array +necessary on insertions, which are again rather expensive. +
+Decide yourself if a list implemented as an array such as +ulList or a linked list implementation like this one fits +your needs better. +

+Once you have constructed a ulLinkedList object, you can insert a node using +one of: +


+  void ulLinkedList::appendNode ( void *data ) ;
+  void ulLinkedList::prependNode ( void *data ) ;
+  void ulLinkedList::insertNode ( void *data, int pos ) ;
+
+While "appendNode" adds the new node at the tail of the list, "prependNode" +will place the new node at the head of the list, and "insertNode" allows +you to specify the desired position ('0' is the first node) yourself; 'pos' +must be either '0' or a number between '0' and the number of nodes minus one. +"prependNode" is equal to calling "insertNode" with pos == 0. +

+Note that ulLinkedList allows you to have two nodes with the same data in +the list. Also note that just as ulList, ulLinkedList +does not make it's own copy of the memory pointed to by 'data', so make sure +that the corresponding memory location holds something useful as long as the +list exists. +

+To retrieve the number of nodes in the list, call: +


+  int ulLinkedList::getNumNodes ( void ) const ;
+
+

+To retrieve the data of the node at a certain position in the list, call: +


+  void * ulLinkedList::getNodeData ( int pos ) const ;
+
+Where 'pos' must be a number between '0' and the number of nodes minus one, +again. +

+If you need to retrieve the position of a node in the list by specifying it's +data value, call: +


+  int ulLinkedList::getNodePosition ( void *data ) const ;
+
+If there is more than one node whose data value is 'data' in the list, this +function will return the position of the first one. If there is no +node with the specified data value in the list, this function will return a +negative number to indicate failure. +

+Checking if the return value of "getNodePosition" is non-negative is also +the recommended way to determine whether there exists at least one node with +a certain data value in a list. +

+To remove a node from the list, call one of: +


+  bool ulLinkedList::removeNode ( void *data ) ;
+  void * ulLinkedList::removeNode ( int pos ) ;
+
+Where the first function returns 'true' if the node was sucessfully removed or +'false' if it could not find a node whose value is 'data'. In case there is +more than one node whose data value is 'data', it will remove the first +one. With the second function, 'pos' has to be a number between '0' and the +number of nodes minus one. It's return value is the removed node's data +value. +

+To iterate over the list (starting from the head) and to have a custom +function being called for each node's data pointer, call: +


+  typedef bool (*ulIterateFunc)( void *data, void *user_data ) ;
+  void * ulLinkedList::forEach ( ulIterateFunc fn, void *user_data = NULL ) const ;
+
+The iteration process will stop if your ulIterateFunc returns 'false', in +which case "forEach" returns the data value of the node at which the iteration +stopped, or if the tail of the list has been reached, in which case "forEach" +returns NULL. The user_data pointer will be passed to your ulIterateFunc as +the second argument. +

+ulLinkedList allows you to maintain a sorted list as an option. To set up +a sorted list, simply be sure to insert all nodes using the sorted insertion +function as soon as there is at least one node in the list. It's prototype is: +


+  typedef int (*ulCompareFunc)( const void *data1, const void *data2 ) ;
+  int ulList::insertSorted ( void *data, ulCompareFunc comparefn ) ;
+
+Where 'comparefn' is your custom comparison function that takes two +data pointers, compares them and returns a memcmp / strcmp-like result: +
    +
  • +A number less than zero if "data1" is "smaller" than "data2", that +means if "data1" had to be inserted before "data2" in the list. + +
  • +A number equal to zero if "data1" is equal to "data2", that means if +"data" had to be inserted immediately before or after "data2" in the list. + +
  • +A number greater than zero if "data1" is "greater" than "data2", +that means if "data" had to be inserted after "data2" in the list. +
+The return value of "insertSorted" is the position of the new node in the list +on success, or a negative value if you tried to do a sorted insertion on a +non-sorted list, that is a list which contains more than one node of which +at least one was not inserted using the sorted insertion function. In the +latter case, the new node would not have been inserted to the list. +

+To determine whether a list is sorted or not, call the following function: +


+  bool ulLinkedList::isSorted ( void ) const ;
+
+Note that with a linked list, you must not modify the criteria of a +node's data that is used for sorting without removing the node from the list +and re-inserting it. +

+Finally, you can empty a list (remove all nodes) by calling: +


+  typedef bool (*ulIterateFunc)( const void *data ) ;
+  void ulList::empty ( ulIterateFunc destroyfn = NULL, void *user_data = NULL ) ;
+
+Where "destroyfn" is an optionally specified function that will be called +with each destroyed node's data pointer as the first and with user_data as +the second argument. This is for example useful if the list entries are +pointers to dynamically allocated objects that have to be freed on destruction +of the list. The return value of the specified function is ignored. + +

+

ulHashTable

+Not yet. + +

Error handling

+ +

ulSetErrorCallback

+

+  typedef void (*ulErrorCallback) ( enum ulSeverity severity, char* msg ) ;
+  void ulSetErrorCallback ( ulErrorCallback cb ) ;
+
+PLIB has an internal error handling system +that the subsystems use to report debug, warning or error messages. +An application can set up an error callback that PLIB will call whenever such +a message occurs in addition to printing the message on the user's terminal. +

+ulSeverety indicates the importance of an message and is currently defined +the following way: +


+  enum ulSeverity
+  {
+    UL_DEBUG,
+    UL_WARNING,
+    UL_FATAL
+  } ;
+
+Where +
    +
  • UL_DEBUG are unimportant debug messages that can usually safely be ignored +
  • UL_WARNING are important warning messages that should not be ignored +
  • UL_FATAL are fatal errors that PLIB can not recover from +
+ +

+

ulGetErrorCallback

+

+  typedef void (*ulErrorCallback) ( enum ulSeverity severity, char* msg ) ;
+  ulErrorCallback ulGetErrorCallback ( void ) ;
+
+Returns the current error callback (if +any, else NULL is returned). + +

+

ulGetError

+

+  char * ulGetError ( void ) ;
+
+Returns a pointer to the error buffer, that is, the last error message or an +empty string if there were not any error messages or if the error buffer has +just been cleared. + +

+

ulClearError

+

+  void ulClearError ( void ) ;
+
+Clears the error buffer. + +

Misc. classes

+ +

class ulDynamicLibrary

+

+  ulDynamicLibrary::ulDynamicLibrary ( const char *libname ) ;
+
+This class provides a portable way to load a dynamic library and to retrieve +the memory address of a specific function afterwards. When constructing an +ulDynamicLibrary object, you have to specify the name of the dynamic library +you want to operate on without the platform-specific file extension +for dynamic libraries. +

+Afterwards, you can retrieve the memory address where a function / symbol of +the library has been loaded by calling: +


+  void * ulDynamicLibrary::getFuncAddress ( const char *funcname ) :
+
+This function returns NULL if the specified symbol was not found. + +

+

ulClock

+

+  ulClock::ulClock () ;
+
+No further documentation yet. + +

ulPropertySet

+Not yet. + +

String handling

+ +

ulStrEqual

+

+  int ulStrEqual ( const char *s1, const char *s2 ) ;
+
+This function provides a portable way to compare two strings while ignoring +the case of the characters. We need it since half of the machines on the +planet provide strcasecmp and the other half stricmp for this purpose. +

+In contrast to the libc string comparison routines, this routine returns '1' +if the strings are equal and '0' if they are not. + +

+

ulStrNEqual

+

+  int ulStrNEqual ( const char *s1, const char *s2, int len ) ;
+
+Same as ulStrEqual, except that it only compares the first 'len' characters +of the strings. + +

+

Endian handling

+

+  bool ulIsLittleEndian ( void ) ;
+  bool ulIsBigEndian ( void ) ;
+
+  unsigned short ulEndianLittle16 ( unsigned short x ) ;
+  unsigned int ulEndianLittle32 ( unsigned int x ) ;
+  float ulEndianLittleFloat ( float x ) ;
+
+  unsigned short ulEndianBig16 ( unsigned short x ) ;
+  unsigned int ulEndianBig32 ( unsigned int x ) ;
+  float ulEndianBigFloat ( float x ) ;
+
+  void ulEndianLittleArray16 ( unsigned short *x, int length ) ;
+  void ulEndianLittleArray32 ( unsigned int *x, int length ) ;
+  void ulEndianLittleArrayFloat ( float *x, int length ) ;
+
+  void ulEndianBigArray16 ( unsigned short *x, int length ) ;
+  void ulEndianBigArray32 ( unsigned int *x, int length ) ;
+  void ulEndianBigArrayFloat ( float *x, int length ) ;
+
+  unsigned short ulEndianReadLittle16 ( FILE *f ) ;
+  unsigned int ulEndianReadLittle32 ( FILE *f ) ;
+  float ulEndianReadLittleFloat ( FILE *f ) ;
+
+  unsigned short ulEndianReadBig16 ( FILE *f ) ;
+  unsigned int ulEndianReadBig32 ( FILE *f ) ;
+  float ulEndianReadBigFloat ( FILE *f ) ;
+
+  size_t ulEndianWriteLittle16 ( FILE *f, unsigned short x ) ;
+  size_t ulEndianWriteLittle32 ( FILE *f, unsigned int x ) ;
+  size_t ulEndianWriteLittleFloat ( FILE *f, float x ) ;
+
+  size_t ulEndianWriteBig16 ( FILE *f, unsigned short x ) ;
+  size_t ulEndianWriteBig32 ( FILE *f, unsigned int x ) ;
+  size_t ulEndianWriteBigFloat ( FILE *f, float x ) ;
+
+No further documentation yet. + +
+ + +
+Valid HTML 4.0! + +
+Sebastian Ude <ude@handshake.de> +
+
+ + + diff --git a/doc/valid-html40.png b/doc/valid-html40.png new file mode 100644 index 0000000000000000000000000000000000000000..a6cf10631b93ee74681c3a67c398ef266bcfd876 GIT binary patch literal 799 zcmV+)1K|9LP)EFRxSFxdFV-p)BjGsHOvz;-W zYXj!NI*#6$G0V$PtYA%KHuH2PtD**}@4ymQt9_hxKj%w@BJIWAxwcXgB9 zg}oav_Qac`JBHA^)`UEYqKWyh8P`oj!gt}|Y#SPBf+Ut08i7YE4({WjghB^+0n!l`Bhguxh8P84kR1ST)*% zd?UL7BTco;a2Bmno~CPefd?Ydacu~J;XiYU_h(q)@%55A1|iQb%WHQzgs9EZw(BWr zRJ&kuAJ4E9hiy1D!lk>w9T&GlJ+Pg9kHVC@B9wBqr4QnumQp0RxGe1ab9rorwwp)P zBdy`sG+E5U2^K=47R5%5XEM20=W*!x%w*lS1217I@?y26B|=Eas1n7xE0WCaYwtWw zH7+RAt3IA(TEf>}ro49QJiU~;HQxP}hMb!D?XUl7-X!n;u1M~`_+WY9Gw=JESm5k| d_N002ovPDHLkV1i=Ke=Yz3 literal 0 HcmV?d00001 diff --git a/doc/whats_inside.html b/doc/whats_inside.html new file mode 100644 index 0000000..ccba9d1 --- /dev/null +++ b/doc/whats_inside.html @@ -0,0 +1,196 @@ + + + + + PLIB: What's In PLIB + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + LIBRARY. +

+
+
+
    +And Lo, the Lion and the Sheep shall lie down together - but the Sheep won't +get much Sleep. +
    +-- Woody Allen +
+
+ +Sourceforge Logo +
+
+ + + + + + +
+HOME
+WHAT'S INSIDE
+LICENSE
+CONTACTS
+REQUIREMENTS
+GALLERY
+DOWNLOAD & INSTALL
+EXAMPLES
+ +SG +SSG
+ +SSGAUX +FNT
+ +PUI +PSL + +UL +JS
+ +NET +SL
+ +PW +PUAUX
+ + +
+

What's Inside PLIB?

+PLIB is comprised of a number of semi-autonomous libraries +that you can pretty much mix and match - using as much or +as little PLIB as you need. The components libraries are: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Picoscopic User Interface Library (PUI)A fully portable set of GUI widgets that need OpenGL and C++ + to operate. Useful for games because rendering these widgets is + performed very quickly by your 3D hardware.
Sound Library (SL)A fully portable Audio driver suited to C++, GLUT + and realtime applications that need low latency. Includes + a simple 'MOD' music loader/player.
Standard Geometry Library(SG)A set of matrix and vector math functions that was + specifically written to simplify the writing of efficient OpenGL + programs. You need to include this code into any SSG applications. + Widely used within PLIB itself. +
Simple Scene Graph Library (SSG)A scene graph library layered on top of OpenGL. Also contains code to load and save lots of 3D file formats.
PUI Auxiliary Library (PUAUX)Additional functionality layered onto PUI - not every PUI program + will need these but they can perhaps save you some effort.
SSG Auxiliary Library (SSGA)Additional functionality layered onto SSG - not every SSG program + will need these but they can perhaps save you some effort.
Joystick wrappers (JS)Supports more Joysticks with more axes and buttons than GLUT.
Fonts'n'Text Library (FNT)Supports text output in OpenGL using texture mapped fonts. + These are much faster to render than GLUT's fonts.
Utility Library (UL)A simple portability library to hide basic operating system + incompatibilities. Widely used within PLIB itself.
Pegasus Network Library (NET)Pegasus is a C++ library for assist you in adding + networking to your games.
PLIB Scripting Language (PSL)A very lightweight, stackless C-like scripting language.
PLIB Windowing Library (PW)A lightweight windowing library. This is a very basic + library - it only supports a single, double-buffered RGB + OpenGL window - with support for keyboard and mouse. + Simple though it is, it's usually plenty for OpenGL games + and other simple OpenGL applications.
+

+

Goodies: Samples and Demo's

+There are quite a few example programs that each show off +one aspect of PLIB's use. We also distribute a set of demonstration +programs. These tend to be more complete and usable applications - some +games, some tools. These also show off PLIB's features - but in a +more holistic way than the example programs. + +

Documentation.

+All documentation is available online (click on the package name +in the section above) and distributed with PLIB in HTML. +

+There are some interdependencies between the PLIB components - all +need the common utility library - many need the standard geometry +library for matix operations: + + + + + +
+
    +
  • PW is independent +
  • UL is independent +
  • JS depends only on UL +
  • SL depends only on UL +
  • SG depends only on UL +
  • NET depends only on UL +
+
+
    +
  • FNT depends on UL and SG +
  • SSG depends on UL and SG +
  • SSGAUX depends on UL, SG and SSG +
  • PUI depends on UL, SG and FNT +
  • PUAUX depends on UL, SG, FNT and PUI +
  • PSL depends on UL, SG and SSG +
+
+ +

+
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/doc/whats_inside.png b/doc/whats_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..17002c5d3fd533f0a94daa85856a9cb90f802f7c GIT binary patch literal 5137 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 literal 0 HcmV?d00001 diff --git a/examples/AUTHORS b/examples/AUTHORS new file mode 100644 index 0000000..fa63a8a --- /dev/null +++ b/examples/AUTHORS @@ -0,0 +1,62 @@ +PLIB was originally written by Steve Baker as a solo project - but +has subsequently been greatly enhanced and expanded by a 'cast of +thousands'. + +Most important of all - the 'official' PLIB developers: + + Alex Perry + Curtis Olson + Ben Woodhead + Dave McClurg + Gerard Decatrel + Gil Carter + Giancarlo Niccolai + James Jones + John Fay ("Mr. PUI") + Juergen Marquardt + Mark Vallevand + Marten Stromberg + Nathan Matias + Norman Vine + Per Liedman + Sam Stickland + Sebastian Ude + Steve Baker <--- Hmmm - must sort this list by SECOND name! + Will Lachance + Wolfram Kuss + +Apologies to anyone I've left out - please let me know +who you are and I'll add you in. + + Christopher St.John + Christian Mayer + Darrell Walisser + Dave Heil + Erik Hofman + Kevin Thompson + Karl Kremer + Negative0 <---- Who? Some kind of 1's complement arithmetic fanatic? + Mark Danks + Michael Kurth + Sean Palmer + Simon Britnell + Themie Gouthas + Tony Knienieder + Trent Gamblin + +Special mention must go to: + + Daryll Strauss (GLIDE port to Linux) + David Bucciarelli (Mesa -> GLIDE interface) + Brian Paul (Mesa) + Curtis Olson (FlightGear guru and all-round "Mr Nice Guy", + who nagged me into writing most of this stuff!) + Vasily Lewis (Tireless maintainer and sysadmin at Woodsoup + where PLIB was first hosted) + Toru Egashira (Who wrote a program called 'NSPmod' which was + the original basis of the SL 'MOD' player - + although the original code has been almost + entirely rewritten in the process of merging + it with SL. + Everyone at OPENGL-GAMEDEV-L + diff --git a/examples/COPYING b/examples/COPYING new file mode 100644 index 0000000..eeb586b --- /dev/null +++ b/examples/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/examples/COPYING.DATA b/examples/COPYING.DATA new file mode 100644 index 0000000..0473e4d --- /dev/null +++ b/examples/COPYING.DATA @@ -0,0 +1,5 @@ +All fonts, textures, models and other data is licensed under the same +license as the code unless otherwise stated. + +Please see COPYING for more details. + diff --git a/examples/ChangeLog b/examples/ChangeLog new file mode 100644 index 0000000..041368b --- /dev/null +++ b/examples/ChangeLog @@ -0,0 +1,34 @@ + +PLIB_Examples Change Log. +========================= + +PLIB_Examples v1.8.0 (March 19th 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added examples for PW, PSL, ssgAux and puAux. + + +PLIB_Examples v1.6.1 (Sept 4th 2002) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Added missing files from the 'water/data' directory. + +PLIB_Examples v1.4.1 (June 25th 2001) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Changes are now reported in main 'PLIB' CHANGES + file. + +PLIB_Examples v1.1.8 (Jan 23rd 2000) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Updated examples and documents to + match latest PLIB main code. + +PLIB_Examples v1.1.2 (Nov 8th 1999) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Example programs split from main + PLIB distribution. + * Example programs are now under GPL + rather than LGPL. Previous releases + of these programs that were released + under LGPL may still be used under + those terms. + diff --git a/examples/INSTALL b/examples/INSTALL new file mode 100644 index 0000000..05782eb --- /dev/null +++ b/examples/INSTALL @@ -0,0 +1,203 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Configure Options +================= +On some occations opengl are not found on systems. Additional +commandline parameters have been added to resolve this issue. + +--with-gl GL prefix + +Examples +./configure --with-gl=/usr/local + +This should help with locating GL when configure is unable to find +the files. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Compiling for Windows with MSVC +=============================== + +You can easily compile plib with MSVC 6. First compile plib +itself. Then open the included workspace in MSVC, choose what you +want to compile (build/set active configuration) and then compile (F7). + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..edaab67 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,16 @@ +SUBDIRS = src + +EXTRA_DIST = README COPYING.DATA plib_examples.dsp plib_examples.dsw install-sh missing autogen.sh + +dist-hook: + tar cf - doc \ + src/ssg/tux/data \ + src/ssg/water/data \ + src/ssg/sky/data \ + src/ssg/majik/data \ + src/ssg/shrubs/data \ + src/psl/data \ + src/fnt/data \ + data \ + | ( cd $(distdir) ; tar xf - ) + diff --git a/examples/NEWS b/examples/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/examples/README b/examples/README new file mode 100644 index 0000000..bf3dce0 --- /dev/null +++ b/examples/README @@ -0,0 +1,23 @@ + +PLIB Examples and Documents +=========================== + by Steve Baker. + +LICENSING: + +These example programs are offered as OpenSource freeware +under the provisions of the GNU Public +License (GPL). Those terms and conditions are +detailed in the file 'COPYING' in this directory. + +REQUIREMENTS: + +In addition to the PLIB libraries that I provide, you +will also need OpenGL (OpenGL 1.1 or later - +Mesa 3.0 or later) and GLUT (The OpenGL utilities +toolkit - version 3.7 or later) and a hardware 3D +graphics accellerator that supports OpenGL. + + by Steve Baker. + + http://www.sjbaker.org diff --git a/examples/autogen.sh b/examples/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/examples/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/examples/configure.in b/examples/configure.in new file mode 100644 index 0000000..30de1bb --- /dev/null +++ b/examples/configure.in @@ -0,0 +1,313 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/js/js_demo.cxx) +AM_INIT_AUTOMAKE(plib_examples, 1.8.5) + + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL + +AC_LANG_CPLUSPLUS + + +dnl Command line arguments. + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + + +AC_ARG_ENABLE(fnt, +[ --enable-fnt build FNT examples default: yes],, +enable_fnt=yes) + +AC_ARG_ENABLE(js, +[ --enable-js build JS examples default: yes],, +enable_js=yes) + +AC_ARG_ENABLE(pw, +[ --enable-pw build PW examples default: yes],, +enable_pw=yes) + +AC_ARG_ENABLE(net, +[ --enable-net build NET examples default: yes],, +enable_net=yes) + +AC_ARG_ENABLE(pui, +[ --enable-pui build PUI examples default: yes],, +enable_pui=yes) + +AC_ARG_ENABLE(sg, +[ --enable-sg build SG examples default: yes],, +enable_sg=yes) + +AC_ARG_ENABLE(psl, +[ --enable-psl build PSL examples default: yes],, +enable_psl=yes) + +AC_ARG_ENABLE(sl, +[ --enable-sl build SL examples default: yes],, +enable_sl=yes) + +AC_ARG_ENABLE(ssg, +[ --enable-ssg build SSG/ssgAux examples default: yes],, +enable_ssg=yes) + +AC_ARG_ENABLE(ul, +[ --enable-ul build UTIL examples default: yes],, +enable_ul=yes) + + +if test "x$enable_fnt" = "xyes"; then need_opengl=yes; fi +if test "x$enable_pui" = "xyes"; then need_opengl=yes; fi +if test "x$enable_sg" = "xyes"; then need_opengl=yes; fi +if test "x$enable_ssg" = "xyes"; then need_opengl=yes; fi +if test "x$enable_ssgaux" = "xyes"; then need_opengl=yes; fi + +AM_CONDITIONAL(BUILD_FNT, test "x$enable_fnt" = "xyes") +AM_CONDITIONAL(BUILD_JS, test "x$enable_js" = "xyes") +AM_CONDITIONAL(BUILD_PW, test "x$enable_pw" = "xyes") +AM_CONDITIONAL(BUILD_NET, test "x$enable_net" = "xyes") +AM_CONDITIONAL(BUILD_PUI, test "x$enable_pui" = "xyes") +AM_CONDITIONAL(BUILD_SG, test "x$enable_sg" = "xyes") +AM_CONDITIONAL(BUILD_PSL, test "x$enable_psl" = "xyes") +AM_CONDITIONAL(BUILD_SL, test "x$enable_sl" = "xyes") +AM_CONDITIONAL(BUILD_SSG, test "x$enable_ssg" = "xyes") +AM_CONDITIONAL(BUILD_UL, test "x$enable_ul" = "xyes") + +if test "x$need_opengl" = "xyes" && test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin*) + + dnl CygWin under Windoze. + + dnl Sound / joystick stuff, mmtimer (ulClock !) + LIBS="$LIBS -lwinmm" + + dnl OpenGL libraries + OGL_LIBS="-lglu32 -lopengl32 -lgdi32" + GLUT_LIBS="-lglut32" + ;; +*-*-mingw32*) + dnl Mingw under Windoze. + + dnl Sound / joystick stuff, mmtimer (ulClock !) + LIBS="$LIBS -lwinmm -lwsock32" + + + dnl OpenGL libraries + OGL_LIBS="-lglu32 -lopengl32 -lgdi32" + GLUT_LIBS="-lglut32" + ;; +*-apple-darwin*) + dnl Mac OS X + + dnl OpenGL libraries + OGL_LIBS="-framework OpenGL" + GLUT_LIBS="-framework GLUT -lobjc" + + dnl Sound libraries + SND_LIBS="-framework Carbon" + ;; +*) + if test "x$need_opengl" = "xyes"; then + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + OGL_LIBS="$X_PRE_LIBS -lXi -lXmu -lXext -lX11 $X_EXTRA_LIBS -lm" + +dnl ========================================================= +dnl if test "x$x_includes" != "x"; then +dnl CPPFLAGS="$CPPFLAGS -I$x_includes" +dnl fi +dnl ========================================================= + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(pthread, pthread_create) + + AC_CHECK_LIB(GL, glNewList, OGL_LIBS="-lGL $OGL_LIBS",,$OGL_LIBS) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList, OGL_LIBS="-lMesaGL $OGL_LIBS", + AC_MSG_ERROR([could not find working GL library]), + $OGL_LIBS) + fi + + AC_CHECK_LIB(GLU, gluLookAt, OGL_LIBS="-lGLU $OGL_LIBS",,$OGL_LIBS) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt, + OGL_LIBS="-lMesaGLU $OGL_LIBS", + AC_MSG_ERROR([could not find working GLU library]), + $OGL_LIBS) + fi + + dnl check for glut...or freeglut + + AC_CHECK_LIB(freeglut, glutGetModifiers, GLUT_LIBS="-lfreeglut",, + $OGL_LIBS) + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xno" ; then + dnl if no freeglut, check for real GLUT + AC_CHECK_LIB(glut, glutGetModifiers, GLUT_LIBS="-lglut", + AC_MSG_ERROR([could not find working GLUT library]), + $OGL_LIBS) + fi + fi + + AC_CHECK_LIB(dl, dlclose) + + dnl Check SGI audio library + AC_CHECK_LIB(audio, ALopenport, SND_LIBS="-laudio") + ;; +esac + +dnl Check PLIB library +AC_CHECK_LIB(plibul, ulInit, true, +AC_MSG_ERROR([there seems to be a problem with the PLIB libraries])) + + +AC_SUBST(OGL_LIBS) +AC_SUBST(GLUT_LIBS) +AC_SUBST(SND_LIBS) + + +dnl Checks for header files. + +AC_HEADER_STDC + +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +if test "x$need_opengl" = "xyes"; then + dnl Check for OpenGl header files + + case "${host}" in + *-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; + *-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADERS(OpenGL/gl.h OpenGL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GLUT/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; + *) + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + AC_CHECK_HEADER(GL/freeglut.h, AC_DEFINE(FREEGLUT_IS_PRESENT), + AC_MSG_ERROR([freeglut header file not found])) + else + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + fi + ;; + esac +fi + +AC_CHECK_HEADER(plib/ul.h,, AC_MSG_ERROR([PLIB header files not found])) + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + + +AC_OUTPUT( \ + Makefile \ + src/Makefile \ + src/js/Makefile \ + src/pw/Makefile \ + src/pui/Makefile \ + src/sg/Makefile \ + src/sl/Makefile \ + src/ssg/Makefile \ + src/ssg/tux/Makefile \ + src/ssg/majik/Makefile \ + src/ssg/load_save/Makefile \ + src/ssg/state_test/Makefile \ + src/ssg/tween_test/Makefile \ + src/ssg/viewer/Makefile \ + src/ssg/water/Makefile \ + src/ssg/sky/Makefile \ + src/ssg/dynamics/Makefile \ + src/ssg/shrubs/Makefile \ + src/ssg/shapes/Makefile \ + src/fnt/Makefile \ + src/net/Makefile \ + src/net/client_server/Makefile \ + src/net/http_get/Makefile \ + src/net/net_echo/Makefile \ + src/psl/Makefile \ + src/util/Makefile ) + +if test "x$need_opengl" = "xyes"; then + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + glut_msg="yes (found freeglut)" + else + glut_msg="yes (found GLUT)" + fi +else + glut_msg="no" +fi + +AC_MSG_RESULT( +[PLIB configuration information: + + Building FNT examples: $enable_fnt + Building JS examples: $enable_js + Building PW examples: $enable_pw + Building NET examples: $enable_net + Building PUI examples: $enable_pui + Building SG examples: $enable_sg + Building PSL examples: $enable_psl + Building SL examples: $enable_sl + Building SSG/ssgAux examples: $enable_ssg + Building UTIL examples: $enable_ul + + Need GLUT: $glut_msg +]) + diff --git a/examples/plib_examples.dsp b/examples/plib_examples.dsp new file mode 100644 index 0000000..f6c5c9f --- /dev/null +++ b/examples/plib_examples.dsp @@ -0,0 +1,61 @@ +# Microsoft Developer Studio Project File - Name="plib_examples" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=plib_examples - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "plib_examples.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "plib_examples.mak" CFG="plib_examples - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "plib_examples - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "plib_examples - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +MTL=midl.exe + +!IF "$(CFG)" == "plib_examples - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "plib_examples - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "plib_examples___Win32_Debug" +# PROP BASE Intermediate_Dir "plib_examples___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "plib_examples - Win32 Release" +# Name "plib_examples - Win32 Debug" +# End Target +# End Project diff --git a/examples/plib_examples.dsw b/examples/plib_examples.dsw new file mode 100644 index 0000000..ee61f00 --- /dev/null +++ b/examples/plib_examples.dsw @@ -0,0 +1,491 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "PointPicker"=.\src\pui\PointPicker.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "WavingFlag"=.\src\ssg\tux\WavingFlag.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "complex"=.\src\pui\complex.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "dynamics"=.\src\ssg\dynamics\dynamics.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "example"=.\src\sl\example.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "fnt_test"=.\src\fnt\fnt_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "http_get"=.\src\net\http_get\http_get.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "js_demo"=.\src\js\js_demo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "load"=.\src\ssg\load_save\load.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "majik_demo"=.\src\ssg\majik\majik_demo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "mod_demo"=.\src\sl\mod_demo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net_echo"=.\src\net\net_echo\net_echo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net_udp_client"=.\src\net\client_server\net_udp_client.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net_udp_server"=.\src\net\client_server\net_udp_server.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "plib_examples"=.\plib_examples.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name complex + End Project Dependency + Begin Project Dependency + Project_Dep_Name example + End Project Dependency + Begin Project Dependency + Project_Dep_Name fnt_test + End Project Dependency + Begin Project Dependency + Project_Dep_Name http_get + End Project Dependency + Begin Project Dependency + Project_Dep_Name js_demo + End Project Dependency + Begin Project Dependency + Project_Dep_Name load + End Project Dependency + Begin Project Dependency + Project_Dep_Name majik_demo + End Project Dependency + Begin Project Dependency + Project_Dep_Name mod_demo + End Project Dependency + Begin Project Dependency + Project_Dep_Name net_echo + End Project Dependency + Begin Project Dependency + Project_Dep_Name net_udp_client + End Project Dependency + Begin Project Dependency + Project_Dep_Name net_udp_server + End Project Dependency + Begin Project Dependency + Project_Dep_Name save + End Project Dependency + Begin Project Dependency + Project_Dep_Name sg_quat_test + End Project Dependency + Begin Project Dependency + Project_Dep_Name simple + End Project Dependency + Begin Project Dependency + Project_Dep_Name slideshow + End Project Dependency + Begin Project Dependency + Project_Dep_Name tux_example + End Project Dependency + Begin Project Dependency + Project_Dep_Name viewer + End Project Dependency + Begin Project Dependency + Project_Dep_Name net_http_get + End Project Dependency + Begin Project Dependency + Project_Dep_Name PointPicker + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_dir + End Project Dependency + Begin Project Dependency + Project_Dep_Name tween_test + End Project Dependency + Begin Project Dependency + Project_Dep_Name WavingFlag + End Project Dependency + Begin Project Dependency + Project_Dep_Name widget_list + End Project Dependency + Begin Project Dependency + Project_Dep_Name water + End Project Dependency + Begin Project Dependency + Project_Dep_Name dynamics + End Project Dependency + Begin Project Dependency + Project_Dep_Name psl_demo + End Project Dependency + Begin Project Dependency + Project_Dep_Name pw_demo + End Project Dependency + Begin Project Dependency + Project_Dep_Name pw_pui + End Project Dependency + Begin Project Dependency + Project_Dep_Name sky + End Project Dependency + Begin Project Dependency + Project_Dep_Name stest + End Project Dependency +}}} + +############################################################################### + +Project: "psl_demo"=.\src\psl\psl_demo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pview"=.\src\ssg\viewer\pview.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pw_demo"=.\src\pw\pw_demo.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pw_pui"=.\src\pw\pw_pui.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "save"=.\src\ssg\load_save\save.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "sg_quat_test"=.\src\sg\sg_quat_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "simple"=.\src\pui\simple.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "sky"=.\src\ssg\sky\sky.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "slideshow"=.\src\fnt\slideshow.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "stest"=.\src\ssg\state_test\stest.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "test_dir"=.\src\util\test_dir.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tux_example"=.\src\ssg\tux\tux_example.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tween_test"=.\src\ssg\tween_test\tween_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "viewer"=.\src\ssg\viewer\viewer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "water"=.\src\ssg\water\water.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "widget_list"=.\src\pui\widget_list.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "widget_test"=.\src\pui\widget_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/examples/src/Makefile.am b/examples/src/Makefile.am new file mode 100644 index 0000000..b5ad0ed --- /dev/null +++ b/examples/src/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = js pui sl sg ssg fnt net util psl pw + diff --git a/examples/src/fnt/Makefile.am b/examples/src/fnt/Makefile.am new file mode 100644 index 0000000..ac132ce --- /dev/null +++ b/examples/src/fnt/Makefile.am @@ -0,0 +1,18 @@ +if BUILD_FNT + +noinst_PROGRAMS = fnt_test slideshow hello_world + +hello_world_SOURCES = hello_world.cxx + +fnt_test_SOURCES = fnt_test.cxx + +slideshow_SOURCES = slideshow.cxx + +hello_world_LDADD = -lplibpw -lplibfnt -lplibsg -lplibul $(OGL_LIBS) +fnt_test_LDADD = -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) +slideshow_LDADD = -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = fnt_test.dsp slideshow.dsp + diff --git a/examples/src/fnt/data/AvantGarde-Book.txf b/examples/src/fnt/data/AvantGarde-Book.txf new file mode 100644 index 0000000000000000000000000000000000000000..fe1318ecf5bc57f04318c19362c561f348a837ca GIT binary patch literal 66708 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ŌW-4%GTVw@{qM2wIlEPaR6{a`o4#h)z)KlZYjx#eG z@M5Z$n^hTx6Yhw;nD9*sxj&T@M04*jn^A@Mm_Ize)}Kf3W%2Z~y=R literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/AvantGarde-BookOblique.txf b/examples/src/fnt/data/AvantGarde-BookOblique.txf new file mode 100644 index 0000000000000000000000000000000000000000..c0ce9493dc12c9e4663e36fc1fa944e8cac7c5f6 GIT binary patch literal 66708 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

UoR?Ql9d1@jB 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 hKMFgIoIk<>=H^P}c%=EpVVM@P8>jB6t7* literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Bookman-DemiItalic.txf b/examples/src/fnt/data/Bookman-DemiItalic.txf new file mode 100644 index 0000000000000000000000000000000000000000..d2f8de569cf8c4dc00c0ee9c16db52c91eb7d179 GIT binary patch literal 66708 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 G1OEr67ZzOr literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Bookman-Light.txf b/examples/src/fnt/data/Bookman-Light.txf new file mode 100644 index 0000000000000000000000000000000000000000..481585f5fb5671506290720e3a874cb4fb37e0ee GIT binary patch literal 66708 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 UZLL-{RMi7jJy6vH|9lVpKipfZJOBUy literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Bookman-LightItalic.txf b/examples/src/fnt/data/Bookman-LightItalic.txf new file mode 100644 index 0000000000000000000000000000000000000000..b2cbc761a784f39d00d1d143d65e08131add4410 GIT binary patch literal 66708 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

8H&a>@>t9LNVZ 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Courier-BoldOblique.txf b/examples/src/fnt/data/Courier-BoldOblique.txf new file mode 100644 index 0000000000000000000000000000000000000000..cb250f54629e01c3d43e8506a79a5ea74657fc54 GIT binary patch literal 66708 zcmeHQ2YeO9)}P(IY4n6@ds0LM#D<6zn%+a|&AsRQpV^(A*_*(__KIKT_e*x}lrv|}^f_f{)aZeuFFQ6xu(RC1R`Q1s z*Wue*BqoMJjo{lV+K9$2lHgB-zpY4aoK(M&XbFEi(R{ynr;#`e{zF9b1LCbl;zan{ zi{>rkk^Un14;9Uu$H6}k{tlvHYF4@i`3sV}Kjua{~NbMKc+G0sO~$X$HXG&BOmr_>V&xhVumc-9^(DiSSQ?|9CI_ zOZa;y!VPXeD8`fd5RuWH?8{f0k&~g5jJB|Jg{x{;T0X$18Uf{O5}OWZuW%KTqt} zf^mBX{_{Qf-@<=^mwqMu7jnCR4q5PD&mka-^@TZEFvM!&& zKhjIT0RB#Th&$8z?b(Aif&dk3?R zu~u!RxnVV8EH^PeHKJ-QjT1^|#@g<>U*Bm~nZq%=vXhoHvE$;m!4j(DD`h$u{xL=0u?rywD_6rOinXN$|qXnvgf zS>qcLcT?%hNoKgk-GR&dJ>9|*=g&=BKe0{fdv9N_fjgpqRr%vFcspV^?dT{Pt|T+Y z60O$f<>cjFF2+=4=N3<^??JJ}k;}Jj%B-%;eIVFuju=s$m6w;Dm6ch%N>?Z;ji{id z?my_1CfRIpX1JOroAKylwk3!3gq9buhFS3<6gpS@hMe)bmt3MR9Q9iUdM>2xwPtN)`JZC|s6kP? zs1%Lhi0SmEQKz42eLXRj3J*5`JK{Z>cRPl_C*x^pu%1{$zoFgqn21@7FgD6Dk-Tm| zmPlD5`75GFgyWA4TO3DZc9f8~ht>s3am0C4)<%P}Mf2g$_2p@@N&G%kwbmq-=u}1L zG9Hi75|zRhhm_H4B4o{?c_Ce%gGq65XgNkkaw%OBXlY&=qUZqcHVQ6Aj<}J+?-tW_ z8VU-rMZYf&5swf}OZLfYiDS#Dzmd!lXHZoKKOWhzLnxqU+WH;9jYAJHJBg~pK7Z}i zG){yZaXu9v=yg9w+(L9YDtKrf<|H4wEoRapaW-L~yh5|h0&FpeX!iy8R#4hWMi*wx zKBR(%k5PUn4TcBOWST0(fHK;6q+jG{bgavb`di|lV!G4_U}*QCGhM60u)~(vX_A;& z?SmhScj|ijT!`c$FNW!K3E2|umK205(;pWdelDyo+wp=2#}fYthfC&nKIZI(Ph?hA zE$XCeYJUXe*EbOhC|o(KwGKx&Pp4@jVIKWx_Rx?RO@(ROmKRn=cYBkrgP;n!(WH_P zOL&ABwQxi~Dy0d*Rl^XQnbBi+_<}py^$kcpxtee;ru8j48JKY z(QNFvF=NJz8#m^`D-;w*bgHIPbt3o%)6PaJnk#9G6DiU;*hhXx<^8N``}jvvmVsNM z$1cjb7~s2Zr_3w;`uhc&9?Pn?$?Q9*X3yY?0C+8O>3V}iX8zykx4cRR>TdJF9q6jB z2U{NKd9Lw2s!%VWMmPopj|=e&fA2If_pS!K`AdS~t?2tk67OxOo&3^ zzwm2|t9EYRnt6#3*X-J!mh~@ME+od#YRBl^miV4t4%9Xzo})SDi1;t8bP%ObVxru` z#3d%``7fA6{c^-nqqsq-k`8~i9 zS5y9ey0(^Ry0Mxv%}sJm5dXs=I7!bY?B~^<*cOLJ=NwkkMs9*dwQIPvvOEYnhu-pA(#*MvyU<^-PmTHOP z#@~yp$eU`53}6fnh7=D8@QnGU9nRUS;K)*3#YSn{r6xG5NpeaeHsfH?H}R_~L<5g+ zpQ7ayIAwyUmxAO;DJhy?X+ai%vpQh%8y6o4iA!N_m=Dz!UqD148m0$g4>~f{^qP?# z7AXz5ZUGmU2y&ZeC@^4?8*vl(#RbyKh+FeBvog!0G~7>Qi)YJrM(-8k!}6T$!VkRT zxFaqq$;ise$josb5)JoVGqUQo1Qyb00O1VES*~oEs_58m!X0SU`FpGc#|9k1?>h zCiUK*Hl?lj@Y*C7H3_8N1FN&rm%VcX3sddUfu2oTze$xxD{y>kT>-X*cpnV?C>^2d zbb)tiPZ3IYIhu}`$Si@o+k=VlNGmPs;eb;fmfZar%n?ZS&hWN47fsE)g)F1HIpQYD zJniGbhoOdg67W)z_V;u{v|)umRuG#@mLD?5*G=TvJGICNxT?_Z!4*qEDNt za+1#Yf!6l5MZ>}6^td;AEzx8LMd#l+bbM!OVEmPUHb&uvHl zzmB+q3N_=#5(gB~_6jOGR>T_%qZ5i3y@fWGumvc=al`K}6#j2(}q7%iPV zu?(Gh*O0jZ&b)Bq!E1k0Bn7WRLIZk=gHG-@c1^X1+|R@JOCSkV`#)GFyo&si+M&!>FA{8MIviUCDf9&BbR~gNpW?B2EmXg8dZ% z9Puz!Vw|${&~|UO0V39SkZ!21rbu~Uq;q3G&b)dcYupyuqU-l1;8tP+Q2pNlT7|Nd zm{n2NQv2dJ7&otg{W_Ia()^=N)FZx_l~JhnI-yVkk6u05 zqgi)R|4=?bxa?cA{tynRrc?Y7OElaG!6z)eRjYXu=Zi%-ViMNgAId3OO2sYIEy~k(gT)gU51^;ccV%fw!dq##W__h zP>3m@+7noCHps1Q6w}C$s;}uv2YsMwAzkbf6Uli=>oI=pnEOvK^V)WU%~;SB(Gk`8 zzVWH&fAPy8am(z5!wjH}hTZpYhi>owH0N$(ql)G2>@Rbo(c)=`8&E@H1c5tj0W4%F zJ&`L5QWncv@M?KLHf`di)N?qp+&5;zZKCyr*|V-xfjoE){~!;0ogItF-7eVTx;2|A zvVO(N<(r<@*>w)zciPAyLx&981z8`zUMv^4$WLH+SfZx}Pr{G)k#s}cpJpJh@5x7Y zZK-E2PTBWeOn_fqdh7{LRrtLZl397QIki{U%a>4f#Z#yIWq2MJRRcC4r*!LcT*G?D zsYwA{Wj9@@hfXXA`5RnCbaU(-xuds+@~{TUphNZwP^I3}7gEas zd@M0CJ+1K3)ErcQ%T*e*Em}Oe9R%2Z(U*Cfww>TlI(vc(yltkgZXHLwgsCGyq*^nx z;ClUf^^%R0PZeZFN5p2e#f52Gwr(w`L6};h{))i(#KZ*A@VGul#2eVNC}F)L`*f=( z5FOKt_6;@!jUGK!6Le3RB*B_Of5%MFAXy&N%^>zepdm3yFO;#-zwNg~z%4z114b1) z`9IKQep@kn%w<_2oMg+i0JPbO0=abG3IzW#KV?17^6J zw04#LAo7XVj2JTP`VM?Q80!!u1O4r+k;D&EnG^xlHz~P^;gU7~$HCn+t|6rZ!1^h* zUjjhqo9|&1czlsKDWwUgHbUVIB?Zpq> zaco%v>pR@Tr!rK>o(x7G!4TSJPU908oi8%*rhQ2mF(g z?A($k-0~c8U1@q&mIRVNTH_$G79AzW(Gne-xxWwY)%|$ib?>E2G=U7~5q%`70h

V>(#VE=Cj1X6}UnzcJDA3OD>-aP)uhFRjK-w7S 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 U@*W9zB;b*NM*6;?)!M(&W#d@ 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Helvetica-Bold.txf b/examples/src/fnt/data/Helvetica-Bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..a435ea6248382cd7bfcb219d2a8ae43ef8115ca5 GIT binary patch literal 66708 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 XmGb+|m4tB8&)+Xzhaf-rTHyZxRx->D literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Helvetica-BoldOblique.txf b/examples/src/fnt/data/Helvetica-BoldOblique.txf new file mode 100644 index 0000000000000000000000000000000000000000..fea9ca7d580b8898e488b8a3222b9d6a8bc562b1 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Helvetica.txf b/examples/src/fnt/data/Helvetica.txf new file mode 100644 index 0000000000000000000000000000000000000000..4ac9638fa5aaae78fa7125be1c847e2f361e7a62 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/NewCenturySchlbk-Bold.txf b/examples/src/fnt/data/NewCenturySchlbk-Bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..cb06095a436734d7c611d24ec25cba5f281caa53 GIT binary patch literal 66708 zcmeHw2Y?hs_V?@V*#rx_?2_RGB}ovKC<+n`peR8B1r!m(sURW-RFWWPMmS7oKoqki zGbmA%oO8}OEU*ikXQsa2>#nNqp4}BW@B6EF(|0np)m5+Fdlmbo5|2J{?-Sh`r4T=c z^IvKE528EpTb1JD1A$ni_S5;4SS|+Xc%(0&q@+M%9F<188l{wt2_)ioKUJsHN-;(v zHAcDyl`j_vl%jS>*F-u7>E1}!qT)$0i81s9(ic*Rim_%qJ%e;@q+^gCi*y}IE)`R( z7`=ydT}nw|_*0RtM~O)>6if4vzKBW`kBN_?Bh+Cx;>F@BH=uUbW@rCG}4#J z{BcM(lj)C;zMK+)b1Y3q`U*-c%Q*jxbaP5cWtgjwZb2y}V`39&57Jl4e4**Wl95w zSEz#xrxZPebVuOEef>Prog|(UknT+7E5%SL`X1?yVzE$$)E~LB5^ixRpkTCy=bWbW#fydYyh z4&$dQ^_687BHa&V0)aT%hV+9}DTVn@71L+}Dta5@##GI>4Ly^z@(gNvWR4Oqh zE)ip`GU`X4C|Kzk8u)J9Y!!ZMaNO0aV zf6BCt_*pP3f+$Zmpjb9Pehav>wDy1ZI(5Kn14`HiNxIFHF?c3?IN_CXe{)~NcN=rJ z`xlorspq)fNxBwMYE>fZ%4eS}?Jwsk@#jmL3q?-G?kYZv6Sj#^aP;6QL2(6$v>%f` z5-0Caf2q8ikBHfdB$Mjw-+uFu^&%uTl=Fd8CH{oJ29-@0qS5$OUffK&JTF^3OZ1Rv zL(T*@sMKNc1O>ds(AU&M92H3JfzB4a)q;~vk(o{pfKpdd1=ds*QOS4m;W=8S`<#@ikbB7R^QtI z%@W~b7f{P;x^pXKfc^sg-^oKiiW*6nE}j+92lP++hlyUjG*8^9x{=W|s9IV`{OFQm zP~{Bqprk+XfkCzHR~B^?qOn`iG|33yzXJQ|pOkb5EyKdvCT4nx?RP!(5;sCS4&!dz)y`e^T6T zCrxS)7Pr{RKXniPI&vmW5@OoNvlOl9^FMP3a!TzQ^&8ZyS?j`*va(5y1;zw3!$Uu2ju4&PYG-NbPx;!SHXAD*xB{Exv*e zt)I})oRC;stSG$gLGPS@JmcRY-}$Gg%75zoxGtSKb-3jQx}sNyPB*jleW4GCR_9+I z{oeC$p%wp2G7TzuKnyB){^==ls=Kr1FG;M$k4Yx~6it9hZ-~r(yg<7BZ&I(5;+vxD z{CB+xTs5RScePD_FW&SF>OA}gcH;S;c+_Vd=vU`ordwtI_w&HQr=3l({{D%SfByIL zz@{+%hHmHm5B2;1NUxp)nbu!Rz;Ot?9~ra)BnhW zqc>RK1;!a(ZZmrf9MDarcF^q>U`v4vU9`xAQM-1{TD2}rty8mB&AMf4*Q!;sc5MkJ zrrw3MYSpTnh|F5G>g$6pa7(RU5hF&oR-sD8$`z~pt&lcKRI6C2Ql$&jcV+du+)7<9 zwL~Q^stV14u53{9g;MNj=NS}J4WQius${*kN;;)-$h`7WPG8YT=OB?-r+Uqc%2kFq zt7fh9y)W!^+>%f@BzE;khu$vh?@;`O!%J;+4JyBbU+rzS8JYPN&`pbS59Vy_ zr<5TEeUO)xnVmVk0Bq=oPMyih$~sV2*8N6=LgLICO`bvTPMmUPPuk9#HGKvR$_R;Z z8kRw=w&aVgwQpZNb@Dfk+QE5Oa)%h`7rmKunK)RAs8%Swl7?qevv4R^Jo2&#;%Aa? zWHBI%`U$bGJhc!a*qF-f6;N}?{t3{FLad0lta(DbC3PQw`-%{cQ|CPKz3Nqi+KF(e zIaD7Jon)G^G1G@|LWZ;1qS_e#ebwP`EFrk#Bi5ddQu)~6Y# z!{Q61G*48*?tjqFBox;Fvwi(

Dm_@o{uBhLcne*@Nf!V<$xT#2}53N!MWA5F;lD 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/NewCenturySchlbk-Roman.txf b/examples/src/fnt/data/NewCenturySchlbk-Roman.txf new file mode 100644 index 0000000000000000000000000000000000000000..7781ea9cb15786d10f207028c0e8be53cf16ddf1 GIT binary patch literal 66708 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 Cp`^6{ literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Palatino-Bold.txf b/examples/src/fnt/data/Palatino-Bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..1261c5d93112fc8739ae8285975fd62c3f063e43 GIT binary patch literal 66708 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 DNi;tm literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Palatino-BoldItalic.txf b/examples/src/fnt/data/Palatino-BoldItalic.txf new file mode 100644 index 0000000000000000000000000000000000000000..a27c45f5f101c2ab93e536cabc1369139318c029 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Palatino-Italic.txf b/examples/src/fnt/data/Palatino-Italic.txf new file mode 100644 index 0000000000000000000000000000000000000000..a9609ac7feb2797e330bbf36586d36896443e279 GIT binary patch literal 66708 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�QFBU^uOE*W6PPV)1l*VeMi^*AA@k$%fC$`*7^RVga@pR;C|O_q0VKv$WJpYe zdP1VSE@jh|(*$esYI&7~VL9MF_%xN)g#3cunq_2bJsGc$DT-eE95*n55SmuO>(f`h z{H(h+ezNG6!8?xbSUUP~mMiEmOBrz^&8D`>FaYAV2%~L$dU#$yDhhUOtgW%(#%qLn zfpH)zItJ|obr(5ovnSHiRY%6+){ECYnG`p-{^mq#v|h27ebwUL>LH~Xxz9>kN%|O+qiu{-Wy_o zZm8JLy&g8}G;D!PK&pLt!@(QhgZNdCawc>D+ zK!OIFTCWP^P4-Q@IRRP#n_kzf!e@t{uOlTLegO&{Fx!tCsr><=nFM31s?57iRix8% zjoGkjei(8?DTT#NFFrItZFj0$AbiqP?Ci6w_o9A*8DLR`g>e=fnZ8ajkRy5~I_%RM zLVk!xp_g;X@#=~a2j3Tv?&NWyxlI`_fiQ=zKYpu%B>R?;TY9`RzJW}^?FdC5e4bSh zBm?0;mp`!JHHvR%80O4_RZl9764Lr2aCm4dTfY2YKsE2O?=E9%Te_z+G>suK^ zf`a28IS)c(L2^S?;zokgG??c-!L`3@G4AJu9xO@GHN4pg3Hg9-`B{zNih`IM)f&C7e*lpM zoeLi7&4${a|QRN{7g z&77@A7A#trwQl(r9`TU2)hBws4N>~)1%D69r(-qNs^Rl z$&|KhOMDKUgxL!}4}s+k&g~ETo5f)wxZd&ZGN`+90= zv#f?qp@TN;C|z3XyWsp|tC@nD_;NS~>tD29{}`3#xI}!WzYupH@Fg%g6bVDu0kY$v zi%dzSO|g?}j1+6sDbQ~GMc2+L7Jd1Vdz3R~lTF3DZCyJT2b9Fi9&Vd4J6jd(@}Y<9 zY?aNy{%J))|jGU46dPo5C#q%xyT z{gy;i|BZ#I;8LmIC(*zW@oX`!NfEDuoS$!x>?s)Z!U=z{`z)H58E*_CiA?@xAOAAv z&-_}naExkN5P?N^y;x`Wq$g^BF5YC9s($W7v28=&>m-G2v)vFT(gy|+y*xUiDGqhV zcVf^y8(xm1sgM6v>p(TjBi+=%d39FqQ!?WG8$k7E9-w8oBlmJP&4I-GDy{Fr68L%a zLJO98yTQSyJD!F`{GelSA3hU`FD5dKppymsmj#P1ZExnu;_bA!Qznhu7uXRZO9kJ5 zB6I)Xa|n*(eXYdoCG8mM1D90AVY%&7#A-qhQq?~48SSkgKE(ds6q=WG+rl8>?4zj^ ze1FH@!?=TDEUz|R03+6So2IYGPRiev;=rAU+HSH{0d=5SNj9UG02;FldjktpP zz3q1rw$P71>)wdVIMiftL_?tuIdOA(U+J|#VO9|gz(eO4h44mniW7+{_P>i**Ss6h z!8{FSXhi+|(afIXwNfgsY%sOGQ5ll9A__|`{jT!CwC=2UCj(xk+MaUwOTd5VmshmF z8>LP89-p;o@C&Jm0KTH~w}Tw!ChG?1Beg1|BQ}j3ti~6+_(;)t|3oYY<%LsL3-^4F zu}iZYLk|7jTL~qDS|NXtnjq7HPk#~h*-<>ONSqDr YKO1N~JGwrP$~+F_aUhQa{~!ncA8d*|umAu6 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Palatino-Roman.txf b/examples/src/fnt/data/Palatino-Roman.txf new file mode 100644 index 0000000000000000000000000000000000000000..98335dfed385c1f2dc10021eb19bd24fd83ad7a6 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/README b/examples/src/fnt/data/README new file mode 100644 index 0000000..55cdd8c --- /dev/null +++ b/examples/src/fnt/data/README @@ -0,0 +1,48 @@ + +These antialiased '.txf' fonts were generated by Andrew Ross from +Ghostscript fonts using his afm2txf script which can be found in the +'tools' area of PLIB. All Ghostscript fonts used were GPL and Copyright +(C) URW++ Design & Development Incorporated. + + +Things that make them better than existing fonts are (quoted from Andrew): + ++ They are complete. Each one has glyphs rendered for every printable + ASCII character. No more property confusion from different strings + rendering identically due to missing glyphs. This alone is enough + to make them worth the price of admission. :) + ++ They are rendered and antialiased from vector descriptions, instead + of being pixel-for-pixel copies of X11 screen fonts. So they look a + lot better under most conditions (at very small sizes the difference + is negligible as mipmapping does the antialiasing for you). + ++ They have more space around the glyphs. Mark Kilgard's 'gentexfont' + program packed the glyphs too tightly togther; at small sizes, you could + see "bleeding" in the textures from nearby characters. + ++ They make better use of space in general. The characters are drawn + as large as practical for simple layout code (i.e., I scale them up, + but don't do any gymnastics like recursive packing or non-rectangular + layout). So you get more font for your texture byte. Most of the + existing '.txf' fonts use only about half of the available texture + space. + ++ They are professional fonts. I don't know where "typewriter.txf" + came from, but it is awfully ugly. I replaced it with the antialiased + Helvetica (in FlightGear), and things look *so* much nicer. + + +Stuff that isn't ideal: + ++ They're stored as 8 bit gray scale images instead of bitmaps, which + means that the files are significantly larger. The texture memory + footprint is the same, however. + + +Note that you still find the inferior textured fonts that came with PLIB +before in the 'old' directory. + + +- Sebastian Ude, August 2002 + diff --git a/examples/src/fnt/data/Times-Bold.txf b/examples/src/fnt/data/Times-Bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..401560aba8b18aa3d9427fabf5a1f1539ad16364 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Times-BoldItalic.txf b/examples/src/fnt/data/Times-BoldItalic.txf new file mode 100644 index 0000000000000000000000000000000000000000..9b76475b136d82d2d1bae1aa3635d7acc7df929e GIT binary patch literal 66708 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 Vu?jM9F}|{&Yzkyk;D14Z{|Dn5+*SYp literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Times-Italic.txf b/examples/src/fnt/data/Times-Italic.txf new file mode 100644 index 0000000000000000000000000000000000000000..78b8114d1cdf00f8d1b476786fedad202de0de72 GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/Times-Roman.txf b/examples/src/fnt/data/Times-Roman.txf new file mode 100644 index 0000000000000000000000000000000000000000..e459c7475da68a2b77bbdd6583b4a2c0791cf2d3 GIT binary patch literal 66708 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 F{trk9KPvzL literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/README b/examples/src/fnt/data/old/README new file mode 100644 index 0000000..71ab94d --- /dev/null +++ b/examples/src/fnt/data/old/README @@ -0,0 +1,34 @@ + +TXF Font Pack. +~~~~~~~~~~~~~~ + +Most of these fonts were created from the X-windows +fonts that are distributed with Xfree86. The +exceptions are Sorority, Curlfont, Default and +Haeberli which came from Mark Kilgards' "texfont" +distribution. + +I used Mark's program called 'gentexfont' to convert +X fonts into '.txf' format - which can be read into +the PLIB FNT component. + +Large bold-faced fonts seem to work best. There +is little point in converting the italic versions +of these fonts since FNT can do a reasonable job +of italicising them on-the-fly. + +Using large fonts gives them the best chance of +scaling them without undue aliasing either in +pixel or texel space. These fonts all fit pretty +well into 256x256 maps - using smaller maps would +require you to go for smaller font sizes - larger +maps would not fit into 3Dfx and similar hardware. + +Medium and fine fonts look pretty terrible when scaled, +I have omitted all the fine fonts and some of the +worst medium fonts from the set that come with Xfree86. + +You can preview these using the fnt_test program, +or use them from within other programs that use +Mark's TXF format. + diff --git a/examples/src/fnt/data/old/charter.txf b/examples/src/fnt/data/old/charter.txf new file mode 100644 index 0000000000000000000000000000000000000000..d5653217e3debaf8730bf55d18d00f1c2eacda48 GIT binary patch literal 9292 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/curlfont.txf b/examples/src/fnt/data/old/curlfont.txf new file mode 100644 index 0000000000000000000000000000000000000000..6a84544336502b5e82ce2ede862dcc3c839b6b36 GIT binary patch literal 8968 zcmeI1U5q5xRmV@wwf36zx@sMNb)0h5jO{p%`N)JMqz1RAZ4w9sNcao^LNvk(7H>5e z$-VMq`i@3QzgV+8Cejis0)zw-FFfFfpxjY|CxU2}N06+|oit()LYo>5!sTqair=YT z8(X_14}s)`yVEnL@BeVNc)$UFJGjnBF~bmx1P{_`FY z!@qam_U@%u6Op$=uix>A`PN+`?}C2rj(0`Z?i6_p`j?g;j@k!AJ`Vj`w>`Xcjq$$+ z{hPN#-y!0lKN~}{{2xF+a`xe+hB04)e&YUjMz3Wee*rzb_aSKpB8Si`XC7E;&x-sj z^v|4KF|WlUC(sYSe+>Px6=>$ppg(l?a@28m$IxqUhh}{PN5A&Sa^2-3IrNX+x!m*iQRt7}vmCXI z-++GdUHMY?h{y%#pUCs3W#2!7{@@@NUFUD1KX4cH-68?{lMm%lhuRqVs;u6XMIC2+ z6#By%f?}^Hq2F_Fwxs>P4gJn6i#RN2`KtW*+547S&hm%Q58i)YWElTv&_8(hJxd08 z{vGtAZ%d*dK;OOr{X=JOU-}k0VC3KA@jGveZi9#O(A$ICmd?W4!_c3;>+I6K$n8n! zA31B6I@bRrG+TosPDbn>T#?NK!c8xR1)V^H0yfufZk7|L_^I7(? zaZgF<=8-e8v4@+IZA*K9kg?p#0g_uUMS2c*8o4_Ze@0RclU6d6C-k^dh-`yf@wkup zU?gcOTbw$BQ}G7ecg9Cn+=-QV+T(+`ka$uX-) zN~v}dXV%3buNavqj=&CH$*j~;lyDk5vvmW0fx+vFvzORvf$JK~H?bV7S&Q-(L5kH> zY(RdjdmV?3gUDK!C_XO0z198gNaT#}AInSN6~_xwl_?y+qqu3;^Ck)NcXmw^DIP`x zILHx)n{mymAsP7Y?(T#e3gSn~;Z}{f=KH3{*UxbN-QB#$RT^icR&kynXPv)(#+mM5 z=Q_?!TB&>_Ve|X~&%vAHXt6%qfp0nRs#xF_9M{pXzULNm@O7OZ))u%q<0E$}oNoYL zp^lP(mwp5uBg)tx?K~&mN($bTz@j(*Um~FwxIX-MnL+QsS>gen0ZK6%4KFP8N7PbO$5w zAn~5@C+Jrq<+@Z=$KF;{|7;BYFA{&z_&WDHv#4zKe7%lsfuqdg4x+TRp;!+nXW6j@ zHkTx_t@@2j`~n{o*oQ5Tl?5z(B3I*pynDQ;O(?)OZ5evJO1en#3Uzm#nvY*AO81}* zK>`l3mec6d2s@xE0=wYR5c{>^gtY~`<7%xp%w4z~j4UhUvhj!+JXK+;vJQ2wHg`E0 zN@v_i@)oi z_|kX4Z?fe7ZWpSeuC!)VjT#~WJWFn&}uO)o@w5p(Ln>uGj7mW&Ebd$XVNno0LWE^Rd`p++5_MhtU-F)wKW~%O)qUoe^c8m!tJCkZI{u$gM_(fa#r;EK@HiRX5oZvJU z2c8srf10x@e=Wu_;&3d%zIbglmM)L;b!;)qVq1=7J@mbpaJj&n+_od|VIC*dNCvOt zaW&~zsuSDf*lKOFxZ?2O{9xi?wBQ84lz>+zjaB3u*YVUdSWki(iO!D(y@sqCTYEwHHe z-WoT(Xe6uX(j{kvh+asnBRMJ{gc2j8z*m@bbEl$&xW?W?X;`NFf{}@*Pj%&3h!u*H zF`x#pCha97uLSx~L-4p4l4cB(s<-T|#^7$5f;1L5ll+eHUFfF}y4-AX9lyE}YuHB+ z)*ppPy>(MXKFY7-hnw*>EG}>c!CxB|Vs!=q96lF!Fj}$n{)sb|eXkOk#OwT4io?-f zhm7kZ*_&;GpGHD%#0mycwL6oG`Q9vQq&|_ogZF}8HiG?g$7EFI>i#HMn$Dt5H7uTL zUV?0UytL#YFVAf5?DVKh3bbxY+=!oF;J2*C)qei`+~wZb>V>=*N^0{U8E3U5DihU{ ziJSWoIbXw<46SQx84|YHqUxe#RqCDD(KT$AGZRy5Q)Azp?-2a-GBUxix|4}$%@iy$ zL1$HyxuX*1kr}uj^)}q!g%DyG+lVap7fB|@(l@m8+SC8K0s03(o-ub>1sE!XXtV#3dw0inpV>CLgXlDiw4DU)M!77nZx%CTNnxV-X_yETH{yQ7o6T!Gxh?BWK9zdc(uD9vz`rwkTG6ye!C>Ntrl1t6{w1 zB>3Cy!5jFPGojlkz;(O~E;)1Xc*%EqHZ;X0=p!t}$&|<^lK3FMj?=5zEsECjU&}^; z@x;@l0?`>Is8}M`J)`P=-KHI$szhuCTG-PF6 zpLL0Dr}J06%J-5jw~!{daDTeL;(@<)AB@t3e3?7c`Q+D-uj5sZ$8pePBV~Wq@9|3W zcj5Z0ctCDFUPl_5G_LU5jss-g<60arw2k_i>m=IX zWgycYrSJjwJ^uBb9-okBA?V9~Il6>6<~W`92+wK{_wTU9X^6^nfnSMUCP|*J7IJu^ zp7U^j$}xOIeaHM7fA{Zb*VEv8s9%)4w9}bV@nMgnGfk#d6=~}_couLM6#;uNz?#sW zT0X@AvSBH0PBhyheUO9uV^Dsn5KPisS_3thCWDsr3>b}2^4dtyhYBg%lz zhFO`0on4zUqbMmBq)6n0GW4d>7+FdR{`_oSgwaM(mDpR6xo0JHrequNL0ME+W=9uf zyDUO^06ruKqh>F-5w(PBt!$2}XV`N1k0;>cB5V@77C62xfCupED8F(9UOg5{Vq;vK zzp9>L;EJLQR5^Z@T-n(t<~~!EJSrrEqQ_G~eM509ZZ|oMc$Dy)V{k(4i^n=Y73yro zg}09=aN%W9s9P&4`UbD#Y-~}%>ImH!g4Zxcs-k$2GasDLiRyr-;Cgg~p(Qpt*5f#0 zbP+s5I@fV6u@QKWqslVuz~eXL8j!`aKDU8S;G#>)cq>BgfiR(%@7G@1pQz1XLQ<04 z^tc0eRxiwN`lC*L2I>xvS~xQwE|k+EQ~U&}s|<+khO!z%!a0u%_DF}|VN)}6XWXdA zIg8>=K`-uw+2sPq{HmR&BwkkTY`Yd}G~}G0*Q?c$&i5zar%_r*iXVbUS(&-{q^w~b z{K%iWQ1RpmHCDVjb$@A#OX~XLgA|R^w0U~D;y?|_-@Go}`;sv@ zZzAymr$7z?yxqBWabB=WTzy{W+x49JSJ9_9;ML@CryEtpI#DXH6B$&K;B~KZvh~WF zvHZ_10IJ;Hljww_Scb;edbGkQ*IqW?yTA}p{ zKdP(BUdjk9`8#FU0W=c|H~rB*oriJp(AF8UMb3Jz?ZiDtm%27KQ2WEC!pvOd+3kX4 zNr+I9a0C|EPIC}$k4U;lHj2L|qH`UkvnOp!?9Fh)ZTz%NL7j=2l}1HO(1 z>$?gOxaz#|V_6-lSP2ppzBSEw8??~);abR*mUxC!KhbT$X+DsF>RRy~Z zhVZcWTyY&}Y=+&RiER-oG|-WY(9KJh;*?Cr!o0*WLtoVkBX<#%4RKqa&#GNJ1kYlF z3H3N2KMtSMp>5^V;2z0(OxXQ8cOuhKj|Z&{*aVzB5#2M@KJ^Sm-K1AZ>UsDDKQLPK zR*|i$9zQ-&<)H57YVlLWVWc=~mdyf3{nNhu!}Xn$;%ZbPX7FlqMb|&}`b4B*yO49t zzbYDY6wk2=@XzD07}2W^SK5GUzYY4V34edBRn-D#|Fr325;%u|XLIlZC+iKVq{fzX z3wNP%S>UyuzF>F~L}XIdoIl%Y)P?pqfwjJ9KWC_%6|XpS7H{GG6z4v`i#?nIm9yfo zHRTlm-BVozr=$3q)@e>(G^XUhUx_Pg_Pb+DaQ`yE{171C|N{x>Ou#xj9)uarf)od!Qq9AAx;B^_g&BlilV z@W2L6^00wPsX9Y%*E9vJCufQ41>?pqF zll1tlc-?U_Wrk*l@cgvmrQT;hzv^D#ab8hW5{6Q%Ip_js4_=cgkQR6~+5%6gv2o<+ z{yNY2yp982$MFp^jFU#IC2*wR^Ecr|(X!_kZL2W`yerT9`+Hmk0fcXoabphGFi^)+ zQ(1%S{i__o(?blhj$Tp&S3ETP_Y1Yu^;QgczzIRFrp|I$cN`iXtkIa)aha01kql%W z2b4$`{%Jpv@f$embzrCsw_+`!GuigDeUii1r{jOkWs;EfI^mogVd#!k=x;P)EHSYq zu^-V_^Fe>!)X?EClhP)ha<+F~q#ti-q$IuX=;MY58oUMO3m*0R*AQK8klfhYCvL`t zH@CJ^j@E`=KsO9==F8n|6ij=PtGB$d**fu`t&^87qLj|mvh7XjWlnjFC65k#!VZpWOPeAL#1yD^FBmqXm%1IgS%O) YlVD`|X2_xs|N3nk^8f$#T}a@+0MKCD)c^nh literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/default.txf b/examples/src/fnt/data/old/default.txf new file mode 100644 index 0000000000000000000000000000000000000000..1b51f46b1cb675289eb0a1ba95ef5bfb92683dff GIT binary patch literal 9196 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/derniere.txf b/examples/src/fnt/data/old/derniere.txf new file mode 100644 index 0000000000000000000000000000000000000000..385dbfa6dff00781e19ff892cfe7f2d5bb6b3d21 GIT binary patch literal 9124 zcmeI1U634Cb;tj=yEEOkhnbNqOCx)<-7Cr9@L>ip$(U?*TIRz}AmkJBQB)W~fRKb_ z`3NkDN^@5-$PcN=kFJEot~{kGRe20aZ`*3`R@9=>HppO#e?hb!Tu%dQ)l!b_AhIr>*syg zzoPVPpiTWT_Iq@O^Jn$v*zY~K=AW?Np;Kp^Qhg2kR~d8AzK;Gssb8nhKxcJ7_Rr`9 z*PP^@pVYfFIpf+>`Y84OC-r-} zeG;^Ny?{N}Xv66!_j*Ea)cBNZPx9*ty-CNgkLxwq59pp#E%(i_XS(Z@beG_9r_>a z4`?uP?U3<&QNLx^w0CKQ{SoWun@4mn_HSE1?U+8ksNdD`&VD!c$GdAr`VjVKb>bfP z%@g`G_DA)KyPXF5Q|wP^aMJq!0`^a-KMuY#(BEKxMt9%oG|}HQ$*OtXgdDllq}b{MBCL zV?dkM^{OM9@!c4o)2Or?UFTzf*@nZ)X*R4oesyjq!)>L{!28_FxHJvzEVof`q7!Pe6w1+1><8p9W`w_$&_s*&0cQ1=>@<*g21;2=m&~srG zY7sTYW2UYEzR5R3R9wLO5e|PpS8LEe;Lp-GF4x8aZpVe(n!aEcS^H*)!DOCo8K@nJ z$4h$DnnjNhm-egk1LPYccwkLC9t@{6gqt#q zXB|JSo>Ote&Es#cTIm9fgD`k|WiPcYkX zyVADt9(k0O%M~qKHL;rc%D9_UFOt-QRn)YWJmh6fp2b=jui#}1Z`v3hdvfe1rxUsw z)5iF)zp|Xh-mM%_(7(`xn${Yv+bol)wCm_=XS%q0hI}>D0XU3r zAyRv8X`*7i;h$Zm)Hpb9tWBt$yWE;66{r{f-DPSkR~n%g&jSdbUAn4WmZ($RhP99N z3_+^2X4UrdCfr!fdDv}vz2lW)Mg$+0Zh@al9#k4tYD%}Ix`>TGBo8m>rEos3+c=r2 zD&35Up^QDEgYet%A)GWND3Y;?!tHRwfpG%ZiYIspM_xw+@?N;mLjBT3Yfg@PPQVjo zc+l|#5t&PUco0VqIk(`RM-Dj8m?@6OH@P2iA5ZPZ9B9Ah2*#jm z&%8GASWb;)JhK7OKKN?b%Pa=q=XQOVjShLnfSrPD^<%k)q4B*(X*YXF+$6|sz>i5c zG9|h1=Iz~Q;FGf*&xy!_(0jus+@|Zf`R$4icrRx$_VABCgr*b>SFS**<1C@Q-0iuR z@KEO;BK3;lOPr@@+{0Ow6~MKR9?6x~eu2ZVkNV_Xr=P)>;Ctwq0=SP$^Q(e4>d2n? zT+tq%dcXunfHo zwVLn8Z-M95m#y%c7cHr@U5B1}iwyD0IjG1w9@dClO9`;TA$-C4@~m52%gL~6hwv1^ zqCjEZO{>+sAd{_=rVS}9$StcnXls0llENuKIFSZ8a$4a`3LDFAWRA1e-gp^u51e#? z@$lOo3PDrE+~3FWm+@dYwF_4%RYfsp^p5(Bdm>;3qwV5n^_5{A`{jr!a|t6Ytj@x# z*ky}5+tf4=_k=X#{C>)Lgb%xFSp#fXTx+18GE;mUU3+{fDM+via{ z>;q1}Z-QUlUvl4x4e7tue24ap=ibdZ&-%5O(!23^RiK!ug$@}osq&Ta@KQ|gL~emJ z5=>hipJ=vY6NUSb*2WQCXA+mwexT`0Pa3yf=7eWN4wd9QtqXY1xI&9;_)H_D*a6T% z(rrV57IaCD(8hR{xLf!fp2Ja6xV6fnz&meJ<=qqhB_18}t$=<^>g1xm6F1vO3z{>& z{1zTK?rV}Kly+*o@83GU**S9a-%O06x0;f{%H|aQIm~I`tNjesnK83OTc(?<5XJF7Ca2#1rJV1 zpZK$NVa+FsCh)F5Z*K3Fc;nvMCw(R3(X?Pp8UA7f2i&C2_&~`lIbZCpIgYlHm5()d zcxGJ1=lydD_wx=uKIC-yjLFc{`+aSHKkszi1<#mb?x@(OIHi4I?Yo+7&BM=Y1KgKT`Cr1NBjPq8E4N1%^-0Sj~L0)x3#7-!W&4$wGMX(5CyLmIQ=d>#g|>%*vn}`Pr^0t zWq`hVR$B$|1+`y+TtpF^79AhihUJi#hn&dgjydg#b8Gvh*s`)9@-=$EE>{r4ZSn5- z*I7q%YfbuGNa#?P22oz_z(@A}rAdv`8a}h6)TNEck2#^N-oU%w zBW1yw?>TC4B6F3$#LbllJo$)pxADT`*Bw4-KQJB%iLvsP+wqY*SM5>C+fPIeSzmLR zdciX`w5(^mJqU`HQ=|WfUn3Q5?$%_);PbAFgZ8)t-ta)ze0q~7nlKP{?hn`7Bpm~@ z_6x?mZolTdiHJ+v&RL-Klq@=)^WzRC)Ql!@FQUeHxVo)?J2Ha0i2zvLaUm+;{2XdY zdBq+8&Q!>e7un4XYw9rZpc~%1ym%Q-bdwCmTjP~2rySHkEh~G>)dea=QGn>M!yy~H zV`Wtrze`@dP(VFwocqM(A)lA1T4}F-%CzFK_M0VJjL^sH_PwpFp63cpw3Zp8jhJ4J zS1pP;f-EaA&C4&#!L#5YUZm(@XN*s+q)t&4SJ}p}w0dSm1;o6O;*jyRu3VOE^w6jV zx!=~KR(Qtr!mR7LEd=d<^F9fUTA?G`IFxzQ88SR<=5|s0-FQoA)M{S2EktQfZAu1} zZQ3jg9t}?bj_`3Ko^v{@S?CLflWtW-IK05^W`0;}xLe|rBF=URDY&-!MXAZer-P{v zS+@+60oja@_9V&9^+9=YJz67Bk_dV5A8GiwZmnY8aElFsH!BZ$ATEdJ)$0Le`=Sdl#a3gpKX9Wzii<+!? zTqA1^KTYXn)e9AlCb9|yQS5T_YW8(0`GV^$GF3OCnw}B znGkEfat+Z>xOQ8lBqu87=uc`%g^q5x0D~7OggWgH4@>Ts^QheJ4iDUyR_ZAz!(!>< zvC7MK-z^{3VN-KzSzVjg$So5|i*%~$dRklyDMIRa^*!dvCM9iLeT`aTP|g>szA`VL zkK6sY8Mf_>RvT43XhJwkcex}N+hs)wcCF**N#klbsN*_hMK$i&O4jXF_+%UpH;L?> zet$Qrh*~A(jnZXhIoo~g@|7?eh5gfVNtX1d5itcXd>LkoV!ZP>VH;&GdD^&=sU>Sh zk{Qqac=7nM<1A-xl#yIOOI1&)l;t{^dsb80cRbfLzlCpmoh-aq)U&|&bfKLCKKHC7 zJi#{@MP`uO9XVAX8a-}XZRxpi@~PuLM+op>V(g=^vM^}VoHKi4`G zb<6M13pRb-vGDHFWZcZzS4ZQodm=ts=()^N<0kKq##@h2i?PT#{FLhhk~`qUMvx>4 z$)$uO45A?MIws-Q?kH$7ijBLD_xo@%E?Q%Gg;A+dX0b8Kb?Kh|;1ZQ|LOt3Y-o?>q zG|GK=Z8o0bjM>9ac{gR;%X@`21ph1^?dU-+)u7G-sZetIhuH>h$85FH;d6%hre13d7*z|4uC literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/haeberli.txf b/examples/src/fnt/data/old/haeberli.txf new file mode 100644 index 0000000000000000000000000000000000000000..22d7e52d1e90b9025f68b3dd750f499403bf99bf GIT binary patch literal 8968 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/helvetica_bold.txf b/examples/src/fnt/data/old/helvetica_bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..83301d39620b19887b492d4b1b92ba70c4dc2729 GIT binary patch literal 9292 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/helvetica_medium.txf b/examples/src/fnt/data/old/helvetica_medium.txf new file mode 100644 index 0000000000000000000000000000000000000000..d6f432c18f50fbf030fc1bda14735577fe70c883 GIT binary patch literal 9292 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 A9smFU literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/lucida.txf b/examples/src/fnt/data/old/lucida.txf new file mode 100644 index 0000000000000000000000000000000000000000..1923cc864c52f2520b1bb4d0de41e9a2eaa7450f GIT binary patch literal 9292 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/schoolbook_bold.txf b/examples/src/fnt/data/old/schoolbook_bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..3491827707bc510ca5f7bfbbb6992733a1a017b5 GIT binary patch literal 9292 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 Yrf<;KeSF^o_dRgm1NS}f|HcFV4Wl*}z5oCK literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/schoolbook_medium.txf b/examples/src/fnt/data/old/schoolbook_medium.txf new file mode 100644 index 0000000000000000000000000000000000000000..92802d289764c4094cc0708b48517be97466f5e5 GIT binary patch literal 9292 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 E4+)c06#xJL literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/sorority.txf b/examples/src/fnt/data/old/sorority.txf new file mode 100644 index 0000000000000000000000000000000000000000..8b457aafb72a70f703aaf9c754eb64da85709072 GIT binary patch literal 9304 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 literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/old/times_bold.txf b/examples/src/fnt/data/old/times_bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..1abc5faed636fa820a7c2c50fe13a8d83b7515b4 GIT binary patch literal 9292 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 E013UcnE(I) literal 0 HcmV?d00001 diff --git a/examples/src/fnt/data/test_slideshow.txt b/examples/src/fnt/data/test_slideshow.txt new file mode 100644 index 0000000..00c79ee --- /dev/null +++ b/examples/src/fnt/data/test_slideshow.txt @@ -0,0 +1,36 @@ +1 +2 +3 Hello to the first page. +4 Advance by pressing "enter" +5 +* +6 This is the second page. +7 Go back with backspace. +8 +9 +* +10 It seems you pressed enter again instead of backspace ;) +11 +12 +13 +14 +15 +16 +17 This time some more lines +18 +19 +20 +* +The fourth page +* +The fifth page +* +The sixth page +* +The seventh page +************************************************| +* +* +* +* + diff --git a/examples/src/fnt/fnt_test.cxx b/examples/src/fnt/fnt_test.cxx new file mode 100644 index 0000000..7830f8a --- /dev/null +++ b/examples/src/fnt/fnt_test.cxx @@ -0,0 +1,282 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt_test.cxx 2032 2005-07-05 15:43:18Z bram $ +*/ + + +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + +static fntRenderer *text ; + +static const char *font_names [] = +{ + "Helvetica.txf", /* This is the default */ + "Helvetica-Bold.txf", + "Helvetica-Oblique.txf", + "Helvetica-BoldOblique.txf", + + "AvantGarde-Book.txf", + "AvantGarde-BookOblique.txf", + "AvantGarde-Demi.txf", + "AvantGarde-DemiOblique.txf", + + "Bookman-Light.txf", + "Bookman-LightItalic.txf", + "Bookman-Demi.txf", + "Bookman-DemiItalic.txf", + + "Courier.txf", + "Courier-Bold.txf", + "Courier-Oblique.txf", + "Courier-BoldOblique.txf", + + "NewCenturySchlbk-Roman.txf", + "NewCenturySchlbk-Italic.txf", + "NewCenturySchlbk-Bold.txf", + "NewCenturySchlbk-BoldItalic.txf", + + "Palatino-Roman.txf", + "Palatino-Italic.txf", + "Palatino-Bold.txf", + "Palatino-BoldItalic.txf", + + "Times-Roman.txf", + "Times-Italic.txf", + "Times-Bold.txf", + "Times-BoldItalic.txf", + + "ZapfChancery-MediumItalic.txf", + + "old/sorority.txf", + "old/charter.txf", +/* "old/courier-bold.txf", + "old/courier_medium.txf", */ + "old/curlfont.txf", + "old/default.txf", + "old/derniere.txf", + "old/haeberli.txf", +/* "old/helvetica_bold.txf", + "old/helvetica_medium.txf", */ + "old/junius.txf", + "old/ledfixed.txf", + "old/lucida.txf", + "old/lucidabright_bold.txf", +/* "old/schoolbook_bold.txf", + "old/schoolbook_medium.txf", */ + "old/symbol.txf", +/* "old/times_bold.txf", + "old/times_medium.txf", + "old/typewriter.txf", */ + NULL +} ; + +static fntTexFont **font_list ; + +static int cur_font = 0 ; +static int max_font = 0 ; + +static void motionfn ( int, int ) +{ + glutPostRedisplay () ; +} + +static void keyfn ( unsigned char key, int, int ) +{ + cur_font++ ; + + if ( cur_font >= max_font ) + cur_font = 0 ; +} + +static void mousefn ( int /*button*/, int /*updown*/, int /*x*/, int /*y*/ ) +{ + exit ( 0 ) ; +} + +static int getWindowHeight () { return glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ; } +static int getWindowWidth () { return glutGet ( (GLenum) GLUT_WINDOW_WIDTH ) ; } + +static void setOpenGLState ( void ) +{ + int w = getWindowWidth () ; + int h = getWindowHeight () ; + + glPushAttrib ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + glEnable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + glViewport ( 0, 0, w, h ) ; + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + gluOrtho2D ( 0, w, 0, h ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; +} + +static void restoreOpenGLState ( void ) +{ + glMatrixMode ( GL_PROJECTION ) ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + glPopAttrib () ; +} + + + +static void displayfn (void) +{ + setOpenGLState () ; + glClearColor ( 0.1f, 0.4f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + + text -> setFont ( font_list [ 0 ] ) ; + text -> setPointSize ( 25.0f ) ; + + text -> begin () ; + glColor3f ( 1.0f, 1.0f, 0.0f ) ; + text -> start2f ( 50.0f, 400.0f ) ; + text -> puts ( "This is the PLIB Font Demo." ) ; + text -> end () ; + + text -> setFont ( font_list [ 0 ] ) ; + text -> setPointSize ( 12.0f ) ; + + text -> begin () ; + glColor3f ( 0.0f, 0.0f, 0.0f ) ; + text -> start2f ( 270.0f, 168.0f ) ; + text -> puts ( "The current font is:" ) ; + text -> start2f ( 300.0f, 148.0f ) ; + text -> puts ( font_names [ cur_font ] ) ; + text -> start2f ( 270.0f, 128.0f ) ; + text -> puts ( "Press any key to change the font.\nClick the mouse to exit." ) ; + text -> end () ; + + text -> setFont ( font_list [ cur_font ] ) ; + text -> setPointSize ( 25.0f ) ; + + text -> begin () ; + glColor3f ( 0.0f, 1.0f, 1.0f ) ; + text -> start2f ( 50.0f, 360.0f ) ; + text -> puts ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) ; + glColor3f ( 1.0f, 0.0f, 1.0f ) ; + text -> start2f ( 50.0f, 320.0f ) ; + text -> puts ( "abcdefghijklmnopqrstuvwxyz" ) ; + glColor3f ( 1.0f, 0.0f, 0.0f ) ; + text -> start2f ( 50.0f, 280.0f ) ; + text -> puts ( "0123456789!@#$%^&*()+-={}[]:\"'<>?,./`~_" ) ; + text -> end () ; + + text -> setPointSize ( 9.0f ) ; + text -> begin () ; + glColor3f ( 0, 0, 0 ) ; + text -> start2f ( -50.0f, 265.0f ) ; + text -> puts ( "This is a very long line, in fact, this line is so long, that it starts left of viewport. We are curious to see wether this line is still visible in plib." ) ; + text -> end () ; + + glDisable ( GL_TEXTURE_2D ) ; + glBegin ( GL_LINE_LOOP ) ; + glColor3f ( 1.0f, 0.0f, 1.0f ) ; + glVertex2f ( 0.0f, 0.0f ) ; + glVertex2f ( 256.0f, 0.0f ) ; + glVertex2f ( 256.0f, 256.0f ) ; + glVertex2f ( 0.0f, 256.0f ) ; + glEnd () ; + + glEnable ( GL_TEXTURE_2D ) ; + glBegin ( GL_TRIANGLE_STRIP ) ; + glColor3f ( 1.0f, 1.0f, 1.0f ) ; + glTexCoord2f( 0.0f, 0.0f ) ; glVertex2f ( 0.0f, 0.0f ) ; + glTexCoord2f( 1.0f, 0.0f ) ; glVertex2f ( 256.0f, 0.0f ) ; + glTexCoord2f( 0.0f, 1.0f ) ; glVertex2f ( 0.0f, 256.0f ) ; + glTexCoord2f( 1.0f, 1.0f ) ; glVertex2f ( 256.0f, 256.0f ) ; + glEnd () ; + + restoreOpenGLState () ; + glutSwapBuffers () ; + glutPostRedisplay () ; +} + + +int main ( int argc, char **argv ) +{ + font_list = new fntTexFont* [ sizeof(font_names) / sizeof(font_names[0]) ] ; + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( "FNT Application" ) ; + glutDisplayFunc ( displayfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutKeyboardFunc ( keyfn ) ; + + text = new fntRenderer () ; + + for ( max_font = 0 ; font_names [ max_font ] != NULL ; max_font++ ) + { + char fname [ 256 ] ; + +#ifdef macintosh + sprintf ( fname, ":data:%s", font_names [ max_font ] ) ; +#else + sprintf ( fname, "data/%s", font_names [ max_font ] ) ; +#endif + + font_list [ max_font ] = new fntTexFont ( fname ) ; + } + + cur_font = 0 ; + + glutMainLoop () ; + return 0 ; +} + diff --git a/examples/src/fnt/fnt_test.dsp b/examples/src/fnt/fnt_test.dsp new file mode 100644 index 0000000..7b6f640 --- /dev/null +++ b/examples/src/fnt/fnt_test.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="fnt_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=fnt_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "fnt_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "fnt_test.mak" CFG="fnt_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "fnt_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "fnt_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "fnt_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"fnt_test.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "fnt_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"fnt_test.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "fnt_test - Win32 Release" +# Name "fnt_test - Win32 Debug" +# Begin Source File + +SOURCE=.\fnt_test.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/fnt/hello_world.cxx b/examples/src/fnt/hello_world.cxx new file mode 100644 index 0000000..7146d7a --- /dev/null +++ b/examples/src/fnt/hello_world.cxx @@ -0,0 +1,65 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: hello_world.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include +#include + +int main ( int argc, char **argv ) +{ + int w, h ; + + pwInit ( 100, 100, 230, 50, false, "FNT Hello World Demo", true, 0 ) ; + pwSetCallbacks ( NULL, NULL, NULL, NULL, NULL ) ; + pwGetSize ( &w, &h ) ; + + fntRenderer *text = new fntRenderer () ; + + text -> setFont ( new fntTexFont ( "data/Helvetica.txf" ) ) ; + text -> setPointSize ( 30.0f ) ; + + glEnable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + glMatrixMode ( GL_PROJECTION ) ; + glLoadIdentity () ; + glOrtho ( 0, w, 0, h, -1, 1 ) ; + + while ( 1 ) + { + glClearColor ( 0.1f, 0.4f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + + text -> begin () ; + glColor3f ( 1.0f, 1.0f, 0.0f ) ; + text -> start2f ( 10.0f, 10.0f ) ; + text -> puts ( "Hello World." ) ; + text -> end () ; + + pwSwapBuffers () ; + } + + return 0 ; +} + diff --git a/examples/src/fnt/slideshow.cxx b/examples/src/fnt/slideshow.cxx new file mode 100644 index 0000000..3b0a6ac --- /dev/null +++ b/examples/src/fnt/slideshow.cxx @@ -0,0 +1,235 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slideshow.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#ifdef WIN32 +# include +# ifdef __CYGWIN32__ +# include +# include +# else +# include +# endif +#else +# include +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + +static fntRenderer *text ; +static fntTexFont *font ; + +static char *file ; +static char **line ; +static int *page ; +static int num_lines = 0 ; +static int num_pages = 0 ; +static int curr_page = 0 ; + +static void motionfn ( int, int ) +{ + glutPostRedisplay () ; +} + +static void keyfn ( unsigned char key, int, int ) +{ + switch ( key ) + { + case 'H' : case 'R' : curr_page = 0 ; break ; + + case '<' : case '{' : curr_page-=5 ; break ; + + case 0x08 : /* Backspace */ + case '[' : case ',' : curr_page-- ; break ; + + case '>' : case '}' : curr_page+=5 ; break ; + case ' ' : + case '\n' : + case '\r' : + case ']' : case '.' : curr_page++ ; break ; + + case 0x03 : /* Ctrl-C */ + case 'x' : + case 'X' : exit ( 0 ) ; + } + + if ( curr_page < 0 ) curr_page = 0 ; + if ( curr_page >= num_pages ) curr_page = num_pages-1 ; +} + +static void mousefn ( int /*button*/, int /*updown*/, int /*x*/, int /*y*/ ) +{ + exit ( 0 ) ; +} + +static int getWindowHeight () { return glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ; } +static int getWindowWidth () { return glutGet ( (GLenum) GLUT_WINDOW_WIDTH ) ; } + +static void setOpenGLState ( void ) +{ + int w = getWindowWidth () ; + int h = getWindowHeight () ; + + glutWarpPointer ( 320, 240 ) ; + + glPushAttrib ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + glEnable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + glViewport ( 0, 0, w, h ) ; + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + gluOrtho2D ( 0, w, 0, h ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; +} + +static void restoreOpenGLState ( void ) +{ + glMatrixMode ( GL_PROJECTION ) ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + glPopAttrib () ; +} + + + +static void displayfn (void) +{ + setOpenGLState () ; + glClearColor ( 0.1f, 0.4f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + + text -> setFont ( font ) ; + text -> setPointSize ( 25.0f ) ; + + int row = 460 ; + + text -> begin () ; + glColor3f ( 1.0f, 1.0f, 1.0f ) ; + + for ( int i = page [ curr_page ] ; + i < page [ curr_page+1 ]-1 && i < num_lines && row > 0 ; i++ ) + { + text -> start2f ( 10.0f, (float)row ) ; + text -> puts ( line[i] ) ; + row -= 30 ; + } + text -> end () ; + + restoreOpenGLState () ; + glutSwapBuffers () ; + glutPostRedisplay () ; +} + + +int main ( int argc, char **argv ) +{ + + struct stat buf ; + const char* fname = NULL ; + + if ( argc > 1 ) + fname = argv[1] ; + else + fname = "data/test_slideshow.txt" ; + + FILE *fd = fopen ( fname, "r" ) ; + + if ( fd == NULL ) + exit ( 1 ) ; + + fstat ( fileno(fd), & buf ) ; + + off_t len = buf.st_size ; + + file = new char [ len+1 ] ; + line = new char* [ len+1 ] ; + page = new int [ len+1 ] ; + + fread ( file, 1, len, fd ) ; + fclose ( fd ) ; + + file [ len ] = '\0' ; + + num_lines = 0 ; + num_pages = 0 ; + + line [ num_lines ] = file ; + page [ num_pages ] = 0 ; + + for ( int i = 0 ; i < len ; i++ ) + { + if ( file [ i ] == '\n' ) + { + file [ i ] = '\0' ; + line [ ++num_lines ] = & file [ i+1 ] ; + + if ( line [ num_lines ][ 0 ] == '*' ) + page [ ++num_pages ] = num_lines+1 ; + } + } + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( "FNT Application" ) ; + glutDisplayFunc ( displayfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutKeyboardFunc ( keyfn ) ; + + text = new fntRenderer () ; + font = new fntTexFont ( "data/old/lucida.txf" ) ; + + glutMainLoop () ; + return 0 ; +} + + diff --git a/examples/src/fnt/slideshow.dsp b/examples/src/fnt/slideshow.dsp new file mode 100644 index 0000000..e9a2aec --- /dev/null +++ b/examples/src/fnt/slideshow.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="slideshow" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=slideshow - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "slideshow.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "slideshow.mak" CFG="slideshow - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "slideshow - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "slideshow - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "slideshow - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libcd.lib" /out:"slideshow.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "slideshow - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"slideshow.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "slideshow - Win32 Release" +# Name "slideshow - Win32 Debug" +# Begin Source File + +SOURCE=.\slideshow.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/js/Makefile.am b/examples/src/js/Makefile.am new file mode 100644 index 0000000..655d2de --- /dev/null +++ b/examples/src/js/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_JS + +noinst_PROGRAMS = js_demo + +js_demo_SOURCES = js_demo.cxx + +js_demo_LDADD = -lplibjs -lplibul + +endif + +EXTRA_DIST = js_demo.dsp + diff --git a/examples/src/js/js_demo.cxx b/examples/src/js/js_demo.cxx new file mode 100644 index 0000000..6b64621 --- /dev/null +++ b/examples/src/js/js_demo.cxx @@ -0,0 +1,124 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: js_demo.cxx 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#include + +int main ( int, char ** ) +{ + jsJoystick *js[2] ; + float *ax[2] ; + + jsInit () ; + + js[0] = new jsJoystick ( 0 ) ; + js[1] = new jsJoystick ( 1 ) ; + + printf ( "Joystick test program.\n" ) ; + printf ( "~~~~~~~~~~~~~~~~~~~~~~\n" ) ; + + if ( js[0]->notWorking () ) + printf ( "Joystick 0 not detected\n" ) ; + else + printf ( "Joystick 0 is \"%s\"\n", js[0]->getName() ) ; + + if ( js[1]->notWorking () ) + printf ( "Joystick 1 not detected\n" ) ; + else + printf ( "Joystick 1 is \"%s\"\n", js[1]->getName() ) ; + + if ( js[0]->notWorking () && js[1]->notWorking () ) exit ( 1 ) ; + + ax[0] = new float [ js[0]->getNumAxes () ] ; + ax[1] = new float [ js[1]->getNumAxes () ] ; + + int i, j ; + + for ( i = 0 ; i < 2 ; i++ ) + printf ( "+---------------JS.%d-----------------", i ) ; + + printf ( "+\n" ) ; + + for ( i = 0 ; i < 2 ; i++ ) + { + if ( js[i]->notWorking () ) + printf ( "| ~~~ Not Detected ~~~ " ) ; + else + { + printf ( "| Btns " ) ; + + for ( j = 0 ; j < js[i]->getNumAxes () ; j++ ) + printf ( "Ax:%d ", j ) ; + + for ( ; j < 6 ; j++ ) + printf ( " " ) ; + } + } + + printf ( "|\n" ) ; + + for ( i = 0 ; i < 2 ; i++ ) + printf ( "+------------------------------------" ) ; + + printf ( "+\n" ) ; + + while (1) + { + for ( i = 0 ; i < 2 ; i++ ) + { + if ( js[i]->notWorking () ) + printf ( "| . . . . . . . . . " ) ; + else + { + int b ; + + js[i]->read ( &b, ax[i] ) ; + + printf ( "| %04x ", b ) ; + + for ( j = 0 ; j < js[i]->getNumAxes () ; j++ ) + printf ( "%+.1f ", ax[i][j] ) ; + + for ( ; j < 6 ; j++ ) + printf ( " . " ) ; + } + } + + printf ( "|\r" ) ; + fflush ( stdout ) ; + + /* give other processes a chance */ + +#ifdef WIN32 + Sleep ( 1 ) ; +#elif defined(sgi) + sginap ( 1 ) ; +#else + usleep ( 1000 ) ; +#endif + } + + return 0 ; +} + + diff --git a/examples/src/js/js_demo.dsp b/examples/src/js/js_demo.dsp new file mode 100644 index 0000000..ca789ae --- /dev/null +++ b/examples/src/js/js_demo.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="js_demo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=js_demo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "js_demo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "js_demo.mak" CFG="js_demo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "js_demo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "js_demo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "js_demo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul.lib js.lib winmm.lib /nologo /subsystem:console /machine:I386 /out:"js_demo.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "js_demo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul_d.lib js_d.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"js_demo.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "js_demo - Win32 Release" +# Name "js_demo - Win32 Debug" +# Begin Source File + +SOURCE=.\js_demo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/net/Makefile.am b/examples/src/net/Makefile.am new file mode 100644 index 0000000..3e3d542 --- /dev/null +++ b/examples/src/net/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = client_server http_get net_echo + diff --git a/examples/src/net/client_server/Makefile.am b/examples/src/net/client_server/Makefile.am new file mode 100644 index 0000000..1d13015 --- /dev/null +++ b/examples/src/net/client_server/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_NET + +noinst_PROGRAMS = net_udp_client net_udp_server + +net_udp_client_SOURCES = net_udp_client.cxx +net_udp_client_LDADD = -lplibnet -lplibul + +net_udp_server_SOURCES = net_udp_server.cxx +net_udp_server_LDADD = -lplibnet -lplibul + +endif + +EXTRA_DIST = net_udp_client.dsp net_udp_server.dsp + diff --git a/examples/src/net/client_server/README b/examples/src/net/client_server/README new file mode 100644 index 0000000..bcc7e35 --- /dev/null +++ b/examples/src/net/client_server/README @@ -0,0 +1,25 @@ + +This example demonstrates a simple one-way communication path +between a client and a server. + +It uses UDP protocol - which is unreliable but fast - don't +use it for messages that utterly must get there! + +The client program can be told to send to the server on any port number of +any host computer - by default it uses 'localhost'. + +The server program can be told to listen out on any port number +and to either listen to just one specific client - or to accept +messages from any client. + +Usage: + + net_udp_client [-p port] [servername] + net_udp_server [-p port] [clientname] + +The default for the client is to use port 5501 on 'localhost'. +The default to the server is to listen on port 5501 and to allow anyone +to send to it. The name "" refers to the broadcast address. +(To enter it on the command line, you'll need to escape the < and > +symbols like this: \ ) + diff --git a/examples/src/net/client_server/net_udp_client.cxx b/examples/src/net/client_server/net_udp_client.cxx new file mode 100644 index 0000000..28724b7 --- /dev/null +++ b/examples/src/net/client_server/net_udp_client.cxx @@ -0,0 +1,105 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: net_udp_client.cxx 1898 2004-03-21 17:13:00Z sjbaker $ +*/ + +// This is an example of a UDP client that blasts messages at a +// server. +// +// UDP is a "fire and forget" protocal so the client and server can be +// started up in any order. The client doesn't care if the server +// receives the messages and the server doesn't care if the client is +// sending messages, so either side can be killed and restarted at any +// time. + +#include +#include +#include + +void help () +{ + fprintf ( stderr, "net_udp_client: Usage -\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, " net_udp_client [-h] [-p port] [hostname]\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, "Where:\n" ) ; + fprintf ( stderr, " -h -- Help (displays this message).\n" ) ; + fprintf ( stderr, " -p port -- Set the port number (def=5501).\n" ) ; + fprintf ( stderr, " hostname -- Set the host name (def=localhost).\n" ) ; + fprintf ( stderr, "\n" ) ; + exit ( 0 ) ; +} + +int main ( int argc, char **argv ) +{ + int port = 5501 ; + char host[256] = "localhost" ; + + for ( int i = 1 ; i < argc ; i++ ) + { + if ( argv [ i ][ 0 ] == '-' || argv [ i ][ 0 ] == '+' ) + switch ( argv [ i ][ 1 ] ) + { + case 'p' : port = atoi ( argv [ ++i ] ) ; break ; + case 'h' : + default : help () ; break ; + } + else + strcpy ( host, argv [ i ] ) ; + } + + fprintf ( stderr, "Talking to host '%s' on port %d\n", host, port ) ; + + // Must call this before any other net stuff + + netInit() ; + + netSocket *sock = new netSocket () ; + + if ( ! sock -> open ( false ) ) // open a UDP socket + { + printf ( "error opening socket\n" ) ; + return -1 ; + } + + sock -> setBlocking ( false ) ; + + if ( sock -> connect( host, port ) == -1 ) + { + printf ( "error connecting to %s:%d\n", host, port ) ; + return -1 ; + } + + char msg[256] = "Hello world!" ; + + int len = strlen ( msg ) ; + + while ( true ) + { + ulSleep ( 1 ) ; + sock -> send( msg, len, 0 ); + printf("msg sent = %s\n", msg); + } + + return 0; +} + + diff --git a/examples/src/net/client_server/net_udp_client.dsp b/examples/src/net/client_server/net_udp_client.dsp new file mode 100644 index 0000000..4384d7b --- /dev/null +++ b/examples/src/net/client_server/net_udp_client.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="net_udp_client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=net_udp_client - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net_udp_client.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net_udp_client.mak" CFG="net_udp_client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net_udp_client - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "net_udp_client - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net_udp_client - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net.lib ul.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"net_udp_client.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "net_udp_client - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net_d.lib ul_d.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"net_udp_client.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "net_udp_client - Win32 Release" +# Name "net_udp_client - Win32 Debug" +# Begin Source File + +SOURCE=.\net_udp_client.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/net/client_server/net_udp_server.cxx b/examples/src/net/client_server/net_udp_server.cxx new file mode 100644 index 0000000..714b44b --- /dev/null +++ b/examples/src/net/client_server/net_udp_server.cxx @@ -0,0 +1,87 @@ +// This is an example of a UDP server that accepts messages coming from a +// client. +// +// UDP is a "fire and forget" protocal so the client and server can be +// started up in any order. The client doesn't care if the server +// receives the messages and the server doesn't care if the client is +// sending messages, so either side can be killed and restarted at any +// time. + +#include +#include + + +void help () +{ + fprintf ( stderr, "net_udp_server: Usage -\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, " net_udp_server [-h] [-p port] [hostname]\n" ) ; + fprintf ( stderr, "\n" ) ; + fprintf ( stderr, "Where:\n" ) ; + fprintf ( stderr, " -h -- Help (displays this message).\n" ) ; + fprintf ( stderr, " -p port -- Set the port number (def=5501).\n" ) ; + fprintf ( stderr, " hostname -- Set the client host name (def=any host).\n"); + fprintf ( stderr, "\n" ) ; + exit ( 0 ) ; +} + + +int main( int argc, char **argv ) +{ + int port = 5501 ; + char host [ 256 ] = "" ; + + for ( int i = 1 ; i < argc ; i++ ) + { + if ( argv [ i ][ 0 ] == '-' || argv [ i ][ 0 ] == '+' ) + switch ( argv [ i ][ 1 ] ) + { + case 'p' : port = atoi ( argv [ ++i ] ) ; break ; + case 'h' : + default : help () ; break ; + } + else + strcpy ( host, argv [ i ] ) ; + } + + if ( host [ 0 ] == '\0' ) + fprintf ( stderr, "Listening to any host on port %d\n", port ) ; + else + fprintf ( stderr, "Listening to host '%s' on port %d\n", host, port ) ; + + // Must call this before any other net stuff + + netInit () ; + + netSocket *sock = new netSocket () ; + + if ( ! sock -> open( false ) ) // open a UDP socket + { + printf ( "error opening socket\n" ) ; + return -1 ; + } + + sock -> setBlocking ( false ) ; + + if ( sock -> bind( host, port ) == -1 ) + { + printf ( "error binding to port %s:%d\n", host, port ) ; + return -1 ; + } + + char msg [ 256 ] ; + int maxlen = 256 ; + int len ; + + while ( true ) + { + if ( (len = sock -> recv(msg, maxlen, 0)) >= 0 ) + { + msg[len] = '\0' ; + printf ( "msg received = %s\n", msg ) ; + } + } + + return 0 ; +} + diff --git a/examples/src/net/client_server/net_udp_server.dsp b/examples/src/net/client_server/net_udp_server.dsp new file mode 100644 index 0000000..041a001 --- /dev/null +++ b/examples/src/net/client_server/net_udp_server.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="net_udp_server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=net_udp_server - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net_udp_server.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net_udp_server.mak" CFG="net_udp_server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net_udp_server - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "net_udp_server - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net_udp_server - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net.lib ul.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"net_udp_server.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "net_udp_server - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net_d.lib ul_d.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"net_udp_server.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "net_udp_server - Win32 Release" +# Name "net_udp_server - Win32 Debug" +# Begin Source File + +SOURCE=.\net_udp_server.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/net/http_get/Makefile.am b/examples/src/net/http_get/Makefile.am new file mode 100644 index 0000000..f335a24 --- /dev/null +++ b/examples/src/net/http_get/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_NET + +noinst_PROGRAMS = http_get + +http_get_SOURCES = http_get.cxx + +http_get_LDADD = -lplibnet -lplibul + +endif + +EXTRA_DIST = http_get.dsp + diff --git a/examples/src/net/http_get/README b/examples/src/net/http_get/README new file mode 100644 index 0000000..6d2ee85 --- /dev/null +++ b/examples/src/net/http_get/README @@ -0,0 +1,6 @@ + +This demonstrates the fetching of a HTTP web page using the network +library. + +It's hardwired to fetch the PLIB home-page. + diff --git a/examples/src/net/http_get/http_get.cxx b/examples/src/net/http_get/http_get.cxx new file mode 100644 index 0000000..adb7b8f --- /dev/null +++ b/examples/src/net/http_get/http_get.cxx @@ -0,0 +1,75 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: http_get.cxx 1906 2004-03-22 19:44:50Z sjbaker $ +*/ + +#include + +class HTTPClient : public netBufferChannel +{ + FILE *fpout ; + +public: + + HTTPClient ( const char* host, const char* path ) + { + open () ; + connect ( host, 80 ) ; + + const char* s = netFormat ( "GET %s HTTP/1.1\r\n", path ) ; + bufferSend ( s, strlen(s) ) ; + const char* h = netFormat ( "host: %s\r\n", host ) ; + bufferSend ( h, strlen(h) ) ; + const char *c = netFormat ( "Connection: close\r\n\r\n" ) ; + bufferSend ( c, strlen(c) ) ; + } + + ~HTTPClient () + { + if( fpout ) + fclose( fpout ); + } + + virtual void handleBufferRead (netBuffer& buffer) + { + const char* s = buffer.getData(); + + while (*s) + fputc(*s++,stdout); + + buffer.remove(); + } +} ; + + +int main ( int argc, char * argv[] ) +{ + netInit ( &argc, argv ) ; + + new HTTPClient ( "plib.sourceforge.net", "/index.html" ) ; + + netChannel::loop ( 0 ) ; + return 0 ; +} + + + + diff --git a/examples/src/net/http_get/http_get.dsp b/examples/src/net/http_get/http_get.dsp new file mode 100644 index 0000000..4a6d509 --- /dev/null +++ b/examples/src/net/http_get/http_get.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="http_get" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=http_get - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "http_get.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "http_get.mak" CFG="http_get - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http_get - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "http_get - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "http_get - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net.lib ul.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"http_get.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "http_get - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net_d.lib ul_d.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"http_get.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "http_get - Win32 Release" +# Name "http_get - Win32 Debug" +# Begin Source File + +SOURCE=.\http_get.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/net/net_echo/Makefile.am b/examples/src/net/net_echo/Makefile.am new file mode 100644 index 0000000..f0566ef --- /dev/null +++ b/examples/src/net/net_echo/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_NET + +noinst_PROGRAMS = net_echo + +net_echo_SOURCES = net_echo.cxx + +net_echo_LDADD = -lplibnet -lplibul + +endif + +EXTRA_DIST = net_echo.dsp + diff --git a/examples/src/net/net_echo/README b/examples/src/net/net_echo/README new file mode 100644 index 0000000..a38fbfb --- /dev/null +++ b/examples/src/net/net_echo/README @@ -0,0 +1,16 @@ + +This is a simple example server that reads lines of text from the client +and echoes them back. An echo server is usually running on TCP +port 7 of Unix machines. + +Start it running someplace on your network, then use 'telnet' to +connect to it on port 8000: + + telnet localhost 8000 + +...notice that you can open multiple telnet sessions - and the +server echoes the right data back to the right client. + +You can choose a port number other than 8000 by putting it on the +command line of the echo server. + diff --git a/examples/src/net/net_echo/net_echo.cxx b/examples/src/net/net_echo/net_echo.cxx new file mode 100644 index 0000000..26ac15c --- /dev/null +++ b/examples/src/net/net_echo/net_echo.cxx @@ -0,0 +1,97 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: net_echo.cxx 1898 2004-03-21 17:13:00Z sjbaker $ +*/ + +#include + +/* +** simple example server that reads lines of text from the client +** and echoes them back. An echo server is usually running on TCP +** port 7 of Unix machines. +*/ + +class EchoChannel : public netChat +{ + netBuffer buffer ; + +public: + + EchoChannel() : buffer(512) { setTerminator("\r\n"); } + + virtual void collectIncomingData (const char* s, int n) + { + buffer.append(s,n); + } + + virtual void foundTerminator (void) + { + const char* s = buffer.getData(); + printf("echo: %s\n",s); + + push(s); + push(getTerminator()); + + buffer.remove(); + } +} ; + +class EchoServer : private netChannel +{ + virtual bool writable (void) { return false ; } + + virtual void handleAccept (void) + { + netAddress addr ; + int handle = accept ( &addr ) ; + printf("Client %s:%d connected\n",addr.getHost(),addr.getPort()); + + EchoChannel * ec = new EchoChannel; + ec->setHandle ( handle ) ; + } + +public: + + EchoServer ( int port ) + { + open () ; + bind ("", port) ; + listen (5) ; + + printf ( "Echo Server started on port %d\n", port ) ; + } +} ; + +int +main (int argc, char * argv[]) +{ + int port = 8000 ; + + if (argc > 1) + port = atoi (argv[1]); + + netInit(&argc,argv); + + new EchoServer ( port ) ; + + netChannel::loop(0); + return 0; +} diff --git a/examples/src/net/net_echo/net_echo.dsp b/examples/src/net/net_echo/net_echo.dsp new file mode 100644 index 0000000..01e8340 --- /dev/null +++ b/examples/src/net/net_echo/net_echo.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="net_echo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=net_echo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net_echo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net_echo.mak" CFG="net_echo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net_echo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "net_echo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net_echo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net.lib ul.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"net_echo.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "net_echo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib net_d.lib ul_d.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"net_echo.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "net_echo - Win32 Release" +# Name "net_echo - Win32 Debug" +# Begin Source File + +SOURCE=.\net_echo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/psl/Makefile.am b/examples/src/psl/Makefile.am new file mode 100644 index 0000000..b2b20fb --- /dev/null +++ b/examples/src/psl/Makefile.am @@ -0,0 +1,13 @@ +if BUILD_PSL + +noinst_PROGRAMS = psl_demo + +psl_demo_SOURCES = psl_demo.cxx + +psl_demo_LDADD = -lplibpsl -lplibul -lm + +endif + +EXTRA_DIST = psl_demo.dsp + + diff --git a/examples/src/psl/data/header.psh b/examples/src/psl/data/header.psh new file mode 100644 index 0000000..9fbe382 --- /dev/null +++ b/examples/src/psl/data/header.psh @@ -0,0 +1,4 @@ + +void newline () { printf ( "\n" ) ; } +void tab () { printf ( "\t" ) ; } + diff --git a/examples/src/psl/data/test.psl b/examples/src/psl/data/test.psl new file mode 100644 index 0000000..2beaac2 --- /dev/null +++ b/examples/src/psl/data/test.psl @@ -0,0 +1,9 @@ + + +void main () +{ + int diff ; + + if ( diff == 1 ) { diff = 2 ; } else { diff = 3 ; } +} + diff --git a/examples/src/psl/psl_demo.cxx b/examples/src/psl/psl_demo.cxx new file mode 100644 index 0000000..cfd3c6b --- /dev/null +++ b/examples/src/psl/psl_demo.cxx @@ -0,0 +1,129 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: psl_demo.cxx 1912 2004-03-24 10:54:56Z nmcevoy $ +*/ + + +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +pslValue my_printf ( int argc, pslValue *argv, pslProgram *p ) +{ + if ( argv[0].getType() != PSL_STRING ) + printf ( "printf: ERROR - First param must be a string!\n" ) ; + else + { + printf ( "%s", argv[0].getString() ) ; + + for ( int i = 1 ; i < argc ; i++ ) + { + switch ( argv[i].getType () ) + { + case PSL_INT : printf ( "%d ", argv[i].getInt () ) ; break ; + case PSL_FLOAT : printf ( "%f ", argv[i].getFloat () ) ; break ; + case PSL_STRING : printf ( "%s ", argv[i].getString () ) ; break ; + case PSL_VOID : printf ( "(void) " ) ; break ; + } + } + } + + pslValue ret ; + return ret ; +} + + +pslValue identify ( int argc, pslValue *argv, pslProgram *p ) +{ + printf ( "I am %s.\n", (char *)( p->getUserData ()) ) ; + + pslValue ret ; + return ret ; +} + + +pslExtension extensions [] = +{ + { "identify", 0, identify }, + { "printf", -1, my_printf }, + { NULL, 0, NULL } +} ; + + +int main () +{ + pslInit () ; + + /* Create program 1 and compile it. */ + + pslProgram *prog_1 = new pslProgram ( extensions, "code1" ) ; + + ulClock ck ; + ck.setMaxDelta ( 100000.0 ) ; + ck.update () ; + + prog_1 -> compile ( "data/test.psl" ) ; + + ck.update () ; + fprintf(stderr, "%fs compiletime elapsed\n", ck.getDeltaTime () ) ; + + prog_1 -> dump () ; + + /* Clone program 2 from program 1 */ + + pslProgram *prog_2 = new pslProgram ( prog_1, "code2" ) ; + + /* Make program 3 from inline strings. */ + + pslProgram *prog_3 = new pslProgram ( extensions, "inline" ) ; + + prog_3 -> compile ( "int main () { printf ( \"Hello World.\\n\" ) ; }\n", + "HelloProgram" ) ; + + /* Make them unique by assigning user data to them */ + + prog_1 -> setUserData ( (void *) "Program 1" ) ; + prog_2 -> setUserData ( (void *) "Program 2" ) ; + prog_3 -> setUserData ( (void *) "Program 3" ) ; + + ck.update () ; + + while ( 1 ) + { + if ( prog_1 -> step () == PSL_PROGRAM_END && + prog_2 -> step () == PSL_PROGRAM_END && + prog_3 -> step () == PSL_PROGRAM_END ) + break ; + } + ck.update () ; + fprintf(stderr, "%fs runtime elapsed\n", ck.getDeltaTime () ) ; + + exit ( 0 ) ; + + return 0 ; +} + + diff --git a/examples/src/psl/psl_demo.dsp b/examples/src/psl/psl_demo.dsp new file mode 100644 index 0000000..6ba3ba0 --- /dev/null +++ b/examples/src/psl/psl_demo.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="psl_demo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=psl_demo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "psl_demo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "psl_demo.mak" CFG="psl_demo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "psl_demo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "psl_demo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "psl_demo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib psl.lib ul.lib /nologo /subsystem:console /machine:I386 /out:"psl_demo.exe" /libpath:"..\..\..\..\plib" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "psl_demo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib psl_d.lib ul_d.lib /nologo /subsystem:console /debug /machine:I386 /out:"psl_demo.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "psl_demo - Win32 Release" +# Name "psl_demo - Win32 Debug" +# Begin Source File + +SOURCE=.\psl_demo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pui/Makefile.am b/examples/src/pui/Makefile.am new file mode 100644 index 0000000..d5e0aaf --- /dev/null +++ b/examples/src/pui/Makefile.am @@ -0,0 +1,31 @@ +if BUILD_PUI + +noinst_PROGRAMS = simple complex widget_list PointPicker widget_test + +simple_SOURCES = simple.cxx + +complex_SOURCES = complex.cxx + +widget_test_SOURCES = widget_test.cxx + +widget_list_SOURCES = widget_list.cxx + +PointPicker_SOURCES = PointPicker.cxx + +simple_LDADD = -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +complex_LDADD = -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +widget_test_LDADD = -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) \ + $(OGL_LIBS) + +widget_list_LDADD = -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) \ + $(OGL_LIBS) + +PointPicker_LDADD = -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) \ + $(OGL_LIBS) + +endif + +EXTRA_DIST = complex.dsp simple.dsp PointPicker.dsp widget_list.dsp widget_test.dsp + diff --git a/examples/src/pui/PointPicker.cxx b/examples/src/pui/PointPicker.cxx new file mode 100644 index 0000000..c2f0618 --- /dev/null +++ b/examples/src/pui/PointPicker.cxx @@ -0,0 +1,446 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: PointPicker.cxx 2143 2009-01-21 17:18:45Z fayjf $ +*/ + +// Point Picker Program by John Fay, October 2000. +// This is a demonstration program for PUI. It displays a set of points in +// a window and allows the user to pick them with the right mouse button. +// It demonstrates the following PUI capabilities: +// - puInput widget +// - puOneShot widget +// - active widget functionality, with up, active, and down callbacks +// - valuators +// +// To use it, the user does the following: +// First Method: Click in the input widget and type the number of the +// point +// Second Method: Click in the input widget and right-click on a point to +// select it. +// +// For both methods, clicking the left (one-shot) button moves the point +// to the right. When it gets far enough to the right, it wraps around to +// the left and moves slightly upwards. + +#include +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include + +//#define VOODOO 1 + +/***********************************\ +* * +* These are the PUI widget pointers * +* * +\***********************************/ + +static int main_window ; +static puText *instruction ; +static puInput *point_no ; +static puOneShot *move_point ; +static puButton *exit_button ; +static puaLargeInput *input ; + +static int picked_point ; +static int mouse_x, mouse_y ; // Mouse coordinates + +//static fntTexFont *tim ; + +/**********************************\ +* * +* Here are the point coordinates * +* * +\**********************************/ + +typedef struct { + float x, y, z ; +} node ; + +#define NUMBER_POINTS 30 + +static node point[NUMBER_POINTS] ; + +static int firsttime; + +static void drawSinglePickableObject ( int i, float x, float y, float z ) +{ + // I realize that putting the point size, normal, and begin/end inside the for-loop + // is less efficient, but for the purposes of the demo I want everything required + // to draw a single pickable object in this routine. + + glPointSize ( 10.0f ) ; + glBegin ( GL_POINTS ) ; + glNormal3f ( 1.0f, 0.0f, 0.0f ) ; // Needed so the points show color + + if ( i == picked_point ) + glColor4f ( 1.0f, 0.0f, 0.0f, 1.0f ) ; + else + glColor4f ( 0.0f, 1.0f, 0.0f, 1.0f ) ; + + glVertex3f ( x, y, z ) ; + + glEnd () ; +} + +static void drawPoints (void) +{ + + if ( firsttime ) + { + /* + Deliberately do this only once - it's a better test of + PUI's attempts to leave the OpenGL state undisturbed + */ + glEnable ( GL_BLEND ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GREATER,0.1f); + + firsttime = FALSE ; + + glMatrixMode ( GL_PROJECTION ) ; + gluPerspective ( 40.0, 1.0, 1.0, 10.0 ) ; + glMatrixMode ( GL_MODELVIEW ) ; + gluLookAt ( 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ) ; + + // Assign point coordinates (random values between zero and one) + + for ( int i = 0; i < NUMBER_POINTS; i++ ) + { + point[i].x = ( (float) i / (float)NUMBER_POINTS * 2 ) - 1 ; + point[i].y = ( (float)( ( i * i * i ) % NUMBER_POINTS) / (float)NUMBER_POINTS * 2 ) - 1 ; + point[i].z = ( (float)( ( i * i ) % NUMBER_POINTS) / (float)NUMBER_POINTS * 2 ) - 1 ; + } + } + + GLfloat matrix[16] ; + glGetFloatv ( GL_MODELVIEW_MATRIX, matrix ) ; + glGetFloatv ( GL_PROJECTION_MATRIX, matrix ) ; + + for ( int i = 0 ; i < NUMBER_POINTS ; i++ ) + { + drawSinglePickableObject ( i, point[i].x, point[i].y, point[i].z ) ; + } +} + + +/**************************************\ +* * +* These three functions capture mouse * +* and keystrokes (special and mundane) * +* from GLUT and pass them on to PUI. * +* * +\**************************************/ + +static void specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + glutPostRedisplay () ; +} + +static puObject *active_widget = (puObject *)NULL ; +static void mousefn ( int button, int updown, int x, int y ) +{ + if ( updown == PU_UP ) active_widget = puActiveWidget () ; + if ( !puMouse ( button, updown, x, y ) ) + { + // PUI didn't take the mouseclick, try the main window + + mouse_x = x ; + mouse_y = puGetWindowHeight () - y ; + + // Check for an active widget. If there is one, call its active callback + + if ( active_widget ) + { + active_widget -> invokeActiveCallback () ; + // Make sure PUI keeps the active widget active + puSetActiveWidget ( active_widget, 0, 0 ) ; + } + } + + glutPostRedisplay () ; +} + +/**************************************\ +* * +* This function redisplays the PUI and * +* the points, flips the double buffer * +* and then asks GLUT to post a * +* redisplay command - so we re-render * +* at maximum rate. * +* * +\**************************************/ + +static void displayfn (void) +{ + /* Clear the screen */ + + glClearColor ( 0.4f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + /* Draw the points */ + + drawPoints () ; + + /* Make PUI redraw */ + + puDisplay () ; + + /* Off we go again... */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +/***********************************\ +* * +* Here are the PUI widget callback * +* functions. * +* * +\***********************************/ + +// Point Number Input Widget Up Callback + +static void point_no_up_cb ( puObject *ob ) +{ + printf ( "Calling the up-callback with partial data: %d\n", picked_point ) ; + if ( input->inputDisabled () ) + input->enableInput () ; + else + input->disableInput () ; +} + +// Point Number Input Widget Active Callback + +static void point_no_active_cb ( puObject *ob ) +{ + GLint viewport[4] ; // Viewport + glGetIntegerv ( GL_VIEWPORT, viewport ) ; + + GLuint buffer[512]; + glSelectBuffer ( 512L, buffer ); // Set up the buffer to put hits into + glRenderMode ( GL_SELECT ) ; // Go into Select mode + + glInitNames (); // Empty the name stack + + glMatrixMode ( GL_PROJECTION ) ; // Set the Projection matrix for picking + glPushMatrix () ; // Save the current matrix + glLoadIdentity(); + + // Many thanks to Dave McClurg for getting the transformation matrices to work correctly + /* create 10x10 pixel picking region near cursor location */ + gluPickMatrix ( (double)mouse_x, (double)mouse_y, + 10.0f, 10.0f, viewport ) ; + + gluPerspective ( 40.0, 1.0, 1.0, 10.0 ) ; + + for ( int i = 0 ; i < NUMBER_POINTS ; i++ ) + { + glPushName ( i ) ; + drawSinglePickableObject ( i, point[i].x, point[i].y, point[i].z ) ; + glPopName () ; + } + + glPopMatrix(); + glFlush(); + + long hits = glRenderMode ( GL_RENDER ) ; // Back to Rendering mode, get number of hits + + if ( hits ) + point_no->setValue ( (int)buffer[3] ) ; + + printf ( "Active point %d %d %ld\n", mouse_x, mouse_y, hits ) ; + + glMatrixMode ( GL_MODELVIEW ); // got set to GL_PROJECTION earlier +} + +// Point Number Input Widget Down Callback + +static void point_no_down_cb ( puObject *ob ) +{ + printf ( "Calling the down callback with full data: %d\n", picked_point ) ; + if ( picked_point < 0 ) + { + printf ( "Value too small, setting it to zero\n" ) ; + picked_point = 0 ; + } + + if ( picked_point > NUMBER_POINTS - 1 ) + { + printf ( "Value too big, setting it to %d\n", NUMBER_POINTS - 1 ) ; + picked_point = NUMBER_POINTS - 1 ; + } + + point_no->setValue ( picked_point ) ; +} + +// Move Point Button Callback + +static void move_point_cb ( puObject *ob ) +{ + point[picked_point].x += 0.1f ; + if ( point[picked_point].x > 1.0 ) + { + point[picked_point].x = -1.0 ; + point[picked_point].y += 0.1f ; + } +} + +static void exit_cb ( puObject * ) +{ + fprintf ( stderr, "Exiting PUI demo program.\n" ) ; + exit ( 1 ) ; +} + + +int main ( int argc, char **argv ) +{ + firsttime = TRUE; + picked_point = 0 ; + + // Set up the window + + glutInitWindowPosition( 100, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + main_window = glutCreateWindow ( "Point Picker PUI Application" ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + glutIdleFunc ( displayfn ) ; + + puInit () ; + +#ifdef VOODOO + puShowCursor () ; +#endif + + // Set up the font + + //tim = new fntTexFont ; + //tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + //puFont times_medium ( tim, 15, 0.3f ) ; + //puSetDefaultFonts ( times_medium, times_medium ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.1f, 0.8f, 0.1f, 1.0f) ; + + // Set up the widgets in the window + + instruction = new puText ( 10, 450 ) ; + instruction -> setColour ( PUCOL_LABEL, 1.0, 1.0, 1.0 ) ; + instruction -> setLabel ( "Enter a point number in the input box, or\n" + "left-click in the input box and right-click on a point." ) ; + + point_no = new puInput ( 150, 60, 240, 80 ) ; + point_no->setValuator ( &picked_point ) ; + point_no->setLabel ( "Point Number" ) ; + point_no->setLabelPlace ( PUPLACE_LEFT_CENTER ) ; + point_no->setCallback ( point_no_up_cb ) ; + point_no->setActiveCallback ( point_no_active_cb ) ; + point_no->setDownCallback ( point_no_down_cb ) ; + + move_point = new puOneShot ( 10, 10, 250, 50 ) ; + move_point->setLegend ( "Move Selected Point" ) ; + move_point->setCallback ( move_point_cb ) ; + move_point->makeReturnDefault ( TRUE ) ; + + exit_button = new puButton ( 260, 10, 360, 50 ) ; + exit_button->setLegend ( "Exit" ) ; + exit_button->setCallback ( exit_cb ) ; + exit_button->setStyle ( PUSTYLE_BOXED ) ; + exit_button->setLegendPlace ( PUPLACE_CENTERED_LEFT ) ; + + input = new puaLargeInput ( 440, 0, 200, 300, 1, 20, TRUE ) ; + //input->setChildStyle ( PUCLASS_ARROW, PUSTYLE_BOXED ) ; + //input->setChildBorderThickness ( PUCLASS_ARROW, 15 ) ; + input->setValue( "This is a large input box\n" + "This is line two of a large input box\n" + "Line 3\n" + "Line 4\n" + "This is an extremely long line in the large input box, so long it passes the box width\n" + "Line 6\n" + "Line 7\n" + "Line 8\n" + "Line 9\n" + "Line 10\n" + "Line 11\n" + "Line 12\n" + "Line 13\n" + "Line 14\n" + "Line 15\n" + "Line 16\n" + "Line 17\n" + "Line 18\n" + "Line 19\n" + "Line 20\n" + "Line 21\n" + "Line 22\n" + "Line 23\n" + "Line 24\n" + "Line 25\n" + "Line 26\n" + "Line 27\n" + "Line 28\n" + "Line 29\n" + "Line 30\n" ) ; + + glutMainLoop () ; + return 0 ; +} + + diff --git a/examples/src/pui/PointPicker.dsp b/examples/src/pui/PointPicker.dsp new file mode 100644 index 0000000..5cef7ed --- /dev/null +++ b/examples/src/pui/PointPicker.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="PointPicker" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=PointPicker - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "PointPicker.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "PointPicker.mak" CFG="PointPicker - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "PointPicker - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "PointPicker - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "PointPicker - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui.lib puAux.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"PointPicker.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "PointPicker - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui_d.lib puAux_d.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"PointPicker.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "PointPicker - Win32 Release" +# Name "PointPicker - Win32 Debug" +# Begin Source File + +SOURCE=.\PointPicker.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pui/complex.cxx b/examples/src/pui/complex.cxx new file mode 100644 index 0000000..8e94822 --- /dev/null +++ b/examples/src/pui/complex.cxx @@ -0,0 +1,636 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: complex.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include + +//#define VOODOO 1 + +/***********************************\ +* * +* These are the PUI widget pointers * +* * +\***********************************/ + +static int main_window ; +static puMenuBar *main_menu_bar ; +static puButton *hide_menu_button ; +static puDialogBox *dialog_box ; +static puText *dialog_box_message ; +static puOneShot *dialog_box_ok_button ; +static puText *timer_text ; +static puGroup *mygroup ; +static puInput *input1 ; +static puInput *input2 ; + +static int slider_window ; +static puSlider *rspeedSlider ; +static puSlider *directSlider ; + +static int save_window ; +static puaFileSelector *file_selector ; + +static int coordinate_window ; +static puaBiSlider *x_coordinate ; +static puaTriSlider *y_coordinate ; + +//static fntTexFont *hel ; +//static fntTexFont *tim ; + +/***********************************\ +* * +* This is a generic tumbling cube * +* * +\***********************************/ + +static const GLfloat light_diffuse [] = {0.0, 1.0, 0.0, 1.0} ; /* Red diffuse light. */ +static const GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0} ; /* Infinite light location. */ + +static const GLfloat cube_n[6][3] = /* Normals */ +{ + {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, + { 0.0,-1.0, 0.0}, {0.0, 0.0,-1.0}, {0.0, 0.0, 1.0} +} ; + +static const GLint cube_i[6][4] = /* Vertex indices */ +{ + {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4}, + {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} +} ; + +static const GLfloat cube_v[8][3] = /* Vertices */ +{ + {-1.0,-1.0, 1.0}, {-1.0,-1.0,-1.0}, {-1.0, 1.0,-1.0}, {-1.0, 1.0, 1.0}, + { 1.0,-1.0, 1.0}, { 1.0,-1.0,-1.0}, { 1.0, 1.0,-1.0}, { 1.0, 1.0, 1.0} +} ; + + +static int firsttime; + +static void drawCube (void) +{ + + if ( firsttime ) + { + /* + Deliberately do this only once - it's a better test of + PUI's attempts to leave the OpenGL state undisturbed + */ + glEnable ( GL_BLEND ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GREATER,0.1f); + + firsttime = FALSE ; + glLightfv ( GL_LIGHT0, GL_DIFFUSE , light_diffuse ) ; + glLightfv ( GL_LIGHT0, GL_POSITION, light_position ) ; + glEnable ( GL_LIGHT0 ) ; + glEnable ( GL_LIGHTING ) ; + glEnable ( GL_DEPTH_TEST ) ; + glMatrixMode ( GL_PROJECTION ) ; + gluPerspective ( 40.0, 1.0, 1.0, 10.0 ) ; + glMatrixMode ( GL_MODELVIEW ) ; + gluLookAt ( 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ) ; + glTranslatef ( 0.0, 0.0, -1.0 ) ; + glRotatef ( 60.0, 1.0, 0.0, 0.0 ) ; + } + + glCullFace ( GL_FRONT ) ; + glEnable ( GL_CULL_FACE ) ; + // glRotatef ( 1.0f, 0.0, 0.0, 1.0 ) ; /* Tumble that cube! */ + + glBegin ( GL_QUADS ) ; + + for ( int i = 0 ; i < 6 ; i++ ) + { + glNormal3fv ( &cube_n[i][0] ) ; + for ( int j = 0; j < 4; j++ ) + { + float xmult =(float)(x_coordinate->getCurrentMax () - x_coordinate->getCurrentMin ()) / + (float)(x_coordinate->getMaxValue () - x_coordinate->getMinValue ()) ; + float ymult =(float)(y_coordinate->getCurrentMax () - y_coordinate->getCurrentMin ()) / + (float)(y_coordinate->getMaxValue () - y_coordinate->getMinValue ()) ; + + float x = cube_v[cube_i[i][j]][0] * xmult ; + float y = cube_v[cube_i[i][j]][1] * ymult ; + float z = cube_v[cube_i[i][j]][2] ; + glVertex3f ( x, y, z ) ; + } + } + + glEnd () ; +} + +/********************************\ +* * +* End of cube renderer in OpenGL * +* * +\********************************/ + + +/**************************************\ +* * +* These three functions capture mouse * +* and keystrokes (special and mundane) * +* from GLUT and pass them on to PUI. * +* * +\**************************************/ + +static void specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + glutPostRedisplay () ; +} + +static void mousefn ( int button, int updown, int x, int y ) +{ + puMouse ( button, updown, x, y ) ; + glutPostRedisplay () ; +} + +/**************************************\ +* * +* This function redisplays the PUI and * +* the tumbling cube, flips the double * +* buffer and then asks GLUT to post a * +* redisplay command - so we re-render * +* at maximum rate. * +* * +\**************************************/ + +static void displayfn (void) +{ + /* Clear the screen */ + + glClearColor ( 0.1f, 0.3f, 0.5f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + /* Draw the tumbling cube */ + + float val ; rspeedSlider->getValue ( &val ) ; + float dir ; directSlider->getValue ( &dir ) ; + + glRotatef( 4*val, 3.0f * ( 2.0f * dir - 1.0f ) , 2.0f * ( 1.0f - 2.0f * dir ) , 1.0f ); + + drawCube () ; + + /* Update the 'timer' */ + + time_t t = time ( NULL ) ; + timer_text -> setLabel ( ctime ( & t ) ) ; + + /* Make PUI redraw */ + + puDisplay () ; + + /* Off we go again... */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + +static void sliderdisplayfn (void) +{ + /* + Function to display only the slider window + We must set the glut window first or we get an annoying flicker in the main window. + */ + + glutSetWindow ( slider_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.3f, 0.5f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + /* Make PUI redraw the slider window */ + + puDisplay ( slider_window ) ; + + /* Off we go again... */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + + +static void savedisplayfn (void) +{ + /* + Function to display only the save window + We must set the glut window first or we get an annoying flicker in the main window. + */ + + glutSetWindow ( save_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.1f, 5.0f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + /* Make PUI redraw the save window */ + + puDisplay ( save_window ) ; + + /* Off we go again... */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + + +static void coorddisplayfn (void) +{ + /* + Function to display only the slider window + We must set the glut window first or we get an annoying flicker in the main window. + */ + + glutSetWindow ( coordinate_window ) ; + + /* Clear the screen */ + + glClearColor ( 0.1f, 0.3f, 0.5f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + /* Make PUI redraw the slider window */ + + puDisplay ( coordinate_window ) ; + + /* Off we go again... */ + + glutSwapBuffers () ; + glutPostRedisplay () ; +} + + +/***********************************\ +* * +* Here are the PUI widget callback * +* functions. * +* * +\***********************************/ + +static void hide_menu_cb ( puObject *cb ) +{ + if ( cb -> getValue () ) + { + main_menu_bar -> reveal () ; + hide_menu_button->setLegend ( "Hide Menu" ) ; + } + else + { + main_menu_bar -> hide () ; + hide_menu_button->setLegend ( "Show Menu" ) ; + } +} + + +static void go_away_cb ( puObject * ) +{ + // Delete the dialog box when its 'OK' button is pressed. + + puDeleteObject( dialog_box ) ; + dialog_box = NULL ; +} + +static void mk_dialog ( char *fmt, ... ) +{ + static char txt [ PUSTRING_MAX ] ; + + va_list argptr ; + va_start(argptr, fmt) ; + vsprintf( txt, fmt, argptr ) ; + va_end(argptr) ; + + dialog_box = new puDialogBox ( 150, 50 ) ; + { + new puFrame ( 0, 0, 400, 100 ) ; + dialog_box_message = new puText ( 10, 70 ) ; + dialog_box_message -> setLabel ( txt ) ; + dialog_box_ok_button = new puOneShot ( 180, 10, 240, 50 ) ; + dialog_box_ok_button -> setLegend ( "OK" ) ; + dialog_box_ok_button -> makeReturnDefault ( TRUE ) ; + dialog_box_ok_button -> setCallback ( go_away_cb ) ; + } + dialog_box -> close () ; + dialog_box -> reveal () ; +} + +static void pick_cb ( puObject * ) +{ + char* filename ; + file_selector -> getValue ( &filename ) ; + + //NOTE: interface creation/deletion must be nested + //the old interface must be deleted *before* a new one is created + //otherwise the interface stack will be messed up + puDeleteObject ( file_selector ) ; + file_selector = 0 ; + glutHideWindow () ; + glutSetWindow ( main_window ) ; + + if ( filename[0] != 0 ) + mk_dialog ( "Saving File:\n%s", filename ) ; + else + mk_dialog ( "Save canceled" ) ; +} + +static void savereshapefn ( int w, int h ) +{ + if ( file_selector ) + file_selector->setSize ( w, h ) ; +} + +static void coordreshapefn ( int w, int h ) +{ + x_coordinate->setSize ( 20, h-40 ) ; + y_coordinate->setSize ( 20, h-40 ) ; + y_coordinate->setPosition ( w/2 - 5, 30 ) ; +} + +static void save_cb ( puObject * ) +{ + static int save_count = 0 ; // Number of buttons in file picker + int w = 320, h = 270 ; + glutSetWindow ( save_window ) ; + glutShowWindow () ; + glutReshapeWindow ( w, h ) ; + glutPositionWindow ( ( 640 - w ) / 2, ( 480 - h ) / 2 ) ; + if ( ++save_count > 2 ) save_count = 0 ; + + file_selector = new puaFileSelector ( 0, 0, w, h, save_count, ".", "Pick Place To Save" ) ; + file_selector -> setCallback ( pick_cb ) ; + file_selector->setChildStyle ( PUCLASS_ONESHOT, PUSTYLE_BOXED ) ; + file_selector->setChildBorderThickness ( PUCLASS_ONESHOT, 5 ) ; + file_selector->setChildColour ( PUCLASS_SLIDER, 0, 0.5, 0.5, 0.5 ) ; +} + +static void ni_cb ( puObject * ) +{ + mk_dialog ( "Warning:\nSorry, that function isn't implemented" ) ; +} + +static void about_cb ( puObject * ) +{ + mk_dialog ( "About:\nThis is the PUI 'complex' program" ) ; +} + +static void help_cb ( puObject * ) +{ + mk_dialog ( "Help:\nSorry, no help is available for this demo" ) ; +} + +static void edit_cb ( puObject * ) +{ +} + +static void exit_cb ( puObject * ) +{ + fprintf ( stderr, "Exiting PUI demo program.\n" ) ; + exit ( 1 ) ; +} + +/* Menu bar entries: */ + +static char *file_submenu [] = { "Exit", "Close", "------------", "Print - this is a long entry", "------------", "Save", "New", NULL } ; +static puCallback file_submenu_cb [] = { exit_cb, exit_cb, NULL, ni_cb, NULL, save_cb, ni_cb, NULL } ; + +static char *edit_submenu [] = { "Do nothing", NULL } ; +static puCallback edit_submenu_cb [] = { edit_cb, NULL } ; + +static char *help_submenu [] = { "About...", "Help", NULL } ; +static puCallback help_submenu_cb [] = { about_cb, help_cb, NULL } ; + + +static void sliderCB( puObject *) +{ + glutPostRedisplay(); +} + +static void coordCB( puObject *) +{ + glutPostRedisplay(); +} + +int main ( int argc, char **argv ) +{ + firsttime = TRUE; + + glutInitWindowPosition( 100, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + main_window = glutCreateWindow ( "Complex PUI Application" ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + glutIdleFunc ( displayfn ) ; + + puInit () ; + +#ifdef VOODOO + puShowCursor () ; +#endif + + //hel = new fntTexFont ; + //tim = new fntTexFont ; + //hel -> load ( "../fnt/data/old/helvetica_medium.txf" ) ; + //tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + //puFont helvetica ( hel, 15 ) ; + //puFont times_medium ( tim, 13 ) ; + //puSetDefaultFonts ( helvetica, times_medium ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.3f, 0.4f, 0.6f, 1.0f) ; + + timer_text = new puText ( 300, 10 ) ; + timer_text -> setColour ( PUCOL_LABEL, 1.0, 1.0, 1.0 ) ; + + /* Make a button to hide the menu bar */ + + hide_menu_button = new puButton ( 10, 10, 150, 50 ) ; + hide_menu_button->setValue ( TRUE ) ; + hide_menu_button->setLegend ( "Hide the Menu" ) ; + hide_menu_button->setCallback ( hide_menu_cb ) ; + // hide_menu_button->setLegendPlace ( PUPLACE_CENTER ) ; + hide_menu_button->makeReturnDefault ( TRUE ) ; + + /* Make the menu bar */ + + main_menu_bar = new puMenuBar () ; + { + main_menu_bar -> add_submenu ( "File", file_submenu, file_submenu_cb ) ; + main_menu_bar -> add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ; + main_menu_bar -> add_submenu ( "Help", help_submenu, help_submenu_cb ) ; + } + main_menu_bar -> close () ; + + mygroup = new puGroup ( 40, 40 ) ; + input1 = new puInput ( 10, 70, 90, 90 ) ; + input2 = new puInput ( 10, 40, 90, 60 ) ; + mygroup->close () ; + + slider_window = glutCreateWindow ( "Slider Window" ) ; + glutPositionWindow ( 20, 100 ) ; + glutReshapeWindow ( 150, 200 ) ; + glutDisplayFunc ( sliderdisplayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + glutIdleFunc ( sliderdisplayfn ) ; + + //load the texture for the 2nd window + //hel -> load ( "../fnt/data/old/helvetica_medium.txf" ) ; + //tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + + puGroup *slider_group = new puGroup ( 0, 0 ) ; // Necessary so that "sliderdisplayfn" will draw all widgets + + rspeedSlider = new puSlider (10,30,150,TRUE); + rspeedSlider->setDelta(0.1f); + rspeedSlider->setCBMode( PUSLIDER_DELTA ); + rspeedSlider->setCallback(sliderCB); + rspeedSlider->setLabel ( "Speed" ) ; + rspeedSlider->setLabelPlace ( PUPLACE_BOTTOM_LEFT ) ; + rspeedSlider->setValue ( 0.8f ) ; + + directSlider = new puSlider (80,30,150,TRUE); + directSlider->setDelta(0.1f); + directSlider->setCBMode( PUSLIDER_DELTA ); + directSlider->setCallback(sliderCB); + directSlider->setLabel ( "Direction" ) ; + directSlider->setLabelPlace ( PUPLACE_BOTTOM_LEFT ) ; + + slider_group -> close () ; + + save_window = glutCreateWindow ( "Saving" ) ; + glutDisplayFunc ( savedisplayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + glutIdleFunc ( savedisplayfn ) ; + glutReshapeFunc ( savereshapefn ) ; + glutHideWindow () ; + + //load the texture for the save window + //hel -> load ( "../fnt/data/old/helvetica_medium.txf" ) ; + //tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + + // Coordinate Selection Window + + coordinate_window = glutCreateWindow ( "Coordinate Window" ) ; + glutPositionWindow ( 420, 100 ) ; + glutReshapeWindow ( 250, 400 ) ; + glutDisplayFunc ( coorddisplayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + glutIdleFunc ( coorddisplayfn ) ; + glutReshapeFunc ( coordreshapefn ) ; + + //load the texture for the 2nd window + //hel -> load ( "../fnt/data/old/helvetica_medium.txf" ) ; + //tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + + puGroup *coordinate_group = new puGroup ( 0, 0 ) ; // Necessary so that "groupdisplayfn" will draw all widgets + + x_coordinate = new puaBiSlider ( 50,10,350,TRUE ) ; + x_coordinate->setMinValue ( -20 ) ; + x_coordinate->setMaxValue ( 20 ) ; + x_coordinate->setCurrentMin ( -20 ) ; + x_coordinate->setCurrentMax ( 20 ) ; +// x_coordinate->setDelta(0.1); +// x_coordinate->setCBMode( PUSLIDER_DELTA ); + x_coordinate->setCallback(coordCB); + x_coordinate->setLabel ( "X-coords" ) ; + x_coordinate->setLabelPlace ( PUPLACE_TOP_CENTERED ) ; + + y_coordinate = new puaTriSlider (200,30,350,TRUE); + y_coordinate->setMinValue ( -20 ) ; + y_coordinate->setMaxValue ( 20 ) ; + y_coordinate->setCurrentMin ( -20 ) ; + y_coordinate->setCurrentMax ( 20 ) ; + y_coordinate->setFreezeEnds ( FALSE ) ; + y_coordinate->setValue ( 0 ) ; +// y_coordinate->setDelta(0.1); +// y_coordinate->setCBMode( PUSLIDER_DELTA ); + y_coordinate->setCallback(coordCB); + y_coordinate->setLabel ( "Y-coords" ) ; + y_coordinate->setLabelPlace ( PUPLACE_BOTTOM_CENTERED ) ; + + coordinate_group -> close () ; + +// hel -> load ( "../fnt/data/old/helvetica_medium.txf" ) ; +// tim -> load ( "../fnt/data/old/times_medium.txf" ) ; + + glutMainLoop () ; + return 0 ; +} + + diff --git a/examples/src/pui/complex.dsp b/examples/src/pui/complex.dsp new file mode 100644 index 0000000..8a6903d --- /dev/null +++ b/examples/src/pui/complex.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="complex" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=complex - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "complex.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "complex.mak" CFG="complex - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "complex - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "complex - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "complex - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui.lib puAux.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"complex.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "complex - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui_d.lib puAux_d.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"complex.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "complex - Win32 Release" +# Name "complex - Win32 Debug" +# Begin Source File + +SOURCE=.\complex.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pui/fltk_demo/Makefile b/examples/src/pui/fltk_demo/Makefile new file mode 100644 index 0000000..721bb3b --- /dev/null +++ b/examples/src/pui/fltk_demo/Makefile @@ -0,0 +1,25 @@ +# -*-Makefile-*- $Id: Makefile 1877 2004-02-25 11:01:31Z stromberg $ + + +CXXFLAGS = $(shell fltk-config --use-gl --cxxflags) -Wall -O2 -DPU_USE_FLTK +LDFLAGS = $(shell fltk-config --use-gl --ldflags) -lplibpu -lplibfnt -lplibul + +OBJS = main.o gui.o puf.o + + +fltk_demo: $(OBJS) + g++ $(OBJS) $(LDFLAGS) -o $@ + +clean: + rm -f gui.{h,cxx} *.o *~ + + +%.o: %.cxx + g++ $(CXXFLAGS) -c $< + +%.h %.cxx: %.fl + fluid -c $< + + +main.o gui.o: gui.h puf.h +puf.o: puf.h diff --git a/examples/src/pui/fltk_demo/gui.fl b/examples/src/pui/fltk_demo/gui.fl new file mode 100644 index 0000000..1e19020 --- /dev/null +++ b/examples/src/pui/fltk_demo/gui.fl @@ -0,0 +1,48 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0104 +header_name {.h} +code_name {.cxx} +Function {make_window()} {open +} { + Fl_Window main_window { + label {fluid window} open + xywh {544 347 265 285} type Double hide + } { + Fl_Box gl_area {selected + xywh {15 50 235 180} labelsize 12 align 1 + code0 {\#include "puf.h"} + class Puf_Window + } + Fl_Menu_Bar {} {open + xywh {0 0 265 20} labelsize 12 textsize 12 + } { + submenu {} { + label File open + xywh {0 0 100 20} labelsize 12 + } { + menuitem {} { + label Quit + callback {exit(0);} + xywh {0 0 100 20} labelsize 12 + code0 {\#include } + } + } + submenu {} { + label Edit open + xywh {0 0 100 20} labelsize 12 + } {} + submenu {} { + label View open + xywh {15 15 100 20} labelsize 12 + } {} + } + Fl_Box {} { + label {OpenGL Area} + xywh {15 30 235 20} labelfont 1 labelsize 12 + } + Fl_Button toggle_other { + label {Show / Hide} + xywh {70 250 120 25} labelsize 12 + } + } +} diff --git a/examples/src/pui/fltk_demo/main.cxx b/examples/src/pui/fltk_demo/main.cxx new file mode 100644 index 0000000..d79a2a6 --- /dev/null +++ b/examples/src/pui/fltk_demo/main.cxx @@ -0,0 +1,143 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: main.cxx 1877 2004-02-25 11:01:31Z stromberg $ +*/ + + +#include "puf.h" +#include "gui.h" +#include + + +static Puf_Window *window1; +static Puf_Window *window2; + +static puButton *button1; +static puButton *button2; + +static puInput *input1; +static puInput *input2; + + +static void hello_cb(puObject *o) +{ + puInput *i = (o == button1 ? input1 : input2); + char *s = 0; + i->getValue(&s); + fprintf(stderr, "[Window %d] Hello: %s\n", o == button1 ? 1 : 2, s); +} + + +static void toggle_cb(puObject *o) +{ + if (main_window->visible()) + main_window->hide(); + else + main_window->show(); +} + + +static void quit_cb(puObject *o) +{ + main_window->hide(); + window1->hide(); +} + + +static void init_pui() +{ + window1->make_current(); + + puInit(); + + new puGroup(0, 0); + puButton *b = new puOneShot(50, 50, 200, 80); + b->setLegend("Show / Hide"); + b->setCallback(toggle_cb); + button1 = new puOneShot(50, 120, 200, 150); + button1->setLegend("Say Hello"); + button1->setCallback(hello_cb); + input1 = new puInput(50, 170, 200, 200); + puMenuBar *menu = new puMenuBar(); + char *menu_text[] = { "Quit", "----", "----", NULL }; + puCallback menu_cb[] = { quit_cb, NULL, NULL }; + char *dummy_text[] = { "----", NULL }; + puCallback dummy_cb[] = { NULL }; + menu->add_submenu("File", menu_text, menu_cb); + menu->add_submenu("Edit", dummy_text, dummy_cb); + menu->add_submenu("View", dummy_text, dummy_cb); + menu->close(); + puPopGroup(); + + window2->make_current(); + + new puInterface(0, 0); + new puGroup(0, 0); + button2 = new puOneShot(50, 50, 200, 80); + button2->setLegend("Say Hello"); + button2->setCallback(hello_cb); + input2 = new puInput(50, 100, 200, 130); + puPopGroup(); +} + + +static void toggle_fl(Fl_Widget *widget, void *data) +{ + if (window1->visible()) + window1->hide(); + else + window1->show(); +} + + +// This is for debugging, to see that redraw occurs when it should. +static void refresh(void *data) +{ + window1->redraw(); + window2->redraw(); + Fl::repeat_timeout(2.0, refresh); +} + + +int main(int argc, char **argv) +{ + window1 = new Puf_Window(250, 270, "OpenGL Window"); + window1->show(); + + make_window()->show(); + window2 = gl_area; + + init_pui(); + + toggle_other->callback(toggle_fl); + Fl::add_timeout(3.0, refresh); + + return Fl::run(); +} + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/examples/src/pui/fltk_demo/puf.cxx b/examples/src/pui/fltk_demo/puf.cxx new file mode 100644 index 0000000..808c0f8 --- /dev/null +++ b/examples/src/pui/fltk_demo/puf.cxx @@ -0,0 +1,186 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puf.cxx 1877 2004-02-25 11:01:31Z stromberg $ +*/ + + +#include "puf.h" +#include +#include + + +int puf_translate_key(int key) +{ + const short xtab[][2] = { + { FL_BackSpace, '\b' }, + { FL_Tab, '\t' }, + { FL_Enter, '\r' }, + { FL_Escape, 0x1b }, + { FL_Home, PU_KEY_HOME }, + { FL_Left, PU_KEY_LEFT }, + { FL_Up, PU_KEY_UP }, + { FL_Right, PU_KEY_RIGHT }, + { FL_Down, PU_KEY_DOWN }, + { FL_Page_Up, PU_KEY_PAGE_UP }, + { FL_Page_Down, PU_KEY_PAGE_DOWN }, + { FL_End, PU_KEY_END }, + { FL_Insert, PU_KEY_INSERT }, + { FL_KP + 0, '0' }, + { FL_KP + 1, '1' }, + { FL_KP + 2, '2' }, + { FL_KP + 3, '3' }, + { FL_KP + 4, '4' }, + { FL_KP + 5, '5' }, + { FL_KP + 6, '6' }, + { FL_KP + 7, '7' }, + { FL_KP + 8, '8' }, + { FL_KP + 9, '9' }, + { FL_KP_Enter, '\r' }, + { FL_F + 1, PU_KEY_F1 }, + { FL_F + 2, PU_KEY_F2 }, + { FL_F + 3, PU_KEY_F3 }, + { FL_F + 4, PU_KEY_F4 }, + { FL_F + 5, PU_KEY_F5 }, + { FL_F + 6, PU_KEY_F6 }, + { FL_F + 7, PU_KEY_F7 }, + { FL_F + 8, PU_KEY_F8 }, + { FL_F + 9, PU_KEY_F9 }, + { FL_F + 10, PU_KEY_F10 }, + { FL_F + 11, PU_KEY_F11 }, + { FL_F + 12, PU_KEY_F12 }, + { FL_Delete, 0x7f }, + }; + + static short xmap[256] = { 0 }; + + if (xmap[ FL_BackSpace & 0xff ] == 0) { + for (unsigned i = 0; i < sizeof(xtab) / sizeof(xtab[0]); i++) + xmap[ xtab[i][0] & 0xff ] = xtab[i][1]; + } + + return key <= 0xff ? key : key >= 0xff00 ? xmap[ key & 0xff ] : 0; +} + + +int puf_handle_event(Fl_Gl_Window *window, int event) +{ + int n, ret = 1; + + switch (event) { + + case FL_PUSH: + case FL_RELEASE: + window->make_current(); + ret = puMouse(Fl::event_button() - 1, event == FL_PUSH ? PU_DOWN : PU_UP, + Fl::event_x(), Fl::event_y()); + window->redraw(); + break; + + case FL_DRAG: + window->make_current(); + ret = puMouse(Fl::event_x(), Fl::event_y()); + window->redraw(); + break; + + case FL_ENTER: + window->take_focus(); + break; + + case FL_FOCUS: + case FL_UNFOCUS: + window->redraw(); + break; + + case FL_KEYDOWN: + case FL_KEYUP: + n = Fl::event_length(); + ret = 0; + if (Fl::event_key() <= 0xff && n > 0) { + const char *s = Fl::event_text(); + window->make_current(); + for (int i = 0; i < n; i++) + ret |= puKeyboard(s[i], event == FL_KEYDOWN ? PU_DOWN : PU_UP); + } else { + int key = puf_translate_key(Fl::event_key()); + if (key != 0) { + window->make_current(); + ret = puKeyboard(key, event == FL_KEYDOWN ? PU_DOWN : PU_UP); + } + } + if (ret != 0) + window->redraw(); + break; + + default: + ret = 0; + } + + return ret; +} + + +int Puf_Window::handle(int event) +{ + return puf_handle_event(this, event) || Fl_Gl_Window::handle(event); +} + + +void Puf_Window::draw() +{ + if (!valid()) + glViewport(0, 0, w(), h()); + + glClear(GL_COLOR_BUFFER_BIT); + +#if 1 // draw a border showing current focus + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w(), 0, h(), -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float c = (Fl::focus() == this ? 0.9f : 0.5f); + glColor3f(c, c, c); + + float x0 = 0.5f, x1 = w() - 0.5f; + float y0 = 0.5f, y1 = h() - 0.5f; + + glBegin(GL_LINE_LOOP); + glVertex2f(x0, y0); + glVertex2f(x1, y0); + glVertex2f(x1, y1); + glVertex2f(x0, y1); + glEnd(); + +#endif + + puDisplay(); +} + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/examples/src/pui/fltk_demo/puf.h b/examples/src/pui/fltk_demo/puf.h new file mode 100644 index 0000000..5a72cf7 --- /dev/null +++ b/examples/src/pui/fltk_demo/puf.h @@ -0,0 +1,61 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puf.h 1877 2004-02-25 11:01:31Z stromberg $ +*/ + + +#ifndef puf_h +#define puf_h + +#include + + +// Functions for passing FLTK events to PUI + +int puf_translate_key(int key_code); +int puf_handle_event(Fl_Gl_Window *, int event); + + +// An FLTK window class for simple PUI integration + +class Puf_Window : public Fl_Gl_Window +{ + protected: + virtual int handle(int); + virtual void draw(); + public: + Puf_Window(int X, int Y, int W, int H, const char *L = 0) + : Fl_Gl_Window(X, Y, W, H, L) {} + Puf_Window(int W, int H, const char *L = 0) + : Fl_Gl_Window(W, H, L) {} +}; + + +#endif + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/examples/src/pui/sdl_example/Demo.cxx b/examples/src/pui/sdl_example/Demo.cxx new file mode 100644 index 0000000..4379006 --- /dev/null +++ b/examples/src/pui/sdl_example/Demo.cxx @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "Demo.h" + + +static puInput* input = NULL; + + + +static void on_quit(puObject*) +{ + exit(0); +} + +static void on_say_hello(puObject*) +{ + printf("Hello World!\n"); +} + +static void on_print_text(puObject*) +{ + printf("Input text: %s\n", input->getStringValue()); +} + + + +void initPUI() +{ + puInit(); + + puSetDefaultStyle(PUSTYLE_SMALL_BEVELLED); + puSetDefaultColourScheme(0.75f, 0.75f, 0.75f, 1.0f); +} + + +void createInterface() +{ + // menu bar + puMenuBar* menu = new puMenuBar(); + char* menu_text[] = { + "Quit", + "----", + "Say Hello", + "Print text", + NULL + }; + puCallback menu_cb[] = { + on_quit, + NULL, + on_say_hello, + on_print_text, + NULL + }; + menu->add_submenu("Demo", menu_text, menu_cb); + menu->close(); + + // input and buttons + puButton* button; + button = new puOneShot(100, 200, 220, 225); + button->setLegend("Quit"); + button->setCallback(on_quit); + + button = new puOneShot(100, 250, 220, 275); + button->setLegend("Say Hello"); + button->setCallback(on_say_hello); + + button = new puOneShot(100, 300, 220, 325); + button->setLegend("Print text"); + button->setCallback(on_print_text); + + input = new puInput(250, 300, 450, 325); +} + diff --git a/examples/src/pui/sdl_example/Demo.h b/examples/src/pui/sdl_example/Demo.h new file mode 100644 index 0000000..efa6a29 --- /dev/null +++ b/examples/src/pui/sdl_example/Demo.h @@ -0,0 +1,10 @@ +#ifndef DEMO_H +#define DEMO_H + + +void initPUI(); + +void createInterface(); + + +#endif diff --git a/examples/src/pui/sdl_example/EventLoopSDL.cxx b/examples/src/pui/sdl_example/EventLoopSDL.cxx new file mode 100644 index 0000000..c1eefe4 --- /dev/null +++ b/examples/src/pui/sdl_example/EventLoopSDL.cxx @@ -0,0 +1,180 @@ +#include +#include +#include + +#include "EventLoopSDL.h" + + +static bool stop_requested; + + + +void EventLoopSDL::run() +{ + SDL_Event event; + + stop_requested = false; + while (!stop_requested) { + + // process events + SDL_WaitEvent(NULL); + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + stop_requested = true; + break; + case SDL_MOUSEMOTION: + onMouseMove(event.motion.state, event.motion.x, event.motion.y); + break; + case SDL_MOUSEBUTTONDOWN: + onMouseDown(event.button.button, event.button.x, event.button.y); + break; + case SDL_MOUSEBUTTONUP: + onMouseUp(event.button.button, event.button.x, event.button.y); + break; + case SDL_KEYDOWN: + onKeyDown(event.key.keysym); + break; + case SDL_KEYUP: + onKeyUp(event.key.keysym); + break; + } + } + + // redraw + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + draw(); + SDL_GL_SwapBuffers(); + } +} + + +void EventLoopSDL::stop() +{ + stop_requested = true; +} + + +void EventLoopSDL::draw() +{ + puDisplay(); +} + + +int EventLoopSDL::translateKey(const SDL_keysym& keysym) +{ + // Printable characters + if (keysym.unicode > 0) + return keysym.unicode; + + // Numpad key, translate no non-numpad equivalent + if (keysym.sym >= SDLK_KP0 && keysym.sym <= SDLK_KP_EQUALS) { + switch (keysym.sym) { + case SDLK_KP0: + return PU_KEY_INSERT; + case SDLK_KP1: + return PU_KEY_END; + case SDLK_KP2: + return PU_KEY_DOWN; + case SDLK_KP3: + return PU_KEY_PAGE_DOWN; + case SDLK_KP4: + return PU_KEY_LEFT; + case SDLK_KP6: + return PU_KEY_RIGHT; + case SDLK_KP7: + return PU_KEY_HOME; + case SDLK_KP8: + return PU_KEY_UP; + case SDLK_KP9: + return PU_KEY_PAGE_UP; + default: + return -1; + } + } + + // Everything else + switch (keysym.sym) { + case SDLK_UP: + return PU_KEY_UP; + case SDLK_DOWN: + return PU_KEY_DOWN; + case SDLK_LEFT: + return PU_KEY_LEFT; + case SDLK_RIGHT: + return PU_KEY_RIGHT; + + case SDLK_PAGEUP: + return PU_KEY_PAGE_UP; + case SDLK_PAGEDOWN: + return PU_KEY_PAGE_DOWN; + case SDLK_HOME: + return PU_KEY_HOME; + case SDLK_END: + return PU_KEY_END; + case SDLK_INSERT: + return PU_KEY_INSERT; + case SDLK_DELETE: + return -1; + + case SDLK_F1: + return PU_KEY_F1; + case SDLK_F2: + return PU_KEY_F2; + case SDLK_F3: + return PU_KEY_F3; + case SDLK_F4: + return PU_KEY_F4; + case SDLK_F5: + return PU_KEY_F5; + case SDLK_F6: + return PU_KEY_F6; + case SDLK_F7: + return PU_KEY_F7; + case SDLK_F8: + return PU_KEY_F8; + case SDLK_F9: + return PU_KEY_F9; + case SDLK_F10: + return PU_KEY_F10; + case SDLK_F11: + return PU_KEY_F11; + case SDLK_F12: + return PU_KEY_F12; + + default: + return -1; + } +} + +int EventLoopSDL::translateMouse(int btn) +{ + // SDL counts buttons from 1, PUI from 0 + return btn-1; +} + + +int EventLoopSDL::onMouseMove(char state, int x, int y) +{ + return false; +} + +int EventLoopSDL::onMouseDown(int btn, int x, int y) +{ + return puMouse(translateMouse(btn), PU_DOWN, x, y); +} + +int EventLoopSDL::onMouseUp(int btn, int x, int y) +{ + return puMouse(translateMouse(btn), PU_UP, x, y); +} + +int EventLoopSDL::onKeyDown(const SDL_keysym& keysym) +{ + return puKeyboard(translateKey(keysym), PU_DOWN); +} + +int EventLoopSDL::onKeyUp(const SDL_keysym& keysym) +{ + return puKeyboard(translateKey(keysym), PU_UP); +} diff --git a/examples/src/pui/sdl_example/EventLoopSDL.h b/examples/src/pui/sdl_example/EventLoopSDL.h new file mode 100644 index 0000000..d8da91f --- /dev/null +++ b/examples/src/pui/sdl_example/EventLoopSDL.h @@ -0,0 +1,32 @@ +#ifndef EVENTLOOPSDL_H +#define EVENTLOOPSDL_H + + +class EventLoopSDL +{ +public: + // starts the main loop + static void run(); + + // requests main loop termination + static void stop(); + + // draw one frame + static void draw(); + + // translates an SDL keysym to the PUI equivalent + static int translateKey(const SDL_keysym& keysym); + + // translates an SDL mouse button to the PUI equivalent + static int translateMouse(int btn); + + // event handlers + static int onMouseMove(char state, int x, int y); + static int onMouseDown(int btn, int x, int y); + static int onMouseUp(int btn, int x, int y); + static int onKeyDown(const SDL_keysym& keysym); + static int onKeyUp(const SDL_keysym& keysym); +}; + + +#endif diff --git a/examples/src/pui/sdl_example/Makefile b/examples/src/pui/sdl_example/Makefile new file mode 100644 index 0000000..df0bc8d --- /dev/null +++ b/examples/src/pui/sdl_example/Makefile @@ -0,0 +1,38 @@ +SDL_LDFLAGS := $(shell sdl-config --libs) +OGL_LDFLAGS := -lGL -lGLU +PUI_LDFLAGS := -lplibpu -lplibfnt -lplibsg -lplibul +LDFLAGS := $(OGL_LDFLAGS) $(SDL_LDFLAGS) $(PUI_LDFLAGS) -lm + +SDL_CFLAGS := $(shell sdl-config --cflags) +PUI_CFLAGS := -DPU_USE_SDL +CXXFLAGS := -Wall -g $(SDL_CFLAGS) $(PUI_CFLAGS) + +CXX := g++ + + +OBJECT := mainSDL.o EventLoopSDL.o Demo.o + + +%.o : %.cxx + $(CXX) $(CXXFLAGS) -c $< + + +sdl_example: $(OBJECT) + $(CXX) -o $@ $+ $(LDFLAGS) + + +.PHONY: clean depend + +clean: + rm -f *.o sdl_example + +depend: + makedepend -Y *.cxx >/dev/null 2>&1 + + +# Do 'make depend' to regenerate dependencies. +# DO NOT DELETE + +Demo.o: Demo.h +EventLoopSDL.o: EventLoopSDL.h +mainSDL.o: Demo.h EventLoopSDL.h diff --git a/examples/src/pui/sdl_example/mainSDL.cxx b/examples/src/pui/sdl_example/mainSDL.cxx new file mode 100644 index 0000000..d43af52 --- /dev/null +++ b/examples/src/pui/sdl_example/mainSDL.cxx @@ -0,0 +1,58 @@ +#include +#include +#include "SDL.h" + +#include "Demo.h" +#include "EventLoopSDL.h" + + +// default window size +static const int WIDTH = 640; +static const int HEIGHT = 480; + + +static void initSDL(int w, int h, const char *title) +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + + if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | SDL_ANYFORMAT/* | SDL_FULLSCREEN*/) == NULL) { + fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError()); + exit(1); + } + + if (title) + SDL_WM_SetCaption(title, NULL); + + SDL_EnableKeyRepeat(150, 75); + SDL_EnableUNICODE(true); +} + + +void initOGL(int w, int h) +{ + glViewport(0, 0, w, h); + glClearColor(0.1f, 0.4f, 0.1f, 1.0f); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GREATER, 0.1f); +} + + +int main(int argc, char *argv[]) +{ + initSDL(WIDTH, HEIGHT, "PUI with SDL sample"); + initOGL(WIDTH, HEIGHT); + initPUI(); + + createInterface(); + + EventLoopSDL::run(); + + return 0; +} + diff --git a/examples/src/pui/simple.cxx b/examples/src/pui/simple.cxx new file mode 100644 index 0000000..9aace3b --- /dev/null +++ b/examples/src/pui/simple.cxx @@ -0,0 +1,131 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: simple.cxx 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + +//#define VOODOO 1 + +void motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + glutPostRedisplay () ; +} + +void mousefn ( int button, int updown, int x, int y ) +{ + puMouse ( button, updown, x, y ) ; + glutPostRedisplay () ; +} + +void displayfn ( void ) +{ + glClearColor ( 0.1f, 0.4f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + + puDisplay () ; + + glutSwapBuffers () ; + + /* The next line is not neccessary - you could remove it safely without + affecting the functionality of this simple example program. + + It exists because in every application which does some more stuff + than creating user interface widgets, you normally do want to + redraw your scenery as often as possible for smooth animation. */ + + glutPostRedisplay () ; +} + +void button_cb ( puObject * ) +{ + fprintf ( stderr, "Hello World.\n" ) ; +} + + +int main ( int argc, char **argv ) +{ +#ifdef VOODOO + glutInitWindowPosition ( 0, 0 ) ; +#endif + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + + /* Note that in order for PUI and this example program to work, you + definitely don't need a depth buffer. + + However, most applications using PUI do some more things than rendering + PUI widgets. In every "real" program, you usually do need a depth + buffer - we are requesting one in the next line so that PLIB programmers + can write their applications upon this example code without running + into problems. */ + + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + + glutCreateWindow ( "PUI Application" ) ; + glutDisplayFunc ( displayfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + puInit () ; + +#ifdef VOODOO + puShowCursor () ; +#endif + + puOneShot *b = new puOneShot ( 50, 50, 200, 80 ) ; + + b -> setLegend ( "Say Hello" ) ; + b -> setCallback ( button_cb ) ; + +printf ( "%d\n", PLIB_VERSION ) ; + + glutMainLoop () ; + + return 0 ; +} + diff --git a/examples/src/pui/simple.dsp b/examples/src/pui/simple.dsp new file mode 100644 index 0000000..a8bdb29 --- /dev/null +++ b/examples/src/pui/simple.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="simple" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=simple - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "simple.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "simple.mak" CFG="simple - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "simple - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "simple - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "simple - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"simple.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "simple - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui_d.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"simple.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "simple - Win32 Release" +# Name "simple - Win32 Debug" +# Begin Source File + +SOURCE=.\simple.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pui/widget_list.cxx b/examples/src/pui/widget_list.cxx new file mode 100644 index 0000000..9e453ee --- /dev/null +++ b/examples/src/pui/widget_list.cxx @@ -0,0 +1,684 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: widget_list.cxx 2143 2009-01-21 17:18:45Z fayjf $ +*/ + +// Sample program to create a copy of each widget in PUI + +#include +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include + +//#define VOODOO 1 + +/***********************************\ +* * +* These are the PUI widget pointers * +* * +\***********************************/ + +static int main_window ; + +static int button_box_window ; +static puButtonBox *button_box ; + +static int frame_window ; +static puFrame *frame ; + +static int text_window ; +static puText *text ; + +static int button_window ; +static puButton *button ; +static puText *button_text ; + +static int one_shot_window ; +static puOneShot *one_shot ; + +static int popup_menu_window ; +static puPopupMenu *popup_menu ; + +static int menu_bar_window ; +static puMenuBar *menu_bar ; + +static int input_window ; +static puInput *input ; + +static int slider_window ; +static puSlider *slider ; + +static int arrow_button_window ; +static puArrowButton *arrow_button ; + +static int dial_window ; +static puDial *dial ; + +static int list_box_window ; +static puListBox *list_box ; + +static int file_selector_window ; +static puaFileSelector *file_selector ; + +static int bislider_window ; +static puaBiSlider *bislider ; + +static int trislider_window ; +static puaTriSlider *trislider ; + +static int vertical_menu_window ; +static puaVerticalMenu *vertical_menu ; + +static int dialog_box_window ; +static puDialogBox *dialog_box ; + +static int large_input_window ; +static puaLargeInput *large_input ; + +static int combo_box_window ; +static puaComboBox *combo_box ; + +static int select_box_window ; +static puaSelectBox *select_box ; + +//static fntTexFont *tim ; + + + +/**************************************\ +* * +* These three functions capture mouse * +* and keystrokes (special and mundane) * +* from GLUT and pass them on to PUI. * +* * +\**************************************/ + +static void specialfn ( int key, int, int ) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void keyfn ( unsigned char key, int, int ) +{ + puKeyboard ( key, PU_DOWN ) ; + glutPostRedisplay () ; +} + +static void motionfn ( int x, int y ) +{ + puMouse ( x, y ) ; + glutPostRedisplay () ; +} + +static void mousefn ( int btn, int updown, int x, int y ) +{ + puMouse ( btn, updown, x, y ) ; + glutPostRedisplay () ; +} + +/**************************************\ +* * +* This function redisplays the PUI * +* * +\**************************************/ + +static void displayfn (void) +{ + /* Clear the screen */ + + glClearColor ( 0.55f, 0.55f, 0.55f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + + /* Make PUI redraw */ + + puDisplay () ; + + /* Swap buffers */ + + glutSwapBuffers () ; +} + + +/***********************************\ +* * +* Here are the PUI widget callback * +* functions. * +* * +\***********************************/ + +static void exit_cb ( puObject * ) +{ + fprintf ( stderr, "Exiting PUI demo program.\n" ) ; + exit ( 1 ) ; +} + +/* Menu bar entries: */ + +static char *file_submenu [] = { "Exit", "Close", "========", "Print", "========", "Save", "New", NULL } ; +static puCallback file_submenu_cb [] = { exit_cb, exit_cb, NULL, NULL , NULL, NULL, NULL, NULL } ; + +static char *edit_submenu [] = { "Do nothing.", NULL } ; +puCallback edit_submenu_cb [] = { NULL, NULL } ; + +static char *help_submenu [] = { "About...", "Help", NULL } ; +puCallback help_submenu_cb [] = { NULL, NULL, NULL } ; + + +int main ( int argc, char **argv ) +{ + glutInitWindowPosition( 100, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &argc, argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE ) ; + main_window = glutCreateWindow ( "PUI Widget List" ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + puInit () ; + +#ifdef VOODOO + puShowCursor () ; +#endif + + //tim = new fntTexFont ; + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + //puFont times_medium ( tim, 12 ) ; + //puSetDefaultFonts ( times_medium, times_medium ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.8f, 0.8f, 0.8f, 1.0f ) ; + + + /* Set up the secondary windows and their widgets */ + + button_box_window = glutCreateWindow ( "Button Box Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + char *button_box_entries [] = { "First Entry", "Second Entry", "Third Entry", NULL } ; + button_box = new puButtonBox ( 10, 10, 130, 80, button_box_entries, TRUE ) ; + button_box->setLabel ( "Label" ) ; + + + frame_window = glutCreateWindow ( "Frame Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + frame = new puFrame ( 10, 10, 130, 80 ) ; + frame->setLabel ( "Label" ) ; + frame->setLegend ( "Legend" ) ; + + + text_window = glutCreateWindow ( "Text Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + text = new puText ( 10, 10 ) ; + text->setLabel ( "Label" ) ; + + + button_window = glutCreateWindow ( "Button Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 220, 160 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + button_text = new puText ( 5, 6 ) ; + button_text->setLabel ( "XCHECK:" ) ; + + button = new puButton ( 90, 10, 90+16, 10+16, PUBUTTON_XCHECK ) ; + button->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + button->setLabel ( "Label" ) ; + button->setValue ( 1 ) ; + + button_text = new puText ( 5, 36 ) ; + button_text->setLabel ( "VCHECK:" ) ; + + button = new puButton ( 90, 40, 90+16, 40+16, PUBUTTON_VCHECK ) ; + button->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + button->setLabel ( "Label" ) ; + button->setValue ( 1 ) ; + + button_text = new puText ( 5, 66 ) ; + button_text->setLabel ( "CIRCLE:" ) ; + + button = new puButton ( 90, 70, 90+16, 70+16, PUBUTTON_CIRCLE ) ; + button->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + button->setLabel ( "Label" ) ; + button->setValue ( 1 ) ; + + button_text = new puText ( 5, 96 ) ; + button_text->setLabel ( "RADIO:" ) ; + + button = new puButton ( 90, 100, 90+16, 100+16, PUBUTTON_RADIO ) ; + button->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + button->setLabel ( "Label" ) ; + button->setValue ( 1 ) ; + + button_text = new puText ( 5, 128 ) ; + button_text->setLabel ( "NORMAL:" ) ; + + button = new puButton ( 90, 130, 170, 150 ) ; + button->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + button->setLabel ( "Label" ) ; + button->setLegend ( "Legend" ) ; + button->setValue ( 1 ) ; + + + one_shot_window = glutCreateWindow ( "One Shot Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + one_shot = new puOneShot ( 10, 10, 90, 30 ) ; + one_shot->setLabel ( "Label" ) ; + one_shot->setLegend ( "Legend" ) ; + + + popup_menu_window = glutCreateWindow ( "Popup Menu Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 80, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + popup_menu = new puPopupMenu ( 10, 10 ) ; + popup_menu->add_item ( "Cut", NULL ) ; + popup_menu->add_item ( "Copy", NULL ) ; + popup_menu->add_item ( "Paste", NULL ) ; + popup_menu->add_item ( "Delete", NULL ) ; + popup_menu->close () ; + popup_menu->reveal () ; + + + menu_bar_window = glutCreateWindow ( "Menu Bar Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 200 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + menu_bar = new puMenuBar () ; + { + menu_bar -> add_submenu ( "File", file_submenu, file_submenu_cb ) ; + menu_bar -> add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ; + menu_bar -> add_submenu ( "Help", help_submenu, help_submenu_cb ) ; + } + menu_bar -> close () ; + + + input_window = glutCreateWindow ( "Input Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + input = new puInput ( 10, 10, 90, 30 ) ; + input->setLabel ( "Label" ) ; + + + slider_window = glutCreateWindow ( "Slider Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + slider = new puSlider ( 10, 10, 120, FALSE ) ; + slider->setLabel ( "Label" ) ; + slider->setLegend ( "Legend" ) ; + + + arrow_button_window = glutCreateWindow ( "Arrow Button Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + arrow_button = new puArrowButton ( 10, 10, 50, 50, PUARROW_RIGHT ) ; + arrow_button->setLabel ( "Label" ) ; + + + dial_window = glutCreateWindow ( "Dial Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + dial = new puDial ( 10, 10, 60 ) ; + dial->setLabel ( "Label" ) ; + dial->setLegend ( "Legend" ) ; + + + list_box_window = glutCreateWindow ( "List Box Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + char *list_box_entries [] = { "First Entry", "Second Entry", "Third Entry", "Very long Entry that will get truncated", NULL } ; + list_box = new puListBox ( 10, 10, 130, 80, list_box_entries ) ; + list_box->setLabel ( "Label" ) ; + + + file_selector_window = glutCreateWindow ( "File Selector Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 300, 220 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + file_selector = new puaFileSelector ( 10, 10, 280, 200, "." ) ; + + + bislider_window = glutCreateWindow ( "BiSlider Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + bislider = new puaBiSlider ( 10, 10, 120, FALSE ) ; + bislider->setLabel ( "Label" ) ; + bislider->setLegend ( "Legend" ) ; + bislider->setMaxValue ( 20 ) ; + bislider->setCurrentMin ( 4 ) ; + bislider->setCurrentMax ( 15 ) ; + + + trislider_window = glutCreateWindow ( "TriSlider Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 100, 200 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + trislider = new puaTriSlider ( 20, 10, 180, TRUE ) ; + trislider->setLabel ( "Label" ) ; + trislider->setLegend ( "Legend" ) ; + trislider->setMaxValue ( 20 ) ; + trislider->setCurrentMin ( 4 ) ; + trislider->setCurrentMax ( 15 ) ; + trislider->setValue ( 12 ) ; + + + vertical_menu_window = glutCreateWindow ( "Vertical Menu Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 200 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + vertical_menu = new puaVerticalMenu () ; + { + vertical_menu -> add_submenu ( "File", file_submenu, file_submenu_cb ) ; + vertical_menu -> add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ; + vertical_menu -> add_submenu ( "Help", help_submenu, help_submenu_cb ) ; + } + vertical_menu -> close () ; + + + dialog_box_window = glutCreateWindow ( "Dialog Box Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 180, 100 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + dialog_box = new puDialogBox ( 10, 10 ) ; + new puOneShot ( 20, 20, "OK" ) ; + new puOneShot ( 100, 20, "Cancel" ) ; + puText *dialog_text = new puText ( 20, 60 ) ; + dialog_text->setLabel ( "Dialog Box Text\nand Widgets" ) ; + dialog_box->close () ; + dialog_box->reveal () ; + + + large_input_window = glutCreateWindow ( "Large Input Window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 300, 200 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + large_input = new puaLargeInput ( 10, 10, 240, 180, 2, 20 ) ; + large_input->setLabel ( "Label" ) ; + large_input->setValue ( "This is text in the Large Input widget.\n" + "This is a second line of text" ) ; + + + combo_box_window = glutCreateWindow ( "Combo Box window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 300, 200 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + char *combo_box_entries [] = { "First Entry", "Second Entry", "Third Entry", "Fourth Entry", "Sixth Entry", "Seventh Entry", NULL } ; + combo_box = new puaComboBox ( 10, 158, 250, 190, + combo_box_entries ) ; + /* Don't do that in your own application ! */ + combo_box -> __getPopupMenu() -> reveal() ; + combo_box->setLabel ( "Label" ) ; + + + select_box_window = glutCreateWindow ( "Select Box window" ) ; + glutPositionWindow ( 200, 100 ) ; + glutReshapeWindow ( 300, 60 ) ; + glutDisplayFunc ( displayfn ) ; + glutKeyboardFunc ( keyfn ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; +#ifdef VOODOO + glutPassiveMotionFunc ( motionfn ) ; +#endif + + //tim -> load ( "../fnt/data/old/times_bold.txf" ) ; + + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GEQUAL, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + select_box = new puaSelectBox ( 10, 14, 250, 46, + combo_box_entries ) ; + select_box->setLabel ( "Label" ) ; + + glutMainLoop () ; + return 0 ; +} + + diff --git a/examples/src/pui/widget_list.dsp b/examples/src/pui/widget_list.dsp new file mode 100644 index 0000000..0e7a92d --- /dev/null +++ b/examples/src/pui/widget_list.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="widget_list" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=widget_list - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "widget_list.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "widget_list.mak" CFG="simple - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "widget_list - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "widget_list - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "widget_list - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui.lib puAux.lib fnt.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"widget_list.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "widget_list - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui_d.lib puAux_d.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"widget_list.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "widget_list - Win32 Release" +# Name "widget_list - Win32 Debug" +# Begin Source File + +SOURCE=.\widget_list.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pui/widget_test.cxx b/examples/src/pui/widget_test.cxx new file mode 100644 index 0000000..a90a253 --- /dev/null +++ b/examples/src/pui/widget_test.cxx @@ -0,0 +1,219 @@ +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + + +int margin = 50; + +char *long_text; +puObject *widget; + + + +// samples + +char text[] = + "This program is meant to be used for widget testing. Insert your code\n" + "between BEGIN and END marks. Use list, long_list, text and long_text\n" + "as widget contents where appropriate. It's intended that there are no resizing\n" + "limits. This allows to verify that the widget works even in insanely small\n" + "sizes, or at least doesn't crash then.\n" + "\n" + "Some random parts from the pui documentation:\n" + "\n" + "Like the constructors of the previously described widgets, the puComboBox\n" + "constructor takes a NULL-terminated array of strings specifying the items\n" + "that should go into the widget as it's fourth parameter. If 'editable'\n" + "is TRUE (default), the input box is made editable for allowing the user\n" + "to enter a string in addition to selecting one of the entries from the\n" + "popup menu.\n" + "\n" + "Depending on the position of the puComboBox inside the window, the widget\n" + "automatically determines whether the popup menu should be shown above or\n" + "below the widget when the user clicks on the arrow. The puArrowButton\n" + "is either pointing in up or down direction to indicate where the menu will\n" + "pop up.\n" + "\n" + "Regardless of whether you passed TRUE or FALSE to the 'editable' parameter\n" + "when constructing the widget, the user can click inside the input box and\n" + "use the Up and Down keys to browse through the items. The Home and End\n" + "keys switch to the first or last item as you would expect, and the PgUp/PgDown\n" + "can be used to toggle the popup menu.\n" + "\n" + "Just like all other classes which store a pointer to an array of strings\n" + "that indicates the widget's elements, the puComboBox class provides a routine\n" + "that allows you to change the list of elements in mid-run - and you can\n" + "also retrieve the number of items.\n"; + +char *list[] = { + "puArrowButton", "puButton", "puButtonBox", "puDial", "puFrame", + "puGroup", "puInput", "puInterface", "puListBox", "puMenuBar", + "puObject", "puOneShot", "puPopup", "puPopupMenu", "puSlider", + "puText", "puValue", 0}; + +char *long_list[] = { + "PUARROW_DOWN", "PUARROW_FASTDOWN", "PUARROW_FASTLEFT", "PUARROW_FASTRIGHT", + "PUARROW_FASTUP", "PUARROW_LEFT", "PUARROW_RIGHT", "PUARROW_UP", + "PUBUTTON_CIRCLE", "PUBUTTON_NORMAL", "PUBUTTON_RADIO", "PUBUTTON_VCHECK", + "PUBUTTON_XCHECK", "PUCLASS_ARROW", "PUCLASS_BUTTON", "PUCLASS_BUTTONBOX", + "PUCLASS_DIAL", "PUCLASS_DIALOGBOX", "PUCLASS_FRAME", "PUCLASS_GROUP", + "PUCLASS_INPUT", "PUCLASS_INTERFACE", "PUCLASS_LISTBOX", "PUCLASS_MENUBAR", + "PUCLASS_OBJECT", "PUCLASS_ONESHOT", "PUCLASS_POPUP", "PUCLASS_POPUPMENU", + "PUCLASS_SLIDER", "PUCLASS_TEXT", "PUCLASS_VALUE", "PUCOL_BACKGROUND", + "PUCOL_EDITFIELD", "PUCOL_FOREGROUND", "PUCOL_HIGHLIGHT", "PUCOL_LABEL", + "PUCOL_LEGEND", "PUCOL_MAX", "PUCOL_MISC", "PUDEACTIVATE_ON_MOUSE_CLICK", + "PUDEACTIVATE_ON_NEXT_WIDGET_ACTIVATION", "PUPLACE_ABOVE", + "PUPLACE_ABOVE_LEFT", "PUPLACE_ABOVE_RIGHT", "PUPLACE_BELOW", + "PUPLACE_BELOW_LEFT", "PUPLACE_BELOW_RIGHT", "PUPLACE_BOTTOM_CENTER", + "PUPLACE_BOTTOM_CENTERED", "PUPLACE_BOTTOM_LEFT", "PUPLACE_BOTTOM_RIGHT", + "PUPLACE_CENTERED", "PUPLACE_CENTERED_CENTERED", "PUPLACE_CENTERED_LEFT", + "PUPLACE_CENTERED_RIGHT", "PUPLACE_DEFAULT", "PUPLACE_LABEL_DEFAULT", + "PUPLACE_LEFT", "PUPLACE_LEFT_CENTER", "PUPLACE_LEGEND_DEFAULT", + "PUPLACE_LOWER_LEFT", "PUPLACE_LOWER_RIGHT", "PUPLACE_RIGHT", + "PUPLACE_RIGHT_CENTER", "PUPLACE_TOP_CENTER", "PUPLACE_TOP_CENTERED", + "PUPLACE_TOP_LEFT", "PUPLACE_TOP_RIGHT", "PUPLACE_UPPER_LEFT", + "PUPLACE_UPPER_RIGHT", "PUSLIDER_ALWAYS", "PUSLIDER_CLICK", + "PUSLIDER_DELTA", "PUSTRING_INITIAL", "PUSTRING_MAX", "PUSTR_BGAP", + "PUSTR_LGAP", "PUSTR_RGAP", "PUSTR_TGAP", "PUSTYLE_BEVELLED", + "PUSTYLE_BOXED", "PUSTYLE_DEFAULT", "PUSTYLE_DROPSHADOW", "PUSTYLE_MAX", + "PUSTYLE_NONE", "PUSTYLE_PLAIN", "PUSTYLE_RADIO", "PUSTYLE_SHADED", + "PUSTYLE_SMALL_BEVELLED", "PUSTYLE_SMALL_SHADED", "PUSTYLE_SPECIAL_UNDERLINED", + "PU_CONTINUAL", "PU_DOWN", "PU_DRAG", "PU_KEY_DOWN", "PU_KEY_END", "PU_KEY_F1", + "PU_KEY_F10", "PU_KEY_F11", "PU_KEY_F12", "PU_KEY_F2", "PU_KEY_F3", + "PU_KEY_F4", "PU_KEY_F5", "PU_KEY_F6", "PU_KEY_F7", "PU_KEY_F8", "PU_KEY_F9", + "PU_KEY_GLUT_SPECIAL_OFFSET", "PU_KEY_HOME", "PU_KEY_INSERT", "PU_KEY_LEFT", + "PU_KEY_PAGE_DOWN", "PU_KEY_PAGE_UP", "PU_KEY_RIGHT", "PU_KEY_UP", + "PU_LEFT_BUTTON", "PU_MIDDLE_BUTTON", "PU_NOBUTTON", "PU_RADIO_BUTTON_SIZE", + "PU_RIGHT_BUTTON", "PU_UP", "PU_UP_AND_DOWN", 0}; + + + + +// callback functions + +void motionfn(int x, int y) +{ + puMouse(x, y); + glutPostRedisplay(); +} + + +void mousefn(int button, int updown, int x, int y) +{ + puMouse(button, updown, x, y); + glutPostRedisplay(); +} + + +void specialfn(int key, int, int) +{ + printf("SPECIAL=%d\n", key); + puKeyboard(key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN); + glutPostRedisplay(); +} + + +void keyfn(unsigned char key, int, int) +{ + printf("KEY=%d\n", key); + puKeyboard(key, PU_DOWN); + glutPostRedisplay(); + if (key == 27) + exit(0); +} + + +void displayfn(void) +{ + glClearColor(0.4f, 0.4f, 0.4f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + puDisplay(); + glutSwapBuffers(); + glutPostRedisplay(); +} + + +void reshapefn(int w, int h) +{ + widget->setSize(w - 2 * margin, h - 2 * margin); +} + + +void callback(puObject *object) +{ + printf("callback value: [%s]\n", object->getStringValue()); +} + + +void atexitfn(void) +{ + delete [] long_text; +} + + + + +int main(int argc, char **argv) +{ + printf("PLIB Version %d\n", PLIB_VERSION); + + atexit(atexitfn); + const int REPEAT = 10; + long_text = new char[REPEAT * strlen(text) + REPEAT + 1]; + long_text[0] = '\0'; + for (int i = 0; i < REPEAT; i++) { + strcat(long_text, text); + strcat(long_text, "\n"); + } + + glutInitWindowSize(640, 480); + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH); + glutCreateWindow("PUI Application"); + glutDisplayFunc(displayfn); + glutMouseFunc(mousefn); + glutKeyboardFunc(keyfn); + glutSpecialFunc(specialfn); + glutMotionFunc(motionfn); + glutReshapeFunc(reshapefn); + + puSetDefaultStyle(PUSTYLE_SMALL_SHADED); + puInit(); + // + //-- BEGIN ---------------------------------------------------------------------- + + + margin = 50; + + widget = new puaLargeInput(margin, margin, 420, 400, 11, 20, false); + widget->setValue(text); + widget->setCallback(callback); + + + //-- END ------------------------------------------------------------------------ + // + int w, h; + widget->getSize(&w, &h); + glutReshapeWindow(w + 2 * margin, h + 2 * margin); + glutMainLoop(); + return 0; +} + diff --git a/examples/src/pui/widget_test.dep b/examples/src/pui/widget_test.dep new file mode 100644 index 0000000..7f75fa3 --- /dev/null +++ b/examples/src/pui/widget_test.dep @@ -0,0 +1,2 @@ +# Microsoft Developer Studio Generated Dependency File, included by widget_test.mak + diff --git a/examples/src/pui/widget_test.dsp b/examples/src/pui/widget_test.dsp new file mode 100644 index 0000000..2c5ae7d --- /dev/null +++ b/examples/src/pui/widget_test.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="widget_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=widget_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "widget_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "widget_test.mak" CFG="widget_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "widget_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "widget_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +F90=df.exe +RSC=rc.exe + +!IF "$(CFG)" == "widget_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE F90 /compile_only /nologo /warn:nofileopt +# ADD F90 /compile_only /nologo /warn:nofileopt +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "widget_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "widget_test___Win32_Debug" +# PROP BASE Intermediate_Dir "widget_test___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "widget_test___Win32_Debug" +# PROP Intermediate_Dir "widget_test___Win32_Debug" +# PROP Target_Dir "" +# ADD BASE F90 /check:bounds /compile_only /dbglibs /debug:full /nologo /traceback /warn:argument_checking /warn:nofileopt +# ADD F90 /check:bounds /compile_only /dbglibs /debug:full /nologo /traceback /warn:argument_checking /warn:nofileopt +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "widget_test - Win32 Release" +# Name "widget_test - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/examples/src/pui/widget_test.mak b/examples/src/pui/widget_test.mak new file mode 100644 index 0000000..92ef5ea --- /dev/null +++ b/examples/src/pui/widget_test.mak @@ -0,0 +1,143 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on widget_test.dsp +!IF "$(CFG)" == "" +CFG=widget_test - Win32 Debug +!MESSAGE No configuration specified. Defaulting to widget_test - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "widget_test - Win32 Release" && "$(CFG)" != "widget_test - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "widget_test.mak" CFG="widget_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "widget_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "widget_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +CPP=cl.exe +F90=df.exe +RSC=rc.exe + +!IF "$(CFG)" == "widget_test - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : + + +CLEAN : + -@erase + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +F90_PROJ=/compile_only /nologo /warn:nofileopt /module:"Release/" /object:"Release/" +F90_OBJS=.\Release/ +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\widget_test.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\widget_test.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\widget_test.pdb" /machine:I386 /out:"$(OUTDIR)\widget_test.exe" +LINK32_OBJS= \ + + +!ELSEIF "$(CFG)" == "widget_test - Win32 Debug" + +OUTDIR=.\widget_test___Win32_Debug +INTDIR=.\widget_test___Win32_Debug + +ALL : + + +CLEAN : + -@erase + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +F90_PROJ=/check:bounds /compile_only /dbglibs /debug:full /nologo /traceback /warn:argument_checking /warn:nofileopt /module:"widget_test___Win32_Debug/" /object:"widget_test___Win32_Debug/" /pdbfile:"widget_test___Win32_Debug/DF60.PDB" +F90_OBJS=.\widget_test___Win32_Debug/ +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\widget_test.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\widget_test.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\widget_test.pdb" /debug /machine:I386 /out:"$(OUTDIR)\widget_test.exe" /pdbtype:sept +LINK32_OBJS= \ + + +!ENDIF + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.SUFFIXES: .fpp + +.for{$(F90_OBJS)}.obj: + $(F90) $(F90_PROJ) $< + +.f{$(F90_OBJS)}.obj: + $(F90) $(F90_PROJ) $< + +.f90{$(F90_OBJS)}.obj: + $(F90) $(F90_PROJ) $< + +.fpp{$(F90_OBJS)}.obj: + $(F90) $(F90_PROJ) $< + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("widget_test.dep") +!INCLUDE "widget_test.dep" +!ELSE +!MESSAGE Warning: cannot find "widget_test.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "widget_test - Win32 Release" || "$(CFG)" == "widget_test - Win32 Debug" + +!ENDIF + diff --git a/examples/src/pui/widget_test.plg b/examples/src/pui/widget_test.plg new file mode 100644 index 0000000..1f30dc9 --- /dev/null +++ b/examples/src/pui/widget_test.plg @@ -0,0 +1,33 @@ + + +

+

Build Log

+

+--------------------Configuration: widget_test - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\fayjf\LOCALS~1\Temp\c\RSP7C.tmp" with contents +[ +/nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/widget_test.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c +"V:\dev\PilgrimWork\plib_svn\trunk\examples\src\pui\widget_test.cxx" +] +Creating command line "cl.exe @C:\DOCUME~1\fayjf\LOCALS~1\Temp\c\RSP7C.tmp" +Creating temporary file "C:\DOCUME~1\fayjf\LOCALS~1\Temp\c\RSP7D.tmp" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pui_d.lib puAux_d.lib fnt_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/widget_test.pdb" /debug /machine:I386 /out:"widget_test.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" +.\Debug\widget_test.obj +] +Creating command line "link.exe @C:\DOCUME~1\fayjf\LOCALS~1\Temp\c\RSP7D.tmp" +

Output Window

+Compiling... +widget_test.cxx +v:\dev\pilgrimwork\plib_svn\trunk\examples\src\pui\widget_test.cxx(17) : fatal error C1083: Cannot open include file: 'GL/glut.h': No such file or directory +Error executing cl.exe. + + + +

Results

+widget_test.exe - 1 error(s), 0 warning(s) +
+ + diff --git a/examples/src/pw/Makefile.am b/examples/src/pw/Makefile.am new file mode 100644 index 0000000..d025721 --- /dev/null +++ b/examples/src/pw/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_PW + +noinst_PROGRAMS = pw_demo pw_pui + +pw_demo_SOURCES = pw_demo.cxx +pw_pui_SOURCES = pw_pui.cxx + +pw_demo_LDADD = -lplibpw -lplibul $(OGL_LIBS) +pw_pui_LDADD = -lplibpw -lplibpu -lplibfnt -lplibsg -lplibul $(OGL_LIBS) + +endif + +EXTRA_DIST = pw_demo.dsp + diff --git a/examples/src/pw/pw_demo.cxx b/examples/src/pw/pw_demo.cxx new file mode 100644 index 0000000..f483e16 --- /dev/null +++ b/examples/src/pw/pw_demo.cxx @@ -0,0 +1,104 @@ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include +#include +#include +#include +#include + +void exFunc () +{ + fprintf ( stderr, "Exiting.\n" ) ; + pwCleanup () ; + exit ( 0 ) ; +} + + +void rsFunc ( int w, int h ) +{ + fprintf ( stderr, "Resized to %d x %d\n", w, h ) ; +} + + +void mpFunc ( int x, int y ) +{ + fprintf ( stderr, "Mouse is at (%4dx%4d)\r", x, y ) ; +} + + +void msFunc ( int button, int updn, int x, int y ) +{ + fprintf ( stderr, "Mouse button %d dirn %d at (%4dx%4d)\n", + button, updn, x, y ) ; +} + + +char keyIsDown [ 512 ] = { 0 } ; + +void kbFunc ( int key, int updn, int x, int y ) +{ + fprintf ( stderr, "Keyboard key %d dirn %d at (%4dx%4d) ", + key, updn, x, y ) ; + + keyIsDown [ key ] = (updn==PW_DOWN) ; + + for ( int i = 0 ; i < 512 ; i++ ) + if ( keyIsDown [ i ] ) + { + if ( i > ' ' && i < 0x7F ) + fprintf ( stderr, "%c ", i ) ; + else + fprintf ( stderr, "0x%2x ", i ) ; + } + + fprintf ( stderr, "\n" ) ; + + if ( updn == PW_DOWN ) + { + if ( key == '<' ) pwSetSize ( 100, 100 ) ; + else + if ( key == '>' ) pwSetSize ( 640, 480 ) ; + else + if ( key == 'a' ) pwSetAutoRepeatKey ( true ) ; + else + if ( key == 'b' ) pwSetAutoRepeatKey ( false ) ; + + if ( key == 0x1B ) /* ESC */ + { + pwCleanup () ; + exit ( 0 ) ; + } + } +} + + +int main ( int, char ** ) +{ + printf ( "PLIB/PW Demo.\n" ) ; + printf ( "=============\n\n" ) ; + printf ( "Type or mouse in the window to see PW callbacks in action.\n" ) ; + printf ( "Hit '<' or '>' to resize the window from within the program.\n" ) ; + printf ( "Hit 'a' or 'b' to enable or disable auto key repeat.\n" ) ; + printf ( "Hit ESC to exit.\n" ) ; + printf ( "\n" ) ; + + pwInit ( 100, 100, 640, 480, false, "PLIB Window Demo", true, 0 ) ; + + pwSetCallbacks ( kbFunc, msFunc, mpFunc, rsFunc, exFunc ) ; + + while ( 1 ) + { + glClearColor ( 0.5f, 0.1f, 0.1f, 1.0 ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + pwSwapBuffers () ; + } + + return 0; +} + + diff --git a/examples/src/pw/pw_demo.dsp b/examples/src/pw/pw_demo.dsp new file mode 100644 index 0000000..278b0e2 --- /dev/null +++ b/examples/src/pw/pw_demo.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="pw_demo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pw_demo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pw_demo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pw_demo.mak" CFG="pw_demo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pw_demo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pw_demo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pw_demo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul.lib pw.lib opengl32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "pw_demo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pw_demo___Win32_Debug" +# PROP BASE Intermediate_Dir "pw_demo___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul_d.lib pw_d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "pw_demo - Win32 Release" +# Name "pw_demo - Win32 Debug" +# Begin Source File + +SOURCE=.\pw_demo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/pw/pw_pui.cxx b/examples/src/pw/pw_pui.cxx new file mode 100644 index 0000000..8b1b4ef --- /dev/null +++ b/examples/src/pw/pw_pui.cxx @@ -0,0 +1,75 @@ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include +#include +#include +#include +#include // This needs to be before +#include + +void exFunc () +{ + fprintf ( stderr, "Exiting.\n" ) ; + pwCleanup () ; + exit ( 0 ) ; +} + + +void rsFunc ( int w, int h ) +{ + fprintf ( stderr, "Resized to %d x %d\n", w, h ) ; +} + + +void mpFunc ( int x, int y ) +{ + puMouse ( x, y ) ; +} + + +void msFunc ( int button, int updn, int x, int y ) +{ + puMouse ( button, updn, x, y ) ; +} + + +void kbFunc ( int key, int updn, int x, int y ) +{ + puKeyboard ( key, updn, x, y ) ; +} + + +void button_cb ( puObject * ) +{ + fprintf ( stderr, "Hello World.\n" ) ; +} + + +int main ( int, char ** ) +{ + pwInit ( 100, 100, 640, 480, false, "PLIB Window Demo", true, 0 ) ; + puInit () ; + + puOneShot *b = new puOneShot ( 50, 50, 200, 80 ) ; + + b -> setLegend ( "Say Hello" ) ; + b -> setCallback ( button_cb ) ; + + pwSetCallbacks ( kbFunc, msFunc, mpFunc, rsFunc, exFunc ) ; + while ( 1 ) + { + glClearColor ( 0.5f, 0.1f, 0.1f, 1.0f ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + puDisplay () ; + pwSwapBuffers () ; + } + + return 0; +} + + diff --git a/examples/src/pw/pw_pui.dsp b/examples/src/pw/pw_pui.dsp new file mode 100644 index 0000000..4056dc3 --- /dev/null +++ b/examples/src/pw/pw_pui.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="pw_pui" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pw_pui - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pw_pui.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pw_pui.mak" CFG="pw_pui - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pw_pui - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pw_pui - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pw_pui - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg.lib fnt.lib pui.lib ul.lib pw.lib opengl32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "pw_pui - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg_d.lib fnt_d.lib pui_d.lib ul_d.lib pw_d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "pw_pui - Win32 Release" +# Name "pw_pui - Win32 Debug" +# Begin Source File + +SOURCE=.\pw_pui.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/sg/Makefile.am b/examples/src/sg/Makefile.am new file mode 100644 index 0000000..004ec69 --- /dev/null +++ b/examples/src/sg/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SG + +noinst_PROGRAMS = sg_quat_test + +sg_quat_test_SOURCES = sg_quat_test.cxx + +sg_quat_test_LDADD = -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = sg_quat_test.dsp + diff --git a/examples/src/sg/sg_quat_test.cxx b/examples/src/sg/sg_quat_test.cxx new file mode 100644 index 0000000..6258afb --- /dev/null +++ b/examples/src/sg/sg_quat_test.cxx @@ -0,0 +1,287 @@ +/* + This is a demonstration of Quaternion vs. Euler rotation + + The cube on the left uses Euler rotation. The cube on + the right uses Quaternion rotation. As you can see the + Euler cube is in midst of a gimble-lock-death-throw, + while the Quaternion cube is happily spinning away. + + I have setup two rows of keys to control the cubes rotations. + + Quaternion Rotation Toggles: + q - toggle X axis rotation + w - toggle Y axis rotation + e - toggle Z axis rotation + r - reset quaternion to initial state + t - untoggle all rotations + + Euler Rotation Toggles: + a - toggle X axis rotation + s - toggle Y axis rotation + d - toggle Z axis rotation + f - reset euler to initial state + g - untoggle all rotations + + ESC exits + + Written by Michael Kurth (kurth@futurekill.com) (negative0@earthlink.net) +*/ +#ifdef WIN32 +# include +#else +# include +#endif + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include + +#define XSIZE 320 +#define YSIZE 320 + +static sgVec3 Euler ; +static sgVec3 EulerPos ; +static sgVec3 EulerRot ; + +static sgVec3 Quat ; +static sgVec3 QuatPos ; +static sgQuat QuatRot ; + +static const GLfloat cube_vertices[] = +{ + -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 0.5, 0.5, 0.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, + -1.0, 1.0, 1.0, 0.0, 0.5, 0.5, + -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, + 1.0, -1.0, -1.0, 0.5, 0.0, 0.5, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, + -1.0, 1.0, -1.0, 0.5, 0.5, 0.0 +} ; + +static const GLubyte frontIndices[] = { 0, 1, 2, 3 } ; +static const GLubyte backIndices[] = { 5, 4, 7, 6 } ; + +static const GLubyte rightIndices[] = { 1, 5, 6, 2 } ; +static const GLubyte leftIndices[] = { 4, 0, 3, 7 } ; + +static const GLubyte topIndices[] = { 3, 2, 6, 7 } ; +static const GLubyte bottomIndices[] = { 4, 5, 1, 0 } ; + +static void InitEulerCube( ) +{ + sgSetVec3( Euler, 1, 1, 1 ) ; + sgSetVec3( EulerRot, 0, 0, 0 ) ; + sgSetVec3( EulerPos, -2, 0, -5 ) ; +} + +static void RotateEulerCube( ) +{ + if( Euler[ SG_X ] ) + { + EulerRot[ SG_X ] += 5.0 ; + if( EulerRot[ SG_X ] > 360. ) + EulerRot[ SG_X ] = 0.0 ; + } + + if( Euler[ SG_Y ] ) + { + EulerRot[ SG_Y ] += 5.0 ; + if( EulerRot[ SG_Y ] > 360. ) + EulerRot[ SG_Y ] = 0.0 ; + } + + if( Euler[ SG_Z ] ) + { + EulerRot[ SG_Z ] += 5.0 ; + if( EulerRot[ SG_Z ] > 360. ) + EulerRot[ SG_Z ] = 0.0 ; + } + +} + +static void InitQuatCube( ) +{ + sgSetVec3( Quat, 1, 1, 1 ) ; + sgMakeIdentQuat( QuatRot ) ; + sgSetVec3( QuatPos, 2, 0, -5 ) ; +} + +static void RotateQuatCube( ) +{ + if( Quat[ SG_X ] ) + sgRotQuat( QuatRot, 5.0, 1.0, 0.0, 0.0 ) ; + + if( Quat[ SG_Y ] ) + sgRotQuat( QuatRot, 5.0, 0.0, 1.0, 0.0 ) ; + + if( Quat[ SG_Z ] ) + sgRotQuat( QuatRot, 5.0, 0.0, 0.0, 1.0 ) ; +} + + +static void DrawCube( ) +{ + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, frontIndices ) ; + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, backIndices ) ; + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, rightIndices ) ; + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, leftIndices ) ; + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, topIndices ) ; + glDrawElements( GL_QUADS, 4, GL_UNSIGNED_BYTE, bottomIndices ) ; +} + +static void Redisplay( void ) +{ + sgMat4 matrix ; + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + glLoadIdentity( ) ; + glTranslatef( 0, 0, -5 ) ; + + glPushMatrix( ) ; + { + glTranslatef( QuatPos[ SG_X ], QuatPos[ SG_Y ], QuatPos[ SG_Z ] ) ; + + sgQuatToMatrix( matrix, QuatRot ) ; + glMultMatrixf( (float*)matrix ) ; + + DrawCube( ) ; + } + glPopMatrix( ) ; + + glPushMatrix( ) ; + { + glTranslatef( EulerPos[ SG_X ], EulerPos[ SG_Y ], EulerPos[ SG_Z ] ) ; + + sgMakeRotMat4( matrix, EulerRot ) ; + glMultMatrixf( (float*)matrix ) ; + + DrawCube( ) ; + } + glPopMatrix( ) ; + + + glutSwapBuffers( ) ; +} + +static void Idle( ) +{ + static int lastTime = 0; + int time = glutGet((GLenum)GLUT_ELAPSED_TIME); + if (time > lastTime + 10) + { + RotateQuatCube( ) ; + RotateEulerCube( ) ; + + lastTime = time; + } + glutPostRedisplay( ) ; +} + +static void Keyboard( unsigned char key, int xPos, int yPos ) +{ + switch( key ) + { + case 27: + exit( 0 ) ; + case 'q': + Quat[ SG_X ] = !Quat[ SG_X ] ; + break ; + case 'w': + Quat[ SG_Y ] = !Quat[ SG_Y ] ; + break ; + case 'e': + Quat[ SG_Z ] = !Quat[ SG_Z ] ; + break ; + case 'r': + InitQuatCube( ) ; + break ; + case 't': + sgSetVec3( Quat, 0, 0, 0 ) ; + break ; + + case 'a': + Euler[ SG_X ] = !Euler[ SG_X ] ; + break ; + case 's': + Euler[ SG_Y ] = !Euler[ SG_Y ] ; + break ; + case 'd': + Euler[ SG_Z ] = !Euler[ SG_Z ] ; + break ; + case 'f': + InitEulerCube( ) ; + break ; + case 'g': + sgSetVec3( Euler, 0, 0, 0 ) ; + break ; + } +} + +static void Reshape( int w, int h ) +{ + + if( h == 0 ) + { + h = 1 ; + } + + glViewport( 0, 0, w, h ) ; + + glMatrixMode( GL_PROJECTION ) ; + glLoadIdentity( ) ; + + gluPerspective( 45.0f, (GLfloat)w / (GLfloat)h, 0.1f, 100.0f ) ; + + glMatrixMode( GL_MODELVIEW ) ; + glLoadIdentity( ) ; +} + +static void Init( void ) +{ + glClearColor( 0.0, 0.0, 0.0, 0.0 ) ; + + glEnable(GL_DEPTH_TEST); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + + glEnableClientState( GL_VERTEX_ARRAY ) ; + glEnableClientState( GL_COLOR_ARRAY ) ; + + glVertexPointer( 3, GL_FLOAT, 6*sizeof(GLfloat), cube_vertices ) ; + glColorPointer( 3, GL_FLOAT, 6*sizeof(GLfloat), &cube_vertices[3] ) ; + + InitEulerCube( ) ; + InitQuatCube( ) ; + + Reshape( XSIZE, YSIZE ) ; +} + +int main( int argc, char **argv ) +{ + glutInit( &argc, argv ) ; + glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH ) ; + glutInitWindowSize( XSIZE, YSIZE ) ; + glutCreateWindow( "Quaternion Demo" ) ; + + Init( ) ; + + glutDisplayFunc( Redisplay ) ; + glutReshapeFunc( Reshape ) ; + glutKeyboardFunc( Keyboard ) ; + glutIdleFunc( Idle ) ; + + glutShowWindow( ) ; + glutMainLoop( ) ; + return 0 ; +} + diff --git a/examples/src/sg/sg_quat_test.dsp b/examples/src/sg/sg_quat_test.dsp new file mode 100644 index 0000000..590e94c --- /dev/null +++ b/examples/src/sg/sg_quat_test.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="sg_quat_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=sg_quat_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sg_quat_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sg_quat_test.mak" CFG="sg_quat_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sg_quat_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "sg_quat_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sg_quat_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"sg_quat_test.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "sg_quat_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"sg_quat_test.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "sg_quat_test - Win32 Release" +# Name "sg_quat_test - Win32 Debug" +# Begin Source File + +SOURCE=.\sg_quat_test.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/sl/Makefile.am b/examples/src/sl/Makefile.am new file mode 100644 index 0000000..d4a1eec --- /dev/null +++ b/examples/src/sl/Makefile.am @@ -0,0 +1,17 @@ +if BUILD_SL + +noinst_PROGRAMS = example mod_demo playsound + +example_SOURCES = example.cxx +mod_demo_SOURCES = mod_demo.cxx +playsound_SOURCES = playsound.cxx + +example_LDADD = -lplibsm -lplibsl -lplibul $(SND_LIBS) -lm +mod_demo_LDADD = -lplibsm -lplibsl -lplibul $(SND_LIBS) -lm +playsound_LDADD = -lplibsm -lplibsl -lplibul $(SND_LIBS) -lm + +endif + +EXTRA_DIST = cuckoo.au scream.ub wheeee.ub zzap.wav tuxr.mod \ + mod_demo.dsp example.dsp + diff --git a/examples/src/sl/README b/examples/src/sl/README new file mode 100644 index 0000000..d9eb2fc --- /dev/null +++ b/examples/src/sl/README @@ -0,0 +1,18 @@ + +The music 'tuxr.mod' was composed by Matt Thomas +specificially for the TuxKart game - it is used here +for demonstration purposes only. Please don't use +it in your applications. + + +example -- Plays a variety of sounds read from samples on disk plus + it generates a sound in-situ in memory. A good general + sound sample replay example. + +playsound -- Plays any sound sample file that you enter on it's + command line. Much simpler than 'example'. + +mod_demo -- Plays a '.mod' music file. + + -- Steve Baker. + diff --git a/examples/src/sl/cuckoo.au b/examples/src/sl/cuckoo.au new file mode 100644 index 0000000000000000000000000000000000000000..f5fb00963f7cf71c07b4afc599aacf1a86f1348c GIT binary patch literal 6400 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 +#include +#include + +/* + Construct a sound scheduler and a mixer. +*/ + +slScheduler sched ( 8000 ) ; +smMixer mixer ( "/dev/mixer" ) ; + +#ifndef M_PI +#define EX_PI 3.1415926535 +#else +#define EX_PI M_PI +#endif + +int main ( int, char ** ) +{ + mixer . setMasterVolume ( 100 ) ; + sched . setSafetyMargin ( 0.128f ) ; + + /* Just for fun, let's make a one second synthetic engine sample... */ + + Uchar buffer [ 8000 ] ; + + for ( int i = 0 ; i < 8000 ; i++ ) + { + /* Sum some sin waves and convert to range 0..1 */ + + float level = (float)( sin ( (double) i * 2.0 * EX_PI / (8000.0/ 50.0) ) + + sin ( (double) i * 2.0 * EX_PI / (8000.0/149.0) ) + + sin ( (double) i * 2.0 * EX_PI / (8000.0/152.0) ) + + sin ( (double) i * 2.0 * EX_PI / (8000.0/192.0) ) + ) / 8.0f + 0.5f ; + + /* Convert to unsigned byte */ + + buffer [ i ] = (Uchar) ( level * 255.0f ) ; + } + + /* Set up four samples and a loop */ + + slSample *s = new slSample ( buffer, 8000 ) ; + slSample *s1 = new slSample ( "scream.ub", & sched ) ; + slSample *s2 = new slSample ( "zzap.wav" , & sched ) ; + slSample *s3 = new slSample ( "cuckoo.au", & sched ) ; + slSample *s4 = new slSample ( "wheeee.ub", & sched ) ; + + /* Mess about with some of the samples... */ + + s1 -> adjustVolume ( 10.0f ) ; + s2 -> adjustVolume ( 0.5f ) ; + s3 -> adjustVolume ( 0.2f ) ; + s3 -> adjustVolume ( 0.3f ) ; + + /* Play the engine sample continuously. */ + + sched . loopSample ( s ) ; + sched . loopSample ( s4 ) ; + + int tim = 0 ; /* My periodic event timer. */ + + while ( SL_TRUE ) + { + tim++ ; /* Time passes */ + + if ( tim % 140 == 0 ) sched.playSample ( s1 ) ; + + /* + For the sake of realism, I'll delay for 1/30th second to + simulate a graphics update process. + */ + +#ifdef WIN32 + Sleep ( 1000 / 30 ) ; /* 30Hz */ +#elif defined(sgi) + sginap( 3 ); /* ARG */ +#else + usleep ( 1000000 / 30 ) ; /* 30Hz */ +#endif + + /* + This would normally be called just before the graphics buffer swap + - but it could be anywhere where it's guaranteed to get called + fairly often. + */ + + sched . update () ; + } + + return 0 ; +} + diff --git a/examples/src/sl/example.dsp b/examples/src/sl/example.dsp new file mode 100644 index 0000000..20fa369 --- /dev/null +++ b/examples/src/sl/example.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="example" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=example - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "example.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "example.mak" CFG="example - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "example - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "example - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "example - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl.lib ul.lib winmm.lib /nologo /subsystem:console /machine:I386 /out:"example.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "example - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl_d.lib ul_d.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"example.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "example - Win32 Release" +# Name "example - Win32 Debug" +# Begin Source File + +SOURCE=.\example.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/sl/mod_demo.cxx b/examples/src/sl/mod_demo.cxx new file mode 100644 index 0000000..8fa0435 --- /dev/null +++ b/examples/src/sl/mod_demo.cxx @@ -0,0 +1,73 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: mod_demo.cxx 2014 2005-02-01 01:14:13Z sjbaker $ +*/ + + +#include +#include +#include + +/* + Construct a sound scheduler and a mixer. +*/ + + +slScheduler sched ( 44100 ) ; + +void callback ( slSample *, slEvent event, int ) +{ + if ( event == SL_EVENT_COMPLETE ) + exit ( 0 ) ; +} + + +int main ( int argc, char **argv ) +{ + sched . setSafetyMargin ( 0.5 ) ; + + if ( argc == 2 ) + sched . playMusic ( argv[1], 1, SL_SAMPLE_ABORT, 0, callback ) ; + else + sched . loopMusic ( "tuxr.mod" ) ; + + + while ( SL_TRUE ) + { + /* + For the sake of realism, I'll delay for 1/30th second to + simulate a graphics update process. + */ + +#ifdef WIN32 + Sleep ( 1000 / 30 ) ; /* 30Hz */ +#elif defined(sgi) + sginap( 3 ); /* ARG */ +#else + usleep ( 1000000 / 30 ) ; /* 30Hz */ +#endif + + sched . update () ; + } + + return 0 ; +} + diff --git a/examples/src/sl/mod_demo.dsp b/examples/src/sl/mod_demo.dsp new file mode 100644 index 0000000..b4c163d --- /dev/null +++ b/examples/src/sl/mod_demo.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="mod_demo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mod_demo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_demo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_demo.mak" CFG="mod_demo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_demo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mod_demo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_demo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl.lib ul.lib winmm.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libcd.lib" /out:"mod_demo.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "mod_demo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sl_d.lib ul_d.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"mod_demo.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "mod_demo - Win32 Release" +# Name "mod_demo - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_demo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/sl/playsound.cxx b/examples/src/sl/playsound.cxx new file mode 100644 index 0000000..9f94268 --- /dev/null +++ b/examples/src/sl/playsound.cxx @@ -0,0 +1,81 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: playsound.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include +#include +#include + +/* + Construct a sound scheduler and a mixer. +*/ + +slScheduler sched ( 8000 ) ; +smMixer mixer ( "/dev/mixer" ) ; + +#ifndef M_PI +#define EX_PI 3.1415926535 +#else +#define EX_PI M_PI +#endif + +int main ( int argc, char **argv ) +{ + mixer . setMasterVolume ( 100 ) ; + sched . setSafetyMargin ( 0.128f ) ; + + /* Set up the samples and a loop */ + + slSample *s1 = new slSample ( argv[1], & sched ) ; + + s1 -> adjustVolume ( 10.0f ) ; + + sched . loopSample ( s1 ) ; + + int tim = 0 ; /* My periodic event timer. */ + + while ( SL_TRUE ) + { + tim++ ; /* Time passes */ + +#ifdef WIN32 + Sleep ( 1000 / 30 ) ; /* 30Hz */ +#elif defined(sgi) + sginap( 3 ); /* ARG */ +#else + usleep ( 1000000 / 30 ) ; /* 30Hz */ +#endif + + /* + This would normally be called just before the graphics buffer swap + - but it could be anywhere where it's guaranteed to get called + fairly often. + */ + + sched . update () ; + } + + return 0 ; +} + + diff --git a/examples/src/sl/scream.ub b/examples/src/sl/scream.ub new file mode 100644 index 0000000..80d41b6 --- /dev/null +++ b/examples/src/sl/scream.ub @@ -0,0 +1 @@ +~~~€‚€€~~€€€€€~€~€€€€€€‚€}~€‚ƒ‚ƒ~||„„„€‚€}y|ƒŠ†‚~||}z{‡ŒŒ€zy€ƒsn{•“xip…Ž{wmƒ‘ˆwŠ„xˆ‡h„Š“‹wz|}Œ€…‡†ik~“{px|…‡~‡‡‚ot„‹€w}{†|‹„vjnŒwu}{†|‚ŠpoŠˆwx{ƒŠ†‹ƒwl‚‹‡xt}|…|ˆ€|ymŠ‹Œt||‡…Š}~xtˆ”‘†n{w~„‰ywjx‘„t}|€„‚Š}qp“†‡uw}€ƒƒ€…jx’€—}„}…„Žsnn’€r}~ƒ‚}ƒ€€|w‹…ƒ}x€~ƒ€{}†r‡ˆŠw‰zŒ†}~so~–‡‹p€ƒ„€€u{y‰€|}y‚ˆ€€„„rrˆ~„ptˆ†xry”yŒ|ƒ„ˆ…}|‚„}t‚€‚{x…‡w|~}q~Œv~jz†…‹p}r}’€|yƒ‡‚‚ƒƒmx}‚prˆ„Žtu€r„™y‰m‡‡‰v„m›t{o†||y{Œ|xt‘uxy‹Ž€|t‡vr›x„mu„€‰p}‚„˜{r†…}„{‡{t–y{r{І†y|wŒ †osŠ~xu{zz‚€q}‡ƒ†~€vrƒŽ~k}†‰ƒv‚v”–}€pˆƒ€}x…uw‰x‚vˆ‚uz}u˜‰vzm€‚Šwy{•‰sƒŠ†}w|„v”Œ|~y†‚‚€ws„‰„kqƒ‡xtŽp‹…yt€Š‰{w|ŒŠ}n„ŠŒvs€v—†~utz„ˆ‚„z†}n…ŠŽxp€„„z‘†zs†‹„|xtŠz‡…|x{z‚sz”…my‚ˆ…pz’zpz„„oz†€xu‘Œus…†‰zx~–‘{yq†Œ†{pƒ††p„…‚„r„„‚}sw•‚{ls”„ny„y{Š~‰rv|„Š|~yˆŽƒuqŠ“‰unzˆz‹ƒzqvƒ†€ˆuy†…~t}…wq~†v†“ƒq~„‡ƒxy”{v}„Š~u~‚‚tw“†|t{†ƒ€z€z–‡zt†ˆŠzt…ƒw†‚ƒvz€ˆ|v†‰~yrƒ‘†zt}†y‹†‚qz„†‚yy‹…~rx‹‡o†s~•}‚q~‹vx‡Œ}px~ކ}rz„xv‰‰{tw‰‹€pt—Ž}wu{އvt}‰‚tŒŠ€~v†††{vx„—~xt„€{sƒw‹‚tp€ŠŒ|r~€†‡vy„~t~‚‚{Š„|wx€Š‡~ovŒ…xv„‹v{†‹zn~yt}†‰vtwŒ˜†ur”†yt|x~„sz}…Š€|t†‰‡vr…pv‚{†’ˆwpx†ˆztˆ’†wt‚”Žwow‹~}“|wsˆ…|x|“‘wrxŽ”pp††w}Š‚yt{†xyx}‹{~†{y}€€s‰”†ym€…ˆxz‚|vˆˆwr€Šƒvx}w‡‡€|u€Šˆ‚ry‹…zv„Ž„xz}ˆws“~rwЇƒx|u–‹wvy‰ˆ„vy‡y~‹ˆ}z{„‰€zwx€vvyŒ‘|u}ˆ‚Œ†{p}‚‰…~}u‰ˆ€uŠr~‚…•ƒ€r|€…‡v‰Ž|pv’“zp€‰„uˆ–|yw†‡…|r‰|zz„Œ„xw……zu……z{xƒ††|n‰“†xqv’{pzzyŠ‚‰puy‹Ž…ss“‡pt‚ƒmy‚zyŽŠxou‹Š„zw…ކ{pvŠvr‡|tŽzsr†‰wv}xxzƒˆ€{wƒ‚~{ˆŠ|sy€‰ˆ~{|z†Šƒ‚}|‚y|sŒ†|ww‡…€z‚‚x{…„}vƒ‹ƒ}w~…’ˆuu‰‚xp†•‹wr‚„‡€}z}†wt€Œ|{z‚~€|v{Žzyx|‡‚ˆx€psˆˆ‡~u}‡‡„ss‘‚{{x‹ƒsv††ƒ|ˆ‹~r~Œ‹}{{{‰‚|x€„‚„€€}t…’ˆ‚u~|ƒ‚|~~•zxt|‡~y|}y|†pyƒŠ…~r~Œ€wx„†€„~z{„zw|ˆŒx|v€—xz{{„Іzxt~ƒvuxŠŽ‰sr„‡‚t„‚yxƒ…‚€{tˆ‡ywxˆ}t|€€’†‰yvwƒ†€}|’}yvy‹’ƒvy…}wŠ„qq~‰†~uu’†}sx„‹tx€‚zy…~rs…ˆŠ}vŠ}zv}wv‚{x’Œ}sr„І{~}‚„~w}†}s|…€{†„vv{‰Œpt‰…{u|‚Œ‡}u€ƒvu’~zrz‰‰wyqŽ‘zz|‰Œtz€~„Žzpt‰ˆ€{yzxŠƒyp†‡ˆwtx€’~€y{Š‚{€‚€{…Š€v~{€‹‚v}z}‘Žvz€ƒx‰„~wxz……uu~zx‡Œ‚vt~……~v‘Œ}xs{‰‹{tx~v}Žyrs„†€y…Ž‚}|u‘|stƒ€wŽ€vp~†‡~‚|…ƒ}}{ƒ“vn„‰{‡’‚}r~‚†€|{’wtx„‚†z{~‚w~zv„‰~y}v}{u}ƒƒ‚|‚|x‹‰€y{Šƒst~x‰—‚t{}Š„t†ˆ}vx~…‚tw~{w„ƒvqz……ƒ|v”ˆ|vv„‚‚‚€y{{’†z|…†w{r‹–}v€ƒ…~zw‡•|xw|ˆ„|z}yw„‹‡€oy‰‰…}tyŒxr|”„yr€ƒw}ˆpvˆŒ‡}uy…xuv”‚p{ƒy…tr‚Š‹~qpˆ|w|‚~‚}|‚‹~z€ƒƒ}}w~Ž~yv|ˆ„‚~‡‚pw†ŒutzŽŠƒw|u{†~w}„†r|’yw~…ƒ~~€|y‰‚y|†}|€|‘|v|…ˆ{vz‹„|z|~„††}}|yt’€}sy‚‡~uˆ“„vƒ€|~…zv‰|xvƒzr}~so~„‰|~ˆwux‘}to†‚€„{qz„†ƒ~u~zx|„ƒ‚~|‚~}Œ~w}…ƒ}}w„’†€|{‹‹x{~v‹”Šxqt„‡ƒ}wƒŒ}r‡•vq€…x‡Šypy„€x†˜ˆrr„……„~x{~|yŽ~yu€ˆ}yyx‚Žy{x|‡}ƒ|ut„tsކrs||„‰……vuy‡†zx~}wy…ŽŠxuyx“‡ytx€„…„sy‡€}w}”‚xo~„}’Žro†…w“stƒ…Šxu…wvЉ€tt‚ˆ„}uz‹€{x~‰€}~€zz}‰{x€Š„{}y‹”‚€wz†}x†•yq‚‹…~t|ƒ‚{ˆ…uv{†……s…’‡|y}Ž‹}uy~wŒ‡tsy‡Š{||‰†~z|}}tw„€w‡’…wn~‰‡zt{Œyvƒˆƒ||€†}x€Š~z|†€|~|t”Œ|xx„„ƒ}w„’‰{wy„†„||‚}|‚}|~†„qsˆ‡zx{†…|y…‰ywŒŒsxŒ„{z}x‚Ž…„vyy‰ƒ‚n}Œ}vz‚†…}z}z‡ˆƒu{€‚‚{}Œ†€{{‡…yv€utˆƒwv}„†|‹‰|y}{‹‡|t~€}…~s|†‹~x{ˆzvy‡„~‚ƒ€}|xu–„xtˆ~t{~ˆ”„~{|€†ƒ~yz†…ƒzz€ƒƒ~|sŠ€xz€ƒ†{|zy€Ž„yu‡‰~zy‚„‡ƒz||……|tv•†}uxˆ‡ƒxz{…Œ‚€}x}Žu‚„„szˆqz„‡~tz‹‚{z|~€ƒ‚‚xuzŽˆvtvŒsyx‘…yzwІ}v‰˜vs‚…‚…u{„ƒz……ys‡Š„u{{’uz{…‡ƒ}„ƒp‘†qsŒ’Štt€|xŠstz‡‡}~{…ƒwz€Œywxƒ€z‹“|ss‚‡‚‚€{vzŠ„||x‡†ƒw|vwŒˆ„x|€‚€}~~Ž|x€€}ˆ€t}†|x‹—‚st†ˆ„{{w”ˆwx|…€~„xxŒ‚t|‰|x|€w†‰~sz‚‡~y{‹„yq€†…„xz€‚„z‰‹y{x…‚|t…“|zx€‡‡~u~|‡’„|sz€ƒ‚€~||}z’‡{uy‡„}z}v–€~uy…‡…|z|„Ž€|xx‡w}ƒ‰wt…Ž}py‡Š|u}x“Œ}{{€€~~~y€ˆƒ{|€z~ƒ„p{{v}ƒˆ}€ƒu‰Ž‰{r|†wˆ…wx|€Œƒxr€„†|z…{w}‚}~|}ˆ~}{~…„€~y…‹ˆy{€‚€€|z€‹€zu{ˆ~u…}ŽŒzxy…„y|€…’…zt…„ƒ}{‚ˆ}ˆ…x€„€x€{|}xuyˆ‡„uz~Œ~wv~~‡zv~‡{„•‹sr{†„}t“‹|uyŒŠ†vz‚|sŒ–‰qt}‰…zy‹~v{ƒ…‹‡yx|‡}x‹”€qq†…~ˆ‚|x…ˆ~}{€‚‚ƒ‡v‡}y…‡ƒ~xvŠ“yvy†ˆw}y€Žz{{ƒ„}‚~‚x‚І~xz‚‡}~x‡Žyt}„‰|{ˆyxy‰†~}~}~€‡‰wz…‚z~€‚x‰“…xux‡‡zyy…Šxw~€€‰€y{„{y€u}„}|…}q”‹yusЉqy€p…}v~ƒ‚†‚v‹†t}Šƒ|}„y‡†ww~‡~{†“ysw‡†‚~}|~„‚„yƒ€{zƒ…‚|ƒ{•~yq‚…ƒ|}„y|……z{|…Š„s|xv’Œƒpv‚…„|ty‚vx}‚{}|€~„†|‚‡yyƒƒwr—†tt}†ˆzz|z†ˆƒzyƒ‡‚~|‚ƒ{€…ˆxx{„ˆ„{s|Œy|€„…}~xyŽ”‡ps‡…ƒ~}|w|„yy„{v‹•‰qr„‰„{w}‚wx‚ƒ‚|~€{}~~€ƒt|yx}ˆ€„s„txƒ‡ƒ‚~€~…‡z{ƒ‚{~€ƒ~s~‘‚vu€…€y{“‡t|€~ƒ‚ƒ|||}{†~x{}‰t‚~u•ˆ€vs„ŠŒwwx…€zu€…~ƒtu€„rx€‡ƒ~‚‘Œ|{wˆ‚zw„{{€~yxw‘p{†zr”ƒst…ˆƒ~€t’yvz…€‚„|}w…ŒŒwu„‡€‚|t–…ru|……€}v‡‘„vz‚|„„|{‚|t‚}zu}ˆ‚|t}Š|xx€~~‚u~‹ˆ~ux{‰~z|†yp”‰rs|†€~yއ|~|„€‚vzˆ~v‡‹}y…}uƒŒ‚~v‚‡‚y{|†z}€‚„{y}€…„zz†~|~…‚y{s…Š|xu€‡„}{{{Œ’}xx~…ˆ|zƒ‚}|z‚‘s{ƒ…z|sŽxy}ˆ{‚rz‡ztz…ƒ‡€|{~€|‰}v{…ˆ}€|s‡‡zxzƒ‡„~zyŽ‚wz„€~zƒˆ€‡‚z|†ƒw{z”‡wuzˆƒ|uˆ{y€‚…€~~~ƒz‡‹yyxŒ‚{€~|Ž’yx|ˆŠ‚{xy}Ž„zz}…€ƒ|~{‡}}|ƒ…ysx…yu{†‡„|yv|‘…wy|„…||ƒzyˆƒ|x}‚{vy~wv~ˆ†}zw‰Ž‡vw|‰‚u‚€~z…zt€†€€pyŽ~wt}‹‹‚yvy‹Œ‡xu|‡…€{x‰„}xwІvwƒwqŽŠtu‚…„yy‹€xy‚‚‚ƒ{€|…‚|‡‚|}~†}z{w’†}wz‚€}x‹†t}~~‰ƒtˆtx‰‰vx‚‡…}~v‚…y{{ƒƒ…z}xƒ~x{…‡„}|€…yxˆ„tv}…‡rŽŠyy‰„„zz{}‡†‚tv€†…~|vu…†qs|‹x{€‡}zy‚€€z|yŠ‹ts„Šˆ||‚{pŽ”‰urƒŒ‰x{€€}{|ƒ€‡z{€|€‚Œyxƒ„€}x†“x{z€‡ƒyx~„€x}ƒ‚†ƒ|z‚{†sv€„‚}~‡q€‰}|t{Љs~„r„‡wq}…ˆ„€|w‚‰}ty‡…}y|~€„‡{z}„…}zˆ”†wxz‚ƒ„~z{{†„Šxx……|~yŠƒzs€…Œ{u}ƒ€…zvu‡~xu‚€u”{sq„‰‹}yt‘~zx€„ƒ‡‚y{~ƒƒ|ŠŒw~~€„„‚t‰–x{w…ˆƒ|{€~‚|tsƒ†‚|}}|ˆ‚xxu…‰€xz‚‚|vŠ’ˆrx‡†€|}x‹Š€{wƒ„…}~~ƒƒuy‹ƒzy‚ˆ~v{–tsxˆ‡yy}Œ‹‚y~‚‚€|€}‚‰‹{mxƒuz‚ƒs‰”ztu†…{~u„“|{~~‡„|}„ˆ…wy}Œ|x„‡pŠ€zu€†‚|uˆ‚{{}…‚‚~}{~€‹uw}Žwszˆ„ryŒƒvu~ˆzy{wŽ~yy{…ƒ€z{z†Š‚|zyІ|tƒ‡zyŒ…{s|‚†‚||x†ƒ}~‚€|x†‡ys……‚|{‚v„vy€ˆƒyw~|Œ€}y}…‚‚€}}€}€Š‚x}ƒˆy}ƒw~Œ„uzЇ{wx‡„{}~‚~ƒ{~€‡~~{|…„}{€ƒv‚Ž~{u|„‰ƒvzw‰{|‚„}{ƒ‚~€}t…{|wx•xu{‰ˆ†xxzw|}„†z|~ƒ†{€†~{yŠ…ƒz{|Ž–ww{†Œ…xr~xŽ„vty†‡|~z~Š„xw‰}zx†„wvˆŽ|q|„„€zy{……~z}‚„€ƒ}y|€p…„}~€||uˆvv‚…†€yy{‰…‚}zy†‹~wx‚‚y|‚‰€v~‡~~„su“†|tvˆŒ‡u|yzˆ‹„uw„‚…€x~‹u†~„‚zu{’{w}ƒƒ…~{‡†zz}Љvt|…„x…}tŒŠ}xzx‚Œ|v|†‡ƒu{}‹ƒ|v€‚„~‚‚{qŽtuŠ’Šyxy{|„ŒŽwsw……yw‹„ty…„~€€~ƒ„|‚ˆzyƒ‡z~zŠ‘{xx„……€yy{‰„|x~ƒ†…~~{€yŠ“‹uu|ˆ€‚{‹}|{ˆ…€z}€~Љˆvyz}‚wu…~w‚“tx‚Š„}zt€‰|}{€€~|~‰„}v†Ž{{‚‚€uŒƒzu€ˆƒz|xy‘„~w„€€€|yˆŒxtw„|}v‚‘‡|s|†‡€xy€{z~‚~~}…„vƒŒzs€ˆ‡|{}{†‘Švuwˆ†€{|z‰{|~{ˆ‚~{~z€|‰Žvw†…‚~|{wy€…‚ƒxx€Žƒ}y{ƒ…~||v‰Œƒ~€|€„‚€}sy†xu{‹‰„u{xx‰‰‚yw€ƒ†„|ƒx~ІzwŠŽ{v€‚}xŒ”v{ƒ‡„~wxƒ‰w{…€€€ƒ„yy…{}„‡~}|vu…‹vz‚ˆ„|}xy…}yz€ƒ„~~z‰Žwp‚‰Š}z}tŽ•†xty††}w‚‹zvƒ†€€€ƒ}|…ƒtz†ƒ}†~{w~Ž…ws€‰‡}|zxŽz|~~ƒ€|u†‹‡z{~ƒ‡€{|vzŒ†}vz€‡„{|yˆŽ„|{}€ƒƒy†ˆyt€…ƒ{‚ˆ~zv‚Ž–vv~„ˆ}xz}‹‚}x|ƒ‡|{zƒ|……{v„†…z~uƒŽ{ww|‡†‚|{}xŠŒ|qz‚ƒ„‚{~|€…‰€wu‰vyƒy‡vr‡€zy~yx€‡ƒ„|~y~‚Šƒr‡ƒ}y…‚q|”…vx‚†|}€yŽ‘ƒ{x€ƒ~~yzŒ…~xywxsyˆy‚~vv…†{|x}Ž{z„‚‚~~y…„yr}Œ}u†y|‰xuŠƒ~v{xˆ|z~€ƒ…~}~~}~Їvy‚‡ƒ~z}~vyv††ƒ{~{€Š‚€y|„}}|ƒ„{}ƒ€€z~‚€||Œwr€…†~v‚…z€…|uzƒˆƒ{x{|ƒ~xx‰‹†ww|††t|vx„‡~{u‚Œ~ux‚…~{~~yŒƒ}ww|‹|s~‚‚zˆŽŒwt{„ƒ}}‚vƒ|y|…†~~~ƒyƒvuyŒˆ~v}ƒxxxy‚‚~„€x‹w~„€y‡‹„~s„†ƒ}~}|zŒˆ‚wzz€‡‚~€}ˆ††~x{€Šz}€€‚‚†z‚ƒ€~€zwŒ†|x~‚…†{ƒ‡~v„ƒ~~…€~Љwv{Š€u~„‚tu’ƒrt†€€~v~‰…€zz}†…ƒ}|}x€‡†…t{††€‚~{‚Ž{vwˆƒ}x}|x‡}vv‚ƒƒƒz{u‡‘ŒytzŠŠyy‚~vu~‘‡vz€‚„‚}u|‡ƒ‚{||ƒ~‚‚}v„‡~~€}‚€}~vz‹†€w€|‡ƒz|xuŒŠwv{ˆ……w{}z~ŒŽˆtr€‰Œxv€~‰Š‡~ww{ˆ‹‰{ww|}†Œ€ys|„‹‰~zu€y‚vt|†…ˆ}zz€zz‰‹ˆ|tzz†‰ˆ|yy€y€‡uqy|††‹~xz€yyƒŽ†„p|}‰‰‡‚x{||y|’Іuz€‚‡Š…zx}‚‚t…‘Œs{|ˆƒ~…„yu|y‹ˆ…{u}}}‚Š„~wz€|„މ~uy€‡€|ƒ…{{}~„ˆ…z|~ƒ€ˆ~}~€~|vŒ‡‡y{€~}‚…„}||~€‹‡„|€~}~‚ƒƒ„x}€~~t~‰†‰}x{€ƒ„†ƒƒ{|}}…zs…ˆ†‚~|z|‚„‡}|z|‚†…us…„ˆ†„€||z~‚†„€~~€~€~}€…‚}~}‡€€…xw€ƒ†~‚€~€„€{€|ƒƒ€‚€yx}ƒ„}}~~~~‚†‡€|yv~‰ŒŠ|}|}ƒ…ƒ|{€‚}~‚ƒ}|}|‚„„†€‚‚€|{}ƒ‚…‚zz}ƒ„~|x€…†Š„~}}z|z‚„‹„€~~}|{}„ˆ„€|‚}~}€‚„€~€€}€ƒyz~‚€{‚‚‚~‚‚€~~}}€‚‚‚‚‚|{‚€€ƒ}z|}€ƒ†ƒ|}€€€~}„ƒ‚~{€‚€~x|„ƒƒ‚‚€~}~‡„~‚ƒ|y|‚ƒ‚~€‚ƒ€~~€€€€€}€~}„‚}{~ƒ‚€€„|}€€}‚†}}}~~€ƒ€~|‚†ƒ{}‚‚€}~€}†ƒ€|}}…„‚€~||~„„„€}|~‚‚‚‚‚ƒ|{€€€…‚€}}€~€~ƒ€ƒ„€€}~~€…„€|{„‚~}ƒ~~||}„~€€~‚ƒ‚||~„€}€€€€~€€‚}~€„~€‚€|{„}~€€€€‚|}€‚~„‚€€|‚ƒƒ€|~‚‚}}‚ƒ~}€€}|€€€‚~|~‚~€€}~~€ƒƒ~€~€€€~„~}}ƒƒ}‚€ƒ€|‚ƒƒ‚~~}~‚€‚‚‚~}|~„ƒ~€€€~}‚‚‚€}€‚‚~~€‚}€€€€‚€~}~~ƒ‚€‚}||€ƒ‚~€}~‚€~„~}€€||‚‚‚~~|„„€ƒ‚~}‚‚€‚„}{~…ƒ‚|}†}~€ƒ€}€€€‚ƒ~||€„€{€‚~~€€€€€~‚}}z‚~ƒ~|€‚‚~€€}~ƒ…{{~}€‚€‚‚~~}€‚~€€€€€€€€€€~€€€€€€€€€€€€€€~€€€€€€€€€€€€€€€€€€€€€€€€€€ \ No newline at end of file diff --git a/examples/src/sl/tuxr.mod b/examples/src/sl/tuxr.mod new file mode 100644 index 0000000000000000000000000000000000000000..5077a67b89e4c95ae301358e9d9f42a6d3a82e73 GIT binary patch literal 55430 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

8Y>$;Zp!po-6Xgm>(W4WWYm>COy)Qb|C#vL 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 literal 0 HcmV?d00001 diff --git a/examples/src/sl/wheeee.ub b/examples/src/sl/wheeee.ub new file mode 100644 index 0000000000000000000000000000000000000000..84d1ae7d385395bdde3c19b178133020f0393d21 GIT binary patch literal 7596 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 mADCkQ|7T)AV&M@6sRglO0E(hGfCGx6qDU3p z9f>FybRmMExOH9#ZrmxjQA80G)Q#vuR1hDC$o2d0tqRFbG#fEVKHjT(=lo~5=iXOY z_vN~Eme?|X6N`WR=z8SxzvzH_%FGSn5x5U-ha2G+??ioPeJq8cJU2rp83s=BJ zKx}s^EC98*4dAGi{CeNRNANP-59d?+FYr8^K<#hAE^7V^9tSkrd|$KA>hFNO>sNt3 z)epft3Z%=t1>cPaeK+>gRnUWQm7hM3b^d8>f5-1g{c)ZCv+KWXw(M=$ z+2OjGu%Y)HwG*DRV5heUH*A`n&MHfMI(k<5#M068$|X%YvMH?R*)fIh**0&5>owaP z+raruj&royJImxa=Ph@x$>caEoA7Po>jQ?tCE=7ETW8JC5vv}#bY)qS6tZ0z)g*;% zSI=toYNJ+Cr2BSc{d)nhbTV*l|wAHcw zqe#h>BSIpgg!SH(jVNJ#G_GvL^Zrmvv$03zSIj&vDg7F`Rc@teRCemJ=$rbM?ZtQO zKJ??5skQuG6viS$8;4JumfMTYSZdgeGij-y#IFXqG{okJs)<3-iLC~)G~~Hag|y-# ztOlvHNnF}&R7I6^Qop9yx=C!{rzKQLD77_xt((LTY+6c{l+tF6DWE_}4Vr0nyTCbt zT77BzB79JLX-V4^7$#7w8{N>V5FkZ2^Bl9?(9)1iN)nlE&`hLp)KYE+baI2UO+4h3 z--O?0Z<~m|`JhcSskC>PO{%UP-X^u!I~-?p>KC**Qd`j8NSg(vQEKeFzSP)vU8%9x zT%}m^Ky#JyfrpwaC4qrvCbXG%nuaAT#r8NYErr{zO_%y5UYj^Cd8=?-iaXIMKhoDu zbjpz$v=f~iDWt@)3gst$miH?HRp?M+v))RCDk{$Kn2pAo-1%szshy8Tn%MbhP@t$) z&0tE=d38v(04-_;Q_7@>t_3l)rWs5rt0L;;K?i@WGUiE~`f#+UO}zain+MtOJW;eH zZkTZDsIhsBl7}d$YbwI|u%dwq2a<*}SG&U5lE+{umSm~{3*j>btl|Nc$3R3YDKbtW zA2J22m>Tr+7=*i$LDC?GuvrXL98!=vlKXwyk#WB!F;;O%J?dv}`(z<{9 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/dynamics/dynamics.cxx b/examples/src/ssg/dynamics/dynamics.cxx new file mode 100644 index 0000000..19d95f0 --- /dev/null +++ b/examples/src/ssg/dynamics/dynamics.cxx @@ -0,0 +1,450 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: dynamics.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#define GUI_BASE 80 +#define VIEW_GUI_BASE 20 +#define FONT_COLOUR 1,1,1,1 +#define DEEPEST_HELL -10000.0 +#define HOT_TOLERANCE 1.0f + +static ssgRoot *scene = NULL ; +static ssgaWaveSystem *ocean = NULL ; +static ssgSimpleState *sea_state = NULL ; +static ssgSimpleState *cube_state = NULL ; + +static ssgaWaveTrain trains [ 3 ] ; + +static ssgTransform *cube [ 10 ] ; +static sgParticle *particle [ 10 ] ; +static sgSpringDamper *spring [ 20 ] ; +static int num_particles ; +static int num_springs ; + +static void resetSMD () +{ + particle [ 0 ] -> setPos ( 2.0f, -4.0f, 0.0f ) ; + particle [ 1 ] -> setPos ( 2.4f, 0.0f, 6.0f ) ; + particle [ 2 ] -> setPos ( 2.1f, 4.0f, 0.0f ) ; + particle [ 3 ] -> setPos ( 4.1f, -4.0f, 4.0f ) ; + particle [ 4 ] -> setPos ( 0.0f, -4.0f, 4.0f ) ; + particle [ 5 ] -> setPos ( 4.0f, 4.0f, 4.0f ) ; + particle [ 6 ] -> setPos ( 0.0f, 4.0f, 4.0f ) ; + + particle [ 0 ] -> setVel ( 0, 0, 0 ) ; + particle [ 1 ] -> setVel ( 0, 0, 30 ) ; + particle [ 2 ] -> setVel ( 0, 0, 0 ) ; + particle [ 3 ] -> setVel ( 0, 0, 0 ) ; + particle [ 4 ] -> setVel ( 0, 0, 0 ) ; + particle [ 5 ] -> setVel ( 0, 0, 0 ) ; + particle [ 6 ] -> setVel ( 0, 0, 0 ) ; +} + +static void initSMD () +{ + particle [ 0 ] = new sgParticle ( 1.0f, 2.0f, -4.0f, 4.0f ) ; + particle [ 1 ] = new sgParticle ( 1.0f, 2.4f, 0.0f, 6.0f ) ; + particle [ 2 ] = new sgParticle ( 1.0f, 2.0f, 4.0f, 4.0f ) ; + particle [ 3 ] = new sgParticle ( 1.0f, 4.0f, -4.0f, 0.0f ) ; + particle [ 4 ] = new sgParticle ( 1.0f, 0.0f, -4.0f, 0.0f ) ; + particle [ 5 ] = new sgParticle ( 1.0f, 4.0f, 4.0f, 0.0f ) ; + particle [ 6 ] = new sgParticle ( 1.0f, 0.0f, 4.0f, 0.0f ) ; + + num_particles = 7 ; + + int s=0; + + spring [s++] = new sgSpringDamper ( particle[0], particle[1], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[0], particle[2], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[0], particle[3], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[0], particle[4], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[0], particle[5], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[1], particle[2], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[2], particle[3], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[2], particle[5], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[2], particle[6], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[3], particle[4], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[3], particle[5], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[4], particle[6], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[5], particle[6], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[3], particle[6], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[4], particle[5], 50.0f, 1.0f ) ; + + spring [s++] = new sgSpringDamper ( particle[2], particle[4], 50.0f, 1.0f ) ; + spring [s++] = new sgSpringDamper ( particle[0], particle[6], 50.0f, 1.0f ) ; + + num_springs = s ; + + for ( int i = 0 ; i < num_particles ; i++ ) + { + ssgaCube *cb = new ssgaCube () ; + cb -> setKidState ( cube_state ) ; + cb -> regenerate () ; + + cube [ i ] = new ssgTransform () ; + cube [ i ] -> addKid ( cb ) ; + cube [ i ] -> clrTraversalMaskBits ( SSGTRAV_ISECT | SSGTRAV_HOT ) ; + + scene -> addKid ( cube[i] ) ; + } +} + + + +static float getHeightAndNormal ( sgVec3 my_position, sgVec3 normal ) +{ + /* Look for the nearest polygon *beneath* my_position */ + + ssgHit *results ; + int num_hits ; + + float hot ; /* H.O.T == Height Of Terrain */ + sgVec3 HOTvec ; + + sgMat4 invmat ; + sgMakeIdentMat4 ( invmat ) ; + invmat[3][0] = - my_position [0] ; + invmat[3][1] = - my_position [1] ; + invmat[3][2] = 0.0 ; + + sgSetVec3 ( HOTvec, 0.0f, 0.0f, my_position [ 2 ]+HOT_TOLERANCE ) ; + + num_hits = ssgHOT ( scene, HOTvec, invmat, &results ) ; + + hot = DEEPEST_HELL ; + + for ( int i = 0 ; i < num_hits ; i++ ) + { + ssgHit *h = &results [ i ] ; + + float hgt = - h->plane[3] / h->plane[2] ; + + if ( hgt >= hot ) + { + hot = hgt ; + + if ( normal != NULL ) + sgCopyVec3 ( normal, h->plane ) ; + } + } + + return hot ; +} + + + +#define RESTITUTION 0.9f + + +static void updateSMD ( float dt ) +{ + int i; + for ( i = 0 ; i < num_particles ; i++ ) + { + sgVec3 friction ; + + sgScaleVec3 ( friction, particle[i]->getVel (), 0.1f ) ; + + particle [ i ] -> gravityOnly () ; + particle [ i ] -> subForce ( friction ) ; + } + + for ( i = 0 ; i < num_springs ; i++ ) + spring [ i ] -> update () ; + + for ( i = 0 ; i < num_particles ; i++ ) + { + particle [ i ] -> update ( dt ) ; + + sgVec3 normal ; + + float hot = getHeightAndNormal ( particle [ i ] -> getPos(), normal ) ; + + if ( particle [ i ] -> getPos ()[2] <= hot ) + { + particle [ i ] -> bounce ( normal, RESTITUTION ) ; + particle [ i ] -> getPos ()[2] = hot ; + } + } + + for ( i = 0 ; i < num_particles ; i++ ) + cube [ i ] -> setTransform ( particle[i] -> getPos () ) ; +} + + +static sgCoord campos = { { 0, -20, 8 }, { 0, -20, 0 } } ; + +static void update_motion ( int frameno ) +{ + ssgSetCamera ( & campos ) ; + + ocean -> setWindDirn ( 25.0f * (float)sin ( frameno / 10.0 ) ) ; + + static ulClock ck ; ck . update () ; + + float t = (float)ck . getAbsTime () ; + float dt = (float)ck . getDeltaTime () ; + + if ( dt > 0.05f ) dt = 0.05f ; + + ocean -> updateAnimation ( t ) ; + + updateSMD ( dt ) ; +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard/mouse events +*/ + + +static void keyboard ( unsigned char key, int, int ) +{ + switch ( key ) + { + case 0x03 : exit ( 0 ) ; + default : resetSMD () ; break ; + } +} + + +static void specialfn ( int key, int x, int y ) +{ +} + + +static void motionfn ( int x, int y ) +{ +} + +static void mousefn ( int button, int updown, int x, int y ) +{ +} + + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ +static int frameno = 0 ; +frameno++ ; + + update_motion ( frameno % 2000 ) ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Simple Scene Graph : Example Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + glutSpecialFunc ( specialfn ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + glutPassiveMotionFunc ( motionfn ) ; + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.1f, -1.0f, 0.1f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + +static void init_states () +{ + cube_state = new ssgSimpleState () ; + cube_state -> setTexture ( "data/mg.rgb" ) ; + cube_state -> enable ( GL_TEXTURE_2D ) ; + cube_state -> setShadeModel ( GL_SMOOTH ) ; + cube_state -> enable ( GL_CULL_FACE ) ; + cube_state -> enable ( GL_BLEND ) ; + cube_state -> enable ( GL_LIGHTING ) ; + cube_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + cube_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + cube_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + cube_state -> setShininess ( 2 ) ; + + sea_state = new ssgSimpleState () ; + sea_state -> setTexture ( "data/bw.rgb" ) ; + sea_state -> enable ( GL_TEXTURE_2D ) ; + sea_state -> setShadeModel ( GL_SMOOTH ) ; + sea_state -> enable ( GL_CULL_FACE ) ; + sea_state -> disable ( GL_BLEND ) ; + sea_state -> enable ( GL_LIGHTING ) ; + sea_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + sea_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + sea_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + sea_state -> setShininess ( 5 ) ; +} + + + +static void load_database () +{ + /* Set up the path to the data files */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* Load the states */ + + init_states () ; + + sgVec4 WHITE = { 1.0, 1.0, 1.0, 1.0 } ; + + /* Set up some interesting defaults. */ + + trains[0] . setSpeed ( 3.1f ) ; + trains[0] . setLength ( 0.8f ) ; + trains[0] . setLambda ( 0.6f ) ; + trains[0] . setHeading ( 47.0f ) ; + trains[0] . setWaveHeight ( 0.2f ) ; + + trains[1] . setSpeed ( 4.6f ) ; + trains[1] . setLength ( 0.8f ) ; + trains[1] . setLambda ( 1.0f ) ; + trains[1] . setHeading ( 36.0f ) ; + trains[1] . setWaveHeight ( 0.1f ) ; + + trains[2] . setSpeed ( 8.5f ) ; + trains[2] . setLength ( 0.6f ) ; + trains[2] . setLambda ( 1.0f ) ; + trains[2] . setHeading ( 65.0f ) ; + trains[2] . setWaveHeight ( 0.1f ) ; + + ocean = new ssgaWaveSystem ( 10000 ) ; + ocean -> setColour ( WHITE ) ; + ocean -> setSize ( 100 ) ; + ocean -> setTexScale ( 3, 3 ) ; + ocean -> setKidState ( sea_state ) ; + ocean -> setWindSpeed ( 10.0f ) ; + ocean -> setWaveTrain ( 0, & trains[0] ) ; + ocean -> setWaveTrain ( 1, & trains[1] ) ; + ocean -> setWaveTrain ( 2, & trains[2] ) ; + ocean -> regenerate () ; + + /* Build the scene graph */ + + scene = new ssgRoot ; + scene -> addKid ( ocean ) ; + + initSMD () ; +} + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + + + diff --git a/examples/src/ssg/dynamics/dynamics.dsp b/examples/src/ssg/dynamics/dynamics.dsp new file mode 100644 index 0000000..68c71b0 --- /dev/null +++ b/examples/src/ssg/dynamics/dynamics.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="dynamics" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dynamics - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dynamics.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dynamics.mak" CFG="dynamics - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dynamics - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dynamics - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dynamics - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssgAux.lib ssg.lib sg.lib ul.lib winmm.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"dynamics.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "dynamics - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssgAux_d.lib ssg_d.lib sg_d.lib ul_d.lib winmm.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"dynamics.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "dynamics - Win32 Release" +# Name "dynamics - Win32 Debug" +# Begin Source File + +SOURCE=.\dynamics.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/load_save/Makefile.am b/examples/src/ssg/load_save/Makefile.am new file mode 100644 index 0000000..fe4b821 --- /dev/null +++ b/examples/src/ssg/load_save/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_SSG + +noinst_PROGRAMS = load save + +load_SOURCES = load.cxx +save_SOURCES = save.cxx + +load_LDADD = -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) +save_LDADD = -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = load.dsp save.dsp + diff --git a/examples/src/ssg/load_save/data/Penguin_beak.rgb b/examples/src/ssg/load_save/data/Penguin_beak.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b1e0e799dad81ab6bd43bea26320cc1867836989 GIT binary patch literal 11194 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/load_save/data/Penguin_body.rgb b/examples/src/ssg/load_save/data/Penguin_body.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/examples/src/ssg/load_save/data/embossed_herring.bmp b/examples/src/ssg/load_save/data/embossed_herring.bmp new file mode 100644 index 0000000000000000000000000000000000000000..795e5d1f7c86118caf32b989ac461972d6652b47 GIT binary patch literal 24630 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/load_save/data/pedestal.ac b/examples/src/ssg/load_save/data/pedestal.ac new file mode 100644 index 0000000..3c112e2 --- /dev/null +++ b/examples/src/ssg/load_save/data/pedestal.ac @@ -0,0 +1,81 @@ +AC3Db +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0.502 0.502 0.502 shi 0 trans 0 +OBJECT world +kids 2 +OBJECT poly +name "box" +texture "wood.rgb" +texrep 5 5 +numvert 8 +-1.29615 -0.6 -1.29615 +-1 0 -1 +1 0 -1 +1.3 -0.6 -1.29615 +-1.29615 -0.6 1.3 +-1 0 1 +1 0 1 +1.3 -0.6 1.3 +numsurf 6 +SURF 0x0 +mat 0 +refs 4 +0 0 0 +1 0.114074 0.114074 +2 0.884445 0.114074 +3 1 0 +SURF 0x0 +mat 0 +refs 4 +6 0.884445 0.884445 +7 1 1 +3 1 0 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +7 1 1 +6 0.884445 0.884445 +5 0.114074 0.884445 +4 0 1 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +5 0.114074 0.884445 +1 0.114074 0.114074 +0 0 0 +SURF 0x0 +mat 0 +refs 4 +1 0.114074 0.114074 +5 0.114074 0.884445 +6 0.884445 0.884445 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +0 0 0 +3 1 0 +7 1 1 +kids 0 +OBJECT poly +name "rect" +loc -0.0500002 0 -2.5 +texture "embossed_herring.bmp" +texrep 20 30 +numvert 4 +-17.95 -0.7 -18.6 +17.95 -0.7 -18.6 +17.95 -0.7 18.6 +-17.95 -0.7 18.6 +numsurf 1 +SURF 0x0 +mat 1 +refs 4 +3 0 0 +2 1 0 +1 1 1 +0 0 1 +kids 0 diff --git a/examples/src/ssg/load_save/data/pedestal.safe b/examples/src/ssg/load_save/data/pedestal.safe new file mode 100644 index 0000000..3d613b9 --- /dev/null +++ b/examples/src/ssg/load_save/data/pedestal.safe @@ -0,0 +1,81 @@ +AC3Db +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0.502 0.502 0.502 shi 0 trans 0 +OBJECT world +kids 2 +OBJECT poly +name "box" +texture "wood.rgb" +texrep 5 5 +numvert 8 +-1.29615 -0.6 -1.29615 +-1 0 -1 +1 0 -1 +1.3 -0.6 -1.29615 +-1.29615 -0.6 1.3 +-1 0 1 +1 0 1 +1.3 -0.6 1.3 +numsurf 6 +SURF 0x0 +mat 0 +refs 4 +0 0 0 +1 0.114074 0.114074 +2 0.884445 0.114074 +3 1 0 +SURF 0x0 +mat 0 +refs 4 +6 0.884445 0.884445 +7 1 1 +3 1 0 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +7 1 1 +6 0.884445 0.884445 +5 0.114074 0.884445 +4 0 1 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +5 0.114074 0.884445 +1 0.114074 0.114074 +0 0 0 +SURF 0x0 +mat 0 +refs 4 +1 0.114074 0.114074 +5 0.114074 0.884445 +6 0.884445 0.884445 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +0 0 0 +3 1 0 +7 1 1 +kids 0 +OBJECT poly +name "rect" +loc -0.0500002 0 -2.5 +texture "embossed_herring.rgb" +texrep 20 30 +numvert 4 +-17.95 -0.7 -18.6 +17.95 -0.7 -18.6 +17.95 -0.7 18.6 +-17.95 -0.7 18.6 +numsurf 1 +SURF 0x0 +mat 1 +refs 4 +3 0 0 +2 1 0 +1 1 1 +0 0 1 +kids 0 diff --git a/examples/src/ssg/load_save/data/tuxedo.ac b/examples/src/ssg/load_save/data/tuxedo.ac new file mode 100644 index 0000000..12e4754 --- /dev/null +++ b/examples/src/ssg/load_save/data/tuxedo.ac @@ -0,0 +1,1695 @@ +AC3Db +MATERIAL "" rgb 0 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 0.702 0 amb 1 0.702 0 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +MATERIAL "" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +OBJECT world +kids 1 +OBJECT group +name "Tuxedo.T.Penguin" +kids 3 +OBJECT poly +name "tuxfeet" +loc 0.0203744 -0.479517 0.101666 +numvert 14 +-0.0744405 0.473224 0.119889 +0.100559 0.473224 -0.200111 +0.175559 0.473224 0.199889 +0.250559 0.473224 -0.200111 +0.425559 0.473224 0.119889 +0.250559 0.523224 -0.200111 +0.100559 0.523224 -0.200111 +-0.425559 0.476776 0.120111 +-0.25056 0.476776 -0.199889 +-0.17556 0.476776 0.200111 +-0.100559 0.476776 -0.199889 +0.0744405 0.476776 0.120111 +-0.100559 0.526776 -0.199889 +-0.25056 0.526776 -0.199889 +numsurf 12 +SURF 0x0 +mat 1 +refs 3 +0 0 0.1 +1 0.9 0 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +3 1 0.1 +2 0 0.8 +1 0.9 0 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +3 1 0.1 +4 0.5 1 +SURF 0x0 +mat 1 +refs 3 +4 0.5 1 +5 1 0.1 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +6 0.9 0 +2 0 0.8 +5 1 0.1 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +6 0.9 0 +0 0 0.1 +SURF 0x0 +mat 1 +refs 3 +7 0 0.1 +8 0.9 0 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +10 1 0.1 +9 0 0.8 +8 0.9 0 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +10 1 0.1 +11 0.5 1 +SURF 0x0 +mat 1 +refs 3 +11 0.5 1 +12 1 0.1 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +13 0.9 0 +9 0 0.8 +12 1 0.1 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +13 0.9 0 +7 0 0.1 +kids 0 +OBJECT poly +name "tuxbody" +loc 0.0124385 0.107258 -0.188333 +texture "Penguin_body.rgb" +numvert 115 +0.097524 1.07 -0.0756895 +-0.0202512 1.11 0 +0.119749 1.07 0 +0.0379068 1.07 -0.127348 +-0.0401753 1.07 -0.138575 +-0.111932 1.07 -0.105805 +-0.154581 1.07 -0.0394426 +-0.154581 1.07 0.0394426 +-0.111932 1.07 0.105805 +-0.0401753 1.07 0.138575 +0.0379068 1.07 0.127348 +0.097524 1.07 0.0756895 +0.184749 0.975 0 +0.152205 0.975 -0.110831 +0.064909 0.975 -0.186475 +-0.0494259 0.975 -0.202913 +-0.154498 0.975 -0.154929 +-0.216947 0.975 -0.057755 +-0.216947 0.975 0.057755 +-0.154498 0.975 0.154929 +-0.0494259 0.975 0.202913 +0.064909 0.975 0.186475 +0.152205 0.975 0.110831 +0.199749 0.68 0 +0.164825 0.68 -0.118941 +0.07114 0.68 -0.200119 +-0.0515605 0.68 -0.217761 +-0.16432 0.68 -0.166265 +-0.231339 0.68 -0.061981 +-0.231339 0.68 0.061981 +-0.16432 0.68 0.166265 +-0.0515605 0.68 0.217761 +0.07114 0.68 0.200119 +0.164825 0.68 0.118941 +0.284749 0.555 0 +0.236331 0.555 -0.164896 +0.10645 0.555 -0.277438 +-0.0636575 0.555 -0.301895 +-0.219984 0.555 -0.230503 +-0.312897 0.555 -0.0859285 +-0.312897 0.555 0.0859285 +-0.219984 0.555 0.230503 +-0.0636575 0.555 0.301895 +0.10645 0.555 0.277438 +0.236331 0.555 0.164896 +0.369749 0.43 0 +0.307837 0.43 -0.21085 +0.14176 0.43 -0.354757 +-0.075754 0.43 -0.38603 +-0.275647 0.43 -0.294742 +-0.394453 0.43 -0.109876 +-0.394453 0.43 0.109876 +-0.275647 0.43 0.294742 +-0.075754 0.43 0.38603 +0.14176 0.43 0.354757 +0.307837 0.43 0.21085 +0.414749 0.305 0 +0.345694 0.305 -0.235179 +0.160454 0.305 -0.39569 +-0.0821585 0.305 -0.430573 +-0.305115 0.305 -0.328751 +-0.437631 0.305 -0.122553 +-0.437631 0.305 0.122553 +-0.305115 0.305 0.328751 +-0.0821585 0.305 0.430573 +0.160454 0.305 0.39569 +0.345694 0.305 0.235179 +0.409749 0.185 0 +0.341488 0.185 -0.232476 +0.158377 0.185 -0.391142 +-0.081447 0.185 -0.425623 +-0.301841 0.185 -0.324973 +-0.432833 0.185 -0.121145 +-0.432833 0.185 0.121145 +-0.301841 0.185 0.324973 +-0.081447 0.185 0.425623 +0.158377 0.185 0.391142 +0.341488 0.185 0.232476 +0.354748 2.23517e-08 0 +0.295219 2.23517e-08 -0.202741 +0.135529 2.23517e-08 -0.341112 +-0.0736195 2.23517e-08 -0.371183 +-0.265824 2.23517e-08 -0.283406 +-0.380061 2.23517e-08 -0.10565 +-0.380061 2.23517e-08 0.10565 +-0.265824 2.23517e-08 0.283406 +-0.0736195 2.23517e-08 0.371183 +0.135529 2.23517e-08 0.341112 +0.295219 2.23517e-08 0.202741 +0.154748 -0.1 0 +0.126968 -0.1 -0.094612 +0.052446 -0.1 -0.159185 +-0.0451563 -0.1 -0.173219 +-0.134852 -0.1 -0.132256 +-0.188163 -0.1 -0.0493032 +-0.188163 -0.1 0.0493032 +-0.134852 -0.1 0.132256 +-0.0451564 -0.1 0.173219 +0.052446 -0.1 0.159185 +0.126968 -0.1 0.094612 +0.211872 0.567025 0.1 +0.211872 0.567025 -0.1 +0.316872 0.532025 0.1 +0.316872 0.532025 -0.1 +0.456872 0.392025 0.1 +0.456872 0.392025 -0.1 +0.56187 0.217025 0 +-0.211873 0.58637 0.1 +-0.211873 0.58637 -0.1 +-0.316873 0.55137 0.1 +-0.316873 0.55137 -0.1 +-0.456872 0.41137 0.1 +-0.456872 0.41137 -0.1 +-0.56187 0.23637 0 +-0.0202512 -0.11 0 +numsurf 218 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +1 -1 1 +2 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +1 -1 1 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +1 -1 1 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +1 -1 1 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +1 -1 1 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +1 -0.5 1 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +1 -0.5 1 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +1 -0.5 1 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +1 -0.5 1 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +1 -0.5 1 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -0.380214 0.967213 +1 -0.5 1 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -1 0.967213 +12 -1 0.889344 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +0 -1 0.967213 +12 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +13 -1 0.889344 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +3 -1 0.967213 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +14 -1 0.889344 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +4 -1 0.967213 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +15 -1 0.889344 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +5 -1 0.967213 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +16 -1 0.889344 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +6 -1 0.967213 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +17 -1 0.889344 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +7 -0.614934 0.967213 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +18 -0.668296 0.889344 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +8 -0.578443 0.967213 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +19 -0.614863 0.889344 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +9 -0.517047 0.967213 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +20 -0.524962 0.889344 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +10 -0.450239 0.967213 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +21 -0.427136 0.889344 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +11 -0.39923 0.967213 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +22 -0.352443 0.889344 +2 -0.380214 0.967213 +SURF 0x10 +mat 2 +refs 3 +12 -0.324599 0.889344 +2 -0.380214 0.967213 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +12 -1 0.889344 +23 -1 0.647541 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +13 -1 0.889344 +23 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +24 -1 0.647541 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +14 -1 0.889344 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +25 -1 0.647541 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +15 -1 0.889344 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +26 -1 0.647541 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +16 -1 0.889344 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +27 -1 0.647541 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +17 -1 0.889344 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +28 -1 0.647541 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +18 -0.668296 0.889344 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +29 -0.68061 0.647541 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +19 -0.614863 0.889344 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +30 -0.623268 0.647541 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +20 -0.524962 0.889344 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +31 -0.526789 0.647541 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +21 -0.427136 0.889344 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +32 -0.421804 0.647541 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +22 -0.352443 0.889344 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +33 -0.341646 0.647541 +12 -0.324599 0.889344 +SURF 0x10 +mat 2 +refs 3 +23 -0.311765 0.647541 +12 -0.324599 0.889344 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +23 -1 0.647541 +34 -1 0.545082 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +24 -1 0.647541 +34 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +35 -1 0.545082 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +25 -1 0.647541 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +36 -1 0.545082 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +26 -1 0.647541 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +37 -1 0.545082 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +27 -1 0.647541 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +38 -1 0.545082 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +28 -1 0.647541 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +39 -1 0.545082 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +29 -0.68061 0.647541 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +40 -0.750392 0.545082 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +30 -0.623268 0.647541 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +41 -0.670894 0.545082 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +31 -0.526789 0.647541 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +42 -0.537139 0.545082 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +32 -0.421804 0.647541 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +43 -0.391592 0.545082 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +33 -0.341646 0.647541 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +44 -0.280464 0.545082 +23 -0.311765 0.647541 +SURF 0x10 +mat 2 +refs 3 +34 -0.239037 0.545082 +23 -0.311765 0.647541 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +34 -1 0.545082 +45 -1 0.442623 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +35 -1 0.545082 +45 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +46 -1 0.442623 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +36 -1 0.545082 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +47 -1 0.442623 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +37 -1 0.545082 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +48 -1 0.442623 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +38 -1 0.545082 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +49 -1 0.442623 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +39 -1 0.545082 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +50 -1 0.442623 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +40 -0.750392 0.545082 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +51 -0.820173 0.442623 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +41 -0.670894 0.545082 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +52 -0.71852 0.442623 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +42 -0.537139 0.545082 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +53 -0.547489 0.442623 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +43 -0.391592 0.545082 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +54 -0.36138 0.442623 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +44 -0.280464 0.545082 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +55 -0.219282 0.442623 +34 -0.239037 0.545082 +SURF 0x10 +mat 2 +refs 3 +45 -0.16631 0.442623 +34 -0.239037 0.545082 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +45 -1 0.442623 +56 -1 0.340164 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +46 -1 0.442623 +56 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +57 -1 0.340164 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +47 -1 0.442623 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +58 -1 0.340164 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +48 -1 0.442623 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +59 -1 0.340164 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +49 -1 0.442623 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +60 -1 0.340164 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +50 -1 0.442623 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +61 -1 0.340164 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +51 -0.820173 0.442623 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +62 -0.857116 0.340164 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +52 -0.71852 0.442623 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +63 -0.743734 0.340164 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +53 -0.547489 0.442623 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +64 -0.552969 0.340164 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +54 -0.36138 0.442623 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +65 -0.345386 0.340164 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +55 -0.219282 0.442623 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +66 -0.186892 0.340164 +45 -0.16631 0.442623 +SURF 0x10 +mat 2 +refs 3 +56 -0.127807 0.340164 +45 -0.16631 0.442623 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +56 -1 0.340164 +67 -1 0.241803 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +57 -1 0.340164 +67 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +68 -1 0.241803 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +58 -1 0.340164 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +69 -1 0.241803 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +59 -1 0.340164 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +70 -1 0.241803 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +60 -1 0.340164 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +71 -1 0.241803 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +61 -1 0.340164 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +72 -1 0.241803 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +62 -0.857116 0.340164 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +73 -0.853011 0.241803 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +63 -0.743734 0.340164 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +74 -0.740933 0.241803 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +64 -0.552969 0.340164 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +75 -0.55236 0.241803 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +65 -0.345386 0.340164 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +76 -0.347163 0.241803 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +66 -0.186892 0.340164 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +77 -0.190491 0.241803 +56 -0.127807 0.340164 +SURF 0x10 +mat 2 +refs 3 +67 -0.132086 0.241803 +56 -0.127807 0.340164 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +67 -1 0.241803 +78 -1 0.0901639 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +68 -1 0.241803 +78 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +79 -1 0.0901639 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +69 -1 0.241803 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +80 -1 0.0901639 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +70 -1 0.241803 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +81 -1 0.0901639 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +71 -1 0.241803 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +82 -1 0.0901639 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +72 -1 0.241803 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +83 -1 0.0901639 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +73 -0.853011 0.241803 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +84 -0.807859 0.0901639 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +74 -0.740933 0.241803 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +85 -0.710116 0.0901639 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +75 -0.55236 0.241803 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +86 -0.545663 0.0901639 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +76 -0.347163 0.241803 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +87 -0.366712 0.0901639 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +77 -0.190491 0.241803 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +88 -0.230079 0.0901639 +67 -0.132086 0.241803 +SURF 0x10 +mat 2 +refs 3 +78 -0.179144 0.0901639 +67 -0.132086 0.241803 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +78 -1 0.0901639 +89 -1 0.00819672 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +79 -1 0.0901639 +89 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +90 -1 0.00819672 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +80 -1 0.0901639 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +91 -1 0.00819672 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +81 -1 0.0901639 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +92 -1 0.00819672 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +82 -1 0.0901639 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +93 -1 0.00819672 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +83 -1 0.0901639 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +94 -1 0.00819672 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +84 -0.807859 0.0901639 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +95 -0.643667 0.00819672 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +85 -0.710116 0.0901639 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +96 -0.598054 0.00819672 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +86 -0.545663 0.0901639 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +97 -0.521309 0.00819672 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +87 -0.366712 0.0901639 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +98 -0.437799 0.00819672 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +88 -0.230079 0.0901639 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +99 -0.374037 0.00819672 +78 -0.179144 0.0901639 +SURF 0x10 +mat 2 +refs 3 +89 -0.350267 0.00819672 +78 -0.179144 0.0901639 +99 -0.374037 0.00819672 +SURF 0x10 +mat 0 +refs 3 +100 0 0 +101 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +102 0 0 +101 0 0 +SURF 0x10 +mat 0 +refs 3 +102 0 0 +103 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +105 0 0 +104 0 0 +103 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 3 +106 0 0 +105 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +104 0 0 +105 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +103 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +107 0 0 +108 0 0 +109 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +109 0 0 +108 0 0 +SURF 0x10 +mat 0 +refs 3 +109 0 0 +110 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +112 0 0 +111 0 0 +110 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 3 +113 0 0 +112 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +111 0 0 +112 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +110 0 0 +109 0 0 +SURF 0x10 +mat 2 +refs 3 +89 -1 0.00819672 +114 -1 0 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +114 -1 0 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +114 -1 0 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +114 -1 0 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +114 -1 0 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +114 -1 0 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +114 -0.5 0 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +114 -0.5 0 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +114 -0.5 0 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +114 -0.5 0 +99 -0.374037 0.00819672 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +114 -0.5 0 +89 -0.350267 0.00819672 +kids 0 +OBJECT poly +name "tuxbeak" +loc -0.0328128 0.372259 0.086667 +texture "Penguin_beak.rgb" +numvert 17 +0.0565685 0.514 0.1 +1.11759e-08 0.5 0.125 +0.08 0.5 0.1 +1.11759e-08 0.528 0.1 +-0.0565685 0.514 0.1 +-0.08 0.5 0.1 +-0.0565685 0.486 0.1 +1.11759e-08 0.472 0.1 +0.0565685 0.486 0.1 +0.16 0.5 -0.125 +0.113137 0.556 -0.125 +1.11759e-08 0.612 -0.125 +-0.113137 0.556 -0.125 +-0.16 0.5 -0.125 +-0.113137 0.444 -0.125 +1.11759e-08 0.388 -0.125 +0.113137 0.444 -0.125 +numsurf 24 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +1 -0.5 -0.5 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +1 -0.5 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +1 -0.5 -0.5 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +1 -0.5 -0.5 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +1 -0.5 -0.5 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +1 -0.5 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +1 -0.5 -0.5 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +1 -0.5 -0.5 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +9 -0.0555556 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +10 -0.18573 -0.18573 +0 -0.342865 -0.342865 +9 -0.0555556 -0.5 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +10 -0.18573 -0.18573 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +11 -0.5 -0.0555556 +3 -0.5 -0.277778 +10 -0.18573 -0.18573 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +11 -0.5 -0.0555556 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +12 -0.81427 -0.18573 +4 -0.657135 -0.342865 +11 -0.5 -0.0555556 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +12 -0.81427 -0.18573 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +13 -0.944444 -0.5 +5 -0.722222 -0.5 +12 -0.81427 -0.18573 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +13 -0.944444 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +14 -0.81427 -0.81427 +6 -0.657135 -0.657135 +13 -0.944444 -0.5 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +14 -0.81427 -0.81427 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +15 -0.5 -0.944444 +7 -0.5 -0.722222 +14 -0.81427 -0.81427 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +15 -0.5 -0.944444 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +16 -0.18573 -0.81427 +8 -0.342865 -0.657135 +15 -0.5 -0.944444 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +16 -0.18573 -0.81427 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +9 -0.0555556 -0.5 +2 -0.277778 -0.5 +16 -0.18573 -0.81427 +kids 0 diff --git a/examples/src/ssg/load_save/data/wood.rgb b/examples/src/ssg/load_save/data/wood.rgb new file mode 100644 index 0000000000000000000000000000000000000000..3094af0a59e91cefe26d47e4531e06440d145876 GIT binary patch literal 11453 zcmeI2d#G*KRmSJZzHM)AlIA4M<0MU*r%88isNK94M0G%k}q7PhN z6_yHVS4*@@la`e}l8MQ!&Zf4R&=O}`TW3(gg$ROzi1;Yt0~DYC_?M#ozPZmn=Oj=S z^e-_t^PIJx>ziYIV~lT%c@F;Q;J~cRTllv!yZ`=OzuxiAJKyoWZ~y+|r~d!f|6L25 zXKUvlH|ENFS-)w_g@;*xX3SMTV$9W7us&qWHTSbVXUr?VjrEVlT>F#89A3-1Y|M3E z#o}|$ejn?f zjd}Hljk#^j;y$;36YI~7dCiX-bI0|pPZ)FOn^?bP%w3N4*T&raqsH8GHS6b%x%WZV z7mWFeZ)SbTnESwf|COwd81vfKv0gA{&1-AE^8vo|;wxEC8uQ>q_Wq5m-)HYX$iDf^ zg(uiMwz%r^?EP)5KVtERWN7+03z4q7HJ9aw!EB5|l?3>SB|1^8wuzrWVe>dxI z+51nhZ|-%|Gwl5#7Wj|8hxJeF{So$k1MB1Leare|_WpJj_qqLN+53I0UuEy#&iXs{ z{!{GxYF1$H53<SY=Pxroy^>6I`arS;U3+(rQ3+qqV`wz44D_Fet0N?ol-+7VG zUHlUJe>FQRli2&$v;LI5v){v)vGCV*Yt|Rp zJD`2cd$Og z-nq~1zsKHxh<(G&JMhz;uV?)Rdk6Pj;J*7^EcScP%)Z&vy}!)f8y0@N5A640!}~wL z-tT06mc74?#cL1now(dwe4M@C#d@9x`2R(V@A*$3zx4etef-kTzx4J0>Er*6??3w; zx<32(8_qn|^JgFbcd+lj>#Z-b;3nCx!3jlfAyVjIrE+iToa=P zo|~7m_K$U~=e6`4F=EI?C-2d7`*rp`dT#$c``7jR?04z$RmYzvCd{qJ*Za?1$78); z_uGH>{geVV7TnM;^T*QGJXaURN) zLLYLUh7iUyk0skO3}YF_7`vF~DILUvU|ukn*4A&V+q$;(N3FN5-)>yvY|}QrwRWqQ zw$?U|$A0a%P2-!UKHs+P3TxhN9=^&va{eL1cU`tIyPSp?hGmn%k%unCkQ`TYn)(#S z*vBxXdCtj|ZkQI&OWmC3E>}EH2#;HHdv58E92c(HtnA0#S`b{j)k|B@f!DdU-!v`o z8@H{mo#MF#Oh-FRS%zTSeQ9%+vHYBd+@>kUGz@v3Lzlvsr#VMoT$q+Nb;~&9af)D? z&Ch|!$q?GMU$0!kSeq6jtsNF}4ZzxFyZi0z#;?J2=r-8NHCuaN58AzzWT^{g#I5JA zwsp0Hz7@P zY6EEIA*|xl-4w?%^(A-=%Ei)+Q)%*;VHZfRs93NLwh>&00Jpc*chp19I~jG zaNOrI^mr*n_&k6wH<9=%mcW~#50;xMCIVr`i*vEUWWf|Grfe>4oUfRml^D+dN4CZn z*X`QEDm(KuX!FI;7IQxV+TyTw<-(?-!D2f!5Qf+TkQ};l?}c8rIrZ3U zvCtLOLarqCio3F3rY=RF-5lW3(329{GWU57CAN83=7A6lb9OOd$l}ukg$jr~x3O{; zyaCb1ZCVSLFq&=FdaAYF9y)OeL}UXtz)ODPR+U1x1Lisw5hj;{A*j7ZFr z8JY@7$ei67>Gj~brsC;xOg@A}US0;DqbtK4$egiVQt!dTOF3;)0By`|4$+6|XJ5vH z0b0jOYwiGP;f?S>VDQunx7~op;-e28ISYNPI~3Q!hfB#z2hEN`F9(r=?P`_?PN~6K zLyAiygY=<{K()&_h2m0jOYDXmyBRviCZ>wcW4kWo)W%HqI_N-$wXlXif-Lyv6rEoy z%34U}S`;55+D1WHCb6+8WU_9DVK}brJl#NgmgEc~3v$8)!^g%Vs zpgq`}gx+i}6HB?rM&zF`kEDaV3eTp|EmIDZnCu9+JbB(VbqjGA=6+s+O-Zb>pqb4j z2n7nbj9uEAzE}2H_|!qPQ#86FR}vAVaR`PTG1AIF9Gn`d(TI8f`+4G(>D!Lc=aV)r0DB#Tp?GG%P_a zTiH+5IFsJQbOc;QB{5?092!cp&f6mir;{vjDlP|te<9xCl84$8w?*74KSD1k)plHX zs!2r8QH1AlNF{C-B5PmVjw_FGcTBqgdU+sI$dT@y2^bOy|A2bmp()~BA^I5&r&A<%Ykx9qHn1cjw zhjT~^Lm+KVB&`s9hrpYrfQ5E|=n1wRE<(V{J1hGHB!G49Qj8r0685jJi!~VC2`2RajxBxNUCu98W8Pdz`RT$%vdK= zP^qyg+JS-xxPX)zDS3306D2v!-3;@+p9?~K^m!O7A}HN<0D{ublN}ZN$Q0#nNfm5F zrk3I+0_D_}xZG@7FUQgyG<&Z}0x)BPMhFg!F(+D*6j6~OxG{kUyP$b-4|Sv`74RaR zCAJw+O~f9|oA)-NtnWA@Nd!k4izM24OJN1viU_2#7PbK#FO$|c?H-Y&(rZ{daT!(F zbpMRpMg#Sc;;$A8UV(Lp)YzUhATkY<-vNID+7HR4Jt{<+tig@Qm%3do{&J-xCk9fD zCFCoa@ENd#3g~OMM|CTRZe}r?kQ7ksuckXmph4Tv5pBX021G%}ze3K=TzTDs3a{n715jr(jJ~t-$eNu zX$Lw498Q(N90~wJ4~T*}Pz`$`GSP^m=|ZT8z6hVHl|*NSK9U!|po=6#s#=l)z-B@O z_tiLICo&ZQx(D=10Da}oLVz#JpBg`dPffM9qk<1A|ROOGp0 z=tLS~&oT+=8repd09-YFx>m9h{EFy3=Hbev*HlQ=6D0v)8wI^P? z)x77tInUS^YA+K0%xtJ`aZ($FV%wFm-eH)Y@}G0Z{0kvKH0BMJnBW87n5&V{lt7Yl z(~JU0Fa|0sO>$8I?wTd&CQQ_uBg_dTeaP)orTP;YN^197)+<=M{;r_$5l3{s5xI>y z=;F8@L$L#>rWrRbR$7>yF#RGIbYv>VEW-{V+G&T7a)*#@q@7gtKnk^LzJyNb1rXA< zXjGxj4r-}8eG$SpR-0uhY9YclwnsHdB9WR>u}n}VrYQ}sq~hv7q+0XCn^Z8K{=r) z{m>VX+JxQ?g*Jx%lY-NdB(g(w9^2BW3m8!z39?O{?tm4&jn=Hp2F{@* zF=_x%dk>x_CmP2FXQYI}+)($~IJ6%eHP%r?93pd3*NJgxkNZe=KLi9g_=*r!2@CK-KTQ$`ba#P5hG~Z(jlO&`V z5OfIPmdwmV!0uGAC-FNA8h1T4vKs zscHy5CkJXCK*Vty!N3kArKasRhy|6yiV7*?SM6h>xANJV7tWv(A|cbX_qYX)3#~sr z35nAxz~GQ{($bVxgUh*D=*$aR1rZ)bzc9)S8>$ZeKB;?ZrX)g0oEqfRm?EKeNB&Tv zpgrnpRgq$$*3pP!up`nHhTWGLjaFWFf{JMXGadEd$@zu6J}PU+d1O-12pPmGZn`BC#Ym0QV_RtYaFR8uezsE^BQTXp@Aw z515N+M%N=KJtkUEPYlT^&^C825`!>|Mw7uD*((H#w1a@)E3Q;YP7+1(N;;Ab+6~T= zxUCa0h`s>^`cm?*Q6VG}LFBg`1oTWZ{fip}<|5pLP9=|Bk6EZ|NOK?|CKZ2nTzZl? zQYH|IjJ^v=t7Uc}G#Y^)qfyZ#C2r6u(v>)|M%XU?nohz*+y)?I2eCWEhqBM{jQ&CF z!}*O;6oWa69rd1h32kr(t&`4A7+!j$5())@&WNxEDBVm(if$N=<1yMfjV&~TQ1#$9 zmZ|+F%1yyf5tK!wm>@ORw7lx_m>*(A@a)>T)4>kP$WiSxQQmR3vN)!u40B80r|d_R z6=IG$*y+MyIJ`H;P2z7pf2CaxE2x{GF zOWlxZ1W0HoXBb6?E{qLe4KfDpz}su|pP*3#elKgM!_^r{-s+?WR1ld_98!~!W$Kup zGGg9vWWjS(;E`&GvWtwG@aYLE30cV$f?cS9fX$SwaSh!PJjPY6P@&&6btMK#0P03h zP~8I}&II8Q*_q6PY}9m%9+N4&D%eEJAA8DvN|WX#P)zNf)GpawM+ics5z1wXt}>xI zOM$6_5;bv%Bc-J+Q~D=ck%#A0aDXY?j4u_B`+)^sdlWQu_fuNU&r^1DEHVzKgqcIQ zJda$G5I9oOBk4^^6NU!SK{q%gH1u=S3gT_GR%WHr zFr|H}@5J*f`5E0mm)0OjUJ2A`p=Y zWEW^74bD>vJiG;><8z(UsOO|q%7370ao&|1T%+EAhLQrO4r1{GRBb4%!>5vDRGzN_FcRd;Z-n+e=MRi| z4+GRy_1`#Zrl|Xar-Q&ohVVL5i=+aj>A&N6Eg+j@m&3l;*N@;vn^%LJwu84o7=N&4Xg{q^d2o+LJ zsFb8&73h7P>Z+G}bB%n?9)j-?4ov_EByQ6-~8 zS@dM2OKP?pkyKHJeKY_Qv#XIvmJHnmN13U1=&~OpaVp;FL4^i_8ldv`4;YF5gBdfZ z)c2AuQTP-Ra^$eIOy&x1L#8k~(IGkW0*#*w^TMbB>hZyjvNYplVxRkv45YWKB-{E! zh=dHmNq_(>=Ai(}aO6r2S8B|S=3#UGYbzekPWk~3bH2pTcIM265vp5;Fcvow@aRXF z#UZMWF(>i^G*hnR)lkjX!M$$<%o_@PWqpEh>iO=W@Ju($k6#uy=4{?}QSK=!5C}4ZN+T6jHQDG0 zkDb~`3j3N9RDFpq4xCHy#~Vv%bqGeXERu^o$EO8qaU4)lk)CKz7%vsh?#TpW)gxcf zKo_mb{V7x^XdU9z`${g-50DsTH-#7z3_Z}uPzNe2`^d=wrV@Z!Z~oZqlbWG&PNShe zgVQCtwlJC`qNbK|i+oBmgE}V8oPU1+4bZ{z0f~)NVKRqi`y?r#S^y3)^ujEh*l<(u z9O5s;6J=K#q~&DwG1WE92(xISGVXMAqz>3q5DJDTcn0KDSOpq~suwayS>DmBLm&&>;&G=aqf;DWaP|biXnG0aF+% Awg3PC literal 0 HcmV?d00001 diff --git a/examples/src/ssg/load_save/load.cxx b/examples/src/ssg/load_save/load.cxx new file mode 100644 index 0000000..3af3d36 --- /dev/null +++ b/examples/src/ssg/load_save/load.cxx @@ -0,0 +1,234 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: load.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +static ssgRoot *scene = NULL ; +static ssgTransform *penguin = NULL ; +static ssgTransform *pedestal = NULL ; + +/* + Something to make some interesting motion + for both Tux and the camera. +*/ + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + sgCoord tuxpos ; + + /* + Spin Tux, make the camera pan sinusoidally left and right + */ + + sgSetCoord ( & campos, 0.0f, -5.0f, 1.0f, 25.0f * (float)sin(frameno/100.0), 0.0f, 0.0f ) ; + sgSetCoord ( & tuxpos, 0.0f, 0.0f, 0.0f, (float)frameno, 0.0f, 0.0f ) ; + + ssgSetCamera ( & campos ) ; + penguin -> setTransform ( & tuxpos ) ; +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard ( unsigned char, int, int ) +{ + exit ( 0 ) ; +} + + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Simple Scene Graph : Example Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* + Create a root node - and a transform to position + the pedestal - and another to position the penguin + beneath that (in the tree that is). + */ + + scene = new ssgRoot ; + pedestal = new ssgTransform ; + penguin = new ssgTransform ; + + /* + Load the models - optimise them a bit + and then add them into the scene. + */ + + ssgEntity *ped_obj = ssgLoadAC ( "pedestal.ac" ) ; + ssgEntity *tux_obj = ssgLoadSSG ( "tuxedo.ssg" ) ; + + if ( ped_obj == NULL ) + ulSetError ( UL_FATAL, "Cannot load pedastal.ac" ) ; + if ( tux_obj == NULL ) + ulSetError ( UL_FATAL, "Cannot load tuxedo.ssg -- Please run 'save' first" ) ; + + tux_obj->print(); + + penguin -> addKid ( tux_obj ) ; + pedestal -> addKid ( ped_obj ) ; + + /*ssgFlatten ( tux_obj ) ;*/ + ssgFlatten ( ped_obj ) ; + /*ssgStripify ( penguin ) ;*/ + ssgStripify ( pedestal ) ; + + pedestal -> addKid ( penguin ) ; + scene -> addKid ( pedestal ) ; +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + + + diff --git a/examples/src/ssg/load_save/load.dsp b/examples/src/ssg/load_save/load.dsp new file mode 100644 index 0000000..59dd658 --- /dev/null +++ b/examples/src/ssg/load_save/load.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="load" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=load - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "load.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "load.mak" CFG="load - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "load - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "load - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "load - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"load.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "load - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"load.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "load - Win32 Release" +# Name "load - Win32 Debug" +# Begin Source File + +SOURCE=.\load.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/load_save/save.cxx b/examples/src/ssg/load_save/save.cxx new file mode 100644 index 0000000..4525afd --- /dev/null +++ b/examples/src/ssg/load_save/save.cxx @@ -0,0 +1,131 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: save.cxx 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +static ssgRoot *scene = NULL ; +static ssgTransform *object = NULL ; +static ssgEntity *obj_obj = NULL ; + + +static void redraw () +{ + return ; +} + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Simple Scene Graph : Example Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + + /* + Initialise SSG + */ + + ssgInit () ; +} + + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* + Create a root node - and a transform to position + the object beneath that (in the tree that is). + */ + + scene = new ssgRoot ; + object = new ssgTransform ; + + /* + Load the models - optimise them a bit + and then add them into the scene. + */ + + obj_obj = ssgLoadAC ( "tuxedo.ac" ) ; + + object -> addKid ( obj_obj ) ; + + ssgFlatten ( obj_obj ) ; + ssgStripify ( object ) ; + scene -> addKid ( object ) ; +} + + +static void save_database () +{ + ssgSaveSSG ( "data/tuxedo.ssg", object ) ; +} + + + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + save_database () ; + return 0 ; +} + diff --git a/examples/src/ssg/load_save/save.dsp b/examples/src/ssg/load_save/save.dsp new file mode 100644 index 0000000..2d3fd23 --- /dev/null +++ b/examples/src/ssg/load_save/save.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="save" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=save - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "save.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "save.mak" CFG="save - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "save - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "save - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "save - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"save.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "save - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"save.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "save - Win32 Release" +# Name "save - Win32 Debug" +# Begin Source File + +SOURCE=.\save.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/majik/Makefile.am b/examples/src/ssg/majik/Makefile.am new file mode 100644 index 0000000..7375cb1 --- /dev/null +++ b/examples/src/ssg/majik/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_SSG + +noinst_PROGRAMS = majik_demo + +majik_demo_SOURCES = elevation_map.h \ + image_map.h \ + majik_demo.cxx + +majik_demo_LDADD = -lplibjs -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = majik_demo.dsp + diff --git a/examples/src/ssg/majik/data/Penguin_beak.rgb b/examples/src/ssg/majik/data/Penguin_beak.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b1e0e799dad81ab6bd43bea26320cc1867836989 GIT binary patch literal 11194 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/majik/data/Penguin_body.rgb b/examples/src/ssg/majik/data/Penguin_body.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/examples/src/ssg/majik/data/bumpnoise.rgb b/examples/src/ssg/majik/data/bumpnoise.rgb new file mode 100644 index 0000000000000000000000000000000000000000..5cac5eeb28d816d4f2d33b43fd5087eccf8deb19 GIT binary patch literal 54254 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/majik/data/majik.rgb b/examples/src/ssg/majik/data/majik.rgb new file mode 100644 index 0000000000000000000000000000000000000000..9ff12626e7dbc5474d8d7a74620a881585204a60 GIT binary patch literal 9885 zcmeI1|5F=hp6A8tX?4?zur;(m!tHO9#(`WXl@!V#2JNl0p(5Q_bBSYNxLZOdUfH+` zCWFh^kl5=iwj6}e#m8zHm{cF%s&{fcVm5M9_y${+{ zskBEx|5c@Wrc0&LZ3X>Dm8#_5gMi&Ks8Vfx8T4ONDt(Vi^@ClYyDC*Fu+(#)dn%RT zW0k7R4*H)eRrzmJD&xOXsZ0nos!~-rLI12$ZS$#AH26S2RH?QPs8l=ZKeh#_- z`Vuq)`fm^`j8X9qK@QLx_rSnYWa1U?fbpyxn;3HmD# zj1h4e1PlQ_5lM)Z1i^O~!}Tn_1e$}m)u3h&_@VuC5C^ft_db(=*v~@jm7qUD?5&_2 z#Q!UZeJ^MO;)mEu{{r+K#1B3h_CxGi(0_v1yC8O$A4ZrTCa71?An4!bg4p3&(J&vj z9{|BRwc{ql4nFUK3$$wjV()<1A)X(GAa?NO*(Hd*1@tY%{U!)(_Q0Io^A_k7=u^0` z@SVy9h7zui3Sup^^X0!3e6K36QV?A4LJ4E9+5>{IEeOV1Wd_xOUirSBZ|CcIS-w~P z^B;e&eBWLWjG@X3st4u!@-~06hk2&@5h!2gmGA!(|EDs~<@p*={x|a_Tr<_b0_FXK zYplxq3v*qy1*8MzWB)%Le?HE=pnUv!<>Scr{mc0CvH!mufBu^1=g

wiv`tNvV8 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/majik/data/tuxedo.ac b/examples/src/ssg/majik/data/tuxedo.ac new file mode 100644 index 0000000..12e4754 --- /dev/null +++ b/examples/src/ssg/majik/data/tuxedo.ac @@ -0,0 +1,1695 @@ +AC3Db +MATERIAL "" rgb 0 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 0.702 0 amb 1 0.702 0 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +MATERIAL "" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +OBJECT world +kids 1 +OBJECT group +name "Tuxedo.T.Penguin" +kids 3 +OBJECT poly +name "tuxfeet" +loc 0.0203744 -0.479517 0.101666 +numvert 14 +-0.0744405 0.473224 0.119889 +0.100559 0.473224 -0.200111 +0.175559 0.473224 0.199889 +0.250559 0.473224 -0.200111 +0.425559 0.473224 0.119889 +0.250559 0.523224 -0.200111 +0.100559 0.523224 -0.200111 +-0.425559 0.476776 0.120111 +-0.25056 0.476776 -0.199889 +-0.17556 0.476776 0.200111 +-0.100559 0.476776 -0.199889 +0.0744405 0.476776 0.120111 +-0.100559 0.526776 -0.199889 +-0.25056 0.526776 -0.199889 +numsurf 12 +SURF 0x0 +mat 1 +refs 3 +0 0 0.1 +1 0.9 0 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +3 1 0.1 +2 0 0.8 +1 0.9 0 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +3 1 0.1 +4 0.5 1 +SURF 0x0 +mat 1 +refs 3 +4 0.5 1 +5 1 0.1 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +6 0.9 0 +2 0 0.8 +5 1 0.1 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +6 0.9 0 +0 0 0.1 +SURF 0x0 +mat 1 +refs 3 +7 0 0.1 +8 0.9 0 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +10 1 0.1 +9 0 0.8 +8 0.9 0 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +10 1 0.1 +11 0.5 1 +SURF 0x0 +mat 1 +refs 3 +11 0.5 1 +12 1 0.1 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +13 0.9 0 +9 0 0.8 +12 1 0.1 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +13 0.9 0 +7 0 0.1 +kids 0 +OBJECT poly +name "tuxbody" +loc 0.0124385 0.107258 -0.188333 +texture "Penguin_body.rgb" +numvert 115 +0.097524 1.07 -0.0756895 +-0.0202512 1.11 0 +0.119749 1.07 0 +0.0379068 1.07 -0.127348 +-0.0401753 1.07 -0.138575 +-0.111932 1.07 -0.105805 +-0.154581 1.07 -0.0394426 +-0.154581 1.07 0.0394426 +-0.111932 1.07 0.105805 +-0.0401753 1.07 0.138575 +0.0379068 1.07 0.127348 +0.097524 1.07 0.0756895 +0.184749 0.975 0 +0.152205 0.975 -0.110831 +0.064909 0.975 -0.186475 +-0.0494259 0.975 -0.202913 +-0.154498 0.975 -0.154929 +-0.216947 0.975 -0.057755 +-0.216947 0.975 0.057755 +-0.154498 0.975 0.154929 +-0.0494259 0.975 0.202913 +0.064909 0.975 0.186475 +0.152205 0.975 0.110831 +0.199749 0.68 0 +0.164825 0.68 -0.118941 +0.07114 0.68 -0.200119 +-0.0515605 0.68 -0.217761 +-0.16432 0.68 -0.166265 +-0.231339 0.68 -0.061981 +-0.231339 0.68 0.061981 +-0.16432 0.68 0.166265 +-0.0515605 0.68 0.217761 +0.07114 0.68 0.200119 +0.164825 0.68 0.118941 +0.284749 0.555 0 +0.236331 0.555 -0.164896 +0.10645 0.555 -0.277438 +-0.0636575 0.555 -0.301895 +-0.219984 0.555 -0.230503 +-0.312897 0.555 -0.0859285 +-0.312897 0.555 0.0859285 +-0.219984 0.555 0.230503 +-0.0636575 0.555 0.301895 +0.10645 0.555 0.277438 +0.236331 0.555 0.164896 +0.369749 0.43 0 +0.307837 0.43 -0.21085 +0.14176 0.43 -0.354757 +-0.075754 0.43 -0.38603 +-0.275647 0.43 -0.294742 +-0.394453 0.43 -0.109876 +-0.394453 0.43 0.109876 +-0.275647 0.43 0.294742 +-0.075754 0.43 0.38603 +0.14176 0.43 0.354757 +0.307837 0.43 0.21085 +0.414749 0.305 0 +0.345694 0.305 -0.235179 +0.160454 0.305 -0.39569 +-0.0821585 0.305 -0.430573 +-0.305115 0.305 -0.328751 +-0.437631 0.305 -0.122553 +-0.437631 0.305 0.122553 +-0.305115 0.305 0.328751 +-0.0821585 0.305 0.430573 +0.160454 0.305 0.39569 +0.345694 0.305 0.235179 +0.409749 0.185 0 +0.341488 0.185 -0.232476 +0.158377 0.185 -0.391142 +-0.081447 0.185 -0.425623 +-0.301841 0.185 -0.324973 +-0.432833 0.185 -0.121145 +-0.432833 0.185 0.121145 +-0.301841 0.185 0.324973 +-0.081447 0.185 0.425623 +0.158377 0.185 0.391142 +0.341488 0.185 0.232476 +0.354748 2.23517e-08 0 +0.295219 2.23517e-08 -0.202741 +0.135529 2.23517e-08 -0.341112 +-0.0736195 2.23517e-08 -0.371183 +-0.265824 2.23517e-08 -0.283406 +-0.380061 2.23517e-08 -0.10565 +-0.380061 2.23517e-08 0.10565 +-0.265824 2.23517e-08 0.283406 +-0.0736195 2.23517e-08 0.371183 +0.135529 2.23517e-08 0.341112 +0.295219 2.23517e-08 0.202741 +0.154748 -0.1 0 +0.126968 -0.1 -0.094612 +0.052446 -0.1 -0.159185 +-0.0451563 -0.1 -0.173219 +-0.134852 -0.1 -0.132256 +-0.188163 -0.1 -0.0493032 +-0.188163 -0.1 0.0493032 +-0.134852 -0.1 0.132256 +-0.0451564 -0.1 0.173219 +0.052446 -0.1 0.159185 +0.126968 -0.1 0.094612 +0.211872 0.567025 0.1 +0.211872 0.567025 -0.1 +0.316872 0.532025 0.1 +0.316872 0.532025 -0.1 +0.456872 0.392025 0.1 +0.456872 0.392025 -0.1 +0.56187 0.217025 0 +-0.211873 0.58637 0.1 +-0.211873 0.58637 -0.1 +-0.316873 0.55137 0.1 +-0.316873 0.55137 -0.1 +-0.456872 0.41137 0.1 +-0.456872 0.41137 -0.1 +-0.56187 0.23637 0 +-0.0202512 -0.11 0 +numsurf 218 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +1 -1 1 +2 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +1 -1 1 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +1 -1 1 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +1 -1 1 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +1 -1 1 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +1 -0.5 1 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +1 -0.5 1 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +1 -0.5 1 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +1 -0.5 1 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +1 -0.5 1 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -0.380214 0.967213 +1 -0.5 1 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -1 0.967213 +12 -1 0.889344 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +0 -1 0.967213 +12 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +13 -1 0.889344 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +3 -1 0.967213 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +14 -1 0.889344 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +4 -1 0.967213 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +15 -1 0.889344 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +5 -1 0.967213 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +16 -1 0.889344 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +6 -1 0.967213 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +17 -1 0.889344 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +7 -0.614934 0.967213 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +18 -0.668296 0.889344 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +8 -0.578443 0.967213 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +19 -0.614863 0.889344 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +9 -0.517047 0.967213 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +20 -0.524962 0.889344 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +10 -0.450239 0.967213 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +21 -0.427136 0.889344 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +11 -0.39923 0.967213 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +22 -0.352443 0.889344 +2 -0.380214 0.967213 +SURF 0x10 +mat 2 +refs 3 +12 -0.324599 0.889344 +2 -0.380214 0.967213 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +12 -1 0.889344 +23 -1 0.647541 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +13 -1 0.889344 +23 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +24 -1 0.647541 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +14 -1 0.889344 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +25 -1 0.647541 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +15 -1 0.889344 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +26 -1 0.647541 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +16 -1 0.889344 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +27 -1 0.647541 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +17 -1 0.889344 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +28 -1 0.647541 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +18 -0.668296 0.889344 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +29 -0.68061 0.647541 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +19 -0.614863 0.889344 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +30 -0.623268 0.647541 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +20 -0.524962 0.889344 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +31 -0.526789 0.647541 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +21 -0.427136 0.889344 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +32 -0.421804 0.647541 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +22 -0.352443 0.889344 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +33 -0.341646 0.647541 +12 -0.324599 0.889344 +SURF 0x10 +mat 2 +refs 3 +23 -0.311765 0.647541 +12 -0.324599 0.889344 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +23 -1 0.647541 +34 -1 0.545082 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +24 -1 0.647541 +34 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +35 -1 0.545082 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +25 -1 0.647541 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +36 -1 0.545082 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +26 -1 0.647541 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +37 -1 0.545082 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +27 -1 0.647541 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +38 -1 0.545082 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +28 -1 0.647541 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +39 -1 0.545082 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +29 -0.68061 0.647541 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +40 -0.750392 0.545082 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +30 -0.623268 0.647541 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +41 -0.670894 0.545082 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +31 -0.526789 0.647541 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +42 -0.537139 0.545082 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +32 -0.421804 0.647541 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +43 -0.391592 0.545082 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +33 -0.341646 0.647541 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +44 -0.280464 0.545082 +23 -0.311765 0.647541 +SURF 0x10 +mat 2 +refs 3 +34 -0.239037 0.545082 +23 -0.311765 0.647541 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +34 -1 0.545082 +45 -1 0.442623 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +35 -1 0.545082 +45 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +46 -1 0.442623 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +36 -1 0.545082 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +47 -1 0.442623 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +37 -1 0.545082 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +48 -1 0.442623 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +38 -1 0.545082 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +49 -1 0.442623 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +39 -1 0.545082 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +50 -1 0.442623 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +40 -0.750392 0.545082 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +51 -0.820173 0.442623 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +41 -0.670894 0.545082 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +52 -0.71852 0.442623 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +42 -0.537139 0.545082 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +53 -0.547489 0.442623 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +43 -0.391592 0.545082 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +54 -0.36138 0.442623 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +44 -0.280464 0.545082 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +55 -0.219282 0.442623 +34 -0.239037 0.545082 +SURF 0x10 +mat 2 +refs 3 +45 -0.16631 0.442623 +34 -0.239037 0.545082 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +45 -1 0.442623 +56 -1 0.340164 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +46 -1 0.442623 +56 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +57 -1 0.340164 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +47 -1 0.442623 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +58 -1 0.340164 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +48 -1 0.442623 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +59 -1 0.340164 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +49 -1 0.442623 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +60 -1 0.340164 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +50 -1 0.442623 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +61 -1 0.340164 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +51 -0.820173 0.442623 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +62 -0.857116 0.340164 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +52 -0.71852 0.442623 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +63 -0.743734 0.340164 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +53 -0.547489 0.442623 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +64 -0.552969 0.340164 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +54 -0.36138 0.442623 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +65 -0.345386 0.340164 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +55 -0.219282 0.442623 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +66 -0.186892 0.340164 +45 -0.16631 0.442623 +SURF 0x10 +mat 2 +refs 3 +56 -0.127807 0.340164 +45 -0.16631 0.442623 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +56 -1 0.340164 +67 -1 0.241803 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +57 -1 0.340164 +67 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +68 -1 0.241803 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +58 -1 0.340164 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +69 -1 0.241803 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +59 -1 0.340164 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +70 -1 0.241803 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +60 -1 0.340164 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +71 -1 0.241803 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +61 -1 0.340164 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +72 -1 0.241803 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +62 -0.857116 0.340164 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +73 -0.853011 0.241803 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +63 -0.743734 0.340164 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +74 -0.740933 0.241803 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +64 -0.552969 0.340164 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +75 -0.55236 0.241803 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +65 -0.345386 0.340164 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +76 -0.347163 0.241803 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +66 -0.186892 0.340164 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +77 -0.190491 0.241803 +56 -0.127807 0.340164 +SURF 0x10 +mat 2 +refs 3 +67 -0.132086 0.241803 +56 -0.127807 0.340164 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +67 -1 0.241803 +78 -1 0.0901639 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +68 -1 0.241803 +78 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +79 -1 0.0901639 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +69 -1 0.241803 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +80 -1 0.0901639 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +70 -1 0.241803 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +81 -1 0.0901639 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +71 -1 0.241803 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +82 -1 0.0901639 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +72 -1 0.241803 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +83 -1 0.0901639 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +73 -0.853011 0.241803 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +84 -0.807859 0.0901639 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +74 -0.740933 0.241803 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +85 -0.710116 0.0901639 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +75 -0.55236 0.241803 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +86 -0.545663 0.0901639 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +76 -0.347163 0.241803 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +87 -0.366712 0.0901639 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +77 -0.190491 0.241803 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +88 -0.230079 0.0901639 +67 -0.132086 0.241803 +SURF 0x10 +mat 2 +refs 3 +78 -0.179144 0.0901639 +67 -0.132086 0.241803 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +78 -1 0.0901639 +89 -1 0.00819672 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +79 -1 0.0901639 +89 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +90 -1 0.00819672 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +80 -1 0.0901639 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +91 -1 0.00819672 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +81 -1 0.0901639 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +92 -1 0.00819672 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +82 -1 0.0901639 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +93 -1 0.00819672 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +83 -1 0.0901639 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +94 -1 0.00819672 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +84 -0.807859 0.0901639 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +95 -0.643667 0.00819672 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +85 -0.710116 0.0901639 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +96 -0.598054 0.00819672 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +86 -0.545663 0.0901639 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +97 -0.521309 0.00819672 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +87 -0.366712 0.0901639 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +98 -0.437799 0.00819672 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +88 -0.230079 0.0901639 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +99 -0.374037 0.00819672 +78 -0.179144 0.0901639 +SURF 0x10 +mat 2 +refs 3 +89 -0.350267 0.00819672 +78 -0.179144 0.0901639 +99 -0.374037 0.00819672 +SURF 0x10 +mat 0 +refs 3 +100 0 0 +101 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +102 0 0 +101 0 0 +SURF 0x10 +mat 0 +refs 3 +102 0 0 +103 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +105 0 0 +104 0 0 +103 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 3 +106 0 0 +105 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +104 0 0 +105 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +103 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +107 0 0 +108 0 0 +109 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +109 0 0 +108 0 0 +SURF 0x10 +mat 0 +refs 3 +109 0 0 +110 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +112 0 0 +111 0 0 +110 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 3 +113 0 0 +112 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +111 0 0 +112 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +110 0 0 +109 0 0 +SURF 0x10 +mat 2 +refs 3 +89 -1 0.00819672 +114 -1 0 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +114 -1 0 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +114 -1 0 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +114 -1 0 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +114 -1 0 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +114 -1 0 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +114 -0.5 0 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +114 -0.5 0 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +114 -0.5 0 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +114 -0.5 0 +99 -0.374037 0.00819672 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +114 -0.5 0 +89 -0.350267 0.00819672 +kids 0 +OBJECT poly +name "tuxbeak" +loc -0.0328128 0.372259 0.086667 +texture "Penguin_beak.rgb" +numvert 17 +0.0565685 0.514 0.1 +1.11759e-08 0.5 0.125 +0.08 0.5 0.1 +1.11759e-08 0.528 0.1 +-0.0565685 0.514 0.1 +-0.08 0.5 0.1 +-0.0565685 0.486 0.1 +1.11759e-08 0.472 0.1 +0.0565685 0.486 0.1 +0.16 0.5 -0.125 +0.113137 0.556 -0.125 +1.11759e-08 0.612 -0.125 +-0.113137 0.556 -0.125 +-0.16 0.5 -0.125 +-0.113137 0.444 -0.125 +1.11759e-08 0.388 -0.125 +0.113137 0.444 -0.125 +numsurf 24 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +1 -0.5 -0.5 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +1 -0.5 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +1 -0.5 -0.5 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +1 -0.5 -0.5 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +1 -0.5 -0.5 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +1 -0.5 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +1 -0.5 -0.5 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +1 -0.5 -0.5 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +9 -0.0555556 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +10 -0.18573 -0.18573 +0 -0.342865 -0.342865 +9 -0.0555556 -0.5 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +10 -0.18573 -0.18573 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +11 -0.5 -0.0555556 +3 -0.5 -0.277778 +10 -0.18573 -0.18573 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +11 -0.5 -0.0555556 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +12 -0.81427 -0.18573 +4 -0.657135 -0.342865 +11 -0.5 -0.0555556 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +12 -0.81427 -0.18573 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +13 -0.944444 -0.5 +5 -0.722222 -0.5 +12 -0.81427 -0.18573 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +13 -0.944444 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +14 -0.81427 -0.81427 +6 -0.657135 -0.657135 +13 -0.944444 -0.5 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +14 -0.81427 -0.81427 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +15 -0.5 -0.944444 +7 -0.5 -0.722222 +14 -0.81427 -0.81427 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +15 -0.5 -0.944444 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +16 -0.18573 -0.81427 +8 -0.342865 -0.657135 +15 -0.5 -0.944444 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +16 -0.18573 -0.81427 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +9 -0.0555556 -0.5 +2 -0.277778 -0.5 +16 -0.18573 -0.81427 +kids 0 diff --git a/examples/src/ssg/majik/elevation_map.h b/examples/src/ssg/majik/elevation_map.h new file mode 100644 index 0000000..645e494 --- /dev/null +++ b/examples/src/ssg/majik/elevation_map.h @@ -0,0 +1,3859 @@ + +unsigned char elevation_map [] = +{ + 0, 0, 7, 22, 37, 46, 51, 53, 54, 63, 71, 75, 76, 73, 65, 55, 44, + 52, 55, 59, 66, 72, 73, 70, 61, 62, 57, 47, 33, 15, 1, 0, 0, 0, + 0, 2, 4, 9, 20, 31, 39, 56, 71, 81, 87, 87, 82, 73, 59, 71, 75, + 75, 73, 70, 67, 61, 48, 46, 43, 37, 31, 22, 9, 0, 0, 0, 3, 9, + 11, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 26, 43, 57, + 65, 70, 69, 65, 71, 75, 74, 67, 53, 29, 2, 0, 1, 23, 41, 51, 51, + 44, 32, 19, 23, 29, 31, 27, 22, 21, 16, 3, 15, 29, 40, 46, 46, 40, + 32, 27, 32, 28, 23, 20, 14, 5, 0, 0, 0, 15, 31, 40, 43, 38, 29, + 20, 22, 18, 11, 8, 5, 0, 0, 0, 0, 0, 9, 14, 14, 9, 4, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 25, 41, 55, 64, 68, 83, + 95,103,108,105, 97, 88, 78, 87, 93, 97,100,105,106,103, 96, 92, 81, + 69, 57, 41, 21, 0, 0, 3, 33, 58, 73, 79, 79, 74, 67, 73, 75, 73, + 63, 47, 25, 2, 0, 0, 0, 0, 4, 7, 8, 7, 3, 6, 11, 21, 26, + 23, 14, 0, 0, 2, 32, 57, 74, 84, 87, 87, 85, 92, 94, 91, 88, 87, + 84, 77, 66, 79, 92,101,105,103, 95, 84, 70, 71, 70, 64, 53, 36, 14, + 0, 0, 11, 27, 43, 55, 63, 67, 69, 70, 70, 79, 84, 87, 85, 81, 74, + 65, 57, 60, 66, 73, 76, 77, 74, 69, 71, 69, 62, 52, 37, 22, 8, 1, + 0, 0, 0, 3, 9, 19, 29, 44, 60, 74, 84, 89, 89, 85, 76, 66, 68, + 74, 77, 76, 74, 71, 65, 61, 59, 56, 50, 39, 25, 9, 0, 0, 2, 8, + 15, 18, 15, 9, 2, 0, 0, 0, 0, 0, 0, 1, 8, 18, 26, 32, 43, + 57, 66, 71, 71, 74, 79, 80, 78, 69, 52, 27, 0, 0, 4, 26, 44, 53, + 54, 48, 37, 25, 22, 28, 31, 28, 25, 19, 12, 16, 29, 43, 54, 59, 59, + 54, 45, 33, 23, 20, 16, 11, 4, 0, 0, 0, 8, 29, 46, 57, 60, 59, + 53, 45, 39, 37, 34, 31, 27, 20, 11, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 2, 1, 1, 2, 4, 7, 12, 23, 39, 52, 60, 69, + 84, 96,106,112,110,103, 94, 92, 95,100,106,113,118,119,114,105, 95, + 85, 74, 60, 41, 20, 0, 0, 5, 33, 57, 72, 79, 79, 75, 68, 73, 74, + 70, 59, 41, 19, 3, 0, 0, 0, 0, 0, 7, 13, 14, 11, 2, 0, 5, + 11, 9, 2, 0, 0, 12, 40, 65, 81, 90, 94, 94, 92, 96, 97, 95, 94, + 92, 88, 80, 73, 75, 84, 93, 96, 95, 87, 75, 60, 55, 52, 45, 31, 12, + 0, 0, 12, 23, 38, 52, 62, 70, 74, 76, 77, 77, 85, 92, 95, 96, 92, + 87, 80, 74, 70, 75, 79, 83, 83, 79, 74, 78, 78, 74, 68, 58, 47, 36, + 27, 20, 12, 9, 13, 18, 25, 36, 48, 64, 78, 88, 93, 94, 90, 82, 74, + 77, 79, 79, 79, 79, 77, 74, 72, 70, 67, 61, 53, 40, 27, 14, 16, 21, + 25, 26, 25, 21, 15, 9, 4, 2, 1, 2, 5, 11, 19, 29, 39, 47, 52, + 56, 60, 68, 74, 76, 81, 84, 85, 80, 70, 51, 23, 11, 8, 18, 28, 43, + 52, 55, 51, 43, 33, 25, 26, 29, 31, 27, 22, 21, 27, 38, 51, 61, 67, + 68, 65, 58, 48, 38, 28, 19, 11, 3, 0, 0, 4, 22, 43, 59, 69, 74, + 74, 72, 66, 62, 59, 57, 54, 48, 41, 32, 20, 6, 1, 3, 4, 2, 0, + 0, 0, 0, 1, 6, 13, 19, 25, 28, 29, 29, 31, 33, 34, 37, 45, 56, + 69, 83, 96,106,113,114,110,105,106,109,113,120,125,130,132,127,115, +103, 91, 79, 62, 41, 18, 8, 3, 11, 28, 52, 67, 75, 78, 75, 71, 68, + 69, 64, 54, 36, 20, 14, 7, 0, 0, 0, 0, 6, 15, 20, 20, 12, 0, + 0, 0, 0, 0, 0, 0, 20, 47, 69, 83, 92, 96, 97, 97, 99,101,102, +100, 96, 91, 82, 80, 81, 81, 84, 88, 85, 78, 66, 52, 34, 28, 20, 6, + 0, 0, 0, 21, 31, 43, 54, 63, 70, 74, 77, 78, 81, 89, 96,101,102, +100, 96, 89, 83, 78, 83, 86, 88, 88, 85, 79, 84, 85, 84, 80, 74, 66, + 57, 51, 44, 33, 20, 21, 25, 31, 40, 53, 68, 81, 91, 96, 98, 94, 87, + 81, 84, 84, 85, 84, 85, 85, 83, 80, 78, 75, 72, 65, 57, 47, 37, 36, + 39, 43, 45, 43, 39, 32, 23, 18, 13, 11, 12, 16, 23, 34, 46, 56, 63, + 67, 70, 72, 74, 78, 82, 87, 90, 88, 83, 70, 50, 33, 27, 25, 32, 40, + 47, 52, 53, 52, 46, 40, 33, 27, 31, 33, 32, 31, 31, 33, 43, 54, 63, + 71, 74, 73, 67, 59, 48, 37, 25, 14, 5, 0, 5, 19, 36, 54, 67, 77, + 83, 85, 84, 81, 79, 76, 74, 70, 64, 57, 48, 37, 25, 12, 2, 4, 4, + 3, 2, 2, 6, 14, 25, 34, 43, 50, 53, 53, 53, 51, 50, 50, 50, 51, + 55, 67, 80, 93,104,113,117,114,118,120,122,127,132,137,140,142,136, +124,110, 96, 81, 63, 40, 28, 25, 20, 21, 31, 41, 58, 68, 74, 74, 73, + 68, 63, 58, 47, 36, 29, 23, 18, 9, 0, 0, 0, 2, 15, 23, 26, 19, + 8, 0, 0, 0, 0, 0, 5, 28, 51, 69, 82, 90, 94, 97, 99,102,105, +107,105,100, 92, 88, 87, 87, 85, 81, 76, 74, 66, 55, 41, 27, 13, 0, + 0, 0, 0, 11, 26, 32, 40, 50, 58, 65, 70, 74, 77, 84, 91, 97,102, +103,102, 98, 92, 87, 84, 88, 92, 93, 93, 90, 84, 88, 91, 91, 88, 84, + 78, 72, 66, 59, 48, 33, 27, 29, 36, 44, 56, 71, 84, 93, 99,101, 97, + 91, 85, 88, 88, 88, 89, 92, 92, 91, 88, 85, 83, 79, 76, 71, 65, 59, + 53, 56, 59, 60, 58, 53, 46, 37, 28, 25, 23, 20, 23, 32, 43, 55, 65, + 73, 76, 78, 79, 80, 83, 86, 91, 93, 92, 84, 71, 52, 47, 40, 37, 41, + 47, 53, 56, 56, 54, 50, 44, 38, 32, 34, 38, 39, 39, 37, 38, 44, 53, + 62, 70, 74, 75, 73, 66, 55, 43, 29, 19, 15, 12, 21, 33, 47, 61, 73, + 81, 88, 91, 92, 91, 88, 86, 83, 79, 74, 67, 58, 48, 38, 27, 18, 9, + 3, 3, 5, 8, 15, 27, 40, 52, 60, 65, 68, 69, 67, 64, 61, 61, 66, + 68, 70, 72, 77, 89,101,110,115,122,128,130,133,136,139,142,145,145, +139,128,113, 97, 81, 61, 47, 45, 43, 39, 37, 37, 43, 50, 58, 66, 72, + 73, 71, 65, 57, 50, 41, 34, 28, 23, 16, 6, 0, 0, 0, 11, 22, 27, + 22, 13, 2, 0, 0, 0, 0, 12, 32, 51, 66, 77, 84, 89, 93, 98,103, +109,111,109,103, 94, 92, 92, 90, 87, 81, 73, 60, 51, 40, 29, 20, 8, + 0, 0, 1, 11, 20, 27, 31, 39, 48, 55, 62, 67, 73, 78, 84, 90, 95, + 99,100, 98, 95, 90, 85, 84, 90, 94, 96, 96, 93, 88, 92, 95, 96, 93, + 89, 84, 78, 73, 66, 55, 40, 31, 32, 38, 46, 58, 73, 85, 95,101,102, + 98, 92, 85, 89, 90, 90, 96,101,101, 98, 96, 95, 91, 87, 85, 83, 80, + 77, 74, 70, 72, 73, 70, 65, 57, 47, 44, 43, 41, 38, 33, 34, 46, 58, + 69, 76, 79, 82, 83, 84, 88, 90, 93, 96, 93, 85, 73, 61, 56, 47, 43, + 45, 48, 52, 55, 55, 54, 52, 47, 40, 34, 41, 46, 48, 46, 43, 40, 43, + 50, 59, 67, 73, 74, 74, 68, 58, 45, 37, 37, 34, 31, 36, 45, 55, 65, + 74, 82, 88, 92, 95, 95, 93, 91, 88, 83, 77, 70, 62, 53, 45, 37, 29, + 22, 16, 12, 8, 11, 21, 36, 50, 62, 70, 74, 77, 77, 75, 71, 73, 79, + 82, 82, 80, 79, 81, 87, 96,106,114,124,132,137,139,139,139,140,140, +140,136,127,113, 96, 79, 62, 60, 58, 57, 56, 56, 58, 59, 59, 57, 60, + 65, 70, 70, 66, 59, 51, 43, 34, 28, 25, 20, 12, 0, 0, 0, 2, 15, + 21, 20, 12, 3, 0, 0, 0, 3, 15, 32, 47, 59, 69, 76, 82, 89, 95, +102,110,113,113,106, 96, 92, 92, 90, 85, 78, 68, 55, 40, 26, 16, 11, + 4, 0, 7, 15, 21, 25, 27, 37, 46, 53, 59, 63, 67, 71, 74, 79, 85, + 89, 91, 92, 89, 86, 82, 77, 83, 88, 94, 96, 97, 95, 91, 93, 96, 97, + 95, 90, 84, 78, 73, 67, 56, 41, 31, 37, 48, 55, 60, 74, 86, 95,100, +101, 96, 91, 84, 88, 89, 96,105,110,110,107,106,108,105,100, 94, 92, + 92, 90, 88, 83, 83, 83, 80, 75, 67, 58, 58, 57, 56, 54, 50, 44, 44, + 56, 67, 75, 79, 82, 84, 88, 94, 96, 95, 97, 95, 88, 75, 66, 60, 50, + 43, 43, 44, 47, 50, 51, 51, 48, 45, 39, 40, 50, 56, 56, 53, 47, 46, + 51, 51, 54, 61, 67, 71, 71, 66, 57, 48, 53, 55, 53, 48, 46, 52, 59, + 66, 74, 79, 85, 90, 93, 95, 93, 91, 88, 83, 76, 68, 60, 52, 45, 39, + 34, 29, 26, 21, 16, 13, 22, 39, 55, 66, 74, 78, 79, 79, 78, 79, 88, + 93, 95, 92, 89, 85, 89, 90, 95,102,113,122,132,137,137,134,142,143, +134,130,128,120,108, 92, 75, 72, 70, 68, 68, 68, 71, 74, 76, 76, 74, + 68, 66, 70, 68, 63, 56, 48, 39, 31, 25, 21, 20, 14, 5, 0, 0, 0, + 4, 12, 12, 7, 2, 0, 0, 0, 4, 14, 28, 40, 50, 58, 67, 79, 88, + 92,100,109,113,113,108, 97, 90, 89, 86, 80, 71, 59, 46, 32, 19, 12, + 8, 6, 12, 21, 27, 28, 28, 28, 38, 46, 53, 57, 59, 61, 63, 67, 72, + 76, 79, 80, 79, 77, 74, 69, 70, 77, 84, 91, 95, 96, 95, 91, 94, 96, + 96, 93, 88, 80, 74, 68, 62, 52, 37, 37, 50, 58, 63, 64, 76, 87, 95, + 99, 99, 94, 88, 81, 84, 93,106,115,121,120,115,114,119,118,113,107, +100,101,100, 97, 92, 94, 93, 91, 85, 77, 67, 66, 65, 65, 65, 62, 58, + 53, 51, 63, 72, 77, 80, 85, 94,100,101, 96, 99, 96, 89, 79, 67, 59, + 47, 38, 36, 37, 38, 40, 43, 44, 43, 39, 37, 50, 60, 64, 64, 60, 53, + 50, 55, 56, 55, 56, 62, 66, 67, 63, 56, 63, 67, 69, 66, 61, 55, 56, + 60, 67, 75, 80, 82, 86, 89, 91, 90, 88, 84, 79, 72, 63, 54, 45, 39, + 36, 33, 31, 28, 23, 19, 15, 23, 40, 56, 68, 74, 78, 78, 79, 79, 91, +100,105,106,102, 96, 91, 96, 98, 99,101,109,119,127,132,133,152,167, +167,156,136,119,113,102, 88, 79, 78, 79, 81, 79, 77, 80, 84, 88, 88, + 84, 78, 69, 70, 69, 64, 55, 44, 38, 29, 19, 16, 16, 15, 9, 0, 0, + 0, 0, 1, 3, 2, 0, 0, 0, 0, 1, 9, 20, 29, 37, 50, 65, 77, + 84, 86, 95,105,113,113,108, 98, 86, 83, 79, 72, 61, 47, 33, 20, 9, + 4, 4, 7, 21, 31, 34, 34, 29, 25, 33, 40, 45, 47, 48, 48, 48, 52, + 57, 62, 65, 66, 65, 62, 58, 58, 62, 71, 79, 86, 91, 93, 92, 88, 94, + 96, 94, 89, 83, 74, 68, 60, 55, 47, 43, 46, 56, 63, 67, 68, 80, 90, + 96, 99, 97, 92, 84, 78, 88,102,115,127,133,133,127,120,125,127,123, +119,112,106,105,102,105,107,106,102, 96, 87, 78, 68, 67, 69, 70, 69, + 67, 64, 60, 59, 68, 74, 81, 90, 98,102,103, 98,101, 99, 92, 83, 70, + 55, 41, 29, 37, 38, 37, 33, 33, 34, 34, 33, 47, 60, 69, 74, 73, 67, + 59, 50, 55, 57, 57, 56, 58, 62, 63, 59, 67, 74, 77, 78, 75, 69, 62, + 57, 68, 79, 86, 92, 93, 92, 90, 87, 86, 84, 79, 74, 65, 56, 45, 36, + 29, 32, 34, 33, 29, 21, 14, 11, 25, 41, 56, 66, 73, 74, 75, 75, 88, +100,109,114,113,108,101, 94,100,103,105,106,107,114,123,128,150,173, +188,190,177,152,125,106, 96, 85, 86, 89, 92, 92, 90, 82, 85, 90, 93, + 94, 91, 84, 76, 66, 66, 61, 54, 51, 46, 38, 27, 12, 14, 14, 12, 5, + 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 3, 2, 9, 16, 29, 45, 59, + 69, 75, 77, 89,101,108,111,106, 97, 85, 73, 68, 59, 47, 33, 26, 16, + 7, 0, 0, 11, 25, 36, 40, 38, 33, 27, 22, 27, 29, 31, 31, 39, 46, + 50, 51, 51, 51, 51, 53, 56, 60, 64, 66, 65, 74, 82, 88, 90, 89, 85, + 90, 91, 89, 84, 77, 73, 70, 62, 55, 50, 44, 50, 59, 65, 69, 72, 83, + 92, 98,100, 97, 92, 84, 85, 94,109,124,137,143,143,136,128,130,133, +133,129,124,120,117,118,121,121,119,113,105, 96, 85, 76, 68, 70, 72, + 73, 73, 71, 68, 67, 70, 78, 86, 94,101,104,103, 99,102,100, 94, 84, + 72, 57, 40, 33, 40, 41, 40, 38, 36, 34, 34, 47, 60, 72, 79, 82, 80, + 74, 66, 56, 54, 56, 57, 58, 59, 60, 63, 69, 76, 81, 84, 84, 80, 74, + 68, 74, 77, 87, 95,101,103,103,101, 97, 93, 88, 82, 75, 68, 60, 53, + 48, 44, 37, 34, 34, 32, 25, 14, 9, 23, 39, 53, 62, 67, 69, 69, 80, + 92,105,114,120,119,113,105, 98, 98,102,105,108,111,115,122,134,158, +181,198,198,186,161,133,113, 97, 95, 98,102,104,103,100, 91, 85, 90, + 94, 96, 93, 88, 80, 72, 65, 60, 57, 53, 47, 40, 28, 14, 14, 15, 13, + 7, 6, 8, 7, 6, 7, 9, 11, 11, 11, 8, 5, 3, 4, 12, 22, 34, + 46, 55, 60, 71, 84, 95,102,105,102, 93, 82, 69, 57, 47, 39, 32, 25, + 18, 9, 0, 0, 11, 26, 37, 41, 41, 36, 28, 22, 21, 26, 32, 40, 48, + 54, 58, 59, 60, 60, 61, 61, 62, 64, 66, 66, 65, 70, 79, 84, 87, 87, + 84, 82, 83, 80, 76, 71, 68, 64, 57, 50, 43, 38, 48, 58, 65, 70, 73, + 83, 92, 97, 99, 97, 92, 87, 89, 97,113,128,140,149,147,140,132,137, +142,142,140,136,132,130,132,136,136,130,121,110, 99, 88, 79, 70, 72, + 74, 76, 76, 75, 74, 73, 75, 81, 89, 96,102,106,105,100,100, 98, 92, + 83, 71, 55, 39, 29, 36, 39, 39, 39, 38, 39, 48, 61, 73, 83, 88, 91, + 88, 81, 72, 66, 63, 63, 66, 67, 66, 69, 73, 79, 84, 88, 89, 88, 84, + 79, 81, 85, 88, 92,101,108,113,114,113,109,103, 96, 89, 82, 74, 67, + 61, 62, 59, 53, 44, 37, 36, 31, 21, 15, 16, 31, 44, 54, 58, 60, 67, + 83, 96,108,118,123,123,118,110,103,101,101,104,113,119,122,125,134, +158,179,194,194,183,163,137,118,103,106,110,113,115,113,109,100, 88, + 85, 90, 93, 92, 89, 83, 74, 67, 61, 55, 50, 43, 34, 25, 16, 15, 14, + 12, 7, 12, 16, 19, 21, 22, 22, 21, 19, 14, 9, 4, 0, 2, 4, 12, + 21, 29, 37, 51, 65, 78, 88, 95, 96, 93, 84, 73, 58, 46, 37, 29, 26, + 21, 16, 8, 7, 4, 9, 23, 34, 40, 40, 36, 27, 27, 31, 37, 43, 50, + 56, 61, 65, 67, 69, 69, 69, 67, 65, 64, 63, 63, 60, 67, 75, 80, 83, + 83, 80, 76, 73, 70, 66, 62, 58, 53, 45, 37, 31, 32, 43, 54, 63, 71, + 75, 81, 88, 95, 97, 97, 94, 89, 90, 97,113,128,140,147,145,139,136, +143,149,149,147,143,140,139,142,147,143,134,123,110, 97, 87, 78, 73, + 74, 79, 84, 85, 83, 77, 76, 78, 84, 90, 97,103,107,106,100, 96, 94, + 88, 79, 66, 50, 33, 20, 26, 29, 32, 33, 36, 45, 60, 72, 83, 91, 96, + 97, 94, 87, 77, 74, 71, 71, 76, 79, 80, 80, 80, 85, 90, 92, 92, 90, + 86, 84, 90, 93, 95, 96,104,113,120,123,123,119,112,102, 93, 85, 77, + 69, 70, 71, 70, 64, 56, 46, 40, 38, 32, 21, 21, 21, 32, 41, 46, 47, + 65, 81, 94,106,115,121,122,118,112,107,106,106,115,124,130,133,130, +136,150,169,181,183,173,158,137,121,111,114,119,122,123,121,115,106, + 94, 80, 84, 88, 89, 87, 82, 74, 65, 57, 50, 41, 34, 28, 22, 18, 14, + 11, 7, 5, 15, 23, 29, 33, 36, 34, 31, 23, 15, 7, 0, 0, 2, 5, + 5, 6, 14, 27, 43, 57, 70, 79, 85, 86, 82, 73, 59, 43, 29, 21, 18, + 15, 13, 13, 16, 15, 11, 8, 21, 31, 37, 37, 33, 26, 32, 38, 43, 48, + 53, 58, 63, 67, 70, 73, 74, 72, 69, 64, 59, 57, 55, 54, 63, 71, 75, + 78, 77, 74, 72, 67, 63, 57, 51, 44, 37, 32, 28, 22, 29, 38, 47, 59, + 69, 77, 81, 85, 91, 96, 97, 96, 93, 90, 96,112,125,137,143,142,134, +142,149,150,149,145,142,139,139,142,147,142,132,120,106, 93, 83, 76, + 74, 81, 88, 92, 93, 90, 82, 77, 78, 84, 90, 97,103,107,106,100, 92, + 90, 84, 74, 59, 44, 31, 15, 15, 16, 20, 23, 32, 52, 67, 79, 88, 97, +102,102, 98, 90, 79, 78, 76, 78, 84, 89, 92, 92, 91, 90, 92, 93, 92, + 90, 87, 91, 96, 98, 97, 98,104,113,122,128,129,124,115,104, 94, 84, + 75, 69, 72, 74, 74, 70, 62, 53, 45, 46, 41, 31, 27, 27, 22, 27, 33, + 36, 57, 76, 89,101,109,114,115,113,109,107,108,115,127,137,143,145, +142,140,147,156,165,165,159,149,134,122,115,120,123,127,127,123,117, +108, 96, 82, 77, 81, 83, 81, 77, 69, 59, 48, 41, 40, 37, 31, 23, 16, + 9, 5, 0, 5, 16, 26, 34, 41, 44, 41, 36, 26, 14, 3, 0, 0, 2, + 4, 5, 3, 4, 16, 31, 44, 57, 66, 72, 72, 66, 55, 39, 21, 14, 4, + 2, 2, 9, 16, 20, 19, 15, 9, 19, 26, 29, 29, 27, 23, 33, 39, 45, + 48, 52, 55, 58, 62, 67, 71, 73, 71, 66, 59, 51, 45, 41, 51, 59, 64, + 68, 69, 67, 65, 63, 58, 52, 45, 36, 29, 26, 21, 18, 20, 29, 39, 47, + 55, 66, 77, 84, 88, 89, 93, 96, 98, 96, 93, 96,111,124,134,140,137, +133,143,147,147,143,137,133,129,129,133,136,132,123,112,100, 88, 80, + 79, 84, 91, 96,100,100, 95, 87, 76, 77, 82, 88, 96,102,105,104, 98, + 89, 85, 79, 68, 52, 38, 26, 12, 11, 9, 6, 13, 34, 55, 70, 82, 91, + 99,104,105,100, 91, 80, 79, 77, 84, 92, 96, 99, 99, 97, 95, 92, 90, + 88, 87, 90, 96,100, 99, 97, 96,102,111,120,127,129,124,114,102, 91, + 80, 71, 65, 69, 73, 74, 70, 64, 56, 50, 52, 48, 40, 32, 33, 31, 22, + 21, 23, 44, 65, 79, 91, 98,103,105,104,102,103,111,123,136,149,156, +156,150,143,154,161,163,161,152,140,130,122,117,121,124,124,123,119, +112,103, 93, 80, 69, 73, 74, 74, 69, 60, 48, 38, 40, 40, 37, 31, 21, + 12, 3, 0, 0, 3, 14, 26, 36, 44, 47, 45, 38, 26, 12, 0, 0, 0, + 3, 4, 5, 3, 3, 3, 14, 26, 38, 48, 53, 53, 46, 33, 16, 6, 0, + 0, 0, 2, 9, 16, 20, 19, 14, 12, 18, 21, 23, 22, 20, 19, 29, 37, + 40, 43, 44, 45, 47, 52, 59, 64, 67, 65, 59, 50, 38, 29, 36, 44, 51, + 55, 56, 55, 53, 51, 50, 45, 38, 29, 19, 14, 12, 7, 7, 16, 27, 38, + 48, 55, 61, 75, 84, 89, 92, 92, 95, 98, 99, 96, 96,111,123,133,137, +136,133,140,142,139,132,125,119,115,114,118,121,119,113,103, 92, 82, + 84, 89, 93, 99,103,106,104, 98, 89, 76, 77, 83, 86, 93, 99,102,102, + 95, 85, 82, 75, 63, 45, 29, 18, 5, 3, 3, 6, 19, 36, 55, 70, 81, + 90, 99,104,104, 99, 90, 79, 77, 77, 88, 96,102,103,102, 98, 95, 91, + 84, 83, 88, 95,100,101, 97, 96, 94, 98,107,114,121,124,120,110, 97, + 85, 80, 72, 58, 60, 65, 67, 66, 62, 55, 51, 54, 53, 46, 37, 39, 39, + 33, 22, 15, 26, 50, 66, 78, 86, 90, 92, 93, 93,100,112,125,140,152, +159,161,156,143,156,169,175,177,169,154,136,123,118,119,120,119,114, +109,102, 96, 87, 75, 62, 63, 64, 62, 56, 46, 33, 36, 39, 39, 34, 26, + 14, 4, 0, 0, 0, 4, 12, 22, 34, 44, 48, 46, 38, 25, 9, 0, 0, + 0, 2, 3, 6, 5, 0, 0, 0, 6, 15, 25, 29, 29, 22, 14, 4, 2, + 2, 0, 0, 1, 8, 14, 18, 15, 12, 14, 18, 19, 18, 14, 12, 11, 22, + 29, 32, 32, 32, 33, 32, 37, 45, 52, 56, 55, 47, 36, 22, 15, 25, 33, + 38, 40, 39, 36, 32, 31, 29, 26, 20, 13, 4, 0, 0, 0, 0, 9, 22, + 36, 47, 55, 60, 71, 82, 89, 92, 93, 93, 96, 97, 95, 98,113,125,136, +139,136,129,133,133,127,120,112,106,102,101,103,107,106,102, 95, 87, + 85, 92, 96, 98,103,107,108,106, 99, 89, 77, 82, 88, 91, 91, 96, 99, + 98, 92, 84, 80, 73, 59, 40, 19, 7, 0, 0, 1, 18, 33, 46, 54, 67, + 79, 88, 96,102,102, 97, 89, 79, 73, 80, 91, 99,102,103,100, 96, 92, + 87, 80, 85, 93, 98,100, 98, 94, 98,103,108,109,111,115,118,113,103, + 98, 96, 92, 83, 70, 55, 54, 57, 58, 56, 52, 48, 52, 52, 46, 41, 46, + 47, 44, 36, 23, 12, 29, 48, 62, 72, 77, 79, 81, 87, 97,110,123,137, +149,156,158,152,142,154,169,181,186,181,167,145,125,121,114,113,111, +105, 98, 92, 85, 78, 66, 57, 56, 54, 50, 43, 31, 26, 32, 36, 34, 27, + 16, 4, 0, 0, 0, 12, 25, 33, 36, 33, 43, 47, 45, 38, 25, 11, 2, + 0, 0, 0, 2, 6, 6, 2, 0, 0, 0, 0, 7, 16, 21, 21, 15, 6, + 3, 2, 4, 3, 9, 15, 16, 14, 12, 14, 19, 20, 18, 19, 18, 11, 2, + 13, 19, 21, 19, 23, 27, 26, 18, 26, 36, 40, 39, 32, 20, 9, 0, 7, + 14, 19, 19, 16, 12, 8, 7, 7, 6, 3, 0, 0, 0, 0, 0, 0, 0, + 15, 31, 44, 54, 60, 65, 78, 86, 91, 93, 92, 92, 93, 90,104,119,133, +143,145,142,132,123,121,115,110,107,104,104,100, 92, 95, 96, 94, 94, + 94, 93, 94, 96, 98,102,106,108,105, 99, 89, 77, 86, 92, 95, 95, 95, + 96, 95, 87, 86, 81, 73, 59, 40, 18, 0, 0, 0, 5, 23, 43, 57, 67, + 73, 76, 85, 94,100,101, 97, 89, 79, 67, 80, 91, 97,101, 99, 95, 90, + 87, 88, 91, 94, 96, 98, 98, 96, 90,102,111,118,120,119,115,114,110, +110,111,108,102, 92, 78, 60, 39, 45, 47, 46, 45, 41, 44, 44, 39, 45, + 52, 54, 53, 46, 34, 22, 12, 32, 46, 57, 63, 66, 74, 82, 92,104,117, +128,137,143,143,139,130,145,163,177,186,183,171,152,129,127,119,110, +102, 96, 88, 82, 74, 74, 71, 69, 66, 60, 52, 38, 20, 22, 27, 28, 25, + 16, 3, 0, 0, 0, 13, 31, 44, 52, 53, 50, 43, 47, 46, 40, 29, 15, + 2, 0, 0, 0, 2, 7, 7, 4, 0, 0, 0, 0, 5, 16, 23, 25, 21, + 13, 2, 13, 16, 18, 22, 25, 23, 18, 11, 20, 23, 23, 20, 22, 22, 15, + 5, 1, 5, 5, 11, 15, 18, 16, 8, 5, 13, 19, 25, 27, 23, 15, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 3, 11, 26, 43, 55, 63, 69, 73, 80, 87, 91, 93, 93, 93, 94,109,124, +139,150,154,150,140,129,121,117,113,113,114,113,109,100, 92, 94, 96, + 98, 99,100,102,103,103,100,103,106,105,100, 92, 82, 86, 92, 96, 97, + 96, 95, 92, 91, 89, 84, 76, 63, 45, 22, 4, 0, 0, 7, 27, 48, 65, + 76, 83, 86, 85, 90, 96, 98, 96, 88, 78, 72, 79, 89, 95, 96, 93, 88, + 93, 96, 97,101,103,104,103,102, 99, 99,106,117,125,130,132,130,127, +124,123,121,117,108, 96, 83, 66, 46, 32, 33, 34, 36, 34, 34, 36, 39, + 46, 53, 56, 55, 50, 39, 27, 18, 22, 34, 45, 54, 62, 74, 88, 98,106, +113,121,129,136,142,143,143,158,161,163,175,177,169,152,134,132,123, +113,102, 92, 85, 84, 82, 80, 79, 76, 72, 65, 55, 40, 31, 29, 27, 22, + 13, 1, 0, 0, 0, 15, 33, 50, 61, 67, 67, 64, 58, 50, 48, 44, 34, + 23, 11, 1, 0, 0, 3, 11, 15, 16, 13, 8, 4, 0, 5, 18, 27, 32, + 31, 26, 27, 31, 36, 39, 40, 39, 34, 26, 14, 22, 27, 26, 22, 26, 25, + 19, 11, 0, 0, 0, 5, 8, 11, 8, 1, 0, 0, 7, 20, 27, 27, 22, + 13, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 12, + 14, 9, 18, 26, 43, 58, 68, 75, 79, 82, 85, 88, 92, 94, 95, 95,107, +121,136,149,156,154,145,136,127,121,119,122,123,121,114,104, 95, 94, + 96, 99,101,102,103,106,107,102,101,104,105,102, 95, 88, 88, 89, 94, + 97, 98, 98, 97, 96, 93, 89, 81, 69, 52, 29, 19, 12, 7, 14, 29, 54, + 72, 84, 92, 96, 94, 88, 90, 92, 91, 84, 75, 75, 80, 88, 92, 92, 88, + 95,102,107,108,110,111,111,110,108,107,108,110,120,132,140,147,147, +145,139,134,128,120,110, 97, 84, 68, 51, 33, 20, 21, 23, 27, 31, 31, + 34, 41, 47, 51, 50, 45, 37, 26, 19, 27, 37, 46, 55, 61, 77, 91,102, +111,120,129,140,149,156,159,158,173,173,167,161,167,163,150,134,132, +125,115,104, 93, 86, 85, 84, 83, 81, 79, 74, 66, 55, 43, 41, 39, 36, + 26, 14, 0, 0, 9, 25, 39, 54, 67, 75, 80, 81, 78, 74, 66, 56, 44, + 38, 31, 21, 13, 6, 5, 11, 19, 23, 26, 23, 21, 20, 18, 14, 27, 38, + 46, 51, 54, 56, 58, 60, 61, 60, 56, 50, 38, 23, 20, 25, 26, 23, 27, + 27, 22, 14, 4, 0, 0, 0, 2, 2, 1, 0, 0, 0, 7, 21, 32, 37, + 36, 29, 23, 19, 15, 13, 12, 11, 8, 5, 2, 0, 1, 5, 8, 12, 15, + 20, 21, 18, 23, 31, 43, 59, 72, 80, 85, 88, 89, 90, 91, 94, 96, 95, +100,112,124,136,145,149,145,137,129,123,125,127,127,122,114,104, 95, + 92, 94, 96, 97, 99,101,104,105,102, 97,102,104,103, 98, 92, 92, 92, + 92, 94, 97, 99, 99, 98, 96, 92, 84, 74, 57, 37, 28, 20, 12, 16, 31, + 57, 76, 88, 97,101,100, 93, 86, 87, 85, 80, 73, 75, 81, 88, 89, 88, + 90,102,111,115,115,115,114,113,113,112,113,115,119,122,134,149,158, +161,159,150,140,128,117,104, 92, 79, 65, 51, 36, 21, 22, 23, 27, 28, + 29, 28, 33, 38, 40, 40, 36, 29, 21, 23, 31, 40, 50, 57, 63, 77, 91, +102,113,124,136,149,159,167,171,169,179,179,171,158,158,156,145,129, +128,123,115,105, 95, 86, 82, 81, 80, 79, 77, 73, 64, 52, 48, 47, 46, + 40, 29, 14, 0, 13, 33, 47, 59, 70, 79, 86, 90, 91, 89, 86, 79, 71, + 57, 40, 34, 28, 21, 21, 20, 19, 23, 28, 31, 32, 32, 36, 36, 31, 41, + 54, 64, 72, 75, 78, 79, 79, 78, 76, 71, 63, 52, 36, 19, 22, 25, 23, + 26, 26, 23, 16, 8, 5, 3, 5, 9, 13, 16, 18, 16, 11, 14, 29, 43, + 50, 53, 50, 46, 43, 40, 37, 34, 29, 26, 20, 14, 13, 14, 15, 16, 18, + 19, 22, 26, 23, 27, 32, 40, 59, 74, 83, 88, 92, 93, 92, 92, 94, 95, + 94, 93,101,111,121,129,134,136,130,124,123,125,125,123,118,110,100, + 91, 88, 89, 91, 92, 92, 95, 98,101,100, 95, 98,101,102,100, 93, 93, + 93, 92, 89, 93, 96, 98, 97, 96, 92, 86, 76, 60, 41, 32, 21, 11, 14, + 33, 59, 78, 89, 97,102,100, 94, 87, 81, 80, 76, 71, 73, 81, 87, 88, + 85, 92,104,113,119,118,115,113,111,110,111,113,121,127,128,134,149, +159,165,163,150,134,120,107, 94, 83, 71, 58, 46, 34, 21, 22, 25, 26, + 28, 28, 28, 27, 27, 28, 28, 25, 21, 25, 28, 34, 43, 52, 59, 66, 74, + 88,100,113,125,139,152,163,171,173,171,175,175,165,152,147,149,140, +125,123,120,113,105, 96, 87, 79, 75, 75, 75, 74, 69, 61, 48, 48, 50, + 47, 40, 29, 14, 11, 32, 52, 64, 72, 79, 87, 92, 96, 97, 97, 95, 89, + 81, 69, 51, 36, 31, 29, 31, 29, 27, 25, 29, 33, 37, 40, 47, 52, 51, + 55, 68, 79, 86, 91, 92, 92, 92, 91, 88, 82, 74, 63, 47, 27, 19, 25, + 25, 21, 22, 22, 15, 11, 9, 15, 19, 23, 26, 28, 31, 32, 29, 23, 36, + 48, 57, 62, 62, 60, 59, 58, 56, 52, 47, 40, 32, 29, 29, 29, 26, 23, + 23, 26, 27, 27, 27, 26, 28, 37, 56, 72, 83, 89, 93, 94, 94, 93, 93, + 94, 92, 91, 91, 96,103,112,118,120,118,115,117,119,119,115,110,102, + 92, 84, 84, 84, 84, 84, 84, 86, 90, 94, 95, 93, 92, 96, 98, 98, 94, + 92, 92, 90, 88, 87, 91, 92, 92, 92, 89, 84, 75, 60, 40, 28, 16, 5, + 7, 34, 59, 75, 86, 93, 96, 96, 90, 82, 78, 77, 72, 65, 69, 79, 85, + 86, 84, 92,102,111,114,113,109,105,102,102,105,112,122,130,134,132, +143,152,158,154,139,122,107, 92, 80, 68, 56, 45, 37, 27, 20, 21, 23, + 25, 26, 27, 33, 37, 38, 36, 31, 27, 27, 28, 29, 34, 43, 53, 61, 68, + 74, 82, 96,109,122,136,149,159,165,165,163,158,159,150,139,139,140, +133,120,117,114,111,103, 95, 86, 78, 73, 69, 69, 68, 64, 57, 45, 43, + 45, 44, 38, 27, 14, 25, 46, 63, 73, 78, 84, 88, 93, 97,101,102,100, + 96, 88, 77, 61, 38, 29, 32, 34, 33, 29, 23, 27, 32, 38, 46, 56, 64, + 66, 67, 79, 88, 96,102,103,103,102,100, 96, 90, 82, 72, 56, 36, 18, + 25, 27, 25, 18, 18, 11, 12, 19, 25, 29, 32, 33, 34, 39, 41, 41, 40, + 40, 52, 60, 65, 67, 67, 69, 69, 68, 65, 60, 52, 44, 46, 46, 41, 36, + 29, 32, 33, 33, 28, 29, 29, 26, 31, 50, 67, 79, 87, 91, 93, 93, 92, + 93, 93, 94, 95, 94, 91, 92, 96,102,104,105,105,106,107,107,104, 99, + 92, 82, 78, 78, 77, 75, 76, 80, 82, 81, 87, 89, 90, 88, 89, 92, 94, + 92, 87, 87, 87, 84, 81, 82, 84, 84, 84, 84, 79, 71, 57, 37, 20, 7, + 0, 9, 36, 56, 71, 79, 84, 87, 86, 81, 74, 76, 75, 69, 59, 66, 78, + 84, 85, 84, 88, 96,102,105,102, 97, 93, 97,101,103,105,118,129,134, +132,133,139,140,134,122,106, 91, 78, 69, 55, 37, 27, 21, 15, 15, 18, + 20, 21, 27, 34, 40, 46, 48, 47, 43, 36, 28, 27, 27, 31, 40, 51, 61, + 70, 76, 80, 90,103,117,129,139,147,149,149,145,137,137,133,127,133, +133,127,113,110,110,106,100, 92, 85, 76, 67, 61, 61, 62, 59, 53, 41, + 32, 36, 36, 31, 22, 23, 34, 53, 67, 75, 79, 83, 87, 92, 96, 99,102, +101, 98, 92, 83, 69, 48, 23, 27, 31, 31, 26, 19, 22, 29, 39, 50, 63, + 74, 79, 80, 86, 95,102,109,110,109,107,104,101, 95, 88, 77, 62, 40, + 26, 26, 29, 28, 21, 11, 5, 16, 25, 31, 34, 36, 36, 37, 40, 45, 48, + 51, 51, 52, 59, 64, 67, 69, 73, 75, 76, 74, 70, 62, 57, 59, 57, 52, + 44, 34, 36, 36, 33, 28, 32, 33, 32, 41, 48, 60, 74, 82, 87, 88, 89, + 91, 93, 92, 93, 96, 96, 95, 92, 88, 91, 93, 92, 92, 93, 94, 94, 92, + 88, 80, 71, 73, 74, 74, 71, 72, 74, 75, 75, 79, 84, 87, 88, 85, 84, + 87, 87, 84, 80, 81, 81, 79, 74, 74, 74, 74, 75, 73, 65, 53, 32, 11, + 0, 0, 12, 34, 52, 63, 69, 73, 74, 74, 71, 75, 77, 74, 67, 57, 63, + 74, 81, 84, 83, 81, 88, 92, 92, 89, 90, 97,102,105,106,105,111,123, +129,129,123,123,122,117,105, 90, 85, 79, 70, 55, 34, 12, 5, 5, 8, + 12, 15, 25, 32, 39, 45, 51, 54, 54, 50, 40, 28, 21, 19, 22, 33, 45, + 58, 68, 75, 80, 84, 98,111,121,129,132,132,130,127,122,119,117,123, +127,125,118,106,103,103,101, 95, 91, 84, 74, 63, 51, 55, 56, 54, 48, + 37, 23, 21, 22, 20, 26, 32, 39, 56, 68, 74, 77, 80, 88, 94, 96, 96, + 98, 98, 96, 92, 85, 74, 56, 33, 20, 23, 23, 19, 12, 18, 37, 53, 63, + 69, 82, 89, 92, 94,100,107,112,113,111,109,106,102, 96, 89, 79, 65, + 45, 38, 28, 33, 31, 23, 12, 4, 16, 27, 33, 36, 36, 36, 36, 39, 45, + 51, 55, 57, 59, 61, 62, 63, 67, 73, 78, 81, 81, 79, 73, 64, 65, 63, + 57, 50, 40, 33, 31, 26, 31, 36, 38, 43, 53, 60, 64, 67, 76, 80, 84, + 89, 93, 94, 93, 89, 93, 95, 96, 93, 90, 86, 82, 80, 79, 80, 82, 82, + 80, 77, 72, 65, 70, 72, 72, 69, 68, 72, 72, 69, 74, 79, 84, 86, 86, + 84, 81, 79, 79, 78, 76, 75, 75, 74, 69, 62, 64, 66, 65, 60, 48, 29, + 7, 0, 0, 12, 31, 45, 54, 57, 59, 61, 67, 73, 76, 77, 73, 64, 60, + 59, 72, 78, 82, 82, 81, 79, 80, 79, 85, 92, 98,102,104,103,102,102, +113,120,121,118,111,107,101, 90, 86, 83, 76, 66, 51, 28, 7, 0, 0, + 1, 8, 19, 27, 33, 39, 45, 51, 54, 53, 48, 38, 25, 9, 7, 11, 21, + 36, 50, 62, 71, 77, 81, 95,106,114,120,121,120,119,113,110,107,113, +119,120,117,109, 98, 98, 97, 94, 91, 86, 78, 67, 56, 54, 53, 51, 48, + 43, 32, 18, 5, 8, 16, 31, 40, 50, 56, 66, 72, 82, 92,102,108,110, +108,102, 95, 93, 91, 85, 75, 60, 40, 28, 16, 16, 13, 9, 28, 48, 63, + 73, 77, 90, 98,104,107,107,109,113,113,117,115,111,103, 96, 90, 79, + 65, 57, 50, 40, 34, 32, 25, 12, 9, 14, 25, 31, 34, 34, 43, 48, 52, + 51, 50, 56, 62, 66, 70, 72, 74, 74, 72, 78, 84, 87, 87, 83, 76, 67, + 62, 58, 52, 45, 37, 32, 32, 36, 39, 41, 54, 61, 66, 69, 71, 75, 80, + 86, 90, 92, 94, 92, 88, 88, 92, 93, 92, 91, 88, 84, 82, 79, 71, 68, + 70, 73, 73, 69, 62, 62, 65, 66, 66, 65, 66, 67, 72, 77, 81, 83, 84, + 85, 84, 84, 84, 81, 76, 76, 75, 73, 73, 71, 66, 56, 55, 57, 54, 45, + 29, 9, 0, 0, 5, 21, 33, 40, 44, 56, 64, 69, 74, 75, 74, 68, 58, + 59, 58, 69, 76, 79, 80, 79, 79, 78, 79, 84, 89, 94, 96, 96, 95, 93, + 92,103,110,112,109,103, 97, 90, 85, 81, 77, 69, 57, 39, 18, 6, 0, + 0, 1, 12, 21, 28, 41, 52, 56, 56, 53, 50, 44, 34, 22, 12, 7, 8, + 9, 25, 40, 54, 65, 73, 77, 90,100,107,111,113,120,123,118,110,105, +110,112,112,106, 98, 96, 95, 92, 87, 84, 78, 68, 59, 60, 58, 56, 52, + 43, 32, 20, 7, 0, 3, 20, 34, 47, 58, 67, 74, 83, 92,102,111,117, +119,114,108,101, 91, 88, 84, 76, 63, 45, 36, 25, 19, 16, 18, 38, 55, + 68, 76, 86, 99,109,114,119,119,119,120,123,128,127,121,112,102, 92, + 83, 74, 67, 59, 50, 34, 29, 22, 11, 13, 19, 25, 32, 39, 47, 53, 57, + 57, 55, 51, 58, 65, 72, 76, 79, 83, 84, 80, 76, 84, 90, 93, 92, 87, + 78, 68, 57, 50, 45, 40, 37, 37, 39, 40, 51, 59, 65, 68, 69, 71, 74, + 79, 84, 88, 89, 89, 88, 84, 84, 88, 90, 91, 90, 88, 85, 83, 79, 70, + 57, 58, 65, 68, 67, 62, 56, 55, 56, 58, 60, 63, 67, 73, 79, 82, 83, + 82, 80, 81, 82, 84, 83, 79, 76, 78, 78, 76, 74, 71, 62, 50, 45, 45, + 40, 29, 13, 0, 0, 0, 8, 19, 32, 48, 61, 67, 71, 72, 72, 67, 59, + 52, 53, 59, 69, 74, 77, 77, 76, 75, 74, 76, 79, 84, 86, 87, 85, 82, + 79, 84, 94, 99,101, 98, 94, 89, 84, 79, 74, 68, 59, 44, 23, 11, 0, + 0, 0, 7, 15, 26, 40, 52, 60, 62, 61, 57, 52, 44, 36, 25, 13, 3, + 3, 3, 18, 34, 50, 61, 69, 74, 82, 92, 97,107,115,123,125,120,111, +104,104,104,102, 96, 95, 95, 92, 88, 81, 75, 67, 57, 62, 62, 59, 55, + 48, 37, 22, 4, 0, 0, 5, 22, 38, 53, 64, 74, 83, 91,100,110,118, +122,122,117,110,102, 92, 88, 83, 75, 64, 47, 41, 33, 28, 25, 29, 47, + 61, 71, 80, 95,109,119,125,128,128,127,127,130,134,133,127,117,106, + 96, 87, 79, 72, 64, 54, 38, 23, 16, 8, 14, 23, 34, 43, 51, 56, 59, + 60, 58, 54, 51, 59, 67, 74, 79, 83, 87, 88, 85, 80, 81, 91, 96, 97, + 93, 85, 75, 63, 47, 41, 38, 37, 37, 38, 46, 56, 61, 64, 65, 65, 67, + 71, 75, 79, 82, 83, 82, 79, 75, 80, 84, 87, 87, 87, 85, 83, 81, 76, + 66, 53, 45, 55, 61, 63, 60, 55, 54, 55, 56, 58, 58, 65, 72, 78, 81, + 81, 79, 74, 74, 76, 80, 82, 79, 75, 79, 81, 81, 78, 73, 66, 56, 43, + 36, 34, 28, 16, 1, 0, 0, 6, 23, 40, 55, 64, 69, 69, 68, 64, 58, + 47, 43, 50, 60, 67, 71, 72, 71, 69, 68, 68, 69, 72, 74, 74, 73, 69, + 64, 61, 75, 84, 88, 88, 86, 83, 79, 74, 70, 64, 57, 44, 27, 9, 2, + 0, 0, 6, 13, 25, 38, 51, 59, 64, 65, 62, 57, 52, 45, 34, 22, 9, + 0, 0, 0, 14, 29, 45, 58, 67, 74, 80, 88, 98,109,118,124,125,119, +108,100, 98, 96, 92, 94, 94, 93, 90, 83, 74, 65, 53, 56, 61, 60, 55, + 47, 38, 25, 9, 0, 0, 0, 9, 25, 39, 54, 66, 77, 87, 96,105,113, +120,122,120,113,106, 99, 90, 86, 81, 74, 63, 48, 43, 38, 33, 29, 41, + 56, 66, 74, 87,102,117,127,132,132,130,128,128,132,136,133,127,118, +107, 97, 88, 80, 74, 66, 55, 39, 20, 8, 5, 12, 26, 39, 51, 58, 61, + 61, 59, 55, 48, 51, 59, 66, 72, 76, 80, 85, 87, 85, 81, 77, 88, 96, + 98, 96, 88, 79, 66, 50, 33, 31, 31, 31, 37, 48, 56, 59, 60, 59, 58, + 60, 64, 69, 73, 74, 74, 72, 68, 70, 75, 79, 80, 80, 79, 78, 76, 75, + 71, 60, 46, 37, 43, 52, 56, 54, 51, 50, 52, 54, 55, 54, 59, 69, 75, + 78, 78, 74, 69, 63, 68, 74, 78, 77, 74, 78, 81, 82, 80, 74, 66, 58, + 46, 33, 28, 25, 18, 5, 0, 0, 15, 33, 48, 59, 65, 67, 66, 62, 55, + 46, 34, 39, 51, 59, 64, 64, 63, 60, 57, 55, 55, 57, 59, 59, 57, 53, + 46, 40, 48, 62, 70, 73, 73, 70, 67, 63, 60, 56, 51, 41, 27, 8, 0, + 1, 2, 11, 14, 22, 36, 48, 57, 63, 65, 64, 60, 57, 55, 48, 37, 22, + 5, 0, 0, 2, 19, 34, 46, 54, 64, 71, 79, 88, 99,109,118,122,121, +113,102, 92, 92, 89, 92, 92, 92, 91, 86, 76, 62, 51, 44, 52, 56, 53, + 45, 34, 21, 8, 0, 0, 1, 7, 12, 25, 38, 51, 63, 75, 86, 96,104, +112,117,117,113,108,101, 95, 89, 84, 78, 69, 58, 44, 38, 36, 33, 34, + 51, 62, 71, 76, 92,107,121,129,132,130,127,123,122,125,129,128,123, +114,105, 96, 90, 80, 74, 65, 54, 38, 19, 1, 0, 5, 23, 41, 54, 61, + 62, 60, 54, 47, 41, 50, 56, 61, 65, 68, 72, 77, 80, 80, 78, 74, 83, + 91, 94, 92, 86, 78, 66, 50, 28, 20, 19, 20, 33, 44, 51, 53, 53, 51, + 54, 58, 57, 61, 64, 65, 63, 59, 56, 62, 67, 70, 71, 70, 68, 66, 64, + 63, 59, 50, 37, 27, 27, 38, 44, 43, 40, 41, 45, 48, 51, 51, 51, 62, + 70, 73, 72, 67, 61, 53, 56, 64, 72, 74, 71, 74, 77, 79, 78, 73, 62, + 55, 46, 36, 22, 20, 15, 5, 0, 5, 25, 41, 54, 61, 64, 64, 60, 54, + 45, 44, 41, 41, 50, 55, 57, 55, 50, 44, 39, 37, 37, 38, 40, 38, 34, + 27, 20, 16, 32, 45, 52, 53, 51, 52, 48, 43, 41, 39, 34, 23, 8, 0, + 0, 2, 12, 19, 20, 29, 43, 55, 61, 65, 64, 61, 59, 60, 58, 51, 39, + 23, 6, 0, 0, 8, 26, 40, 51, 57, 59, 67, 76, 87, 96,106,113,115, +113,104, 93, 86, 85, 89, 91, 91, 88, 86, 80, 68, 51, 34, 37, 43, 44, + 39, 28, 15, 2, 0, 5, 13, 20, 25, 28, 28, 33, 44, 57, 70, 81, 92, +100,106,110,110,106,101, 98, 95, 88, 81, 73, 62, 50, 36, 26, 26, 26, + 38, 55, 67, 74, 79, 92,108,120,125,125,123,119,114,113,117,120,120, +115,109,101, 97, 92, 83, 73, 64, 52, 36, 18, 0, 0, 0, 19, 39, 53, + 60, 61, 56, 47, 37, 40, 45, 52, 56, 57, 55, 56, 61, 67, 71, 72, 70, + 74, 82, 85, 84, 79, 73, 62, 47, 28, 7, 5, 8, 23, 34, 40, 43, 41, + 43, 51, 53, 50, 51, 54, 54, 51, 46, 43, 50, 55, 56, 56, 54, 51, 48, + 46, 46, 44, 36, 23, 15, 11, 19, 25, 25, 23, 28, 34, 40, 44, 45, 44, + 53, 61, 65, 64, 59, 53, 48, 48, 54, 64, 69, 70, 66, 69, 71, 71, 65, + 56, 46, 41, 33, 22, 13, 8, 1, 1, 11, 32, 48, 58, 62, 62, 59, 54, + 51, 54, 53, 48, 40, 44, 46, 46, 39, 31, 29, 25, 15, 13, 15, 16, 14, + 9, 3, 0, 0, 12, 22, 27, 27, 37, 41, 39, 31, 20, 20, 16, 6, 2, + 0, 0, 8, 20, 23, 21, 34, 48, 58, 64, 64, 61, 56, 61, 62, 59, 52, + 39, 22, 5, 0, 0, 14, 29, 43, 52, 57, 60, 61, 72, 83, 92,101,106, +107,102, 94, 83, 82, 87, 89, 89, 87, 84, 79, 73, 59, 39, 21, 25, 28, + 27, 20, 7, 1, 3, 14, 25, 32, 36, 39, 41, 40, 47, 50, 55, 60, 74, + 84, 92, 98,102,102, 99, 96, 98, 95, 87, 77, 65, 52, 38, 22, 9, 12, + 16, 38, 56, 69, 77, 81, 90,104,113,118,117,113,109,109,107,106,110, +111,108,103, 97, 96, 93, 85, 74, 63, 51, 33, 15, 0, 0, 0, 11, 33, + 50, 57, 57, 51, 39, 28, 34, 46, 53, 55, 54, 48, 40, 40, 48, 56, 61, + 64, 64, 70, 74, 73, 69, 63, 56, 44, 27, 8, 0, 0, 8, 20, 26, 29, + 29, 36, 41, 41, 36, 40, 44, 43, 39, 32, 25, 32, 37, 37, 36, 32, 28, + 26, 23, 25, 25, 21, 14, 4, 0, 0, 2, 2, 3, 8, 18, 27, 34, 38, + 38, 40, 52, 56, 56, 52, 48, 48, 46, 46, 56, 65, 68, 67, 61, 59, 59, + 54, 46, 37, 32, 27, 19, 8, 0, 0, 0, 16, 37, 53, 61, 63, 61, 56, + 56, 62, 64, 61, 54, 43, 32, 32, 29, 25, 27, 27, 21, 11, 0, 0, 1, + 3, 1, 0, 0, 0, 0, 0, 3, 15, 27, 33, 32, 23, 9, 3, 5, 6, + 4, 0, 0, 12, 20, 21, 23, 38, 52, 60, 64, 63, 58, 55, 60, 62, 58, + 50, 37, 19, 2, 0, 0, 15, 31, 43, 52, 56, 58, 60, 64, 76, 87, 94, + 98, 98, 92, 83, 79, 86, 89, 89, 87, 83, 77, 72, 63, 48, 29, 22, 14, + 11, 7, 0, 0, 1, 16, 29, 38, 44, 46, 48, 50, 59, 68, 72, 72, 66, + 65, 75, 84, 90, 94, 96, 94, 96, 96, 92, 84, 78, 70, 56, 36, 12, 0, + 0, 8, 32, 54, 69, 78, 84, 86, 97,105,108,106,104,106,106,102, 96, +101,102,102, 98, 94, 95, 92, 85, 76, 67, 57, 44, 28, 9, 0, 0, 2, + 25, 43, 52, 52, 45, 33, 20, 32, 43, 48, 48, 45, 38, 28, 18, 28, 39, + 50, 56, 60, 61, 60, 57, 54, 52, 46, 37, 26, 12, 0, 0, 0, 4, 12, + 16, 18, 20, 23, 21, 26, 31, 33, 32, 27, 20, 15, 8, 13, 14, 14, 18, + 20, 19, 15, 7, 9, 9, 6, 1, 0, 0, 0, 0, 0, 0, 0, 9, 19, + 26, 29, 28, 41, 48, 50, 47, 45, 46, 43, 33, 50, 61, 66, 68, 64, 58, + 50, 41, 34, 29, 22, 19, 13, 5, 0, 0, 0, 20, 41, 57, 64, 66, 63, + 58, 66, 71, 71, 66, 57, 43, 26, 14, 18, 22, 23, 21, 14, 7, 0, 0, + 0, 1, 5, 5, 1, 0, 0, 0, 0, 8, 20, 27, 26, 19, 9, 2, 9, + 12, 12, 9, 6, 8, 13, 13, 25, 39, 52, 60, 63, 62, 56, 50, 55, 56, + 52, 43, 28, 11, 0, 0, 0, 13, 27, 39, 47, 52, 54, 56, 59, 68, 79, + 87, 90, 89, 84, 74, 83, 88, 90, 89, 84, 78, 73, 64, 55, 44, 34, 25, + 13, 2, 0, 0, 0, 7, 22, 34, 43, 46, 48, 51, 64, 75, 84, 88, 88, + 84, 77, 68, 78, 85, 90, 93, 93, 92, 92, 88, 87, 83, 76, 64, 47, 23, + 0, 0, 0, 23, 48, 66, 78, 84, 87, 88, 95, 97, 97,101,102,100, 96, + 90, 94, 96, 97, 96, 92, 89, 88, 82, 78, 69, 59, 50, 37, 20, 0, 0, + 0, 16, 34, 43, 44, 37, 25, 18, 26, 36, 40, 40, 37, 33, 31, 21, 16, + 23, 37, 47, 55, 60, 61, 59, 52, 44, 46, 45, 39, 27, 12, 0, 0, 0, + 0, 2, 5, 6, 7, 11, 15, 20, 23, 25, 28, 28, 23, 16, 11, 7, 7, + 12, 14, 15, 12, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 6, 15, 20, 22, 32, 43, 47, 48, 46, 44, 41, 41, 45, 56, 63, 65, 64, + 59, 54, 47, 40, 27, 20, 18, 13, 7, 0, 0, 0, 20, 41, 57, 66, 68, + 66, 69, 75, 79, 77, 70, 59, 43, 33, 27, 23, 23, 23, 23, 20, 14, 6, + 0, 2, 11, 18, 19, 16, 11, 4, 0, 0, 5, 16, 23, 27, 25, 20, 13, + 12, 16, 19, 19, 18, 16, 20, 27, 38, 47, 52, 58, 62, 61, 56, 50, 45, + 47, 43, 33, 20, 13, 3, 0, 1, 13, 23, 34, 45, 54, 61, 67, 69, 67, + 69, 78, 82, 82, 77, 78, 87, 91, 92, 89, 84, 81, 79, 72, 62, 53, 43, + 31, 19, 5, 0, 0, 5, 16, 23, 32, 40, 45, 48, 62, 74, 86, 94, 99, +101, 97, 91, 84, 75, 79, 86, 91, 93, 95, 94, 93, 92, 88, 82, 71, 54, + 31, 7, 0, 0, 15, 40, 61, 74, 83, 87, 88, 90, 92, 94, 95, 94, 94, + 96,100,104,103, 97, 95, 92, 90, 88, 87, 84, 75, 62, 50, 39, 25, 6, + 0, 0, 7, 21, 29, 29, 23, 19, 19, 21, 29, 34, 36, 33, 34, 31, 22, + 6, 8, 22, 37, 50, 57, 60, 67, 67, 61, 51, 54, 52, 43, 28, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 6, 19, 28, 34, 34, 29, 22, 16, 12, + 9, 6, 9, 12, 11, 7, 1, 0, 0, 0, 4, 7, 9, 12, 9, 1, 1, + 6, 12, 14, 15, 19, 22, 37, 46, 52, 54, 54, 53, 53, 54, 54, 55, 58, + 59, 57, 54, 52, 46, 33, 19, 19, 19, 18, 12, 8, 13, 16, 37, 54, 63, + 66, 67, 77, 84, 84, 82, 74, 62, 47, 46, 40, 34, 32, 31, 29, 26, 21, + 15, 16, 15, 18, 26, 29, 29, 26, 19, 11, 5, 7, 18, 26, 31, 32, 29, + 25, 21, 20, 19, 21, 22, 25, 28, 34, 45, 53, 56, 55, 59, 60, 57, 52, + 46, 40, 37, 33, 28, 23, 14, 8, 8, 21, 33, 44, 54, 62, 70, 76, 79, + 76, 73, 71, 76, 79, 79, 82, 90, 95, 96, 92, 86, 85, 84, 79, 71, 62, + 53, 41, 27, 19, 15, 12, 16, 26, 32, 33, 37, 45, 54, 69, 81, 92,101, +108,111,109,104, 98, 90, 81, 77, 84, 89, 93, 96, 96, 95, 92, 84, 74, + 57, 34, 11, 4, 2, 12, 31, 53, 69, 79, 84, 87, 88, 90, 91, 89, 95, +100,103,113,119,118,111,102, 92, 91, 90, 90, 88, 81, 70, 55, 39, 27, + 11, 4, 0, 0, 7, 13, 14, 15, 16, 18, 22, 26, 31, 32, 33, 32, 28, + 20, 5, 0, 8, 25, 39, 58, 71, 78, 79, 73, 64, 62, 62, 57, 46, 28, + 18, 13, 6, 1, 0, 0, 0, 0, 0, 11, 21, 28, 34, 36, 32, 25, 18, + 12, 7, 5, 6, 13, 18, 19, 19, 16, 14, 12, 11, 12, 15, 19, 16, 13, + 13, 13, 20, 22, 19, 16, 19, 33, 47, 56, 61, 63, 63, 63, 62, 60, 56, + 53, 48, 51, 51, 52, 48, 38, 22, 20, 23, 25, 22, 16, 21, 25, 31, 48, + 58, 61, 74, 83, 88, 89, 85, 77, 65, 57, 56, 50, 41, 37, 34, 32, 29, + 27, 32, 36, 36, 33, 32, 38, 39, 37, 31, 20, 12, 7, 14, 22, 29, 33, + 36, 34, 32, 31, 29, 28, 28, 29, 32, 37, 45, 53, 56, 56, 54, 57, 56, + 53, 47, 43, 39, 37, 34, 32, 25, 23, 23, 25, 37, 47, 57, 66, 74, 81, + 84, 83, 79, 75, 77, 82, 84, 85, 94,100,102, 98, 92, 88, 88, 84, 78, + 72, 63, 53, 39, 38, 34, 31, 26, 32, 37, 38, 34, 46, 57, 73, 84, 94, +104,112,117,119,117,113,105, 96, 85, 76, 81, 88, 93, 96, 95, 91, 83, + 72, 54, 32, 13, 12, 9, 21, 31, 43, 60, 73, 80, 84, 86, 87, 86, 95, +103,108,115,129,136,133,125,113,102, 90, 89, 91, 91, 85, 75, 62, 46, + 28, 14, 15, 11, 7, 3, 4, 7, 9, 12, 18, 23, 28, 31, 32, 31, 28, + 23, 16, 4, 0, 0, 16, 41, 61, 74, 82, 84, 79, 72, 72, 74, 69, 60, + 45, 39, 33, 27, 19, 13, 7, 6, 8, 16, 28, 39, 47, 51, 50, 44, 36, + 28, 22, 18, 15, 15, 21, 27, 31, 32, 31, 28, 26, 25, 22, 22, 22, 23, + 25, 25, 20, 26, 28, 26, 19, 16, 31, 48, 61, 68, 71, 72, 70, 67, 63, + 57, 52, 48, 45, 44, 48, 47, 39, 25, 20, 26, 29, 27, 19, 21, 23, 28, + 43, 53, 61, 74, 84, 90, 90, 86, 79, 68, 64, 62, 55, 45, 38, 33, 31, + 29, 36, 44, 50, 51, 48, 44, 43, 45, 44, 37, 25, 14, 6, 5, 14, 22, + 31, 36, 38, 38, 37, 34, 33, 33, 33, 36, 39, 41, 48, 53, 54, 50, 52, + 53, 51, 46, 41, 38, 37, 37, 36, 32, 33, 34, 37, 39, 44, 54, 63, 72, + 80, 85, 85, 82, 79, 79, 84, 87, 88, 97,104,108,106, 99, 89, 90, 88, + 84, 79, 72, 61, 56, 54, 51, 46, 41, 37, 39, 39, 37, 48, 62, 74, 85, + 95,104,113,120,124,127,125,120,111, 99, 86, 74, 79, 87, 91, 91, 87, + 78, 65, 47, 25, 14, 15, 16, 29, 41, 50, 53, 64, 74, 79, 82, 84, 93, +104,113,117,128,145,154,150,137,123,110, 97, 88, 88, 89, 86, 78, 67, + 53, 38, 25, 26, 20, 15, 9, 4, 1, 3, 7, 15, 23, 28, 31, 31, 29, + 28, 23, 13, 2, 0, 0, 14, 39, 59, 74, 82, 84, 80, 75, 81, 83, 79, + 72, 60, 58, 54, 46, 38, 31, 29, 28, 25, 33, 44, 53, 59, 61, 59, 53, + 45, 37, 29, 23, 20, 19, 26, 33, 37, 39, 39, 38, 36, 34, 32, 31, 31, + 33, 34, 33, 27, 31, 32, 28, 21, 13, 29, 51, 64, 73, 76, 75, 74, 69, + 62, 55, 48, 44, 43, 44, 44, 43, 38, 27, 20, 26, 29, 27, 19, 14, 16, + 22, 38, 48, 56, 70, 81, 87, 88, 84, 78, 69, 68, 65, 56, 45, 39, 36, + 29, 28, 39, 51, 58, 60, 58, 53, 46, 48, 46, 39, 26, 12, 2, 0, 3, + 12, 22, 31, 37, 38, 37, 36, 33, 33, 33, 37, 41, 43, 43, 46, 48, 45, + 44, 47, 46, 41, 38, 40, 37, 33, 34, 33, 36, 39, 41, 43, 45, 47, 54, + 64, 74, 81, 83, 81, 79, 80, 87, 90, 89, 99,109,113,113,108, 97, 90, + 89, 86, 83, 77, 68, 66, 64, 60, 56, 53, 47, 41, 39, 37, 51, 67, 81, + 93,104,113,119,121,125,132,134,132,124,113, 99, 83, 68, 78, 84, 84, + 79, 70, 55, 36, 14, 12, 15, 20, 37, 51, 59, 61, 60, 66, 73, 77, 85, +101,113,123,127,137,159,167,161,147,129,114,102, 91, 84, 85, 83, 77, + 68, 57, 45, 33, 32, 28, 21, 15, 8, 4, 6, 9, 13, 22, 28, 31, 28, + 27, 27, 27, 23, 18, 13, 9, 9, 32, 53, 67, 76, 79, 78, 84, 91, 92, + 89, 81, 74, 73, 69, 63, 55, 52, 52, 51, 45, 48, 57, 64, 68, 67, 63, + 56, 47, 38, 29, 22, 19, 19, 26, 33, 38, 40, 41, 41, 40, 38, 37, 36, + 37, 38, 39, 38, 32, 32, 31, 27, 20, 11, 29, 52, 66, 74, 77, 76, 74, + 67, 61, 65, 65, 60, 50, 40, 43, 40, 34, 27, 19, 23, 26, 23, 13, 1, + 3, 21, 36, 45, 48, 60, 73, 79, 81, 79, 74, 69, 69, 65, 56, 53, 53, + 52, 47, 45, 43, 52, 61, 64, 63, 58, 51, 51, 47, 38, 23, 13, 8, 3, + 2, 1, 11, 21, 28, 32, 32, 29, 28, 28, 29, 33, 40, 44, 39, 39, 40, + 39, 34, 39, 38, 33, 43, 48, 47, 38, 28, 29, 32, 37, 39, 40, 41, 43, + 44, 51, 63, 72, 75, 76, 76, 82, 88, 92, 91,101,112,120,122,117,106, + 93, 89, 88, 84, 79, 72, 72, 69, 64, 60, 57, 53, 47, 40, 36, 53, 71, + 85,100,114,128,139,142,137,133,139,140,136,125,111, 94, 75, 66, 74, + 74, 69, 58, 41, 21, 6, 6, 11, 19, 41, 58, 67, 69, 66, 59, 65, 71, + 89,107,123,134,139,142,163,169,163,147,129,114,102, 92, 84, 78, 77, + 74, 66, 57, 47, 38, 36, 32, 26, 20, 16, 21, 25, 25, 21, 28, 33, 34, + 33, 32, 37, 39, 36, 29, 25, 20, 15, 23, 43, 58, 67, 71, 82, 93,100, +102, 97, 89, 84, 84, 80, 75, 69, 68, 68, 67, 61, 62, 68, 73, 74, 72, + 65, 56, 45, 36, 26, 22, 19, 18, 23, 29, 34, 37, 38, 39, 38, 37, 36, + 34, 36, 38, 39, 38, 32, 29, 27, 22, 15, 13, 31, 52, 66, 74, 76, 74, + 71, 67, 75, 79, 79, 75, 65, 50, 41, 39, 32, 28, 20, 20, 20, 16, 6, + 0, 3, 20, 33, 41, 44, 45, 59, 67, 71, 70, 67, 69, 69, 64, 56, 58, + 62, 62, 62, 62, 58, 53, 60, 64, 63, 58, 52, 52, 46, 37, 20, 18, 15, + 12, 12, 11, 7, 9, 18, 20, 21, 20, 19, 19, 21, 26, 34, 40, 38, 33, + 34, 32, 26, 29, 28, 34, 47, 55, 57, 51, 38, 22, 25, 29, 33, 33, 33, + 33, 34, 36, 46, 59, 65, 68, 74, 83, 89, 92, 91,101,113,123,128,123, +113, 99, 88, 88, 85, 80, 74, 73, 69, 62, 58, 55, 52, 47, 41, 36, 54, + 72, 88,103,121,139,154,159,154,142,137,142,142,133,119,102, 84, 63, + 61, 62, 57, 45, 28, 11, 6, 5, 6, 19, 44, 63, 73, 74, 71, 62, 58, + 70, 92,112,130,147,152,149,159,163,156,142,125,111, 99, 90, 83, 76, + 69, 67, 61, 54, 46, 39, 34, 33, 29, 27, 32, 36, 38, 37, 32, 41, 46, + 47, 46, 43, 43, 44, 41, 44, 40, 33, 22, 16, 32, 47, 58, 74, 89,100, +108,110,105, 97, 88, 89, 88, 84, 80, 79, 79, 77, 72, 76, 80, 82, 80, + 75, 67, 55, 44, 43, 41, 40, 41, 41, 38, 34, 31, 32, 33, 33, 32, 29, + 28, 27, 28, 31, 33, 32, 27, 25, 21, 15, 7, 15, 31, 51, 64, 71, 73, + 70, 67, 76, 84, 88, 88, 84, 74, 59, 41, 40, 36, 29, 25, 18, 16, 11, + 0, 2, 6, 15, 28, 36, 39, 39, 40, 51, 56, 59, 65, 68, 68, 64, 54, + 58, 63, 66, 70, 71, 68, 62, 56, 60, 59, 55, 55, 53, 46, 36, 18, 16, + 15, 14, 16, 16, 13, 7, 4, 7, 7, 9, 14, 18, 20, 21, 22, 29, 29, + 31, 31, 28, 21, 20, 19, 36, 50, 59, 62, 59, 50, 33, 16, 22, 25, 26, + 25, 31, 33, 33, 29, 44, 53, 61, 73, 81, 87, 89, 88, 99,113,123,129, +125,114,101, 88, 88, 86, 80, 74, 70, 64, 56, 51, 51, 46, 41, 38, 40, + 60, 74, 85,102,120,140,158,165,161,147,132,139,140,134,122,106, 88, + 68, 52, 53, 47, 36, 22, 12, 8, 6, 2, 20, 46, 65, 75, 77, 73, 64, + 53, 70, 93,114,136,154,161,158,147,152,145,133,123,115,106, 95, 81, + 74, 64, 59, 54, 48, 48, 44, 43, 37, 36, 38, 41, 45, 47, 46, 41, 50, + 55, 57, 57, 55, 54, 54, 57, 60, 57, 52, 44, 34, 32, 48, 64, 80, 94, +106,113,117,113,106, 97, 91, 91, 90, 88, 87, 85, 85, 87, 90, 92, 92, + 88, 80, 69, 56, 57, 56, 55, 58, 60, 58, 55, 50, 45, 40, 37, 33, 31, + 31, 32, 32, 32, 31, 25, 25, 21, 16, 11, 5, 11, 18, 31, 50, 62, 68, + 69, 66, 71, 80, 88, 93, 94, 88, 79, 66, 50, 40, 37, 29, 27, 22, 15, + 9, 5, 6, 9, 15, 20, 28, 32, 34, 36, 40, 47, 55, 61, 65, 66, 62, + 53, 50, 57, 65, 72, 74, 73, 68, 63, 60, 59, 58, 57, 54, 47, 36, 18, + 7, 8, 9, 15, 16, 13, 8, 5, 4, 5, 7, 11, 13, 16, 18, 20, 22, + 25, 27, 27, 23, 18, 11, 18, 33, 48, 58, 62, 60, 53, 39, 22, 18, 20, + 25, 31, 37, 39, 39, 34, 36, 46, 57, 67, 74, 79, 82, 87, 98,110,120, +125,122,113,100, 88, 87, 84, 78, 69, 65, 57, 48, 53, 51, 45, 36, 33, + 47, 68, 83, 93, 99,111,129,147,158,156,147,133,128,130,128,118,104, + 88, 69, 52, 46, 40, 29, 21, 12, 7, 5, 1, 19, 45, 63, 73, 74, 70, + 61, 53, 71, 93,114,136,156,163,161,152,143,137,130,125,118,109, 97, + 84, 75, 65, 52, 48, 51, 51, 48, 44, 40, 41, 44, 47, 51, 54, 53, 50, + 53, 58, 61, 63, 64, 65, 66, 70, 72, 70, 66, 60, 53, 47, 56, 71, 85, + 97,109,117,120,118,112,105,100, 96, 93, 94, 96, 97,100,102,104,104, +102, 95, 85, 72, 67, 68, 66, 71, 74, 73, 70, 65, 59, 54, 50, 45, 41, + 41, 41, 41, 40, 37, 32, 26, 20, 16, 7, 6, 8, 13, 19, 32, 50, 60, + 64, 65, 62, 71, 82, 91, 96, 97, 93, 84, 73, 59, 46, 34, 31, 29, 26, + 21, 14, 8, 11, 13, 15, 19, 21, 23, 27, 31, 36, 41, 48, 54, 58, 59, + 57, 51, 38, 47, 60, 69, 74, 75, 73, 68, 64, 62, 59, 56, 52, 44, 33, + 18, 0, 0, 2, 7, 9, 8, 4, 1, 0, 0, 0, 2, 4, 6, 8, 11, + 14, 16, 19, 19, 16, 11, 7, 16, 29, 43, 52, 57, 56, 50, 38, 23, 15, + 19, 25, 33, 39, 44, 44, 39, 32, 39, 47, 56, 62, 70, 79, 86, 96,105, +113,118,114,108, 97, 87, 83, 79, 73, 62, 58, 50, 45, 47, 45, 38, 27, + 29, 55, 75, 90,102,107,109,115,130,142,145,140,132,117,117,115,109, + 98, 84, 67, 54, 45, 38, 31, 23, 13, 6, 5, 4, 15, 39, 57, 66, 68, + 64, 55, 56, 73, 92,111,130,147,158,158,150,142,136,129,122,114,106, + 96, 85, 75, 66, 54, 46, 48, 50, 48, 43, 46, 48, 48, 50, 54, 57, 58, + 57, 53, 57, 61, 65, 68, 71, 74, 77, 79, 77, 74, 69, 65, 61, 60, 74, + 87, 98,108,114,118,117,113,110,106,102, 99, 97,102,108,113,115,117, +115,110,102, 90, 76, 74, 75, 79, 83, 84, 81, 77, 71, 64, 59, 55, 50, + 50, 48, 47, 46, 43, 39, 32, 23, 20, 16, 14, 12, 9, 12, 16, 31, 47, + 57, 61, 61, 58, 68, 80, 90, 96, 97, 95, 88, 77, 66, 54, 41, 31, 32, + 31, 26, 19, 16, 19, 21, 23, 22, 19, 21, 22, 22, 26, 31, 36, 40, 45, + 47, 47, 43, 37, 37, 51, 62, 70, 74, 74, 70, 65, 60, 56, 51, 45, 38, + 28, 15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 6, 13, 16, 19, 18, 16, 13, 12, 22, 32, 40, 45, 45, 41, 32, 20, + 14, 13, 21, 31, 39, 45, 46, 41, 32, 28, 34, 46, 58, 68, 76, 84, 91, + 98,103,106,105,101, 93, 86, 77, 72, 65, 54, 48, 41, 36, 36, 34, 27, + 20, 36, 60, 80, 95,106,113,113,112,113,124,129,130,125,113,102,102, + 97, 89, 78, 64, 56, 47, 39, 32, 25, 14, 4, 3, 9, 14, 32, 48, 58, + 60, 56, 56, 61, 72, 88,103,119,132,140,143,140,136,129,123,115,108, +100, 92, 83, 76, 67, 57, 47, 43, 44, 43, 46, 51, 55, 56, 56, 55, 58, + 61, 61, 59, 54, 58, 62, 67, 72, 75, 78, 79, 77, 74, 72, 69, 68, 68, + 74, 84, 94,102,107,110,111,110,109,108,105,102, 98,106,114,121,125, +125,123,117,107, 95, 81, 78, 81, 88, 90, 90, 87, 81, 74, 67, 61, 57, + 53, 54, 53, 51, 47, 41, 37, 31, 23, 19, 16, 18, 18, 16, 14, 11, 26, + 41, 52, 56, 56, 54, 63, 76, 86, 93, 96, 93, 87, 79, 69, 58, 46, 33, + 36, 36, 31, 25, 22, 23, 26, 28, 31, 29, 25, 20, 21, 20, 20, 21, 23, + 28, 33, 37, 37, 33, 26, 38, 51, 61, 67, 69, 66, 60, 54, 47, 40, 34, + 27, 20, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 11, 18, 21, 22, 21, 18, 13, 12, 19, 25, 29, 31, 28, 23, + 20, 16, 7, 14, 25, 34, 41, 43, 39, 29, 19, 25, 41, 54, 63, 71, 77, + 83, 88, 91, 93, 93, 91, 88, 84, 77, 67, 56, 46, 38, 33, 26, 21, 19, + 14, 14, 39, 62, 81, 96,107,113,114,112,106,107,113,117,115,107, 93, + 88, 84, 79, 68, 65, 57, 48, 39, 32, 23, 15, 3, 2, 13, 20, 26, 40, + 48, 52, 56, 62, 65, 68, 81, 93,103,113,121,125,125,123,119,113,106, + 99, 92, 86, 81, 75, 67, 59, 51, 40, 37, 39, 47, 54, 59, 61, 62, 61, + 57, 61, 63, 62, 57, 52, 56, 61, 67, 71, 74, 74, 72, 69, 67, 67, 68, + 70, 73, 79, 86, 92, 96, 98,100,101,102,104,104,102,100,106,115,124, +128,129,127,121,112,101, 88, 80, 86, 92, 95, 94, 89, 83, 75, 68, 62, + 59, 55, 56, 55, 52, 46, 45, 41, 34, 25, 21, 16, 18, 19, 20, 18, 20, + 29, 36, 44, 50, 51, 50, 55, 69, 80, 88, 90, 88, 84, 76, 68, 58, 46, + 37, 41, 41, 36, 28, 26, 25, 27, 29, 33, 34, 33, 27, 18, 12, 9, 8, + 9, 13, 18, 23, 27, 26, 22, 22, 36, 47, 56, 59, 57, 51, 41, 32, 25, + 18, 13, 8, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 2, 0, 0, 0, 8, 16, 22, 26, 25, 21, 15, 8, 3, 7, 12, 14, 15, + 16, 19, 16, 7, 5, 16, 26, 33, 36, 32, 22, 16, 23, 34, 47, 55, 61, + 66, 70, 74, 76, 78, 79, 80, 80, 79, 75, 67, 54, 38, 26, 23, 18, 12, + 5, 2, 18, 39, 60, 78, 91,102,108,110,108,102, 96, 97,101,102, 96, + 86, 74, 70, 64, 68, 66, 58, 47, 39, 31, 22, 13, 2, 1, 13, 21, 29, + 38, 47, 55, 62, 66, 67, 65, 71, 79, 88, 95,102,107,110,108,105,100, + 94, 88, 84, 84, 79, 73, 67, 61, 54, 44, 34, 39, 45, 53, 59, 64, 66, + 65, 61, 58, 61, 62, 58, 51, 47, 52, 56, 60, 62, 61, 59, 57, 57, 58, + 62, 66, 71, 72, 74, 78, 81, 84, 86, 89, 93, 97,100,101,100,102,112, +121,124,125,125,122,115,106, 95, 82, 88, 95, 97, 95, 90, 83, 77, 74, + 64, 60, 56, 57, 55, 51, 44, 45, 41, 33, 25, 25, 22, 15, 18, 18, 16, + 26, 38, 45, 46, 41, 45, 45, 45, 61, 74, 80, 83, 81, 76, 69, 62, 53, + 46, 46, 50, 48, 41, 32, 27, 31, 31, 28, 32, 34, 36, 33, 25, 11, 0, + 0, 6, 11, 11, 8, 14, 16, 15, 12, 19, 31, 39, 45, 43, 34, 25, 26, + 25, 16, 3, 0, 0, 0, 6, 9, 9, 6, 2, 0, 0, 0, 0, 2, 3, + 3, 2, 0, 0, 0, 5, 15, 23, 27, 27, 22, 15, 7, 3, 0, 2, 6, + 9, 13, 15, 13, 6, 3, 6, 15, 22, 25, 22, 14, 21, 29, 33, 36, 43, + 46, 48, 52, 54, 56, 63, 69, 70, 71, 73, 72, 65, 53, 37, 20, 13, 9, + 3, 0, 5, 22, 36, 54, 70, 83, 92, 98,101,101, 96, 90, 82, 84, 87, + 84, 75, 60, 57, 65, 68, 66, 58, 46, 39, 27, 19, 9, 1, 2, 11, 20, + 29, 40, 51, 59, 65, 68, 68, 63, 57, 67, 76, 81, 83, 89, 92, 92, 91, + 87, 82, 77, 79, 79, 75, 69, 67, 63, 57, 47, 39, 36, 38, 47, 57, 63, + 67, 67, 63, 57, 57, 58, 56, 50, 41, 39, 43, 45, 46, 44, 45, 53, 57, + 58, 55, 58, 64, 66, 66, 64, 64, 67, 71, 75, 81, 87, 92, 96, 98, 99, +105,113,117,119,121,122,119,113,102, 92, 88, 96, 97, 96, 91, 85, 84, + 79, 69, 63, 59, 57, 54, 48, 40, 39, 34, 26, 27, 28, 28, 23, 16, 14, + 16, 31, 44, 52, 55, 52, 44, 39, 39, 54, 65, 72, 74, 72, 66, 58, 55, + 53, 51, 57, 59, 57, 48, 36, 28, 37, 41, 40, 34, 32, 34, 33, 26, 13, + 8, 9, 11, 13, 11, 4, 1, 5, 6, 6, 5, 13, 21, 26, 25, 16, 26, + 29, 28, 20, 5, 0, 0, 1, 11, 15, 16, 13, 7, 1, 0, 0, 1, 6, + 8, 7, 4, 0, 0, 0, 0, 12, 21, 26, 27, 23, 15, 14, 15, 13, 7, + 2, 3, 5, 7, 6, 2, 1, 4, 6, 9, 12, 9, 13, 25, 33, 37, 34, + 27, 25, 26, 28, 38, 53, 63, 69, 69, 65, 65, 66, 61, 52, 36, 16, 6, + 0, 0, 0, 11, 29, 44, 54, 59, 71, 79, 86, 90, 92, 90, 84, 76, 67, + 71, 69, 60, 55, 60, 66, 68, 66, 57, 52, 46, 33, 16, 4, 3, 4, 5, + 15, 27, 39, 50, 58, 64, 67, 65, 59, 65, 69, 77, 80, 79, 74, 75, 76, + 75, 73, 69, 69, 72, 73, 69, 65, 67, 65, 60, 52, 40, 36, 26, 38, 50, + 59, 65, 67, 64, 58, 50, 52, 50, 45, 38, 31, 27, 31, 28, 38, 48, 56, + 61, 63, 61, 58, 55, 59, 60, 60, 64, 68, 70, 70, 67, 76, 84, 91, 96, +100,103,106,110,113,118,122,123,119,111,100, 88, 95, 97, 96, 92, 87, + 85, 80, 71, 69, 65, 60, 54, 48, 39, 28, 19, 21, 26, 31, 32, 32, 27, + 19, 20, 34, 47, 57, 61, 60, 53, 43, 32, 48, 59, 65, 65, 61, 58, 55, + 54, 59, 65, 69, 70, 67, 58, 44, 26, 37, 44, 46, 44, 39, 33, 29, 25, + 26, 29, 31, 28, 21, 11, 1, 0, 0, 0, 0, 1, 1, 6, 11, 9, 19, + 27, 33, 32, 27, 15, 0, 0, 0, 11, 16, 16, 13, 7, 2, 0, 0, 0, + 3, 5, 4, 1, 0, 0, 0, 0, 7, 16, 23, 26, 22, 15, 22, 25, 22, + 15, 8, 1, 0, 0, 0, 0, 0, 4, 6, 5, 1, 0, 13, 26, 34, 37, + 33, 23, 13, 4, 18, 36, 52, 62, 68, 68, 63, 56, 59, 57, 48, 36, 19, + 2, 0, 0, 0, 18, 37, 51, 60, 65, 66, 66, 74, 79, 82, 81, 77, 70, + 60, 51, 56, 57, 59, 65, 69, 70, 66, 56, 56, 51, 39, 22, 4, 3, 3, + 0, 16, 29, 40, 46, 54, 60, 63, 61, 68, 74, 78, 79, 77, 74, 68, 57, + 64, 64, 60, 56, 57, 61, 62, 58, 64, 68, 68, 65, 58, 48, 37, 41, 39, + 40, 47, 56, 60, 60, 56, 47, 43, 41, 37, 34, 37, 39, 40, 43, 47, 52, + 56, 62, 65, 64, 62, 60, 61, 64, 69, 74, 78, 80, 81, 79, 76, 79, 82, + 91, 98,104,110,115,119,118,124,128,127,121,110, 97, 92, 95, 95, 92, + 88, 84, 79, 78, 77, 74, 70, 63, 54, 45, 37, 34, 33, 29, 29, 36, 38, + 37, 38, 43, 41, 48, 58, 63, 62, 56, 46, 45, 53, 59, 63, 63, 61, 57, + 62, 68, 72, 76, 79, 79, 75, 67, 55, 38, 31, 39, 44, 46, 45, 41, 39, + 38, 40, 44, 45, 44, 37, 27, 14, 4, 3, 3, 0, 0, 0, 0, 3, 8, + 18, 29, 38, 39, 33, 22, 7, 0, 0, 8, 13, 13, 11, 15, 16, 14, 8, + 2, 2, 2, 2, 1, 0, 2, 5, 5, 13, 20, 22, 23, 21, 22, 29, 31, + 27, 20, 11, 12, 13, 12, 8, 6, 3, 3, 5, 5, 1, 0, 9, 22, 29, + 32, 28, 20, 11, 14, 15, 27, 44, 56, 62, 64, 60, 54, 53, 52, 47, 37, + 22, 7, 0, 0, 1, 20, 38, 52, 60, 65, 66, 65, 62, 64, 69, 70, 68, + 64, 65, 61, 59, 61, 65, 69, 71, 70, 65, 59, 59, 54, 43, 26, 15, 12, + 8, 8, 23, 37, 46, 52, 56, 59, 62, 66, 73, 79, 83, 84, 82, 77, 71, + 65, 61, 60, 57, 52, 44, 45, 50, 56, 63, 69, 71, 70, 66, 59, 50, 56, + 48, 46, 44, 40, 47, 50, 47, 41, 33, 31, 36, 40, 43, 46, 48, 51, 55, + 58, 61, 62, 66, 66, 65, 64, 66, 70, 76, 82, 87, 91, 92, 92, 88, 84, + 86, 87, 92,101,109,117,122,123,129,136,137,133,123,110, 99, 93, 90, + 89, 88, 85, 84, 84, 84, 81, 78, 72, 64, 56, 51, 48, 48, 43, 34, 37, + 43, 46, 58, 63, 62, 57, 55, 59, 59, 54, 51, 58, 63, 67, 67, 65, 60, + 65, 74, 78, 79, 82, 83, 82, 79, 72, 62, 48, 33, 33, 40, 45, 47, 47, + 47, 46, 48, 53, 55, 55, 50, 41, 31, 20, 13, 14, 11, 3, 0, 0, 2, + 8, 21, 32, 38, 40, 36, 26, 13, 0, 0, 6, 8, 11, 19, 27, 31, 31, + 26, 21, 18, 16, 13, 11, 13, 18, 20, 19, 21, 28, 29, 26, 19, 28, 36, + 36, 31, 22, 18, 23, 27, 26, 22, 16, 11, 3, 3, 3, 3, 7, 14, 18, + 23, 27, 25, 20, 20, 23, 25, 23, 28, 43, 52, 54, 53, 48, 47, 48, 45, + 37, 25, 12, 0, 0, 0, 16, 33, 46, 55, 59, 61, 61, 57, 48, 52, 55, + 63, 70, 72, 69, 66, 67, 69, 71, 71, 68, 61, 60, 59, 54, 44, 28, 23, + 19, 14, 13, 29, 40, 50, 54, 58, 61, 64, 68, 73, 79, 84, 85, 85, 82, + 78, 74, 71, 66, 60, 53, 44, 39, 43, 52, 61, 69, 74, 74, 73, 68, 62, + 66, 61, 54, 46, 41, 37, 34, 34, 34, 36, 39, 44, 48, 50, 50, 52, 54, + 57, 61, 63, 61, 65, 67, 66, 66, 68, 74, 80, 87, 93, 99,102,102, 99, + 93, 91, 92, 92, 96,104,113,121,124,136,145,149,147,137,124,113,105, + 96, 88, 84, 84, 86, 87, 87, 85, 82, 76, 70, 63, 59, 59, 58, 54, 44, + 36, 44, 60, 72, 77, 76, 72, 66, 61, 58, 58, 61, 66, 70, 71, 69, 64, + 61, 73, 80, 84, 83, 84, 83, 81, 78, 73, 65, 55, 43, 41, 41, 44, 48, + 51, 52, 52, 54, 58, 61, 62, 59, 54, 45, 34, 23, 27, 25, 18, 7, 0, + 0, 9, 20, 29, 36, 37, 33, 26, 16, 12, 8, 4, 5, 13, 23, 33, 40, + 41, 40, 38, 34, 32, 28, 27, 27, 29, 31, 28, 32, 38, 39, 33, 28, 32, + 38, 38, 32, 22, 23, 31, 36, 36, 31, 22, 14, 5, 0, 4, 9, 15, 20, + 22, 21, 20, 21, 23, 29, 31, 29, 28, 26, 23, 34, 40, 41, 39, 41, 45, + 43, 37, 26, 15, 4, 0, 0, 7, 22, 34, 43, 47, 51, 52, 48, 39, 33, + 53, 66, 74, 77, 74, 71, 70, 70, 70, 69, 64, 58, 58, 57, 53, 43, 31, + 27, 21, 15, 19, 32, 43, 50, 54, 57, 61, 63, 66, 70, 76, 80, 84, 85, + 84, 83, 80, 78, 73, 66, 56, 44, 41, 39, 45, 56, 66, 73, 76, 76, 74, + 71, 71, 68, 63, 58, 52, 47, 43, 41, 43, 46, 52, 56, 60, 60, 58, 53, + 53, 56, 59, 61, 59, 64, 66, 65, 65, 67, 74, 81, 89, 96,103,109,110, +106, 99, 96, 98, 96, 92, 98,108,117,128,142,154,159,159,152,142,130, +121,111,100, 88, 84, 84, 87, 88, 85, 82, 77, 71, 65, 62, 63, 64, 60, + 52, 38, 50, 67, 79, 84, 84, 80, 74, 69, 65, 64, 66, 70, 73, 73, 69, + 62, 66, 77, 83, 84, 82, 79, 77, 74, 72, 68, 63, 56, 48, 50, 50, 50, + 50, 54, 55, 55, 56, 60, 63, 66, 65, 61, 55, 46, 36, 40, 39, 33, 21, + 6, 0, 7, 15, 23, 28, 29, 29, 27, 28, 26, 21, 15, 7, 13, 22, 33, + 41, 45, 46, 47, 47, 45, 43, 39, 37, 38, 37, 36, 46, 51, 48, 41, 36, + 40, 38, 37, 29, 20, 25, 33, 38, 38, 33, 23, 21, 18, 9, 14, 18, 21, + 23, 25, 26, 28, 29, 33, 36, 34, 33, 31, 28, 22, 15, 25, 28, 28, 38, + 41, 41, 36, 27, 16, 6, 0, 0, 0, 7, 18, 25, 29, 33, 37, 36, 40, + 43, 55, 69, 77, 80, 77, 73, 70, 69, 68, 65, 59, 55, 54, 52, 47, 38, + 32, 28, 21, 14, 23, 34, 41, 48, 57, 62, 65, 66, 66, 65, 70, 74, 79, + 82, 84, 84, 83, 81, 77, 70, 60, 46, 41, 40, 39, 45, 58, 67, 73, 74, + 74, 73, 68, 68, 67, 65, 63, 60, 57, 55, 54, 54, 61, 67, 70, 69, 64, + 57, 48, 52, 55, 56, 54, 60, 63, 62, 61, 64, 70, 79, 88, 96,103,110, +112,108,102,102,103,101, 96, 92,101,115,130,145,158,165,167,165,158, +149,139,127,113, 98, 87, 84, 83, 84, 81, 78, 73, 67, 62, 60, 62, 65, + 63, 56, 50, 53, 69, 81, 86, 86, 83, 78, 72, 68, 65, 65, 69, 72, 71, + 66, 59, 68, 77, 81, 81, 76, 72, 67, 63, 60, 59, 57, 53, 53, 55, 56, + 56, 55, 55, 57, 58, 58, 60, 64, 67, 68, 66, 62, 55, 47, 52, 52, 46, + 33, 15, 2, 2, 8, 14, 23, 34, 41, 44, 43, 39, 33, 26, 19, 21, 29, + 38, 45, 50, 52, 54, 53, 53, 51, 47, 44, 43, 40, 51, 59, 62, 59, 50, + 39, 43, 40, 32, 26, 18, 23, 31, 37, 37, 29, 31, 34, 32, 23, 23, 23, + 25, 28, 39, 46, 50, 48, 44, 40, 37, 34, 32, 28, 25, 18, 9, 15, 20, + 33, 38, 38, 33, 26, 20, 12, 3, 0, 0, 0, 0, 4, 8, 19, 32, 44, + 51, 53, 58, 71, 79, 81, 77, 72, 69, 66, 64, 60, 54, 47, 45, 44, 40, + 31, 29, 26, 18, 20, 27, 34, 47, 57, 63, 66, 68, 68, 67, 65, 61, 66, + 71, 74, 78, 79, 80, 79, 77, 72, 62, 50, 38, 38, 37, 36, 44, 55, 63, + 67, 69, 69, 59, 62, 65, 68, 69, 69, 68, 65, 62, 62, 70, 75, 77, 75, + 69, 59, 46, 45, 48, 48, 51, 57, 58, 57, 56, 59, 66, 74, 82, 91, 99, +105,108,105,103,108,109,105, 98, 89, 99,115,132,145,158,165,169,171, +169,165,159,147,129,111, 92, 84, 79, 76, 74, 69, 63, 58, 54, 53, 57, + 61, 62, 60, 63, 61, 67, 78, 84, 84, 80, 75, 70, 64, 61, 61, 64, 67, + 65, 61, 59, 68, 74, 76, 74, 67, 61, 63, 60, 54, 45, 45, 45, 53, 57, + 58, 59, 59, 59, 60, 63, 65, 61, 62, 66, 68, 68, 65, 58, 56, 61, 62, + 56, 41, 23, 6, 0, 1, 19, 37, 48, 55, 56, 53, 47, 40, 34, 29, 28, + 33, 39, 45, 48, 53, 56, 56, 54, 54, 51, 47, 45, 52, 63, 71, 73, 69, + 58, 43, 43, 39, 28, 19, 12, 19, 26, 29, 31, 29, 41, 47, 45, 36, 31, + 29, 28, 39, 53, 61, 65, 63, 57, 46, 43, 44, 39, 31, 26, 21, 14, 5, + 14, 27, 34, 34, 31, 28, 27, 21, 8, 3, 0, 0, 0, 0, 8, 20, 36, + 50, 57, 61, 62, 73, 79, 80, 75, 72, 71, 67, 61, 55, 48, 38, 33, 32, + 29, 22, 23, 20, 23, 26, 29, 44, 56, 63, 67, 68, 67, 66, 65, 64, 60, + 55, 58, 63, 67, 70, 73, 74, 73, 70, 63, 53, 37, 32, 31, 29, 27, 37, + 47, 53, 57, 58, 45, 52, 59, 66, 71, 74, 74, 72, 67, 69, 76, 81, 81, + 78, 69, 57, 43, 38, 40, 40, 47, 53, 54, 52, 50, 61, 68, 72, 74, 84, + 92, 97,100, 99,108,113,114,110,101, 91, 96,113,128,142,152,171,179, +175,173,175,173,163,145,123,102, 84, 76, 65, 63, 58, 52, 47, 46, 44, + 47, 54, 57, 68, 73, 73, 69, 72, 76, 76, 74, 68, 62, 57, 53, 52, 55, + 58, 58, 55, 59, 66, 70, 69, 64, 61, 67, 70, 67, 59, 47, 31, 40, 50, + 55, 57, 58, 58, 59, 61, 65, 67, 65, 61, 65, 67, 68, 65, 59, 63, 68, + 69, 62, 47, 28, 8, 0, 4, 26, 45, 57, 61, 61, 57, 50, 44, 40, 37, + 36, 36, 38, 41, 45, 50, 55, 57, 54, 55, 53, 50, 46, 61, 73, 79, 81, + 76, 65, 50, 39, 36, 25, 11, 5, 12, 18, 22, 22, 34, 48, 56, 55, 45, + 37, 33, 29, 46, 61, 71, 75, 74, 66, 55, 47, 48, 45, 38, 28, 25, 20, + 12, 6, 20, 28, 31, 28, 31, 32, 27, 16, 8, 3, 0, 0, 7, 18, 26, + 39, 54, 62, 66, 67, 74, 79, 79, 73, 74, 72, 65, 56, 50, 43, 31, 20, + 19, 18, 13, 19, 27, 31, 31, 34, 51, 60, 66, 67, 66, 63, 60, 58, 58, + 56, 53, 48, 47, 52, 56, 59, 62, 64, 64, 61, 54, 41, 26, 23, 21, 19, + 14, 25, 33, 38, 41, 29, 39, 51, 61, 69, 74, 75, 74, 69, 75, 81, 84, + 83, 76, 65, 50, 32, 33, 34, 37, 47, 52, 52, 48, 45, 58, 65, 70, 73, + 74, 82, 88, 90,101,111,118,118,113,104, 94, 88,104,119,134,158,177, +190,188,169,175,179,175,158,134,112, 92, 74, 61, 51, 46, 48, 50, 50, + 46, 38, 46, 60, 73, 79, 80, 78, 73, 68, 68, 64, 62, 60, 57, 54, 50, + 45, 48, 50, 53, 59, 63, 64, 62, 56, 63, 69, 72, 69, 61, 48, 36, 33, + 44, 50, 53, 54, 59, 63, 63, 61, 63, 62, 61, 64, 66, 66, 63, 58, 66, + 72, 73, 66, 51, 31, 11, 0, 5, 27, 46, 57, 61, 60, 54, 47, 45, 41, + 41, 43, 45, 46, 45, 41, 47, 54, 57, 56, 54, 55, 53, 50, 65, 77, 84, + 85, 80, 70, 54, 34, 31, 20, 6, 0, 5, 11, 14, 15, 34, 51, 59, 59, + 51, 39, 34, 31, 48, 65, 75, 80, 79, 74, 62, 48, 45, 44, 38, 33, 32, + 27, 20, 8, 12, 21, 26, 26, 29, 32, 28, 18, 16, 12, 4, 4, 18, 31, + 39, 44, 58, 66, 69, 70, 76, 79, 78, 71, 73, 68, 60, 50, 43, 36, 23, + 7, 6, 6, 11, 27, 36, 38, 37, 36, 51, 59, 63, 62, 58, 53, 47, 45, + 45, 45, 44, 43, 41, 40, 40, 41, 46, 52, 56, 56, 53, 44, 32, 15, 14, + 12, 6, 4, 12, 19, 23, 34, 41, 45, 56, 66, 73, 75, 74, 74, 79, 84, + 84, 80, 71, 56, 37, 29, 29, 29, 38, 46, 50, 47, 41, 40, 52, 60, 64, + 67, 70, 74, 79, 88, 99,110,117,118,113,106, 96, 88, 99,114,134,158, +179,190,190,173,173,179,179,167,145,121,101, 83, 65, 48, 48, 50, 51, + 50, 45, 38, 39, 58, 72, 79, 82, 80, 77, 73, 70, 68, 66, 64, 61, 58, + 60, 58, 54, 51, 55, 58, 59, 59, 55, 55, 61, 66, 68, 65, 57, 44, 38, + 37, 40, 45, 52, 58, 62, 65, 65, 62, 59, 59, 61, 63, 65, 64, 62, 57, + 64, 70, 71, 64, 50, 29, 9, 0, 4, 26, 43, 52, 55, 52, 54, 58, 56, + 52, 47, 46, 52, 56, 58, 56, 48, 53, 57, 58, 53, 54, 54, 53, 66, 78, + 85, 87, 84, 74, 59, 40, 23, 13, 3, 0, 0, 5, 9, 12, 29, 46, 57, + 59, 53, 40, 36, 32, 46, 63, 75, 82, 83, 79, 70, 58, 47, 40, 40, 40, + 40, 37, 31, 20, 9, 9, 16, 23, 29, 31, 28, 23, 22, 18, 11, 12, 27, + 41, 51, 54, 61, 69, 72, 73, 77, 78, 76, 69, 69, 63, 53, 43, 37, 28, + 16, 0, 3, 12, 18, 32, 40, 41, 40, 39, 48, 55, 56, 53, 46, 45, 45, + 41, 34, 29, 31, 32, 34, 36, 38, 41, 41, 37, 43, 47, 48, 47, 41, 28, + 13, 6, 0, 0, 0, 5, 21, 37, 44, 48, 52, 65, 74, 77, 77, 74, 79, + 82, 80, 75, 63, 45, 27, 27, 26, 27, 37, 43, 43, 38, 31, 37, 46, 53, + 56, 58, 61, 66, 73, 83, 94,104,111,112,109,102, 94, 88, 96,112,130, +152,171,183,181,165,169,179,181,175,156,132,110, 92, 77, 61, 52, 52, + 52, 50, 45, 38, 39, 50, 65, 74, 79, 79, 77, 74, 72, 70, 69, 67, 65, + 70, 72, 70, 64, 58, 56, 56, 55, 52, 51, 55, 58, 61, 60, 55, 46, 36, + 34, 34, 39, 45, 52, 58, 62, 64, 63, 59, 56, 55, 57, 59, 61, 61, 60, + 56, 57, 63, 64, 57, 43, 22, 5, 6, 7, 25, 39, 46, 46, 47, 60, 65, + 63, 59, 53, 46, 56, 64, 67, 66, 60, 51, 56, 56, 52, 48, 50, 55, 68, + 79, 86, 88, 85, 77, 63, 46, 29, 15, 4, 0, 0, 0, 4, 8, 20, 38, + 51, 56, 54, 44, 37, 34, 39, 58, 73, 81, 84, 82, 75, 65, 55, 45, 45, + 46, 46, 45, 40, 32, 22, 15, 20, 26, 31, 32, 29, 22, 22, 18, 18, 22, + 39, 53, 61, 64, 65, 71, 75, 75, 74, 74, 71, 68, 65, 57, 44, 38, 31, + 22, 18, 11, 12, 20, 23, 32, 38, 39, 38, 37, 45, 48, 46, 41, 39, 38, + 37, 31, 22, 15, 18, 21, 25, 28, 33, 38, 39, 38, 38, 47, 57, 61, 59, + 52, 38, 25, 11, 0, 0, 6, 23, 34, 43, 47, 51, 65, 75, 79, 80, 77, + 76, 78, 75, 68, 55, 34, 23, 21, 19, 27, 34, 36, 32, 26, 21, 33, 39, + 41, 43, 45, 47, 53, 62, 74, 86, 95,101,102,100, 95, 89, 85, 91,106, +124,143,159,169,165,152,163,173,179,175,159,139,118,100, 86, 73, 57, + 52, 51, 47, 43, 39, 45, 47, 54, 64, 71, 73, 73, 72, 71, 70, 69, 67, + 73, 77, 79, 77, 71, 62, 55, 53, 48, 47, 52, 54, 54, 52, 47, 40, 31, + 23, 25, 28, 34, 41, 50, 55, 59, 59, 57, 52, 47, 46, 48, 51, 54, 56, + 56, 53, 46, 53, 54, 47, 33, 14, 9, 13, 13, 22, 34, 39, 37, 47, 62, + 68, 66, 61, 54, 46, 58, 67, 73, 72, 67, 59, 55, 55, 51, 40, 45, 57, + 69, 79, 85, 88, 85, 78, 66, 51, 33, 16, 3, 0, 0, 0, 0, 5, 9, + 27, 43, 51, 53, 47, 36, 33, 29, 51, 67, 77, 82, 81, 76, 67, 58, 50, + 44, 46, 47, 48, 46, 40, 33, 26, 22, 28, 32, 33, 31, 25, 20, 22, 27, + 34, 51, 63, 71, 73, 72, 74, 78, 79, 78, 74, 70, 66, 60, 48, 39, 33, + 32, 34, 31, 22, 22, 27, 27, 27, 32, 32, 29, 33, 40, 39, 36, 33, 32, + 28, 22, 15, 6, 2, 5, 8, 12, 18, 23, 34, 45, 51, 52, 50, 61, 70, + 72, 68, 60, 51, 38, 25, 9, 5, 21, 27, 36, 41, 48, 66, 77, 82, 83, + 80, 74, 73, 70, 61, 46, 25, 16, 13, 16, 25, 28, 27, 21, 16, 27, 37, + 39, 37, 32, 26, 27, 34, 46, 60, 74, 84, 88, 90, 89, 86, 83, 81, 85, +101,117,133,147,152,149,143,154,163,171,167,156,139,121,105, 92, 80, + 66, 50, 45, 40, 36, 37, 45, 50, 53, 54, 58, 63, 65, 66, 66, 66, 66, + 69, 76, 79, 80, 79, 73, 62, 53, 47, 41, 47, 51, 51, 46, 40, 32, 22, + 12, 8, 15, 25, 29, 36, 44, 50, 53, 52, 47, 40, 34, 33, 34, 39, 44, + 47, 50, 48, 41, 40, 43, 38, 25, 8, 12, 16, 16, 18, 27, 31, 28, 40, + 56, 63, 62, 58, 51, 43, 55, 67, 74, 74, 69, 63, 57, 54, 50, 44, 47, + 59, 68, 76, 82, 84, 83, 77, 67, 52, 34, 15, 1, 0, 0, 0, 0, 2, + 8, 16, 32, 44, 50, 48, 40, 29, 23, 40, 58, 70, 76, 76, 73, 64, 55, + 46, 39, 39, 43, 45, 46, 44, 38, 33, 27, 26, 29, 31, 29, 25, 23, 26, + 32, 44, 59, 70, 76, 78, 76, 76, 82, 84, 82, 77, 71, 64, 55, 39, 36, + 41, 48, 48, 44, 33, 33, 34, 31, 22, 22, 21, 21, 29, 32, 28, 28, 26, + 22, 15, 13, 9, 4, 4, 2, 0, 0, 4, 20, 36, 50, 58, 62, 62, 62, + 74, 79, 79, 75, 69, 60, 47, 31, 12, 15, 15, 23, 31, 50, 67, 78, 83, + 84, 81, 75, 68, 65, 56, 41, 20, 7, 3, 13, 19, 20, 22, 21, 21, 34, + 41, 40, 33, 26, 21, 16, 11, 22, 40, 57, 68, 74, 77, 77, 76, 78, 81, + 83, 95,110,123,133,136,132,132,140,150,156,154,145,132,118,105, 94, + 84, 71, 55, 34, 29, 23, 27, 38, 47, 53, 55, 55, 53, 55, 58, 60, 61, + 60, 69, 74, 77, 76, 74, 68, 56, 47, 40, 41, 47, 50, 45, 37, 26, 19, + 15, 9, 7, 13, 20, 22, 27, 37, 41, 43, 40, 34, 25, 26, 25, 20, 22, + 28, 34, 39, 39, 34, 29, 32, 29, 20, 11, 11, 14, 15, 13, 19, 21, 19, + 26, 41, 51, 51, 47, 41, 34, 50, 62, 69, 70, 66, 62, 59, 57, 55, 53, + 48, 58, 65, 72, 77, 79, 78, 73, 63, 50, 32, 12, 3, 2, 0, 0, 0, + 2, 7, 12, 22, 37, 45, 48, 45, 34, 18, 29, 47, 59, 66, 66, 62, 54, + 45, 36, 29, 25, 29, 36, 39, 39, 37, 34, 29, 23, 23, 25, 23, 21, 21, + 26, 32, 51, 64, 74, 79, 80, 78, 79, 86, 88, 85, 79, 71, 61, 48, 38, + 50, 59, 63, 62, 55, 44, 44, 41, 34, 22, 11, 9, 18, 22, 21, 20, 21, + 18, 15, 16, 14, 9, 12, 9, 5, 0, 0, 0, 14, 32, 50, 62, 69, 72, + 70, 74, 83, 85, 84, 80, 74, 63, 46, 23, 6, 1, 9, 27, 52, 68, 78, + 83, 83, 80, 74, 66, 62, 54, 39, 18, 0, 0, 6, 12, 19, 22, 21, 27, + 38, 40, 36, 26, 27, 26, 20, 8, 0, 16, 34, 47, 56, 61, 63, 68, 77, + 80, 82, 91,104,115,123,124,120,121,128,134,139,137,130,121,111,101, + 92, 83, 72, 57, 38, 15, 8, 13, 26, 38, 47, 53, 55, 54, 48, 48, 52, + 54, 57, 63, 67, 68, 66, 63, 56, 44, 39, 34, 43, 47, 46, 39, 27, 27, + 26, 22, 14, 5, 7, 11, 12, 18, 27, 32, 32, 27, 19, 13, 16, 15, 12, + 6, 13, 19, 25, 27, 25, 21, 26, 25, 19, 11, 11, 8, 11, 9, 7, 12, + 11, 6, 20, 28, 31, 28, 26, 25, 40, 54, 60, 61, 59, 57, 59, 57, 58, + 57, 53, 54, 60, 65, 70, 71, 70, 66, 58, 45, 26, 12, 16, 16, 11, 0, + 0, 0, 4, 13, 16, 29, 40, 46, 46, 39, 23, 29, 34, 46, 52, 52, 46, + 38, 28, 20, 14, 9, 12, 19, 25, 28, 29, 29, 26, 21, 14, 13, 13, 12, + 14, 19, 34, 53, 66, 74, 79, 80, 77, 84, 90, 92, 88, 80, 70, 57, 41, + 54, 67, 74, 77, 74, 65, 53, 53, 47, 37, 22, 6, 4, 9, 12, 7, 12, + 12, 16, 21, 20, 15, 16, 18, 13, 7, 7, 5, 4, 5, 23, 45, 62, 74, + 79, 79, 76, 83, 87, 88, 87, 82, 73, 56, 31, 4, 0, 3, 29, 53, 68, + 76, 79, 79, 77, 72, 67, 63, 55, 40, 20, 0, 0, 0, 9, 19, 22, 22, + 27, 34, 34, 26, 29, 32, 29, 22, 9, 0, 0, 12, 26, 37, 45, 51, 64, + 74, 79, 84, 94,102,111,115,115,111,110,115,121,123,121,115,109,101, + 94, 88, 79, 70, 55, 37, 15, 0, 0, 9, 25, 38, 47, 52, 52, 47, 39, + 45, 47, 50, 54, 54, 53, 50, 45, 38, 34, 31, 38, 44, 46, 43, 33, 29, + 32, 32, 27, 18, 5, 0, 0, 0, 8, 18, 22, 21, 15, 6, 4, 12, 16, + 16, 12, 7, 8, 9, 12, 12, 18, 23, 25, 21, 14, 8, 8, 7, 5, 4, + 1, 2, 0, 0, 3, 5, 6, 7, 14, 28, 40, 47, 48, 53, 59, 60, 58, + 56, 56, 53, 47, 53, 57, 60, 62, 62, 58, 51, 38, 20, 25, 31, 31, 23, + 11, 0, 0, 1, 12, 19, 25, 36, 44, 46, 40, 36, 40, 43, 39, 34, 32, + 26, 16, 8, 2, 3, 1, 0, 0, 7, 14, 18, 19, 18, 15, 9, 2, 0, + 0, 1, 13, 33, 52, 65, 74, 78, 78, 80, 89, 95, 96, 91, 82, 69, 54, + 53, 70, 81, 87, 88, 82, 73, 60, 59, 53, 39, 22, 8, 2, 0, 0, 0, + 2, 14, 23, 27, 25, 18, 19, 19, 19, 19, 15, 14, 12, 6, 14, 38, 59, + 75, 84, 88, 86, 80, 85, 88, 88, 85, 76, 60, 34, 4, 0, 3, 28, 51, + 64, 72, 74, 73, 71, 72, 71, 68, 60, 45, 25, 2, 0, 0, 7, 18, 23, + 25, 25, 26, 23, 28, 33, 34, 31, 22, 8, 0, 0, 0, 9, 22, 33, 44, + 57, 68, 74, 87,100,110,117,118,113,107,100,105,109,111,109,105, 99, + 93, 88, 83, 76, 66, 53, 34, 14, 0, 0, 0, 8, 23, 37, 44, 45, 43, + 34, 41, 44, 41, 40, 38, 33, 29, 22, 26, 28, 36, 43, 45, 45, 39, 28, + 32, 36, 33, 26, 18, 5, 0, 0, 0, 1, 8, 13, 11, 9, 6, 3, 13, + 19, 21, 20, 15, 9, 6, 0, 9, 20, 27, 31, 29, 25, 16, 7, 8, 11, + 11, 7, 1, 0, 0, 0, 0, 0, 0, 2, 14, 26, 33, 34, 50, 57, 60, + 59, 55, 51, 47, 38, 51, 57, 58, 56, 55, 52, 45, 34, 32, 36, 40, 40, + 33, 18, 0, 0, 0, 9, 20, 26, 34, 41, 44, 39, 46, 51, 51, 46, 37, + 23, 9, 0, 2, 5, 7, 7, 5, 2, 0, 0, 3, 6, 7, 5, 1, 0, + 0, 0, 0, 7, 27, 47, 61, 70, 74, 76, 86, 95,100, 99, 92, 82, 68, + 52, 68, 82, 92, 97, 96, 89, 79, 65, 63, 55, 41, 25, 7, 0, 0, 0, + 0, 7, 21, 31, 33, 31, 23, 18, 25, 27, 27, 31, 31, 28, 21, 9, 33, + 58, 76, 87, 92, 92, 88, 83, 86, 88, 85, 77, 61, 36, 5, 0, 2, 26, + 46, 59, 64, 65, 73, 77, 78, 77, 74, 64, 50, 28, 5, 0, 0, 4, 15, + 23, 28, 29, 31, 31, 34, 38, 38, 33, 31, 22, 7, 0, 0, 3, 14, 27, + 40, 53, 65, 78, 92,106,119,128,130,127,120,112,106,104, 98, 98, 96, + 98, 97, 93, 85, 75, 65, 53, 36, 19, 5, 0, 0, 0, 9, 22, 31, 33, + 31, 36, 43, 44, 40, 34, 25, 27, 32, 32, 33, 37, 41, 44, 44, 41, 33, + 33, 37, 43, 45, 41, 32, 16, 2, 0, 0, 1, 4, 11, 14, 13, 11, 7, + 13, 20, 25, 25, 23, 19, 14, 11, 8, 19, 28, 34, 36, 33, 27, 21, 12, + 13, 18, 19, 16, 12, 5, 0, 0, 0, 0, 0, 4, 15, 22, 26, 40, 51, + 57, 58, 57, 54, 50, 47, 57, 64, 66, 65, 62, 57, 52, 48, 47, 46, 45, + 43, 34, 19, 0, 0, 0, 9, 20, 28, 33, 38, 43, 48, 55, 58, 56, 50, + 38, 23, 15, 19, 20, 19, 14, 12, 12, 9, 7, 6, 5, 5, 5, 3, 1, + 0, 0, 0, 0, 1, 19, 39, 55, 65, 72, 79, 89, 96,100, 98, 91, 79, + 64, 69, 83, 95,104,108,106, 97, 86, 73, 63, 55, 43, 26, 9, 0, 0, + 0, 5, 15, 27, 37, 41, 43, 41, 39, 40, 43, 45, 46, 45, 40, 32, 21, + 31, 55, 74, 86, 92, 93, 91, 86, 84, 86, 84, 75, 60, 34, 5, 0, 2, + 25, 43, 53, 57, 64, 76, 81, 82, 80, 75, 66, 51, 29, 6, 0, 0, 1, + 14, 25, 33, 38, 39, 40, 41, 43, 41, 45, 41, 33, 18, 0, 0, 2, 11, + 21, 37, 52, 67, 81, 96,112,127,139,143,140,134,128,122,114,107, 99, + 99,101,100, 96, 88, 79, 69, 57, 44, 29, 15, 1, 0, 0, 0, 8, 15, + 19, 28, 38, 44, 44, 39, 29, 22, 31, 37, 39, 40, 43, 44, 43, 40, 34, + 37, 45, 55, 60, 61, 56, 47, 32, 20, 18, 15, 18, 20, 20, 18, 16, 14, + 11, 18, 26, 31, 32, 31, 27, 23, 20, 19, 18, 20, 28, 32, 33, 31, 28, + 22, 12, 19, 25, 28, 27, 23, 19, 14, 7, 1, 0, 0, 9, 19, 25, 31, + 44, 53, 58, 60, 60, 60, 59, 63, 71, 75, 75, 74, 70, 65, 62, 60, 58, + 55, 47, 37, 23, 11, 4, 7, 9, 20, 28, 34, 40, 47, 54, 59, 61, 58, + 50, 36, 27, 34, 39, 39, 37, 31, 22, 13, 14, 15, 18, 20, 20, 20, 19, + 18, 16, 16, 16, 15, 9, 14, 29, 46, 59, 68, 79, 88, 93, 95, 92, 84, + 73, 73, 86, 97,110,119,122,119,109, 96, 83, 70, 56, 41, 27, 12, 13, + 14, 16, 23, 36, 46, 55, 60, 62, 61, 60, 60, 61, 61, 59, 56, 51, 43, + 32, 26, 48, 67, 79, 86, 88, 87, 84, 82, 84, 81, 73, 57, 33, 4, 0, + 2, 23, 40, 48, 51, 63, 76, 82, 82, 79, 74, 62, 46, 25, 13, 5, 0, + 0, 12, 26, 37, 44, 47, 48, 47, 47, 51, 53, 50, 40, 26, 8, 0, 0, + 7, 14, 32, 50, 66, 83, 99,115,132,145,152,152,149,145,140,133,124, +114,104,102,100, 96, 89, 81, 72, 62, 50, 36, 21, 7, 0, 0, 0, 0, + 7, 18, 27, 37, 41, 40, 34, 25, 16, 27, 36, 40, 43, 44, 43, 39, 34, + 36, 53, 64, 71, 74, 74, 68, 60, 48, 38, 33, 29, 29, 31, 31, 28, 22, + 15, 14, 23, 33, 38, 39, 38, 34, 29, 27, 26, 25, 23, 21, 21, 26, 28, + 31, 27, 19, 18, 27, 34, 37, 37, 33, 27, 19, 9, 3, 5, 7, 19, 26, + 31, 36, 48, 57, 62, 66, 68, 68, 68, 76, 81, 84, 83, 79, 76, 74, 71, + 67, 62, 54, 44, 32, 21, 23, 26, 25, 22, 25, 32, 39, 46, 54, 60, 61, + 56, 46, 32, 40, 50, 54, 54, 48, 41, 31, 20, 14, 19, 26, 32, 34, 34, + 34, 33, 32, 31, 32, 31, 27, 21, 29, 44, 56, 67, 75, 82, 86, 87, 83, + 76, 78, 88,101,113,125,136,139,132,120,106, 92, 80, 68, 54, 38, 33, + 31, 29, 33, 40, 51, 60, 68, 72, 74, 74, 74, 74, 74, 72, 69, 64, 58, + 51, 44, 34, 39, 56, 68, 75, 79, 79, 78, 79, 80, 78, 69, 54, 29, 3, + 0, 5, 22, 38, 46, 46, 57, 71, 78, 78, 74, 66, 53, 38, 27, 19, 14, + 11, 6, 11, 26, 39, 48, 53, 53, 51, 52, 55, 56, 53, 45, 32, 14, 7, + 0, 1, 7, 25, 44, 63, 81, 98,114,130,145,154,158,159,159,159,156, +145,130,115,101, 99, 94, 88, 81, 77, 71, 59, 45, 27, 11, 0, 0, 0, + 0, 2, 13, 23, 31, 36, 34, 28, 18, 7, 18, 28, 36, 38, 39, 38, 33, + 33, 53, 68, 77, 82, 83, 81, 77, 70, 61, 53, 46, 40, 39, 39, 39, 36, + 31, 25, 20, 32, 39, 44, 45, 43, 39, 40, 37, 32, 29, 27, 25, 22, 20, + 20, 26, 26, 21, 13, 25, 34, 40, 41, 39, 32, 23, 13, 7, 11, 13, 19, + 28, 36, 40, 44, 54, 62, 68, 72, 74, 74, 78, 85, 88, 89, 88, 84, 81, + 78, 74, 67, 59, 50, 41, 43, 44, 43, 39, 34, 29, 26, 31, 39, 50, 56, + 56, 52, 41, 34, 46, 56, 61, 59, 53, 44, 32, 21, 11, 18, 28, 38, 43, + 45, 45, 44, 41, 41, 43, 43, 41, 38, 34, 44, 55, 63, 70, 75, 78, 79, + 80, 81, 87,100,113,127,140,150,152,143,129,114,101, 89, 78, 65, 53, + 53, 51, 45, 47, 53, 60, 68, 74, 79, 80, 81, 82, 83, 81, 78, 74, 69, + 63, 57, 53, 46, 37, 43, 54, 62, 69, 74, 75, 77, 76, 74, 65, 50, 27, + 2, 1, 12, 21, 37, 44, 44, 45, 60, 67, 67, 62, 52, 44, 34, 26, 20, + 20, 20, 16, 13, 26, 39, 50, 54, 53, 50, 53, 55, 56, 54, 47, 37, 20, + 15, 4, 0, 0, 15, 36, 57, 77, 95,111,125,139,149,156,159,167,173, +173,163,147,125,107, 96, 92, 87, 87, 84, 79, 69, 55, 38, 22, 8, 3, + 0, 0, 0, 4, 14, 20, 23, 22, 19, 11, 1, 4, 14, 22, 28, 29, 36, + 39, 45, 64, 78, 85, 88, 88, 85, 81, 76, 71, 65, 60, 54, 46, 45, 44, + 41, 38, 31, 32, 37, 45, 50, 50, 47, 46, 51, 48, 44, 38, 33, 26, 23, + 22, 22, 23, 21, 19, 12, 19, 29, 37, 40, 38, 29, 20, 9, 8, 13, 18, + 20, 29, 38, 45, 47, 51, 60, 67, 72, 74, 76, 77, 85, 91, 93, 92, 89, + 85, 82, 76, 70, 67, 66, 64, 62, 60, 57, 51, 43, 37, 33, 28, 29, 38, + 45, 46, 43, 34, 33, 46, 56, 60, 58, 51, 39, 27, 15, 6, 11, 26, 39, + 47, 51, 51, 50, 48, 50, 48, 52, 53, 52, 48, 44, 53, 60, 67, 74, 79, + 83, 84, 85, 96,109,121,134,149,158,159,150,134,120,106, 95, 85, 74, + 69, 69, 67, 60, 57, 61, 67, 73, 77, 79, 81, 82, 84, 85, 84, 80, 75, + 71, 66, 65, 61, 59, 55, 48, 57, 65, 70, 72, 72, 72, 72, 68, 60, 46, + 26, 2, 6, 16, 28, 37, 44, 43, 36, 43, 50, 50, 48, 43, 36, 28, 22, + 20, 22, 27, 26, 22, 26, 38, 47, 52, 51, 48, 51, 53, 54, 53, 48, 39, + 26, 22, 11, 0, 0, 5, 25, 47, 70, 88,103,117,128,137,145,154,165, +177,181,175,158,133,111, 94, 89, 92, 93, 91, 85, 76, 63, 48, 32, 19, + 15, 11, 4, 4, 6, 7, 7, 7, 8, 6, 2, 0, 0, 0, 9, 21, 32, + 40, 46, 54, 71, 82, 88, 90, 88, 86, 84, 83, 79, 74, 71, 66, 59, 51, + 47, 45, 41, 37, 39, 43, 48, 52, 53, 51, 52, 57, 56, 54, 52, 48, 45, + 38, 29, 23, 26, 26, 20, 12, 12, 21, 29, 32, 29, 22, 12, 4, 7, 13, + 18, 21, 28, 39, 47, 53, 55, 56, 63, 68, 71, 74, 75, 82, 90, 94, 94, + 92, 88, 84, 82, 85, 85, 84, 79, 75, 72, 66, 57, 52, 51, 46, 38, 29, + 33, 34, 32, 31, 26, 26, 38, 48, 53, 51, 40, 28, 21, 13, 1, 2, 20, + 36, 46, 52, 54, 53, 57, 60, 58, 57, 59, 60, 58, 54, 52, 57, 65, 74, + 80, 84, 84, 92,102,112,123,136,149,156,156,149,134,121,108, 98, 89, + 80, 81, 81, 79, 74, 65, 68, 71, 74, 76, 77, 78, 79, 81, 83, 81, 78, + 74, 74, 76, 75, 70, 69, 68, 65, 59, 66, 68, 68, 66, 65, 64, 61, 54, + 41, 23, 5, 9, 22, 36, 45, 48, 46, 39, 28, 36, 39, 38, 33, 27, 20, + 18, 18, 25, 32, 34, 33, 32, 40, 47, 51, 48, 46, 46, 50, 51, 52, 50, + 43, 31, 28, 16, 0, 0, 0, 20, 40, 60, 81, 96,107,117,125,136,145, +156,171,181,177,161,136,113, 94, 90, 96, 98, 96, 90, 80, 68, 54, 39, + 28, 27, 23, 18, 11, 11, 9, 7, 5, 5, 5, 3, 1, 0, 0, 6, 20, + 33, 44, 52, 59, 74, 83, 88, 88, 91, 96, 96, 93, 87, 79, 76, 72, 65, + 58, 51, 48, 46, 40, 41, 45, 47, 52, 54, 53, 53, 58, 59, 58, 58, 58, + 57, 53, 45, 34, 28, 29, 26, 19, 11, 14, 18, 21, 19, 11, 11, 15, 16, + 15, 15, 21, 25, 37, 47, 55, 59, 61, 59, 62, 65, 68, 71, 77, 88, 93, + 95, 93, 89, 91, 96,100, 99, 96, 90, 83, 78, 71, 65, 65, 63, 57, 48, + 36, 29, 31, 28, 23, 16, 14, 25, 34, 39, 37, 37, 40, 38, 28, 13, 0, + 12, 29, 43, 51, 54, 55, 63, 66, 63, 61, 65, 66, 65, 61, 55, 55, 62, + 73, 79, 82, 86, 93,101,110,120,132,142,149,147,140,130,118,107, 98, + 91, 84, 88, 90, 88, 83, 73, 73, 74, 74, 74, 72, 71, 71, 74, 76, 76, + 74, 78, 81, 83, 81, 75, 77, 79, 77, 72, 63, 63, 61, 57, 56, 55, 53, + 47, 38, 22, 13, 14, 27, 41, 52, 56, 54, 47, 37, 27, 31, 29, 25, 23, + 26, 27, 27, 25, 36, 40, 43, 41, 46, 51, 52, 48, 54, 54, 50, 51, 52, + 51, 46, 34, 32, 21, 4, 0, 0, 15, 36, 51, 74, 88, 99,108,122,136, +147,143,161,173,173,159,136,113, 96, 92, 98,100, 97, 92, 82, 69, 54, + 39, 34, 36, 34, 31, 25, 19, 12, 7, 5, 6, 7, 8, 8, 7, 6, 5, + 20, 34, 46, 57, 65, 73, 81, 86, 94,101,105,106,102, 94, 84, 74, 71, + 64, 58, 55, 53, 50, 44, 36, 40, 43, 50, 53, 55, 55, 54, 55, 55, 57, + 60, 60, 58, 53, 43, 31, 33, 31, 26, 19, 14, 15, 14, 8, 16, 23, 28, + 31, 29, 26, 21, 18, 29, 43, 54, 61, 65, 65, 63, 57, 61, 65, 73, 84, + 92, 95, 95, 92,101,108,111,109,102, 95, 86, 79, 78, 77, 75, 72, 65, + 54, 40, 25, 26, 26, 23, 20, 15, 12, 18, 23, 36, 45, 50, 47, 38, 22, + 3, 5, 25, 39, 50, 55, 57, 63, 65, 61, 65, 69, 71, 70, 66, 59, 57, + 57, 69, 75, 78, 84, 91, 97,105,113,124,133,139,139,133,123,113,103, + 96, 90, 88, 93, 96, 94, 88, 79, 79, 77, 74, 71, 67, 63, 64, 65, 68, + 71, 76, 81, 84, 85, 84, 78, 83, 85, 84, 79, 70, 56, 48, 44, 45, 44, + 43, 40, 33, 23, 21, 22, 29, 44, 54, 59, 59, 54, 45, 36, 29, 27, 28, + 31, 33, 36, 36, 36, 39, 46, 50, 51, 54, 57, 56, 58, 62, 61, 57, 56, + 58, 57, 52, 41, 32, 22, 11, 5, 3, 6, 26, 43, 67, 82, 93,102,113, +128,137,136,145,156,158,147,129,111, 98, 95,100,102, 98, 91, 80, 74, + 65, 53, 47, 43, 41, 40, 38, 33, 27, 21, 14, 7, 11, 16, 20, 19, 15, + 15, 19, 34, 50, 62, 72, 80, 87, 94,102,109,113,113,108,100, 90, 81, + 74, 68, 64, 61, 58, 54, 48, 40, 32, 31, 40, 47, 52, 55, 57, 58, 57, + 53, 57, 60, 59, 55, 47, 36, 36, 34, 31, 26, 20, 16, 15, 20, 27, 36, + 40, 43, 41, 38, 33, 29, 37, 41, 48, 58, 64, 67, 66, 62, 57, 60, 69, + 83, 92, 96, 97, 98,108,114,117,113,106, 96, 90, 88, 86, 84, 80, 75, + 66, 54, 38, 22, 23, 25, 23, 22, 19, 18, 18, 22, 36, 45, 51, 48, 40, + 26, 7, 6, 20, 36, 47, 54, 58, 60, 61, 63, 66, 70, 71, 69, 65, 58, + 58, 58, 65, 71, 73, 79, 86, 98,111,121,128,130,132,132,129,125,121, +114,104, 92, 90, 96, 99, 98, 93, 83, 82, 79, 74, 67, 62, 66, 70, 71, + 71, 74, 79, 83, 86, 87, 84, 80, 84, 87, 84, 79, 69, 55, 45, 46, 47, + 41, 32, 32, 28, 29, 27, 31, 28, 41, 53, 59, 61, 59, 53, 45, 39, 37, + 38, 39, 40, 43, 44, 44, 45, 52, 56, 58, 61, 63, 62, 64, 67, 67, 64, + 65, 69, 70, 66, 56, 41, 32, 23, 18, 11, 4, 12, 36, 61, 77, 88, 96, +102,113,123,124,129,134,134,127,114,101, 98, 99,105,105,100, 92, 86, + 81, 74, 63, 60, 55, 50, 50, 47, 43, 38, 32, 23, 14, 19, 27, 32, 33, + 29, 26, 29, 36, 51, 65, 78, 87, 95,102,109,113,115,113,109,102, 92, + 84, 78, 73, 69, 65, 61, 57, 51, 44, 36, 27, 28, 34, 43, 48, 53, 56, + 55, 51, 56, 59, 60, 57, 51, 39, 36, 37, 34, 32, 28, 27, 27, 32, 39, + 45, 48, 50, 47, 44, 39, 37, 43, 47, 50, 52, 58, 62, 63, 61, 54, 56, + 66, 80, 91, 97,101,102,111,117,119,115,109, 99, 95, 92, 89, 86, 81, + 74, 63, 48, 31, 20, 22, 23, 23, 22, 21, 19, 19, 22, 28, 39, 46, 46, + 40, 27, 11, 6, 14, 29, 41, 50, 54, 57, 58, 59, 61, 63, 63, 61, 56, + 54, 56, 57, 64, 69, 69, 74, 88,101,113,123,129,134,137,139,140,137, +133,127,114,101, 93,100,104,104, 99, 88, 82, 77, 73, 73, 78, 82, 84, + 81, 76, 79, 82, 85, 87, 88, 86, 82, 82, 83, 80, 74, 63, 60, 61, 59, + 53, 43, 32, 26, 31, 33, 34, 38, 33, 37, 48, 56, 60, 60, 57, 51, 46, + 45, 45, 46, 51, 53, 52, 50, 52, 57, 61, 65, 69, 71, 69, 68, 70, 72, + 70, 73, 78, 80, 78, 70, 58, 47, 38, 28, 19, 8, 3, 28, 53, 71, 83, + 92, 97,102,107,112,113,115,113,108, 99, 96, 98,107,111,109,102, 95, + 92, 88, 79, 73, 69, 64, 58, 57, 54, 50, 44, 38, 31, 26, 28, 36, 40, + 44, 43, 37, 39, 44, 51, 67, 81, 92,101,107,111,113,112,109,103, 97, + 90, 84, 77, 73, 68, 64, 59, 54, 48, 41, 34, 28, 28, 31, 32, 36, 41, + 46, 46, 47, 54, 58, 59, 57, 51, 40, 33, 36, 37, 36, 34, 34, 36, 40, + 46, 51, 52, 52, 48, 45, 40, 39, 45, 52, 54, 55, 54, 54, 57, 56, 51, + 51, 63, 79, 89, 97,103,107,111,117,118,115,110,102, 96, 92, 89, 84, + 78, 69, 55, 47, 38, 41, 43, 40, 33, 22, 20, 18, 18, 19, 23, 28, 36, + 39, 36, 26, 12, 5, 11, 23, 34, 43, 47, 52, 53, 50, 51, 52, 50, 46, + 43, 50, 53, 59, 66, 68, 69, 73, 86, 99,110,119,125,130,136,140,145, +145,142,134,121,106,100,105,111,111,104, 94, 83, 81, 84, 89, 95,100, +102,100, 94, 89, 83, 84, 87, 88, 87, 84, 80, 76, 74, 72, 72, 72, 71, + 68, 61, 51, 38, 26, 32, 36, 38, 41, 40, 32, 41, 50, 56, 58, 57, 53, + 50, 48, 54, 60, 63, 64, 60, 54, 56, 63, 67, 72, 75, 77, 76, 71, 73, + 74, 74, 78, 83, 86, 85, 79, 69, 59, 48, 38, 25, 12, 11, 18, 43, 63, + 76, 85, 91, 94, 96, 97, 99, 98, 95, 90, 88, 98,108,115,118,113,104, +101, 97, 92, 83, 77, 74, 70, 66, 64, 60, 54, 47, 40, 36, 38, 39, 41, + 46, 50, 51, 47, 46, 51, 54, 68, 82, 93,102,107,108,106,102, 98, 93, + 88, 83, 77, 72, 67, 62, 57, 51, 44, 38, 32, 26, 23, 26, 27, 29, 31, + 32, 33, 37, 43, 50, 55, 56, 54, 47, 37, 29, 33, 36, 37, 37, 37, 38, + 43, 47, 50, 50, 47, 44, 40, 37, 37, 44, 52, 56, 58, 57, 54, 51, 50, + 47, 45, 61, 75, 87, 96,105,112,114,114,115,113,110,103, 95, 90, 85, + 80, 73, 66, 66, 62, 60, 64, 65, 62, 55, 41, 23, 14, 13, 13, 16, 20, + 23, 28, 28, 22, 12, 3, 6, 16, 27, 33, 40, 46, 46, 39, 37, 34, 31, + 27, 38, 45, 54, 64, 68, 67, 73, 75, 83, 94,103,110,115,122,128,134, +140,143,142,134,121,109,102,112,117,117,110, 98, 92, 93, 96,102,109, +114,118,115,110,102, 95, 86, 83, 84, 84, 83, 81, 80, 79, 79, 78, 77, + 74, 71, 62, 52, 40, 28, 31, 36, 39, 41, 43, 39, 33, 40, 47, 52, 53, + 51, 50, 58, 65, 70, 73, 71, 66, 57, 60, 69, 74, 76, 80, 83, 81, 76, + 74, 77, 78, 79, 84, 88, 88, 84, 74, 66, 56, 44, 31, 16, 15, 14, 28, + 51, 67, 77, 83, 85, 86, 86, 87, 88, 87, 89,100,109,117,122,123,115, +108,106,102, 95, 84, 79, 75, 74, 74, 70, 64, 59, 55, 48, 41, 47, 52, + 53, 51, 52, 54, 53, 51, 56, 59, 68, 82, 92, 99,102,100, 95, 89, 84, + 78, 74, 69, 65, 60, 55, 50, 43, 34, 27, 20, 15, 12, 14, 18, 20, 22, + 25, 26, 27, 31, 37, 44, 48, 50, 47, 40, 31, 23, 28, 31, 32, 32, 32, + 33, 38, 41, 43, 40, 38, 34, 31, 29, 29, 39, 50, 56, 59, 58, 55, 51, + 47, 43, 43, 60, 75, 86, 95,105,114,120,121,117,111,108,102, 96, 88, + 80, 74, 74, 78, 78, 75, 78, 80, 80, 77, 71, 58, 39, 16, 7, 7, 8, + 11, 13, 16, 20, 18, 11, 0, 1, 11, 19, 25, 33, 38, 39, 31, 20, 16, + 13, 25, 34, 48, 61, 68, 69, 70, 76, 79, 79, 87, 94, 99,104,110,117, +123,129,134,133,128,115,109,110,119,123,121,113,101,100,102,106,112, +118,124,128,127,120,112,102, 92, 81, 78, 79, 79, 79, 79, 80, 80, 79, + 77, 73, 66, 57, 47, 37, 27, 27, 32, 36, 37, 43, 41, 37, 31, 37, 41, + 44, 48, 55, 64, 71, 76, 77, 75, 70, 60, 67, 75, 79, 79, 84, 87, 86, + 81, 77, 79, 81, 80, 83, 86, 87, 83, 75, 67, 58, 47, 33, 19, 15, 13, + 11, 34, 53, 66, 73, 75, 74, 73, 75, 79, 87, 99,110,118,123,127,125, +117,114,113,107, 97, 85, 78, 79, 81, 79, 75, 69, 66, 63, 56, 48, 57, + 63, 66, 65, 60, 54, 55, 54, 60, 64, 67, 79, 88, 94, 94, 88, 81, 74, + 71, 62, 53, 50, 46, 43, 37, 29, 21, 16, 12, 7, 3, 2, 3, 5, 9, + 13, 15, 16, 18, 21, 27, 34, 38, 39, 37, 29, 20, 16, 21, 23, 23, 22, + 21, 21, 25, 28, 28, 26, 23, 20, 22, 21, 21, 31, 44, 53, 57, 56, 53, + 46, 41, 34, 47, 67, 82, 92, 99,103,114,121,123,119,112,104,101, 95, + 88, 76, 75, 83, 88, 89, 87, 89, 92, 91, 88, 81, 70, 53, 28, 4, 6, + 9, 6, 6, 6, 11, 12, 7, 0, 0, 6, 13, 15, 22, 28, 28, 20, 8, + 2, 13, 28, 43, 56, 65, 70, 70, 74, 80, 83, 82, 79, 84, 88, 96,102, +103,110,115,120,120,115,112,109,120,128,129,124,113,104,104,106,110, +114,121,127,130,129,123,114,105, 95, 84, 71, 70, 71, 73, 75, 76, 76, + 74, 71, 64, 56, 46, 37, 29, 23, 22, 25, 29, 27, 37, 40, 39, 34, 26, + 29, 39, 47, 55, 65, 73, 78, 79, 78, 72, 62, 74, 80, 84, 84, 88, 90, + 89, 84, 82, 81, 84, 84, 82, 80, 81, 77, 71, 66, 61, 53, 39, 22, 12, + 8, 6, 14, 36, 51, 59, 62, 60, 57, 63, 78, 93,106,115,122,125,127, +124,122,123,120,112,100, 87, 76, 83, 85, 84, 79, 73, 69, 67, 61, 55, + 64, 72, 76, 75, 70, 60, 53, 57, 64, 69, 71, 77, 84, 87, 84, 77, 79, + 79, 74, 64, 48, 28, 23, 20, 16, 19, 18, 15, 9, 5, 4, 7, 9, 8, + 6, 2, 3, 5, 5, 8, 14, 21, 25, 26, 23, 16, 7, 8, 12, 13, 11, + 8, 5, 4, 7, 11, 11, 8, 14, 20, 23, 21, 13, 23, 37, 47, 52, 51, + 45, 38, 31, 29, 54, 74, 88, 97,103,106,112,120,121,118,110,101, 96, + 92, 84, 75, 85, 93, 98,101, 99, 96, 98, 97, 95, 88, 78, 62, 39, 13, + 5, 9, 7, 2, 3, 4, 6, 4, 0, 0, 3, 8, 9, 11, 15, 15, 7, + 0, 6, 19, 33, 47, 59, 66, 70, 70, 79, 84, 87, 85, 79, 73, 79, 90, + 96, 96, 97,102,106,106,109,107,120,130,136,134,125,113,103,103,109, +115,120,120,122,125,124,119,112,102, 93, 82, 70, 57, 58, 61, 65, 67, + 67, 65, 59, 51, 39, 29, 22, 23, 22, 22, 20, 19, 16, 27, 34, 37, 36, + 32, 33, 38, 45, 53, 63, 72, 78, 80, 79, 74, 63, 78, 85, 88, 89, 89, + 92, 91, 86, 88, 88, 87, 87, 84, 80, 74, 67, 69, 69, 67, 61, 53, 39, + 21, 3, 0, 0, 14, 31, 41, 45, 44, 39, 62, 81, 96,107,115,120,122, +123,128,133,134,129,119,105, 90, 75, 83, 86, 86, 83, 77, 71, 67, 61, + 63, 72, 79, 82, 81, 75, 64, 50, 61, 70, 74, 77, 77, 80, 81, 77, 80, + 82, 81, 75, 64, 46, 23, 4, 11, 14, 16, 15, 12, 6, 3, 6, 9, 12, + 13, 11, 6, 1, 0, 0, 0, 0, 5, 9, 11, 8, 2, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 9, 15, 16, 14, 6, 16, 29, 39, 43, + 40, 33, 23, 15, 37, 60, 79, 92,102,107,108,106,113,117,113,107, 99, + 90, 84, 76, 85, 95,104,111,113,112,107,100, 99, 97, 92, 83, 69, 48, + 23, 1, 6, 5, 3, 3, 2, 3, 1, 0, 0, 1, 5, 6, 4, 2, 1, + 0, 3, 13, 22, 32, 44, 55, 62, 65, 75, 84, 88, 91, 88, 82, 74, 67, + 79, 86, 89, 89, 91, 96,102,104,117,129,139,142,136,124,111, 97,105, +114,123,129,132,129,123,115,112,106, 97, 88, 78, 65, 52, 39, 44, 48, + 52, 52, 50, 43, 33, 21, 27, 29, 29, 27, 29, 29, 26, 29, 19, 25, 32, + 36, 41, 47, 53, 59, 64, 64, 70, 77, 80, 79, 74, 68, 81, 88, 92, 94, + 93, 92, 92, 94, 96, 97, 96, 93, 89, 84, 80, 80, 80, 75, 72, 69, 64, + 55, 41, 25, 4, 0, 0, 8, 19, 25, 29, 45, 67, 84, 96,105,112,113, +127,137,145,150,150,142,128,112, 96, 81, 79, 84, 85, 84, 81, 77, 73, + 70, 72, 77, 82, 84, 82, 75, 66, 61, 68, 76, 81, 84, 84, 83, 82, 81, + 82, 82, 79, 72, 59, 40, 22, 14, 8, 11, 12, 11, 9, 16, 23, 27, 25, + 21, 16, 13, 8, 6, 5, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 6, 3, 3, 12, 22, 29, + 29, 25, 15, 7, 27, 48, 69, 85, 97,107,111,112,109,106,108,108,104, + 98, 92, 85, 88, 96,107,117,124,127,124,118,110, 99, 97, 93, 85, 74, + 55, 32, 9, 3, 3, 4, 3, 1, 0, 0, 0, 4, 5, 4, 5, 4, 0, + 0, 2, 12, 22, 31, 39, 46, 53, 60, 68, 77, 85, 90, 92, 90, 84, 78, + 71, 70, 75, 80, 85, 89, 95,102,112,123,136,143,143,136,123,109,104, +106,115,125,133,137,136,132,124,118,109,100, 91, 81, 72, 61, 52, 40, + 29, 32, 33, 32, 27, 22, 23, 31, 33, 32, 31, 37, 40, 38, 41, 32, 23, + 27, 38, 48, 57, 63, 70, 73, 72, 69, 75, 79, 78, 73, 73, 84, 92, 96, + 98, 99, 99,100,102,106,107,105,102, 97, 93, 91, 92, 92, 88, 81, 74, + 71, 66, 57, 44, 26, 6, 0, 0, 9, 16, 34, 55, 74, 87, 96,103,107, +121,142,158,165,171,169,158,137,118,100, 85, 82, 81, 84, 84, 83, 80, + 77, 75, 77, 81, 84, 85, 84, 81, 77, 73, 76, 84, 89, 92, 92, 90, 87, + 84, 82, 79, 74, 65, 52, 34, 32, 26, 21, 18, 18, 19, 26, 36, 44, 46, + 45, 40, 34, 27, 21, 16, 14, 13, 9, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 7, 16, + 20, 18, 9, 1, 19, 40, 59, 78, 92,103,113,117,117,114,111,104,100, +100, 96, 93, 89, 98,107,119,129,137,139,134,125,117,105,100, 96, 88, + 76, 59, 38, 16, 1, 0, 1, 0, 0, 0, 0, 3, 9, 12, 12, 9, 6, + 4, 5, 12, 21, 29, 37, 41, 47, 52, 58, 66, 74, 82, 88, 90, 88, 84, + 79, 75, 74, 74, 80, 87, 93,100,109,118,129,139,145,143,136,123,111, +106,100,110,120,129,136,139,137,133,127,119,110,101, 92, 82, 72, 61, + 50, 36, 22, 25, 27, 26, 23, 21, 27, 28, 27, 34, 41, 46, 47, 47, 40, + 32, 25, 38, 52, 62, 70, 74, 76, 74, 73, 74, 78, 77, 74, 77, 86, 93, + 98,101,102,103,105,109,113,113,112,108,103,100, 97,100,102, 99, 93, + 84, 74, 72, 67, 58, 45, 29, 14, 3, 14, 28, 46, 64, 79, 90, 97,102, +108,129,154,171,181,186,181,167,143,122,103, 88, 87, 88, 87, 85, 83, + 81, 79, 77, 79, 83, 87, 89, 90, 88, 86, 82, 84, 92, 99,102,101, 96, + 91, 85, 80, 74, 65, 54, 39, 36, 37, 34, 31, 29, 28, 34, 44, 51, 57, + 60, 58, 54, 46, 38, 29, 22, 18, 16, 14, 8, 1, 0, 0, 0, 0, 0, + 5, 6, 4, 0, 1, 7, 11, 7, 1, 0, 0, 0, 0, 0, 0, 0, 4, + 9, 11, 6, 4, 3, 27, 50, 67, 83, 96,108,115,120,120,119,113,106, + 96, 95, 94, 93, 95,105,114,127,137,145,147,140,130,120,109,102, 97, + 89, 77, 61, 41, 21, 6, 0, 0, 0, 0, 0, 0, 7, 14, 18, 16, 15, + 12, 11, 12, 18, 26, 32, 38, 41, 44, 47, 52, 59, 68, 76, 81, 84, 84, + 83, 79, 78, 78, 78, 79, 88, 96,104,113,121,129,137,140,139,132,122, +112,104, 99,100,109,119,128,133,137,136,132,125,118,109, 99, 88, 78, + 67, 54, 39, 25, 26, 27, 27, 25, 21, 20, 21, 27, 34, 41, 48, 51, 47, + 43, 37, 31, 38, 51, 63, 71, 75, 75, 74, 71, 76, 79, 77, 78, 81, 86, + 92, 96,100,102,103,106,111,114,115,113,110,105,102,102,104,109,108, +104, 96, 85, 74, 72, 67, 58, 48, 38, 28, 27, 41, 55, 70, 82, 91, 96, +100,110,130,156,173,183,188,183,169,145,123,104, 91, 92, 93, 94, 92, + 88, 81, 77, 76, 78, 83, 88, 92, 94, 95, 93, 90, 92,102,108,111,109, +102, 94, 84, 75, 65, 52, 37, 23, 32, 37, 39, 37, 36, 40, 47, 54, 59, + 63, 66, 65, 60, 53, 41, 32, 22, 18, 16, 15, 11, 3, 0, 0, 0, 0, + 5, 12, 13, 9, 3, 6, 13, 15, 9, 2, 0, 0, 0, 0, 0, 0, 0, + 1, 4, 7, 11, 9, 6, 32, 53, 70, 85, 97,108,115,119,120,119,114, +106, 95, 89, 90, 90, 96,106,115,128,139,145,147,140,129,119,109,106, +100, 90, 78, 61, 43, 29, 16, 3, 0, 0, 0, 0, 0, 8, 16, 19, 19, + 16, 14, 13, 13, 19, 28, 37, 40, 41, 39, 38, 48, 55, 59, 66, 72, 75, + 77, 77, 78, 79, 79, 79, 79, 87, 95,104,113,119,124,128,129,129,125, +120,113,104, 95, 89, 96,105,113,121,127,129,128,125,121,113,102, 92, + 80, 68, 54, 37, 23, 27, 28, 27, 23, 20, 18, 20, 23, 29, 37, 44, 47, + 40, 41, 40, 34, 40, 52, 59, 67, 71, 70, 68, 72, 79, 80, 78, 79, 83, + 84, 88, 92, 95, 96, 98,102,107,112,112,110,106,103,101,101,105,112, +114,113,106, 96, 84, 73, 71, 66, 61, 56, 48, 41, 47, 59, 70, 80, 88, + 94, 97,108,125,145,159,171,177,173,161,140,121,104, 93, 96, 98, 99, + 97, 92, 84, 74, 72, 74, 79, 86, 92, 96, 99, 99, 96,100,110,118,120, +115,106, 95, 83, 69, 53, 36, 18, 11, 22, 32, 37, 38, 43, 50, 54, 58, + 61, 64, 67, 67, 62, 53, 41, 29, 19, 13, 13, 14, 9, 3, 0, 0, 1, + 1, 8, 15, 16, 13, 6, 9, 14, 14, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 11, 16, 16, 16, 13, 29, 52, 68, 83, 94,102,109,113,113,113, +110,103, 92, 83, 84, 84, 90,101,110,121,132,139,139,134,125,115,113, +109,101, 91, 78, 63, 53, 40, 25, 9, 1, 0, 0, 0, 0, 7, 14, 16, + 16, 14, 11, 8, 9, 18, 29, 38, 43, 44, 41, 40, 48, 51, 55, 59, 62, + 64, 66, 70, 73, 76, 79, 79, 79, 83, 92,100,107,111,113,113,114,115, +115,113,111,105, 96, 84, 82, 90, 97,105,112,114,117,118,115,111,102, + 90, 78, 63, 47, 29, 26, 27, 27, 25, 20, 16, 14, 14, 15, 20, 26, 32, + 38, 31, 39, 40, 38, 43, 54, 60, 62, 62, 61, 66, 76, 82, 83, 80, 78, + 83, 84, 82, 84, 86, 88, 90, 93, 99,103,104,102,100, 97, 96, 98,103, +111,117,118,113,103, 91, 77, 71, 70, 68, 66, 61, 54, 48, 57, 66, 76, + 84, 89, 94,102,114,127,136,145,152,154,145,132,115,102, 93, 96,101, +102,101, 94, 84, 74, 66, 73, 77, 83, 90, 96,101,102,100,106,117,125, +127,120,108, 94, 79, 66, 58, 45, 26, 13, 11, 21, 29, 37, 46, 52, 55, + 57, 58, 60, 63, 63, 58, 48, 37, 23, 20, 16, 9, 11, 7, 2, 0, 4, + 6, 5, 11, 18, 18, 14, 8, 11, 13, 12, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 16, 26, 29, 27, 20, 22, 44, 61, 76, 86, 94, 99,102,102, +103,102, 97, 89, 79, 75, 74, 80, 90, 99,109,119,127,129,127,120,113, +114,110,102, 90, 77, 69, 59, 46, 29, 16, 8, 0, 0, 0, 0, 3, 9, + 12, 11, 7, 4, 2, 4, 14, 26, 34, 40, 43, 41, 44, 53, 56, 55, 56, + 62, 66, 68, 68, 66, 71, 75, 77, 76, 77, 84, 92, 96, 98, 98, 98, 99, +101,103,105,105,102, 96, 86, 74, 74, 81, 88, 93, 98,102,105,105,102, + 95, 84, 70, 54, 36, 27, 27, 26, 23, 18, 12, 8, 5, 4, 4, 6, 9, + 16, 22, 23, 33, 38, 38, 46, 55, 60, 61, 57, 56, 70, 80, 85, 86, 83, + 77, 79, 81, 79, 74, 75, 76, 79, 82, 88, 93, 94, 93, 92, 97,101,101, + 99,108,115,119,117,108, 95, 81, 69, 70, 71, 70, 66, 60, 51, 48, 57, + 66, 75, 83, 89, 93,101,108,113,121,127,130,128,121,110, 98, 91, 96, +101,103,102, 95, 85, 72, 63, 74, 79, 83, 88, 96,102,104,102,110,122, +130,130,122,108, 92, 75, 70, 64, 54, 36, 23, 20, 12, 19, 34, 45, 50, + 52, 52, 52, 53, 56, 56, 52, 43, 31, 23, 21, 16, 9, 8, 6, 1, 2, + 7, 11, 11, 11, 18, 18, 14, 8, 9, 11, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 21, 34, 41, 43, 38, 29, 34, 51, 65, 76, 83, 88, 89, + 90, 91, 92, 88, 83, 75, 65, 62, 65, 76, 85, 96,106,114,119,119,113, +111,113,108,100, 89, 77, 70, 60, 48, 31, 25, 16, 7, 0, 0, 0, 0, + 3, 4, 3, 0, 0, 0, 0, 6, 18, 28, 36, 39, 41, 44, 53, 57, 58, + 60, 61, 63, 67, 67, 65, 63, 70, 72, 71, 69, 74, 80, 84, 84, 83, 88, + 93, 95, 95, 93, 96, 96, 92, 84, 74, 66, 68, 68, 74, 79, 84, 89, 92, + 90, 84, 74, 58, 40, 27, 27, 28, 27, 21, 12, 3, 0, 0, 0, 0, 0, + 0, 0, 9, 15, 27, 33, 41, 51, 57, 61, 60, 56, 65, 73, 81, 87, 88, + 85, 80, 74, 76, 74, 69, 62, 67, 73, 75, 76, 82, 84, 87, 92, 96, 99, + 98, 95,103,111,115,113,105, 93, 80, 66, 68, 70, 70, 66, 60, 51, 40, + 44, 56, 66, 74, 82, 88, 93, 96, 96,101,108,112,113,110,102, 93, 85, + 92, 98,102,101, 95, 85, 72, 57, 70, 78, 82, 88, 96,102,104,102,111, +122,130,130,122,107, 91, 74, 71, 66, 57, 41, 27, 23, 15, 13, 31, 41, + 46, 51, 57, 59, 56, 48, 48, 44, 36, 25, 20, 16, 12, 5, 8, 6, 1, + 2, 9, 14, 14, 9, 16, 16, 13, 7, 9, 8, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 23, 40, 50, 53, 51, 45, 38, 50, 57, 64, 71, 74, + 76, 77, 78, 79, 79, 76, 71, 63, 54, 48, 59, 71, 84, 95,104,111,113, +110,104,106,103, 96, 88, 77, 65, 56, 43, 38, 33, 26, 15, 4, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 11, 21, 29, 37, 41, 44, 47, 51, + 55, 60, 64, 65, 62, 61, 59, 54, 61, 63, 63, 61, 60, 65, 68, 67, 76, + 83, 88, 90, 91, 89, 86, 84, 82, 78, 70, 59, 59, 60, 60, 58, 65, 73, + 77, 77, 72, 61, 44, 32, 38, 41, 40, 36, 26, 14, 2, 0, 0, 0, 0, + 0, 5, 9, 13, 22, 28, 38, 46, 54, 58, 60, 59, 63, 72, 79, 83, 87, + 89, 88, 84, 79, 76, 74, 71, 67, 64, 69, 73, 74, 75, 79, 83, 87, 90, + 92, 90, 92, 98,104,107,104, 96, 85, 72, 63, 66, 68, 67, 64, 57, 47, + 38, 43, 54, 60, 65, 75, 84, 88, 92, 92, 88, 88, 94, 96, 96, 92, 86, + 79, 85, 92, 96, 96, 92, 83, 71, 56, 62, 72, 78, 87, 95,101,102,100, +109,119,125,125,117,103, 88, 73, 70, 66, 58, 44, 26, 23, 18, 21, 31, + 40, 50, 57, 61, 62, 59, 51, 40, 36, 28, 20, 14, 8, 5, 9, 12, 9, + 3, 0, 7, 14, 15, 12, 16, 16, 12, 7, 9, 13, 13, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 22, 40, 53, 59, 59, 55, 51, 52, 59, 62, 61, + 59, 70, 77, 79, 76, 72, 65, 62, 57, 52, 44, 56, 64, 73, 85, 96,103, +106,105,101, 96, 95, 90, 83, 74, 64, 55, 47, 44, 39, 33, 23, 12, 0, + 1, 2, 3, 5, 7, 7, 5, 6, 5, 2, 6, 18, 27, 36, 43, 47, 52, + 56, 58, 56, 61, 65, 65, 59, 48, 43, 51, 53, 52, 50, 47, 46, 48, 55, + 65, 74, 79, 84, 85, 84, 83, 82, 78, 68, 61, 53, 45, 48, 48, 46, 44, + 53, 59, 60, 58, 57, 51, 44, 47, 50, 46, 39, 27, 13, 5, 2, 0, 1, + 2, 4, 11, 15, 19, 26, 33, 41, 48, 54, 56, 58, 65, 72, 78, 84, 88, + 88, 89, 90, 88, 84, 81, 78, 74, 71, 66, 64, 68, 71, 72, 74, 78, 79, + 80, 80, 82, 87, 91, 94, 93, 89, 81, 70, 56, 60, 62, 63, 62, 59, 53, + 45, 40, 44, 43, 48, 57, 69, 78, 84, 87, 88, 84, 77, 73, 77, 79, 77, + 74, 71, 78, 84, 88, 88, 84, 76, 65, 53, 55, 64, 74, 84, 92, 97, 98, + 96,104,113,117,115,108, 96, 82, 74, 69, 65, 57, 44, 25, 21, 19, 28, + 39, 48, 56, 61, 64, 64, 60, 53, 44, 34, 26, 16, 8, 7, 8, 14, 18, + 14, 7, 2, 4, 12, 15, 11, 16, 16, 12, 6, 13, 20, 22, 15, 6, 2, + 0, 0, 1, 6, 8, 5, 1, 16, 37, 53, 61, 64, 63, 59, 57, 60, 63, + 64, 68, 71, 75, 79, 77, 72, 63, 53, 48, 45, 40, 47, 56, 61, 76, 86, + 94, 98, 98, 95, 91, 85, 80, 74, 67, 59, 53, 47, 45, 43, 38, 28, 19, + 12, 15, 15, 16, 18, 16, 14, 8, 6, 4, 0, 5, 15, 26, 36, 43, 48, + 52, 57, 58, 56, 60, 65, 66, 61, 51, 38, 38, 40, 38, 34, 31, 29, 32, + 39, 51, 61, 70, 75, 78, 79, 78, 77, 73, 62, 53, 46, 36, 32, 32, 29, + 23, 32, 39, 53, 60, 61, 57, 50, 52, 52, 47, 39, 27, 13, 11, 11, 14, + 15, 15, 12, 12, 16, 21, 26, 34, 41, 47, 52, 55, 65, 73, 78, 82, 87, + 89, 89, 87, 88, 88, 86, 83, 79, 75, 71, 66, 60, 60, 63, 66, 68, 69, + 69, 67, 70, 75, 79, 80, 80, 77, 71, 61, 48, 51, 54, 55, 55, 54, 51, + 46, 40, 36, 39, 37, 34, 48, 60, 70, 76, 79, 79, 76, 71, 64, 58, 55, + 57, 57, 62, 68, 73, 75, 75, 72, 65, 57, 50, 46, 57, 71, 81, 88, 92, + 92, 92, 98,104,106,104, 97, 87, 75, 74, 69, 63, 54, 41, 25, 15, 19, + 32, 44, 53, 58, 62, 63, 62, 59, 53, 45, 37, 27, 16, 5, 8, 11, 16, + 21, 18, 11, 3, 2, 9, 13, 12, 18, 18, 12, 3, 15, 25, 29, 25, 16, + 11, 7, 4, 11, 16, 19, 18, 9, 9, 31, 50, 61, 66, 67, 65, 62, 63, + 66, 71, 75, 79, 81, 78, 74, 69, 61, 50, 37, 36, 32, 33, 44, 51, 65, + 76, 84, 88, 88, 88, 84, 81, 75, 68, 57, 48, 45, 43, 41, 40, 37, 32, + 26, 26, 28, 29, 29, 28, 25, 19, 9, 4, 0, 0, 4, 15, 25, 34, 40, + 45, 48, 53, 54, 51, 58, 64, 66, 61, 51, 43, 38, 27, 21, 16, 12, 9, + 12, 25, 41, 55, 62, 64, 68, 69, 68, 67, 63, 52, 44, 40, 29, 15, 13, + 8, 4, 21, 40, 55, 62, 63, 61, 56, 53, 51, 46, 39, 28, 15, 20, 25, + 27, 27, 25, 20, 12, 13, 19, 21, 31, 39, 45, 47, 57, 68, 76, 80, 84, + 86, 88, 88, 85, 83, 84, 83, 80, 76, 71, 65, 60, 55, 50, 53, 56, 57, + 56, 54, 50, 59, 65, 67, 65, 61, 54, 44, 36, 38, 40, 41, 41, 41, 40, + 38, 36, 33, 32, 31, 33, 36, 38, 47, 56, 62, 66, 66, 63, 59, 54, 50, + 46, 45, 46, 50, 54, 57, 58, 57, 55, 53, 52, 48, 41, 51, 65, 77, 84, + 87, 85, 84, 90, 94, 95, 92, 87, 78, 76, 74, 67, 59, 50, 37, 22, 7, + 13, 29, 43, 52, 56, 58, 58, 57, 54, 48, 44, 37, 28, 21, 14, 13, 18, + 18, 21, 19, 12, 3, 2, 7, 11, 12, 19, 19, 14, 6, 12, 23, 31, 29, + 22, 16, 11, 11, 18, 23, 28, 29, 25, 13, 25, 44, 58, 66, 69, 67, 64, + 64, 69, 73, 78, 84, 87, 84, 77, 63, 55, 44, 31, 25, 21, 18, 27, 40, + 54, 64, 71, 75, 77, 77, 77, 75, 71, 63, 52, 36, 31, 31, 32, 32, 34, + 33, 29, 37, 39, 39, 39, 37, 31, 21, 9, 0, 0, 0, 9, 25, 36, 41, + 40, 39, 41, 44, 44, 45, 57, 64, 65, 60, 48, 44, 40, 28, 12, 0, 0, + 0, 8, 23, 40, 55, 62, 64, 60, 55, 54, 53, 47, 36, 37, 33, 23, 7, + 0, 0, 6, 26, 43, 55, 61, 64, 63, 60, 55, 48, 44, 38, 28, 28, 32, + 36, 37, 36, 31, 23, 14, 5, 12, 12, 23, 33, 40, 44, 57, 68, 76, 79, + 80, 83, 84, 83, 80, 74, 75, 75, 73, 67, 60, 54, 48, 45, 40, 36, 40, + 40, 39, 34, 34, 44, 50, 50, 44, 34, 23, 25, 26, 27, 32, 33, 29, 25, + 21, 21, 21, 23, 26, 28, 31, 33, 36, 37, 37, 41, 44, 44, 41, 39, 37, + 34, 33, 32, 32, 34, 36, 38, 46, 52, 54, 54, 51, 46, 38, 43, 59, 71, + 78, 79, 77, 75, 81, 84, 84, 81, 76, 74, 75, 72, 63, 53, 41, 29, 16, + 5, 2, 20, 36, 45, 50, 51, 50, 47, 44, 41, 40, 36, 36, 33, 25, 14, + 20, 20, 19, 16, 11, 3, 1, 5, 8, 12, 19, 21, 16, 7, 5, 18, 26, + 26, 20, 14, 8, 14, 21, 27, 33, 38, 37, 28, 21, 38, 53, 62, 67, 66, + 63, 67, 74, 75, 76, 83, 87, 87, 81, 70, 52, 36, 23, 13, 9, 4, 18, + 31, 43, 51, 57, 60, 63, 64, 66, 67, 65, 58, 46, 28, 12, 14, 19, 27, + 31, 29, 37, 43, 45, 45, 44, 40, 33, 22, 8, 0, 0, 0, 14, 31, 44, + 51, 51, 45, 36, 31, 29, 44, 56, 63, 63, 57, 45, 43, 38, 26, 9, 0, + 0, 0, 6, 21, 39, 53, 61, 62, 57, 44, 34, 32, 27, 27, 31, 27, 16, + 1, 0, 0, 13, 29, 44, 54, 60, 63, 63, 63, 59, 53, 43, 38, 44, 46, + 44, 44, 45, 40, 33, 23, 13, 3, 2, 1, 14, 26, 33, 38, 52, 64, 72, + 74, 74, 75, 75, 74, 72, 66, 61, 62, 59, 52, 50, 48, 41, 29, 27, 26, + 20, 21, 19, 14, 16, 23, 28, 25, 16, 9, 13, 21, 32, 37, 38, 37, 33, + 27, 19, 11, 5, 11, 16, 22, 26, 28, 28, 27, 25, 19, 14, 13, 13, 14, + 15, 15, 15, 15, 15, 15, 15, 29, 41, 50, 54, 53, 48, 43, 37, 44, 53, + 65, 71, 72, 67, 64, 70, 73, 73, 71, 66, 71, 72, 68, 58, 47, 40, 27, + 9, 0, 0, 7, 23, 33, 37, 38, 37, 34, 32, 32, 33, 38, 43, 43, 34, + 21, 16, 16, 13, 13, 9, 3, 0, 6, 8, 12, 19, 22, 20, 12, 5, 5, + 14, 16, 12, 6, 7, 14, 20, 28, 36, 43, 46, 41, 34, 40, 48, 58, 62, + 62, 59, 72, 79, 82, 80, 78, 84, 85, 81, 72, 57, 37, 14, 5, 0, 0, + 9, 22, 31, 37, 46, 52, 52, 50, 54, 58, 57, 51, 38, 20, 2, 3, 13, + 20, 23, 32, 41, 46, 46, 46, 45, 40, 33, 22, 9, 0, 0, 0, 14, 33, + 47, 55, 56, 51, 41, 31, 27, 43, 55, 61, 61, 54, 41, 37, 32, 20, 8, + 8, 7, 12, 11, 19, 36, 51, 58, 59, 52, 36, 15, 11, 9, 20, 25, 21, + 18, 14, 6, 5, 19, 33, 45, 53, 57, 60, 62, 63, 61, 55, 52, 53, 58, + 60, 59, 52, 51, 44, 34, 21, 11, 2, 0, 0, 4, 15, 26, 32, 41, 55, + 63, 66, 65, 65, 65, 64, 61, 56, 46, 44, 39, 46, 52, 52, 46, 34, 18, + 13, 9, 2, 0, 0, 0, 3, 5, 0, 0, 3, 19, 32, 39, 41, 40, 37, + 32, 27, 26, 21, 14, 4, 5, 12, 16, 18, 18, 15, 11, 4, 0, 0, 0, + 0, 4, 7, 7, 4, 0, 0, 3, 20, 36, 45, 48, 47, 43, 36, 37, 47, + 55, 58, 63, 63, 57, 52, 58, 61, 62, 60, 61, 66, 67, 62, 56, 57, 54, + 45, 29, 7, 0, 0, 8, 18, 22, 23, 23, 25, 21, 22, 27, 38, 46, 47, + 41, 28, 12, 8, 8, 9, 7, 3, 5, 7, 9, 11, 19, 23, 23, 16, 6, + 0, 0, 0, 0, 0, 4, 9, 16, 25, 34, 45, 51, 52, 47, 53, 56, 56, + 57, 56, 63, 76, 85, 89, 89, 84, 78, 81, 78, 71, 58, 40, 20, 1, 0, + 0, 2, 13, 19, 29, 39, 44, 43, 36, 41, 46, 47, 41, 29, 18, 4, 0, + 6, 13, 27, 39, 46, 47, 46, 45, 44, 40, 34, 25, 13, 0, 0, 0, 9, + 29, 45, 54, 57, 53, 45, 33, 41, 48, 52, 58, 58, 51, 37, 27, 23, 20, + 22, 22, 28, 34, 33, 28, 37, 46, 54, 54, 45, 27, 5, 0, 2, 13, 22, + 31, 33, 28, 21, 20, 27, 36, 45, 51, 55, 61, 61, 61, 61, 64, 66, 65, + 70, 72, 69, 61, 55, 47, 34, 26, 18, 7, 0, 0, 0, 6, 18, 26, 33, + 41, 51, 54, 60, 62, 60, 55, 51, 45, 36, 22, 33, 44, 51, 52, 46, 33, + 18, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 23, 34, 40, 40, 37, + 32, 38, 41, 41, 37, 29, 19, 7, 0, 3, 5, 5, 2, 0, 0, 0, 0, + 0, 3, 6, 8, 6, 0, 0, 0, 0, 9, 25, 36, 39, 37, 32, 26, 38, + 52, 60, 63, 61, 55, 47, 38, 46, 51, 53, 54, 54, 59, 60, 56, 62, 65, + 64, 58, 45, 23, 0, 0, 0, 4, 11, 20, 26, 28, 25, 15, 21, 32, 43, + 46, 43, 32, 16, 0, 6, 9, 9, 14, 18, 18, 13, 8, 18, 23, 26, 22, + 13, 1, 0, 0, 0, 0, 1, 12, 23, 34, 41, 45, 54, 57, 61, 66, 68, + 67, 62, 52, 66, 79, 89, 95, 96, 92, 84, 76, 74, 67, 56, 39, 26, 11, + 0, 0, 0, 3, 12, 23, 32, 36, 32, 22, 29, 34, 38, 38, 31, 18, 4, + 0, 7, 23, 38, 47, 52, 52, 48, 46, 47, 44, 38, 32, 22, 9, 0, 0, + 0, 18, 37, 48, 53, 52, 45, 36, 45, 54, 59, 58, 53, 46, 32, 16, 26, + 31, 33, 38, 46, 51, 50, 41, 50, 56, 57, 52, 39, 20, 0, 0, 0, 16, + 33, 44, 46, 43, 36, 29, 37, 39, 43, 52, 60, 65, 65, 57, 69, 74, 76, + 75, 76, 76, 73, 63, 60, 51, 36, 25, 15, 5, 0, 0, 0, 4, 6, 18, + 27, 37, 45, 54, 61, 64, 62, 58, 51, 43, 34, 28, 29, 39, 45, 45, 38, + 26, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 19, 26, 34, 38, 37, + 33, 39, 46, 50, 50, 47, 40, 31, 20, 12, 6, 4, 3, 4, 6, 9, 14, + 18, 16, 13, 13, 15, 13, 7, 1, 1, 1, 12, 19, 20, 22, 21, 16, 25, + 39, 52, 60, 64, 63, 57, 50, 44, 53, 54, 50, 52, 53, 55, 58, 62, 69, + 73, 72, 66, 54, 34, 11, 0, 0, 0, 8, 18, 23, 25, 22, 14, 20, 27, + 37, 41, 39, 28, 14, 9, 11, 16, 22, 29, 33, 33, 29, 19, 18, 22, 26, + 23, 16, 8, 2, 0, 3, 7, 12, 19, 32, 43, 52, 56, 61, 68, 74, 78, + 79, 78, 73, 71, 73, 81, 91, 96, 97, 94, 88, 80, 74, 66, 58, 50, 37, + 22, 6, 0, 0, 2, 13, 22, 28, 28, 23, 13, 21, 29, 34, 32, 23, 13, + 9, 12, 27, 41, 53, 59, 60, 58, 55, 62, 63, 58, 50, 39, 32, 21, 8, + 0, 0, 3, 22, 37, 45, 47, 44, 37, 41, 53, 60, 62, 59, 52, 40, 32, + 29, 37, 41, 51, 58, 62, 61, 55, 58, 64, 65, 60, 48, 29, 7, 0, 4, + 25, 43, 54, 57, 55, 50, 45, 45, 48, 52, 58, 64, 68, 71, 72, 76, 81, + 83, 82, 80, 79, 74, 67, 62, 51, 34, 18, 12, 9, 3, 7, 4, 11, 14, + 15, 18, 31, 41, 52, 59, 61, 60, 56, 50, 43, 36, 29, 27, 33, 37, 34, + 26, 13, 0, 0, 0, 0, 1, 2, 3, 5, 9, 18, 28, 38, 44, 44, 40, + 37, 33, 41, 47, 52, 53, 51, 45, 37, 28, 21, 16, 14, 14, 16, 21, 27, + 33, 38, 40, 39, 36, 33, 31, 29, 29, 29, 28, 23, 25, 21, 13, 1, 7, + 22, 36, 46, 55, 58, 56, 52, 45, 51, 58, 59, 56, 51, 55, 59, 64, 70, + 76, 79, 79, 73, 61, 43, 19, 0, 0, 0, 0, 7, 13, 15, 13, 9, 18, + 27, 34, 37, 34, 28, 22, 18, 20, 27, 34, 43, 47, 48, 46, 37, 29, 34, + 37, 36, 32, 26, 21, 20, 23, 28, 31, 34, 36, 46, 56, 63, 70, 77, 84, + 88, 88, 87, 84, 88, 89, 88, 88, 93, 94, 91, 84, 78, 73, 68, 62, 55, + 45, 32, 16, 18, 18, 20, 25, 29, 32, 32, 28, 20, 15, 21, 22, 19, 18, + 20, 19, 34, 47, 58, 66, 70, 69, 66, 66, 72, 73, 69, 61, 50, 38, 29, + 18, 3, 0, 0, 7, 22, 33, 39, 39, 37, 36, 48, 58, 63, 63, 59, 53, + 46, 44, 47, 54, 61, 67, 72, 72, 68, 62, 67, 69, 65, 55, 39, 18, 4, + 13, 34, 52, 62, 66, 65, 61, 57, 57, 60, 65, 71, 76, 79, 82, 82, 84, + 88, 89, 88, 84, 81, 75, 65, 58, 46, 31, 26, 26, 23, 18, 25, 19, 15, + 20, 22, 23, 23, 33, 45, 52, 53, 51, 46, 41, 36, 31, 27, 25, 25, 25, + 20, 9, 0, 0, 0, 2, 4, 6, 8, 12, 16, 25, 33, 44, 52, 56, 56, + 52, 44, 37, 40, 46, 51, 51, 50, 45, 39, 33, 27, 23, 22, 23, 26, 32, + 40, 47, 55, 59, 60, 60, 59, 58, 58, 57, 57, 54, 46, 32, 22, 13, 1, + 7, 15, 26, 36, 41, 45, 44, 41, 45, 56, 61, 62, 59, 54, 56, 62, 69, + 76, 82, 84, 83, 77, 65, 47, 26, 6, 0, 0, 0, 0, 0, 2, 1, 6, + 15, 26, 33, 37, 37, 32, 26, 23, 28, 34, 44, 53, 59, 61, 59, 52, 40, + 46, 48, 47, 44, 40, 37, 38, 43, 46, 48, 50, 48, 46, 57, 66, 76, 84, + 92, 97, 99, 97, 99,102,104,102, 95, 86, 86, 83, 77, 72, 69, 66, 63, + 57, 50, 37, 37, 37, 36, 37, 39, 40, 40, 39, 34, 26, 14, 9, 7, 11, + 20, 25, 41, 55, 63, 72, 76, 79, 77, 73, 71, 76, 78, 75, 70, 60, 46, + 33, 23, 11, 0, 0, 0, 5, 18, 27, 33, 36, 32, 41, 53, 61, 65, 64, + 61, 56, 54, 56, 61, 67, 74, 78, 80, 79, 72, 69, 71, 68, 60, 47, 28, + 20, 25, 45, 61, 71, 74, 74, 71, 67, 72, 74, 77, 82, 86, 90, 92, 91, + 92, 96, 96, 92, 87, 82, 76, 65, 51, 44, 44, 41, 41, 39, 34, 44, 33, + 25, 22, 26, 29, 31, 31, 29, 36, 37, 33, 29, 26, 22, 20, 19, 16, 13, + 11, 4, 2, 0, 0, 3, 6, 7, 8, 12, 15, 23, 33, 43, 53, 59, 63, + 63, 60, 53, 45, 43, 45, 47, 47, 46, 44, 39, 34, 31, 31, 33, 36, 39, + 41, 48, 57, 66, 72, 75, 78, 79, 79, 78, 77, 76, 73, 63, 47, 26, 13, + 14, 19, 18, 13, 20, 25, 27, 27, 38, 48, 57, 61, 61, 57, 53, 55, 63, + 72, 79, 85, 88, 85, 79, 68, 52, 32, 19, 6, 0, 0, 0, 0, 0, 0, + 1, 11, 22, 31, 36, 36, 33, 28, 28, 39, 43, 51, 60, 66, 70, 69, 63, + 54, 56, 57, 56, 54, 51, 48, 53, 58, 61, 62, 61, 59, 55, 56, 69, 79, + 91,100,107,110,108,113,118,119,113,105, 93, 79, 72, 67, 63, 61, 61, + 60, 56, 48, 48, 50, 50, 48, 47, 47, 47, 45, 43, 38, 29, 19, 7, 3, + 15, 29, 46, 59, 68, 74, 79, 83, 84, 83, 78, 73, 77, 79, 79, 75, 68, + 57, 40, 26, 16, 4, 0, 0, 0, 2, 13, 23, 31, 33, 34, 47, 57, 63, + 66, 65, 62, 60, 60, 63, 69, 75, 81, 85, 86, 82, 74, 73, 71, 65, 55, + 40, 37, 40, 54, 69, 78, 82, 81, 79, 78, 84, 87, 87, 91, 95, 98, 99, + 97,101,104,102, 97, 89, 81, 75, 65, 59, 58, 57, 56, 55, 53, 50, 59, + 52, 40, 29, 26, 29, 32, 32, 27, 18, 12, 9, 6, 4, 4, 4, 5, 4, + 2, 4, 3, 0, 0, 3, 8, 8, 9, 16, 21, 21, 23, 36, 46, 55, 62, + 66, 67, 64, 59, 53, 47, 53, 55, 56, 56, 53, 50, 46, 43, 43, 45, 50, + 54, 57, 58, 64, 74, 81, 87, 90, 92, 92, 92, 90, 88, 84, 74, 58, 36, + 19, 26, 29, 25, 14, 2, 7, 21, 32, 41, 48, 54, 57, 55, 52, 48, 52, + 62, 72, 80, 87, 88, 86, 79, 69, 55, 45, 36, 21, 9, 4, 0, 0, 0, + 0, 0, 3, 15, 25, 31, 33, 32, 29, 37, 47, 52, 55, 64, 72, 75, 74, + 70, 62, 64, 65, 63, 61, 58, 61, 65, 70, 73, 73, 73, 72, 70, 67, 75, + 83, 96,109,118,123,121,124,130,132,123,112, 98, 84, 70, 57, 53, 53, + 54, 54, 52, 55, 56, 56, 55, 54, 52, 50, 47, 45, 43, 38, 31, 21, 11, + 20, 34, 48, 60, 70, 75, 79, 83, 86, 87, 85, 81, 75, 74, 77, 79, 79, + 74, 65, 52, 33, 19, 11, 3, 5, 5, 4, 5, 13, 26, 32, 32, 39, 51, + 59, 64, 66, 64, 60, 60, 62, 69, 74, 81, 87, 90, 89, 84, 76, 74, 71, + 63, 52, 51, 54, 62, 76, 85, 89, 89, 87, 91, 97, 99, 99,100,104,106, +106,103,109,112,109,102, 92, 79, 74, 72, 72, 71, 70, 69, 70, 68, 63, + 70, 66, 58, 45, 33, 28, 28, 27, 21, 9, 0, 0, 0, 0, 0, 0, 0, + 2, 6, 7, 6, 4, 8, 18, 21, 18, 26, 32, 33, 31, 28, 32, 44, 54, + 61, 65, 67, 65, 61, 56, 52, 57, 62, 64, 64, 62, 58, 54, 51, 51, 55, + 60, 66, 70, 72, 73, 79, 88, 94, 99,102,102,101, 99, 96, 92, 81, 65, + 45, 36, 38, 38, 29, 16, 11, 11, 23, 33, 39, 44, 47, 48, 46, 48, 51, + 56, 60, 70, 79, 85, 88, 85, 79, 71, 61, 58, 50, 37, 26, 21, 15, 8, + 0, 0, 0, 0, 4, 15, 23, 28, 29, 29, 41, 53, 57, 58, 67, 74, 78, + 78, 73, 69, 71, 71, 69, 67, 70, 75, 79, 81, 83, 84, 85, 85, 84, 79, + 79, 86,102,117,129,137,137,132,140,140,130,117,102, 88, 78, 71, 59, + 52, 54, 56, 58, 59, 58, 56, 54, 52, 50, 46, 43, 40, 37, 33, 27, 18, + 19, 33, 46, 58, 67, 74, 77, 79, 82, 85, 86, 85, 81, 76, 70, 74, 77, + 79, 77, 70, 58, 41, 23, 16, 12, 19, 21, 20, 15, 8, 19, 29, 34, 34, + 44, 54, 60, 63, 61, 57, 60, 70, 76, 79, 79, 86, 92, 92, 90, 84, 79, + 77, 72, 63, 61, 64, 69, 83, 92, 96, 97, 96,102,109,112,111,109,113, +113,113,109,115,118,113,105, 93, 79, 74, 80, 81, 80, 78, 80, 81, 79, + 74, 76, 75, 71, 62, 50, 36, 32, 26, 16, 3, 0, 1, 2, 0, 0, 0, + 0, 4, 9, 12, 13, 13, 23, 31, 32, 33, 44, 51, 52, 46, 37, 31, 38, + 48, 56, 61, 63, 63, 60, 56, 52, 58, 64, 67, 68, 66, 62, 58, 54, 55, + 60, 67, 74, 78, 81, 83, 83, 93,101,106,109,109,107,103,101, 96, 85, + 71, 61, 52, 48, 45, 34, 27, 28, 26, 22, 29, 33, 33, 36, 36, 44, 53, + 60, 67, 70, 69, 76, 83, 86, 84, 80, 74, 70, 67, 59, 47, 44, 41, 37, + 28, 16, 6, 4, 2, 3, 8, 14, 22, 27, 29, 41, 53, 59, 60, 68, 74, + 78, 78, 74, 74, 77, 76, 74, 73, 78, 83, 86, 89, 92, 94, 95, 95, 93, + 88, 81, 89,107,124,142,154,154,145,143,143,133,119,104, 92, 87, 79, + 67, 59, 60, 61, 61, 59, 55, 51, 48, 46, 43, 44, 41, 36, 28, 25, 19, + 12, 22, 38, 51, 61, 68, 73, 74, 74, 78, 80, 81, 80, 78, 73, 66, 68, + 74, 77, 76, 72, 61, 45, 27, 21, 21, 32, 37, 37, 29, 21, 16, 26, 34, + 38, 38, 47, 55, 58, 56, 55, 66, 76, 83, 86, 86, 84, 90, 93, 92, 88, + 84, 84, 80, 74, 69, 72, 75, 90,100,106,108,106,113,121,123,121,120, +122,122,118,114,120,121,117,107, 94, 80, 79, 85, 87, 86, 86, 89, 90, + 88, 83, 79, 80, 78, 72, 63, 53, 40, 32, 23, 22, 25, 27, 27, 22, 15, + 7, 0, 7, 15, 21, 23, 27, 34, 40, 41, 52, 61, 66, 66, 60, 48, 38, + 31, 41, 50, 55, 57, 57, 54, 50, 46, 54, 61, 65, 66, 64, 60, 56, 52, + 53, 59, 67, 75, 81, 85, 88, 88, 99,107,113,114,113,111,109,103, 98, + 89, 83, 74, 63, 58, 52, 40, 43, 43, 37, 26, 20, 20, 19, 20, 33, 46, + 58, 69, 75, 77, 76, 74, 81, 84, 85, 83, 79, 74, 71, 64, 63, 63, 62, + 59, 52, 40, 27, 12, 8, 4, 9, 15, 18, 25, 29, 34, 48, 55, 60, 68, + 74, 77, 76, 72, 78, 80, 80, 79, 79, 80, 85, 89, 97,102,103,102,101, + 99, 94, 86, 94,113,133,154,167,167,158,142,142,133,120,106, 96, 89, + 80, 68, 66, 66, 65, 62, 57, 51, 44, 40, 47, 53, 54, 51, 44, 33, 20, + 8, 5, 21, 36, 48, 58, 64, 67, 66, 67, 69, 72, 73, 72, 69, 63, 56, + 62, 69, 73, 73, 67, 57, 41, 25, 26, 32, 44, 48, 48, 41, 31, 21, 29, + 33, 39, 41, 43, 50, 53, 51, 60, 72, 81, 88, 91, 91, 89, 87, 91, 92, + 90, 92, 92, 88, 83, 74, 76, 81, 96,109,118,121,121,120,129,132,130, +133,134,130,124,115,120,121,117,107, 95, 91, 88, 87, 88, 92, 95, 97, + 97, 94, 88, 81, 82, 81, 78, 73, 65, 57, 48, 44, 44, 46, 47, 45, 40, + 32, 21, 13, 16, 23, 31, 37, 40, 45, 51, 57, 66, 74, 78, 77, 71, 59, + 41, 33, 36, 44, 48, 50, 51, 56, 58, 55, 59, 61, 62, 61, 61, 61, 62, + 62, 62, 59, 64, 74, 81, 87, 90, 92,103,112,118,119,117,122,124,120, +111,102, 94, 84, 73, 69, 62, 51, 52, 48, 40, 25, 5, 3, 8, 22, 36, + 53, 67, 77, 82, 84, 82, 79, 79, 84, 87, 88, 86, 84, 80, 79, 79, 80, + 80, 77, 72, 62, 50, 36, 18, 11, 7, 15, 20, 22, 27, 34, 41, 51, 59, + 67, 72, 74, 72, 74, 79, 82, 84, 84, 83, 84, 88, 96,105,112,113,113, +110,105,101, 97, 96,114,136,158,171,175,167,150,136,128,119,107, 96, + 88, 79, 74, 72, 70, 66, 61, 54, 46, 38, 44, 52, 56, 57, 54, 46, 36, + 23, 12, 12, 25, 38, 50, 59, 67, 71, 71, 67, 60, 59, 59, 57, 53, 46, + 51, 57, 62, 65, 63, 57, 45, 29, 31, 32, 43, 53, 58, 57, 51, 41, 32, + 31, 34, 37, 40, 43, 48, 50, 53, 61, 73, 81, 88, 92, 92, 92, 90, 92, + 94, 97,100,100, 97, 92, 84, 79, 86,103,120,132,140,140,140,142,145, +149,150,149,142,133,120,114,115,112,104,101,100, 97, 96, 96, 98,101, +102,100, 96, 89, 83, 84, 84, 82, 78, 73, 66, 60, 57, 56, 57, 57, 55, + 50, 43, 33, 25, 25, 29, 38, 45, 52, 59, 63, 67, 74, 80, 84, 83, 77, + 65, 48, 33, 31, 38, 40, 48, 57, 63, 66, 66, 70, 71, 70, 70, 69, 69, + 69, 71, 70, 68, 66, 71, 80, 88, 93, 95,104,113,119,120,125,136,140, +136,125,114,104, 93, 84, 81, 74, 64, 56, 51, 38, 20, 0, 0, 13, 27, + 46, 63, 76, 84, 88, 89, 87, 84, 81, 84, 88, 91, 92, 92, 92, 92, 94, + 95, 93, 91, 84, 77, 66, 55, 39, 19, 9, 9, 16, 20, 21, 26, 36, 46, + 55, 61, 65, 66, 65, 73, 79, 83, 84, 84, 84, 84, 88, 97,108,115,121, +121,119,114,110,105,102,111,128,149,165,171,167,156,140,127,114,105, + 97, 89, 83, 77, 73, 69, 64, 58, 50, 40, 32, 41, 48, 53, 53, 50, 43, + 32, 20, 9, 18, 31, 45, 56, 64, 70, 72, 70, 65, 57, 50, 44, 40, 40, + 41, 45, 48, 52, 51, 47, 39, 27, 32, 36, 38, 51, 60, 64, 63, 59, 52, + 44, 39, 37, 38, 40, 46, 50, 51, 54, 63, 70, 76, 83, 88, 89, 90, 91, + 93, 98,103,107,109,107,102, 92, 80, 89,109,129,149,161,163,161,161, +163,167,167,163,154,140,124,109,108,105,103,106,106,103,101,100,100, +100, 99, 96, 91, 84, 83, 84, 84, 83, 80, 76, 72, 67, 64, 63, 62, 61, + 58, 54, 48, 41, 36, 33, 36, 47, 58, 66, 73, 75, 75, 79, 85, 88, 86, + 80, 69, 53, 33, 27, 33, 39, 50, 59, 67, 70, 73, 76, 76, 75, 74, 73, + 72, 73, 75, 75, 74, 71, 69, 79, 88, 95, 98,102,113,119,120,130,143, +152,149,137,125,113,100, 96, 92, 86, 77, 65, 52, 37, 23, 12, 2, 15, + 36, 54, 70, 81, 88, 92, 92, 90, 87, 84, 83, 89, 94, 97, 99,101,102, +105,105,103,100, 94, 86, 78, 69, 57, 41, 21, 3, 7, 13, 15, 15, 25, + 37, 46, 54, 57, 57, 62, 71, 77, 80, 81, 81, 81, 82, 86, 95,107,117, +123,127,125,122,117,112,107,103,115,132,147,158,159,154,140,125,112, +100, 95, 88, 83, 76, 70, 64, 58, 51, 43, 34, 27, 33, 40, 44, 44, 40, + 33, 23, 13, 6, 19, 34, 47, 58, 65, 69, 69, 65, 58, 50, 43, 37, 33, + 33, 37, 38, 36, 34, 31, 25, 16, 23, 33, 37, 44, 55, 62, 66, 66, 63, + 58, 54, 51, 48, 50, 51, 53, 56, 58, 60, 65, 71, 73, 74, 79, 84, 86, + 89, 94,101,108,113,117,114,109,100, 87, 93,114,140,165,179,183,179, +177,177,179,177,171,159,143,127,109,100, 99,106,110,109,106,102, 99, + 97, 95, 92, 88, 83, 82, 79, 80, 81, 81, 79, 77, 74, 70, 67, 66, 64, + 62, 59, 56, 53, 50, 46, 41, 50, 62, 73, 79, 84, 86, 86, 84, 88, 90, + 88, 82, 71, 54, 34, 25, 31, 36, 47, 58, 66, 70, 76, 79, 78, 75, 73, + 72, 72, 73, 75, 77, 78, 77, 74, 79, 88, 96,100,102,111,118,121,129, +142,152,152,143,130,117,111,109,103, 96, 88, 78, 67, 54, 40, 26, 12, + 16, 38, 56, 72, 82, 89, 92, 92, 90, 87, 83, 82, 89, 95, 99,102,105, +109,112,112,110,105, 99, 92, 85, 79, 71, 60, 44, 25, 9, 12, 14, 15, + 16, 25, 37, 44, 47, 47, 55, 64, 72, 75, 76, 76, 75, 77, 86, 93,102, +113,123,128,129,127,122,118,111,105,102,113,125,136,142,142,132,120, +106, 92, 89, 84, 79, 70, 61, 53, 46, 39, 32, 26, 21, 21, 28, 32, 31, + 26, 20, 12, 4, 4, 19, 34, 48, 58, 64, 65, 62, 56, 46, 38, 29, 31, + 40, 47, 52, 52, 45, 32, 15, 1, 11, 21, 28, 32, 43, 53, 59, 63, 64, + 63, 61, 59, 57, 57, 57, 58, 60, 62, 64, 67, 69, 69, 70, 69, 70, 75, + 80, 85, 92,102,111,118,121,119,113,103, 90, 98,122,150,179,194,196, +190,186,183,181,177,171,158,140,123,105, 92,102,108,111,110,105,100, + 96, 95, 92, 89, 86, 83, 80, 74, 74, 75, 76, 76, 74, 73, 70, 73, 75, + 78, 79, 77, 74, 69, 62, 55, 51, 62, 74, 84, 91, 94, 96, 95, 90, 90, + 91, 88, 82, 70, 53, 32, 25, 29, 31, 41, 53, 61, 65, 75, 77, 75, 72, + 69, 67, 67, 69, 73, 78, 84, 84, 83, 85, 91, 95, 99,101,109,119,123, +122,130,140,143,137,127,121,123,120,113,105, 96, 88, 79, 67, 53, 37, + 18, 12, 32, 51, 67, 78, 85, 88, 89, 87, 84, 79, 83, 91, 96, 99,101, +104,108,112,112,110,105,100, 95, 90, 85, 81, 74, 63, 48, 27, 18, 16, + 15, 11, 13, 26, 34, 38, 38, 44, 55, 63, 67, 68, 68, 68, 74, 84, 92, + 98,110,121,129,132,130,130,129,122,113,102, 96,105,114,122,124,119, +109, 96, 84, 82, 77, 70, 59, 47, 37, 29, 23, 19, 15, 13, 12, 14, 16, + 15, 12, 6, 2, 0, 5, 16, 33, 47, 56, 60, 59, 53, 43, 32, 22, 29, + 41, 52, 59, 62, 59, 51, 34, 16, 13, 6, 14, 19, 27, 37, 44, 51, 54, + 57, 59, 59, 59, 59, 60, 60, 61, 63, 65, 68, 72, 75, 76, 76, 75, 72, + 74, 79, 82, 90,101,111,119,122,120,113,103, 93,104,130,163,190,204, +204,196,188,183,177,171,163,149,133,115, 99, 95,104,111,112,109,102, + 97, 97, 96, 92, 88, 84, 79, 74, 62, 63, 66, 68, 70, 71, 71, 71, 77, + 82, 87, 90, 90, 87, 80, 72, 62, 59, 74, 85, 94,100,102,102,101, 96, + 92, 92, 88, 80, 68, 51, 28, 25, 31, 33, 34, 44, 52, 61, 71, 72, 70, + 66, 71, 70, 64, 63, 68, 79, 87, 89, 88, 91, 96, 98, 96, 97,109,119, +125,125,120,125,129,127,123,129,132,128,121,113,103, 95, 86, 74, 60, + 41, 20, 2, 19, 38, 56, 69, 77, 82, 83, 81, 78, 79, 88, 96,101,102, +100, 98,102,106,107,105,102, 99, 98, 95, 91, 89, 85, 78, 66, 48, 25, + 16, 13, 6, 3, 16, 26, 29, 29, 28, 41, 51, 57, 63, 65, 65, 71, 81, + 89, 96,106,119,129,134,134,136,137,134,128,118,106, 94, 96,103,107, +104, 96, 86, 75, 73, 66, 57, 43, 27, 16, 9, 5, 4, 3, 3, 3, 3, + 3, 2, 2, 2, 1, 2, 8, 15, 32, 45, 54, 56, 52, 43, 29, 22, 28, + 36, 47, 58, 65, 67, 63, 52, 37, 34, 28, 18, 4, 6, 16, 25, 31, 36, + 40, 45, 48, 52, 55, 56, 57, 59, 60, 62, 64, 67, 72, 77, 80, 82, 82, + 80, 78, 80, 82, 88, 99,110,118,120,117,109, 99, 94,113,140,175,200, +208,206,194,192,186,169,159,149,136,122,107, 94, 99,108,113,112,107, + 99, 96, 96, 94, 90, 84, 78, 71, 65, 46, 48, 53, 58, 63, 67, 69, 70, + 77, 84, 92, 96, 97, 94, 88, 77, 66, 70, 84, 94,102,107,108,106,103, + 97, 95, 93, 88, 79, 65, 45, 25, 26, 33, 37, 36, 34, 39, 55, 64, 66, + 71, 79, 81, 79, 74, 64, 63, 79, 88, 92, 93, 95,100,102, 99, 96,109, +121,129,130,124,114,115,115,127,134,137,133,125,117,108,100, 90, 79, + 63, 43, 19, 0, 2, 20, 39, 55, 65, 72, 74, 74, 77, 84, 94,101,103, +102, 99, 93, 93, 97, 99, 99,102,108,109,105, 96, 96, 93, 88, 78, 62, + 40, 15, 8, 0, 1, 8, 19, 22, 22, 19, 31, 48, 60, 67, 71, 72, 76, + 79, 84, 91,104,119,130,139,139,137,142,142,140,132,120,106, 92, 86, + 90, 88, 82, 73, 65, 60, 53, 40, 25, 18, 14, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 11, 16, 32, 45, 52, 52, 46, 34, 21, 27, + 39, 46, 51, 61, 67, 69, 63, 53, 53, 50, 40, 25, 5, 0, 3, 9, 14, + 21, 27, 29, 34, 40, 45, 47, 50, 56, 60, 62, 62, 61, 67, 74, 79, 83, + 84, 82, 79, 81, 81, 88, 98,109,114,115,111,102, 93, 98,121,152,186, +206,212,206,192,200,192,173,147,134,123,110, 96, 93,103,111,114,112, +105, 96, 92, 92, 88, 84, 77, 68, 59, 51, 27, 31, 38, 47, 57, 64, 69, + 72, 73, 82, 90, 96, 98, 96, 89, 79, 66, 80, 92,102,109,111,110,105, +101,100, 98, 95, 88, 78, 61, 40, 16, 27, 36, 39, 40, 38, 39, 53, 61, + 73, 83, 88, 91, 88, 81, 72, 61, 78, 88, 93, 96, 96, 99,101, 98,104, +111,123,132,133,127,115,104,117,128,136,137,134,127,118,109,102, 92, + 80, 64, 44, 18, 0, 0, 1, 19, 37, 51, 59, 67, 74, 77, 89, 98,103, +104,102, 96, 92, 84, 89, 94,102,109,113,114,110,101,102,101, 95, 85, + 70, 48, 22, 1, 3, 5, 4, 14, 19, 20, 25, 28, 47, 60, 69, 74, 76, + 77, 79, 77, 92,106,122,136,145,149,143,137,140,142,137,128,115,101, + 87, 74, 74, 67, 59, 54, 51, 45, 37, 23, 25, 21, 15, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 6, 21, 36, 47, 53, 52, 44, 31, 14, + 31, 43, 51, 56, 61, 67, 67, 61, 63, 62, 57, 45, 27, 9, 0, 0, 6, + 19, 29, 34, 37, 36, 33, 31, 40, 48, 54, 58, 60, 60, 58, 55, 63, 70, + 75, 77, 77, 79, 79, 79, 88, 99,108,113,112,105, 96, 87,104,129,163, +196,212,214,206,190,198,192,173,147,124,113,100, 87, 96,106,113,115, +112,104, 94, 84, 82, 79, 73, 64, 54, 41, 32, 46, 45, 39, 38, 51, 62, + 71, 76, 79, 78, 85, 92, 95, 93, 87, 77, 78, 90,101,109,113,112,108, +107,107,104,101, 95, 87, 74, 57, 34, 23, 25, 33, 40, 43, 41, 53, 63, + 73, 82, 90, 95, 96, 93, 86, 77, 68, 75, 86, 93, 96, 98, 99,100,102, +109,115,122,129,130,124,115,113,117,128,134,136,134,137,134,125,111, + 97, 83, 67, 48, 27, 7, 0, 2, 8, 19, 38, 55, 66, 74, 79, 92,101, +105,105,102,101, 99, 92, 92, 98,105,113,118,118,113,105,107,106,100, + 89, 75, 55, 29, 18, 11, 7, 4, 9, 20, 27, 33, 38, 45, 59, 69, 76, + 80, 84, 84, 86, 94,109,124,139,150,154,152,145,134,133,132,127,117, +105, 93, 81, 73, 66, 62, 58, 53, 46, 37, 31, 32, 28, 22, 14, 5, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 26, 40, 50, 54, 52, 44, 29, + 21, 28, 41, 51, 57, 61, 64, 65, 66, 67, 65, 57, 44, 23, 12, 2, 7, + 13, 26, 36, 40, 41, 40, 43, 46, 50, 54, 57, 61, 63, 66, 66, 64, 60, + 58, 63, 67, 71, 73, 75, 84, 93,102,109,111,107,100, 90, 92,109,134, +167,196,210,210,198,183,192,183,165,142,122,106, 94, 91, 99,108,114, +117,113,104, 94, 84, 73, 63, 55, 45, 33, 34, 43, 61, 61, 55, 45, 52, + 61, 74, 82, 85, 85, 81, 88, 92, 91, 85, 79, 88, 98,108,113,114,111, +106,111,109,103, 98, 91, 81, 69, 51, 37, 33, 28, 29, 37, 41, 48, 62, + 72, 79, 86, 92, 96, 97, 94, 88, 81, 76, 81, 86, 90, 95, 98,100,102, +103,109,117,123,128,129,127,123,122,124,128,133,140,149,154,154,142, +125,110, 94, 79, 63, 45, 25, 7, 14, 19, 23, 32, 52, 65, 74, 80, 93, +102,106,106,101,102,102, 96, 94,102,111,118,123,124,120,112,109,108, +103, 93, 80, 63, 44, 33, 25, 20, 16, 12, 23, 33, 40, 47, 54, 61, 68, + 77, 84, 88, 92, 95, 96,107,122,134,145,149,149,145,137,125,121,119, +113,105, 96, 86, 79, 73, 68, 62, 56, 46, 37, 39, 40, 38, 32, 25, 25, + 23, 21, 20, 19, 14, 8, 2, 2, 4, 8, 16, 28, 39, 47, 50, 47, 39, + 28, 29, 34, 41, 48, 57, 62, 65, 67, 67, 66, 62, 53, 37, 22, 9, 14, + 21, 28, 37, 44, 50, 52, 53, 54, 55, 58, 60, 63, 65, 68, 69, 69, 67, + 63, 60, 59, 61, 66, 74, 81, 89, 97,105,109,109,103, 94, 90, 95,111, +133,161,183,192,188,175,177,179,171,154,134,117,103, 92, 96,104,112, +117,117,112,103, 92, 83, 72, 60, 47, 36, 40, 50, 57, 72, 72, 66, 56, + 58, 65, 75, 85, 91, 91, 88, 87, 91, 90, 85, 88, 95,105,112,115,113, +109,107,111,107, 99, 92, 83, 72, 58, 40, 41, 40, 37, 33, 32, 37, 55, + 68, 76, 82, 86, 90, 93, 93, 91, 87, 82, 84, 89, 93, 96, 98, 99,100, +100,102,105,112,119,125,129,130,129,128,130,133,139,147,158,165,165, +156,136,119,102, 88, 72, 55, 37, 20, 21, 26, 29, 36, 47, 63, 73, 79, + 92,102,106,106,102, 99,100, 96, 96,104,114,123,129,130,127,120,111, +109,105, 97, 86, 73, 57, 46, 37, 31, 25, 20, 23, 36, 46, 55, 63, 70, + 75, 78, 84, 92, 97,102,103,102,113,123,130,134,136,137,134,127,115, +107,104,100, 95, 88, 82, 77, 73, 66, 58, 46, 43, 46, 47, 45, 40, 43, + 44, 43, 41, 41, 39, 33, 25, 14, 8, 8, 11, 16, 26, 36, 40, 41, 39, + 32, 33, 37, 41, 47, 52, 55, 61, 64, 65, 64, 60, 54, 43, 26, 18, 14, + 22, 29, 38, 46, 54, 59, 61, 60, 59, 59, 61, 62, 64, 65, 67, 68, 67, + 64, 60, 58, 59, 63, 69, 77, 85, 92,100,106,108,106, 99, 90, 92, 98, +108,127,147,161,165,159,150,163,163,152,137,122,109, 97, 92,100,107, +113,115,113,108, 99, 89, 79, 69, 56, 41, 45, 52, 59, 66, 76, 77, 73, + 63, 62, 70, 76, 87, 93, 94, 92, 90, 93, 92, 92, 95, 99,108,113,114, +112,106,106,108,102, 92, 83, 72, 58, 43, 37, 41, 44, 43, 38, 32, 40, + 58, 71, 78, 82, 84, 84, 85, 85, 84, 82, 81, 90, 95, 98,100,100,100, +100,100, 99, 99,105,112,119,124,127,129,129,130,132,137,143,152,161, +163,156,139,121,104, 89, 74, 57, 40, 26, 21, 27, 31, 36, 45, 61, 72, + 77, 90,101,106,107,102, 93, 94, 92, 95,105,117,127,134,136,133,127, +119,110,107,100, 92, 81, 69, 57, 46, 38, 32, 26, 22, 34, 48, 60, 70, + 78, 83, 85, 84, 91, 99,105,108,106,102,108,113,117,120,123,125,122, +115,106, 96, 92, 90, 86, 82, 79, 75, 69, 60, 47, 45, 51, 52, 50, 53, + 56, 57, 56, 56, 56, 54, 47, 37, 23, 12, 9, 9, 14, 22, 28, 31, 31, + 27, 31, 36, 41, 47, 51, 53, 53, 58, 61, 61, 57, 51, 41, 29, 19, 12, + 16, 23, 31, 41, 52, 59, 64, 66, 64, 61, 60, 60, 60, 63, 66, 66, 64, + 62, 58, 54, 53, 54, 60, 67, 76, 84, 92, 98,102,104,102, 96, 87, 93, + 99,102,115,128,136,136,132,140,147,142,132,120,108, 97, 88, 93,101, +107,110,110,106, 99, 91, 82, 73, 62, 47, 45, 51, 56, 63, 70, 75, 78, + 74, 67, 63, 71, 76, 86, 93, 94, 93, 94, 99, 98, 96,100,102,106,110, +111,107,102,103,102, 96, 84, 74, 62, 44, 25, 29, 38, 44, 44, 40, 36, + 45, 61, 72, 78, 79, 78, 76, 75, 74, 74, 73, 83, 92, 98,100,101,100, + 99, 97, 97, 97, 96, 96,102,109,114,120,123,124,124,127,136,136,137, +145,149,143,130,114,100, 85, 70, 53, 37, 23, 15, 22, 27, 32, 43, 59, + 70, 74, 88, 98,105,107,102, 93, 85, 85, 92,103,115,127,134,137,136, +130,124,117,109,102, 96, 88, 79, 71, 64, 57, 50, 41, 31, 28, 46, 60, + 73, 82, 88, 92, 91, 87, 96,104,107,105,100, 93, 96, 99,102,107,113, +114,113,107, 97, 86, 82, 81, 79, 79, 76, 72, 62, 50, 45, 51, 52, 56, + 61, 64, 64, 64, 63, 64, 62, 56, 44, 29, 13, 9, 12, 11, 16, 20, 20, + 18, 23, 29, 36, 43, 50, 53, 53, 51, 53, 55, 53, 46, 37, 26, 14, 9, + 9, 13, 19, 27, 39, 52, 61, 66, 67, 65, 61, 58, 57, 63, 67, 69, 68, + 65, 60, 56, 53, 51, 47, 52, 61, 70, 79, 86, 91, 96, 97, 96, 92, 84, + 90, 96, 97,102,111,115,115,115,127,129,122,113,102, 92, 84, 82, 90, + 97,102,102,100, 95, 88, 79, 70, 60, 50, 40, 45, 51, 56, 62, 69, 71, + 75, 74, 68, 61, 68, 73, 83, 90, 92, 92,102,108,107,102,102,104,102, +104,104,102, 99,101, 97, 88, 83, 77, 67, 50, 28, 22, 29, 38, 40, 37, + 43, 50, 63, 73, 76, 75, 76, 78, 76, 70, 60, 65, 81, 92, 96, 98, 98, + 96, 94, 92, 92, 93, 93, 89, 92, 98,104,110,113,114,114,128,139,140, +133,124,128,125,115,104, 91, 76, 60, 41, 26, 15, 5, 12, 19, 25, 40, + 57, 67, 71, 84, 96,103,106,102, 92, 80, 75, 87, 99,112,123,130,133, +132,129,125,120,113,106, 99, 93, 87, 81, 77, 74, 71, 64, 56, 43, 40, + 57, 72, 84, 92, 96, 96, 92, 91, 99,102,101, 96, 88, 81, 82, 86, 92, + 99,105,107,104, 96, 86, 74, 73, 74, 76, 76, 72, 64, 52, 40, 46, 53, + 60, 65, 66, 66, 65, 65, 67, 66, 59, 47, 31, 14, 15, 16, 12, 12, 12, + 9, 14, 20, 27, 34, 43, 50, 52, 51, 46, 46, 47, 43, 33, 23, 15, 5, + 4, 4, 6, 11, 19, 33, 47, 58, 65, 66, 63, 58, 54, 60, 67, 70, 70, + 68, 63, 57, 52, 48, 45, 43, 40, 50, 59, 68, 74, 80, 85, 89, 90, 88, + 84, 84, 90, 92, 88, 96, 99,100,105,112,112,105, 96, 86, 75, 65, 74, + 83, 90, 92, 92, 88, 81, 73, 63, 52, 43, 32, 34, 40, 45, 51, 56, 63, + 64, 70, 70, 66, 60, 62, 66, 79, 87, 90,103,113,120,119,113,102,105, +102, 97, 97, 96, 98, 98, 93, 84, 83, 79, 70, 55, 38, 31, 21, 28, 32, + 36, 45, 53, 65, 73, 74, 72, 78, 80, 79, 74, 64, 69, 78, 88, 92, 93, + 92, 89, 87, 84, 84, 85, 86, 84, 82, 88, 93, 98,102,104,112,127,139, +142,134,121,108,107,100, 91, 78, 63, 45, 26, 12, 4, 0, 0, 7, 20, + 39, 54, 63, 66, 79, 92,101,103,100, 91, 78, 68, 81, 94,106,115,122, +124,123,122,121,119,114,109,102, 95, 90, 86, 84, 84, 84, 79, 74, 62, + 47, 51, 68, 82, 92, 98, 99, 95, 89, 91, 95, 94, 89, 84, 78, 76, 74, + 76, 85, 94, 99, 99, 94, 84, 72, 63, 67, 72, 74, 72, 65, 54, 39, 40, + 54, 61, 64, 65, 64, 64, 63, 65, 65, 58, 46, 31, 19, 19, 18, 12, 8, + 6, 6, 16, 28, 36, 39, 41, 48, 50, 46, 40, 39, 38, 31, 19, 15, 7, + 0, 0, 0, 1, 2, 12, 28, 43, 54, 62, 64, 62, 56, 52, 62, 68, 70, + 68, 64, 57, 50, 44, 39, 37, 34, 33, 33, 41, 51, 58, 65, 73, 79, 83, + 84, 82, 78, 81, 84, 80, 82, 86, 88, 93, 97, 96, 88, 84, 79, 71, 67, + 68, 72, 79, 81, 79, 74, 64, 54, 41, 29, 21, 16, 25, 31, 36, 46, 55, + 59, 56, 63, 65, 63, 59, 55, 63, 76, 84, 99,113,127,133,133,124,113, +104,102, 93, 92, 96, 98, 97, 92, 81, 80, 77, 70, 56, 43, 34, 25, 16, + 20, 33, 44, 58, 68, 74, 74, 69, 75, 79, 79, 74, 74, 79, 81, 82, 85, + 85, 85, 85, 84, 82, 78, 73, 74, 72, 74, 78, 83, 88, 91, 92,107,121, +133,137,132,119,104, 92, 86, 77, 63, 47, 34, 21, 7, 0, 0, 0, 1, + 19, 37, 50, 58, 60, 75, 88, 96, 98, 94, 84, 71, 61, 76, 88,100,108, +113,113,112,111,113,113,111,108,104, 99, 93, 86, 86, 88, 88, 88, 83, + 74, 61, 45, 61, 77, 88, 96, 97, 94, 88, 83, 88, 88, 84, 81, 81, 79, + 74, 65, 72, 82, 89, 91, 87, 79, 66, 53, 59, 66, 70, 70, 65, 55, 41, + 39, 52, 58, 61, 65, 70, 72, 69, 62, 62, 56, 45, 29, 19, 18, 15, 7, + 8, 5, 12, 23, 36, 44, 45, 40, 46, 45, 40, 32, 32, 28, 20, 7, 5, + 0, 0, 0, 0, 3, 9, 12, 27, 40, 51, 60, 63, 61, 56, 50, 59, 64, + 64, 61, 55, 45, 36, 28, 23, 25, 26, 25, 25, 27, 31, 36, 45, 56, 66, + 74, 78, 79, 77, 72, 74, 74, 69, 74, 78, 79, 82, 79, 79, 79, 74, 67, + 70, 70, 69, 66, 68, 64, 59, 54, 46, 34, 20, 2, 5, 14, 21, 36, 47, + 56, 59, 54, 53, 56, 56, 55, 53, 65, 79, 93,109,125,140,150,149,137, +124,113,105, 98, 94, 97, 99, 97, 92, 83, 74, 72, 66, 54, 40, 34, 26, + 18, 20, 34, 50, 61, 69, 74, 74, 69, 69, 73, 74, 71, 79, 85, 88, 88, + 88, 88, 87, 85, 82, 79, 74, 68, 63, 63, 65, 68, 72, 75, 78, 87,100, +113,123,127,122,112, 99, 86, 75, 65, 55, 44, 32, 21, 12, 1, 0, 0, + 0, 15, 31, 43, 51, 57, 72, 84, 91, 92, 87, 76, 59, 57, 72, 84, 94, +100,103,103,102,110,112,111,107,105,103,101, 96, 91, 84, 88, 90, 90, + 87, 79, 68, 54, 54, 70, 82, 88, 90, 88, 83, 78, 81, 82, 80, 79, 79, + 78, 74, 68, 72, 76, 78, 80, 78, 70, 58, 45, 48, 57, 64, 66, 63, 55, + 48, 50, 53, 59, 65, 70, 74, 74, 72, 65, 58, 53, 43, 28, 15, 14, 11, + 8, 9, 12, 19, 29, 41, 50, 52, 46, 41, 39, 32, 27, 23, 18, 8, 0, + 0, 0, 0, 0, 0, 8, 15, 16, 26, 38, 45, 56, 60, 59, 54, 48, 55, + 58, 56, 51, 41, 37, 33, 25, 16, 15, 18, 19, 18, 18, 23, 29, 34, 39, + 53, 63, 71, 74, 74, 70, 64, 65, 62, 65, 71, 74, 74, 75, 75, 74, 69, + 67, 70, 70, 68, 66, 63, 61, 57, 52, 43, 31, 15, 0, 2, 11, 23, 37, + 47, 55, 57, 52, 45, 43, 46, 47, 58, 74, 89,103,120,140,158,165,161, +149,133,120,111,102, 95, 96, 97, 97, 93, 85, 71, 66, 60, 50, 34, 31, + 23, 16, 25, 37, 48, 59, 67, 71, 72, 68, 61, 64, 65, 71, 81, 88, 92, + 92, 92, 90, 87, 83, 79, 73, 69, 67, 65, 62, 58, 54, 55, 57, 68, 79, + 92,102,111,113,111,102, 91, 79, 68, 57, 47, 38, 28, 26, 21, 8, 0, + 0, 0, 11, 25, 36, 41, 54, 69, 79, 85, 84, 78, 65, 46, 53, 67, 79, + 88, 94, 96, 95, 96,102,105,103,101, 99, 99, 97, 94, 89, 84, 88, 91, + 91, 88, 81, 71, 58, 60, 61, 72, 78, 79, 76, 72, 70, 74, 76, 75, 74, + 74, 74, 70, 72, 75, 78, 78, 74, 66, 59, 50, 39, 33, 45, 54, 58, 59, + 55, 57, 58, 61, 65, 70, 74, 75, 74, 71, 64, 54, 47, 37, 22, 14, 12, + 8, 7, 8, 19, 25, 36, 50, 57, 59, 54, 45, 37, 31, 23, 15, 15, 11, + 12, 14, 12, 4, 2, 3, 9, 16, 18, 23, 32, 37, 47, 52, 52, 48, 47, + 53, 54, 48, 39, 36, 34, 28, 19, 16, 14, 11, 12, 12, 8, 11, 16, 22, + 25, 37, 51, 60, 66, 68, 67, 62, 54, 53, 55, 63, 69, 72, 73, 72, 69, + 63, 63, 65, 66, 65, 64, 62, 59, 55, 50, 40, 27, 13, 1, 6, 16, 27, + 39, 48, 54, 55, 47, 41, 34, 32, 41, 64, 82, 97,113,132,154,171,177, +171,156,136,123,113,103, 95, 92, 95, 96, 94, 88, 75, 60, 55, 46, 31, + 23, 19, 15, 25, 34, 44, 53, 60, 65, 68, 66, 59, 54, 55, 67, 79, 88, + 92, 94, 92, 88, 84, 77, 71, 70, 71, 70, 68, 63, 56, 45, 34, 44, 58, + 71, 82, 91, 97,100, 97, 91, 81, 69, 57, 46, 38, 29, 31, 32, 27, 14, + 0, 0, 0, 5, 18, 27, 36, 51, 64, 74, 79, 77, 69, 53, 33, 46, 61, + 74, 82, 87, 88, 88, 87, 92, 94, 94, 92, 92, 92, 90, 88, 84, 83, 87, + 90, 90, 88, 80, 71, 62, 63, 63, 58, 62, 63, 60, 55, 61, 66, 69, 69, + 69, 69, 70, 72, 74, 78, 79, 78, 73, 62, 47, 39, 33, 26, 29, 40, 47, + 52, 55, 60, 63, 65, 68, 71, 73, 73, 71, 65, 58, 48, 39, 28, 15, 12, + 11, 7, 5, 14, 25, 31, 43, 55, 63, 65, 60, 51, 41, 31, 29, 29, 27, + 27, 32, 33, 29, 22, 14, 13, 11, 14, 16, 20, 23, 23, 34, 39, 41, 40, + 48, 52, 50, 40, 33, 32, 28, 21, 13, 12, 9, 5, 6, 4, 2, 0, 1, + 7, 12, 20, 33, 45, 54, 59, 60, 57, 50, 41, 44, 56, 64, 69, 69, 66, + 61, 54, 53, 55, 57, 57, 58, 57, 55, 51, 44, 33, 21, 12, 11, 18, 27, + 36, 40, 46, 51, 51, 39, 33, 27, 22, 45, 69, 87,102,119,137,159,175, +179,171,154,136,122,111,105, 97, 92, 91, 93, 93, 89, 79, 63, 52, 43, + 29, 14, 11, 12, 20, 29, 44, 53, 56, 57, 61, 62, 58, 52, 43, 58, 72, + 81, 88, 90, 88, 83, 75, 66, 67, 68, 69, 70, 68, 62, 53, 39, 25, 28, + 45, 57, 68, 76, 82, 84, 83, 78, 68, 56, 43, 33, 26, 27, 32, 33, 28, + 16, 1, 0, 0, 0, 12, 21, 32, 46, 59, 69, 73, 70, 59, 41, 20, 37, + 53, 66, 74, 79, 81, 80, 79, 80, 83, 84, 84, 84, 83, 80, 78, 79, 81, + 85, 88, 88, 85, 79, 68, 64, 65, 62, 56, 44, 41, 38, 41, 51, 55, 57, + 59, 61, 63, 67, 71, 75, 78, 79, 76, 70, 59, 44, 28, 27, 23, 15, 25, + 34, 40, 51, 58, 61, 63, 65, 67, 68, 66, 62, 56, 46, 37, 28, 18, 8, + 11, 11, 7, 3, 20, 32, 38, 48, 59, 66, 69, 64, 55, 44, 43, 44, 44, + 39, 45, 50, 51, 47, 39, 27, 21, 18, 13, 13, 14, 13, 12, 20, 26, 36, + 44, 50, 50, 44, 33, 29, 27, 21, 12, 6, 4, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 13, 25, 36, 45, 50, 48, 41, 29, 33, 48, 58, 63, 63, + 58, 51, 40, 34, 37, 40, 44, 46, 47, 45, 40, 37, 33, 27, 18, 23, 32, + 40, 46, 48, 47, 43, 43, 26, 21, 16, 29, 48, 71, 88,103,119,136,158, +171,173,165,149,130,118,111,110,103,100, 96, 90, 92, 91, 84, 69, 50, + 41, 28, 12, 2, 4, 11, 31, 48, 58, 60, 58, 53, 57, 57, 55, 51, 55, + 58, 70, 78, 81, 78, 72, 61, 60, 62, 63, 64, 66, 65, 59, 48, 32, 21, + 20, 27, 39, 48, 57, 63, 66, 66, 61, 53, 39, 27, 18, 18, 25, 29, 31, + 27, 14, 0, 0, 0, 5, 9, 16, 26, 39, 53, 62, 66, 62, 51, 31, 8, + 25, 41, 55, 64, 69, 72, 72, 71, 73, 75, 76, 76, 75, 73, 71, 73, 74, + 77, 81, 84, 84, 81, 74, 65, 64, 64, 60, 52, 38, 21, 16, 28, 36, 39, + 41, 45, 50, 54, 60, 67, 72, 75, 74, 71, 64, 53, 39, 25, 19, 20, 16, + 11, 19, 26, 40, 50, 53, 54, 57, 58, 58, 55, 50, 40, 31, 22, 16, 6, + 4, 7, 9, 8, 14, 27, 39, 45, 52, 60, 67, 69, 65, 55, 53, 56, 58, + 56, 54, 59, 62, 62, 60, 54, 41, 27, 21, 14, 9, 11, 9, 14, 25, 34, + 41, 46, 48, 46, 38, 26, 26, 21, 13, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 6, 14, 25, 33, 36, 29, 19, 23, 39, 51, 56, + 54, 47, 36, 22, 11, 11, 16, 22, 27, 29, 36, 39, 38, 33, 27, 25, 32, + 39, 46, 51, 51, 47, 38, 29, 9, 6, 18, 34, 50, 70, 86,100,113,128, +145,158,161,152,137,123,112,113,113,110,112,108, 99, 91, 92, 86, 74, + 57, 41, 29, 13, 0, 0, 8, 33, 52, 62, 64, 61, 53, 52, 56, 57, 56, + 60, 59, 53, 62, 66, 62, 54, 52, 53, 54, 55, 57, 59, 59, 54, 41, 26, + 18, 14, 8, 16, 23, 32, 39, 44, 45, 43, 36, 22, 15, 11, 11, 18, 23, + 26, 22, 9, 0, 3, 8, 13, 18, 21, 21, 33, 46, 56, 59, 55, 43, 22, + 1, 9, 26, 40, 50, 56, 59, 59, 60, 62, 65, 66, 66, 63, 60, 61, 64, + 67, 71, 75, 78, 79, 76, 70, 61, 60, 60, 55, 45, 31, 14, 6, 12, 18, + 20, 22, 32, 41, 46, 50, 58, 65, 68, 67, 63, 55, 44, 32, 20, 11, 15, + 16, 12, 7, 13, 25, 34, 38, 39, 41, 43, 41, 38, 31, 22, 12, 9, 4, + 1, 0, 5, 8, 19, 26, 36, 48, 55, 56, 60, 65, 66, 62, 55, 61, 66, + 69, 68, 65, 68, 70, 70, 69, 63, 53, 36, 19, 9, 3, 4, 6, 20, 32, + 40, 44, 45, 45, 40, 29, 26, 23, 16, 5, 0, 0, 0, 5, 11, 11, 8, + 9, 8, 3, 0, 0, 3, 5, 3, 1, 4, 14, 19, 15, 13, 16, 31, 40, + 46, 43, 33, 19, 7, 2, 0, 0, 0, 9, 22, 31, 33, 32, 27, 22, 27, + 33, 40, 46, 50, 48, 41, 31, 15, 0, 0, 18, 37, 54, 68, 82, 94,105, +117,130,140,143,139,127,114,106,111,113,122,125,121,111, 98, 92, 88, + 79, 64, 46, 32, 15, 0, 0, 7, 33, 54, 64, 67, 62, 54, 46, 54, 59, + 60, 62, 61, 54, 43, 45, 41, 40, 43, 44, 45, 45, 47, 51, 52, 46, 34, + 19, 13, 7, 0, 0, 1, 8, 16, 22, 26, 25, 20, 9, 11, 8, 9, 11, + 16, 19, 15, 4, 4, 12, 18, 22, 28, 34, 34, 34, 43, 48, 53, 50, 37, + 18, 0, 0, 8, 22, 32, 39, 41, 44, 44, 47, 52, 53, 53, 51, 47, 47, + 52, 55, 60, 66, 70, 72, 71, 65, 57, 54, 54, 48, 39, 25, 9, 4, 0, + 0, 0, 11, 23, 34, 41, 43, 45, 54, 58, 57, 52, 43, 33, 22, 13, 5, + 12, 16, 15, 11, 4, 7, 15, 18, 18, 20, 21, 21, 18, 12, 3, 0, 0, + 0, 0, 0, 3, 18, 31, 37, 46, 58, 65, 66, 63, 61, 61, 57, 59, 67, + 74, 77, 78, 76, 74, 74, 74, 72, 67, 57, 41, 20, 1, 0, 0, 9, 25, + 36, 41, 43, 41, 39, 33, 27, 27, 22, 14, 6, 0, 0, 5, 16, 21, 21, + 18, 19, 19, 14, 5, 7, 11, 15, 13, 5, 0, 0, 1, 6, 13, 15, 22, + 32, 36, 31, 20, 21, 21, 15, 4, 0, 0, 1, 13, 21, 23, 22, 18, 22, + 26, 31, 38, 43, 44, 41, 33, 21, 5, 0, 0, 14, 34, 54, 70, 82, 90, + 95,105,117,125,129,127,119,109,100,110,122,134,139,134,123,108, 94, + 91, 82, 69, 53, 38, 20, 1, 0, 4, 31, 52, 63, 66, 62, 53, 43, 52, + 60, 64, 62, 57, 50, 37, 22, 29, 33, 36, 40, 47, 51, 50, 41, 43, 38, + 27, 14, 8, 3, 0, 0, 0, 0, 1, 6, 12, 14, 12, 4, 7, 5, 4, + 4, 9, 12, 9, 0, 11, 20, 27, 31, 40, 48, 50, 44, 52, 58, 58, 53, + 38, 18, 0, 0, 0, 4, 13, 19, 21, 22, 23, 26, 32, 36, 37, 37, 37, + 36, 38, 38, 44, 53, 60, 64, 65, 61, 55, 44, 45, 40, 32, 20, 7, 0, + 0, 0, 0, 4, 18, 29, 37, 40, 39, 38, 44, 43, 40, 39, 36, 27, 15, + 0, 9, 16, 19, 18, 12, 5, 0, 0, 0, 0, 2, 2, 1, 0, 0, 0, + 0, 0, 1, 5, 13, 27, 39, 44, 59, 70, 75, 77, 74, 69, 61, 52, 61, + 71, 79, 84, 86, 85, 81, 74, 73, 71, 66, 57, 40, 19, 0, 0, 0, 11, + 25, 34, 38, 37, 33, 29, 31, 32, 33, 34, 29, 20, 6, 0, 9, 21, 28, + 29, 28, 25, 22, 19, 21, 25, 26, 22, 19, 9, 0, 0, 0, 6, 14, 19, + 20, 26, 28, 23, 29, 33, 34, 28, 20, 11, 1, 0, 3, 11, 13, 16, 19, + 21, 23, 27, 33, 37, 37, 33, 25, 12, 0, 0, 2, 19, 34, 54, 71, 83, + 91, 96, 96,107,114,119,117,113,114,111,114,129,143,152,149,136,120, +105, 93, 84, 74, 62, 48, 29, 8, 0, 0, 20, 43, 56, 61, 59, 52, 43, + 50, 59, 65, 65, 60, 51, 39, 29, 28, 32, 37, 44, 50, 52, 51, 41, 38, + 36, 33, 26, 18, 9, 0, 0, 0, 0, 0, 0, 3, 7, 7, 2, 3, 2, + 0, 0, 6, 8, 9, 9, 15, 26, 34, 43, 53, 59, 61, 56, 63, 69, 70, + 66, 54, 36, 13, 0, 0, 0, 1, 0, 0, 7, 16, 21, 20, 18, 18, 27, + 38, 46, 51, 52, 48, 43, 48, 56, 60, 59, 54, 44, 36, 33, 27, 19, 8, + 3, 0, 0, 0, 7, 13, 22, 31, 36, 36, 36, 36, 38, 40, 40, 37, 31, + 19, 11, 9, 18, 22, 23, 21, 15, 9, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 8, 15, 22, 29, 37, 47, 59, 72, 80, 85, 86, 84, 78, 71, 70, + 74, 77, 85, 92, 96, 96, 92, 85, 73, 67, 62, 54, 38, 18, 1, 0, 0, + 9, 19, 27, 28, 27, 29, 37, 41, 47, 52, 51, 46, 37, 22, 8, 8, 22, + 31, 36, 37, 36, 33, 33, 36, 39, 40, 37, 29, 16, 3, 0, 0, 6, 14, + 20, 22, 23, 25, 28, 33, 38, 38, 40, 37, 28, 16, 7, 5, 12, 16, 20, + 22, 31, 38, 40, 39, 36, 31, 26, 16, 6, 0, 0, 6, 21, 34, 56, 73, + 84, 91, 94, 93,100,106,109,113,122,125,123,120,134,150,161,161,152, +136,119,105, 94, 84, 73, 58, 40, 19, 0, 0, 6, 29, 45, 54, 54, 50, + 43, 46, 57, 64, 66, 62, 54, 43, 33, 32, 36, 39, 45, 50, 51, 47, 39, + 45, 46, 44, 36, 26, 16, 7, 2, 4, 7, 6, 2, 0, 0, 2, 1, 1, + 0, 4, 9, 15, 20, 23, 25, 27, 36, 45, 55, 63, 68, 69, 69, 76, 80, + 82, 78, 68, 52, 31, 11, 4, 2, 2, 0, 0, 0, 6, 12, 13, 14, 18, + 27, 43, 54, 60, 61, 57, 48, 52, 56, 57, 57, 53, 44, 32, 29, 27, 25, + 20, 14, 8, 5, 2, 7, 14, 16, 23, 28, 29, 29, 29, 33, 36, 37, 36, + 31, 25, 26, 25, 20, 26, 28, 28, 25, 20, 14, 6, 0, 0, 0, 5, 12, + 13, 8, 18, 26, 33, 40, 46, 53, 61, 71, 81, 88, 92, 93, 90, 84, 80, + 84, 87, 87, 95,103,108,110,106, 98, 86, 70, 58, 51, 39, 22, 16, 8, + 8, 16, 23, 28, 29, 36, 45, 51, 55, 59, 62, 62, 59, 52, 40, 26, 16, + 22, 34, 41, 44, 45, 44, 43, 45, 47, 48, 45, 38, 26, 12, 2, 7, 11, + 13, 16, 19, 20, 21, 23, 31, 40, 46, 48, 46, 40, 32, 25, 21, 21, 23, + 29, 36, 43, 48, 51, 48, 45, 40, 32, 23, 12, 0, 0, 7, 20, 38, 59, + 74, 84, 88, 91, 89, 95, 99,104,118,129,137,139,134,137,152,165,169, +163,150,132,117,104, 92, 80, 66, 50, 29, 9, 0, 0, 13, 31, 43, 47, + 46, 41, 43, 55, 63, 66, 63, 56, 45, 36, 39, 41, 41, 43, 47, 47, 45, + 47, 54, 55, 52, 44, 33, 23, 14, 8, 11, 16, 16, 13, 6, 0, 0, 0, + 0, 5, 12, 20, 27, 33, 38, 40, 44, 51, 59, 66, 72, 74, 77, 82, 87, + 92, 92, 88, 79, 65, 47, 28, 22, 20, 13, 3, 0, 0, 1, 9, 18, 21, + 23, 27, 45, 57, 64, 66, 60, 50, 55, 61, 63, 60, 53, 44, 32, 33, 34, + 33, 29, 22, 15, 8, 4, 3, 9, 13, 14, 20, 22, 21, 21, 23, 27, 29, + 29, 33, 39, 41, 39, 34, 29, 33, 33, 31, 27, 22, 14, 2, 2, 2, 14, + 23, 27, 25, 34, 43, 50, 55, 60, 65, 72, 79, 88, 95, 98, 98, 94, 92, + 92, 95, 96, 96,107,115,122,123,120,111, 98, 82, 62, 51, 41, 32, 32, + 26, 21, 21, 26, 32, 40, 50, 56, 59, 61, 65, 68, 69, 67, 62, 54, 41, + 31, 32, 39, 47, 52, 52, 52, 50, 51, 53, 52, 50, 43, 32, 19, 15, 20, + 23, 25, 23, 22, 23, 28, 29, 37, 44, 50, 52, 52, 48, 44, 40, 38, 39, + 40, 44, 48, 52, 55, 56, 56, 53, 48, 40, 29, 15, 1, 0, 6, 21, 46, + 64, 77, 84, 86, 87, 88, 92, 92,106,121,134,145,152,152,147,150,161, +167,167,159,142,125,111, 96, 84, 71, 54, 37, 20, 1, 0, 0, 15, 31, + 39, 41, 39, 39, 53, 62, 66, 64, 57, 47, 40, 45, 47, 46, 45, 43, 43, + 48, 56, 61, 61, 56, 48, 38, 28, 18, 13, 14, 23, 26, 25, 18, 7, 0, + 0, 3, 11, 20, 29, 38, 44, 48, 52, 57, 63, 70, 74, 79, 81, 88, 92, + 97,101,102, 97, 89, 76, 60, 45, 43, 43, 38, 27, 13, 14, 19, 20, 21, + 26, 26, 26, 44, 57, 64, 65, 58, 50, 59, 65, 66, 62, 55, 45, 33, 38, + 41, 40, 36, 27, 18, 8, 3, 0, 2, 6, 8, 11, 13, 12, 11, 13, 16, + 21, 33, 43, 50, 52, 50, 45, 38, 37, 37, 34, 31, 26, 18, 9, 12, 14, + 21, 33, 39, 39, 50, 56, 62, 66, 70, 74, 78, 84, 92, 99,102,101,102, +103,103,105,105,109,120,130,136,136,130,121,108, 92, 74, 54, 47, 44, + 46, 41, 38, 38, 43, 47, 54, 59, 62, 63, 64, 67, 70, 72, 72, 68, 62, + 53, 45, 45, 46, 54, 59, 59, 58, 55, 55, 55, 54, 51, 45, 36, 25, 27, + 32, 34, 34, 33, 29, 33, 40, 41, 39, 44, 48, 52, 53, 52, 52, 52, 53, + 53, 54, 56, 57, 58, 59, 60, 60, 58, 54, 46, 33, 16, 2, 0, 5, 32, + 55, 70, 79, 84, 84, 83, 88, 89, 92,106,120,134,149,161,167,167,161, +152,158,161,156,143,127,112, 97, 84, 71, 55, 40, 26, 11, 0, 0, 2, + 18, 29, 34, 36, 37, 52, 62, 66, 64, 58, 47, 47, 51, 51, 50, 47, 44, + 47, 54, 61, 65, 64, 58, 50, 41, 34, 22, 14, 16, 28, 34, 34, 28, 18, + 5, 0, 5, 16, 27, 38, 45, 51, 55, 59, 65, 72, 77, 81, 84, 89, 95, +100,105,109,110,105, 96, 85, 72, 58, 61, 63, 61, 54, 41, 28, 34, 36, + 31, 26, 27, 26, 41, 54, 60, 59, 52, 54, 63, 68, 68, 62, 54, 43, 36, + 43, 46, 45, 39, 29, 16, 5, 0, 0, 0, 0, 2, 4, 5, 3, 3, 4, + 13, 25, 37, 48, 55, 56, 54, 51, 46, 39, 37, 34, 29, 25, 18, 13, 18, + 21, 28, 41, 48, 50, 60, 66, 71, 74, 75, 78, 81, 87, 93,100,102,107, +113,114,113,113,113,124,136,145,147,143,136,125,113, 98, 82, 64, 53, + 53, 55, 53, 51, 52, 55, 59, 63, 65, 66, 64, 64, 66, 69, 71, 72, 71, + 67, 59, 54, 55, 56, 63, 67, 67, 64, 65, 63, 57, 55, 53, 47, 40, 33, + 37, 40, 43, 41, 39, 36, 40, 48, 51, 50, 50, 50, 50, 51, 53, 55, 59, + 62, 63, 64, 64, 63, 62, 62, 62, 62, 62, 58, 50, 36, 15, 1, 0, 14, + 41, 62, 76, 83, 85, 84, 84, 88, 88, 91,103,117,130,145,161,175,181, +179,169,150,145,143,134,121,107, 94, 80, 67, 52, 39, 29, 18, 1, 0, + 0, 6, 19, 26, 28, 34, 51, 61, 66, 65, 58, 48, 50, 52, 52, 50, 46, + 46, 51, 56, 62, 66, 64, 57, 48, 45, 39, 27, 13, 19, 33, 41, 43, 37, + 25, 9, 0, 7, 20, 32, 41, 48, 53, 56, 61, 69, 75, 82, 86, 89, 95, +101,105,110,114,114,112,103, 92, 81, 72, 76, 80, 80, 76, 67, 53, 45, + 47, 43, 32, 25, 23, 37, 47, 53, 51, 45, 57, 65, 69, 67, 60, 51, 39, + 40, 46, 50, 48, 41, 29, 14, 6, 1, 0, 0, 0, 0, 0, 1, 0, 7, + 11, 13, 26, 38, 50, 56, 57, 55, 54, 51, 46, 39, 31, 26, 21, 15, 12, + 19, 25, 33, 47, 55, 57, 67, 73, 76, 77, 78, 79, 81, 86, 92, 99,103, +115,123,125,123,121,129,142,152,156,152,145,134,124,113,101, 88, 73, + 59, 58, 61, 59, 58, 60, 63, 66, 69, 69, 68, 66, 70, 69, 66, 68, 70, + 70, 67, 61, 60, 61, 68, 74, 76, 74, 73, 74, 73, 68, 61, 55, 52, 45, + 40, 43, 47, 48, 47, 45, 41, 45, 52, 55, 56, 59, 61, 62, 62, 60, 57, + 63, 68, 71, 71, 70, 69, 72, 70, 64, 65, 65, 62, 53, 37, 13, 0, 0, + 21, 48, 69, 81, 87, 88, 84, 85, 88, 85, 88, 99,114,130,143,156,175, +188,190,181,163,140,128,122,111, 99, 86, 74, 59, 45, 33, 27, 19, 6, + 0, 0, 0, 8, 16, 20, 32, 50, 61, 66, 66, 59, 50, 48, 50, 48, 45, + 41, 45, 48, 54, 59, 63, 60, 54, 50, 47, 41, 29, 12, 20, 36, 46, 48, + 43, 29, 13, 0, 8, 22, 34, 43, 48, 51, 54, 60, 68, 77, 84, 90, 94, + 97,102,106,112,115,118,115,109, 99, 88, 82, 88, 93, 96, 94, 87, 76, + 60, 55, 51, 40, 25, 21, 32, 40, 43, 39, 46, 58, 66, 68, 65, 60, 51, + 40, 47, 54, 55, 51, 43, 29, 13, 4, 0, 0, 0, 0, 0, 0, 0, 2, + 12, 18, 20, 25, 38, 47, 54, 54, 53, 53, 53, 51, 46, 39, 26, 18, 12, + 7, 15, 23, 37, 51, 58, 61, 71, 76, 79, 79, 79, 82, 82, 84, 91, 96, +108,123,133,136,133,130,145,158,163,159,150,139,127,118,111,101, 91, + 79, 68, 62, 63, 61, 60, 63, 67, 71, 73, 72, 70, 70, 75, 76, 74, 68, + 67, 68, 65, 61, 64, 72, 80, 84, 86, 84, 79, 80, 79, 74, 68, 61, 57, + 54, 47, 46, 50, 52, 52, 50, 46, 46, 53, 55, 57, 62, 67, 70, 71, 70, + 66, 65, 72, 75, 76, 74, 75, 78, 77, 70, 68, 68, 65, 55, 37, 12, 0, + 0, 23, 52, 73, 84, 91, 92, 88, 84, 86, 83, 83, 99,115,133,145,147, +165,181,190,183,167,145,123,108, 99, 88, 79, 70, 59, 47, 34, 22, 18, + 7, 0, 0, 0, 0, 7, 11, 31, 48, 61, 67, 67, 62, 52, 40, 41, 39, + 36, 36, 44, 50, 53, 52, 55, 53, 51, 51, 47, 40, 28, 11, 21, 38, 48, + 51, 45, 31, 13, 0, 8, 22, 33, 40, 44, 46, 48, 55, 68, 77, 84, 92, + 99,103,105,104,109,114,118,118,113,105, 96, 88, 96,103,108,108,102, + 92, 78, 63, 58, 48, 33, 19, 28, 33, 34, 29, 43, 55, 62, 65, 67, 62, + 54, 43, 52, 58, 61, 57, 47, 31, 13, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 12, 20, 25, 28, 37, 45, 51, 51, 57, 57, 53, 52, 50, 44, 33, 15, + 11, 2, 11, 20, 37, 52, 60, 62, 73, 78, 79, 79, 84, 87, 87, 82, 88, + 95,113,130,145,150,147,140,156,165,165,158,145,132,120,112,106,100, + 93, 86, 78, 70, 65, 60, 59, 62, 67, 72, 74, 75, 74, 70, 75, 78, 77, + 74, 70, 65, 62, 63, 75, 85, 92, 96, 96, 93, 88, 82, 81, 77, 72, 69, + 69, 69, 64, 54, 52, 54, 55, 54, 52, 50, 48, 50, 53, 59, 66, 71, 74, + 74, 72, 66, 74, 79, 80, 79, 79, 81, 79, 72, 73, 73, 69, 58, 39, 12, + 0, 1, 23, 53, 74, 88, 95, 98, 96, 92, 89, 86, 84, 98,113,128,137, +139,156,173,181,179,165,143,123,108, 96, 88, 79, 69, 59, 48, 37, 27, + 19, 12, 6, 0, 0, 0, 0, 8, 28, 47, 60, 66, 66, 61, 52, 40, 32, + 29, 29, 38, 45, 51, 53, 52, 48, 48, 50, 48, 45, 38, 26, 9, 22, 38, + 48, 50, 43, 27, 8, 0, 9, 22, 32, 38, 40, 41, 45, 62, 74, 81, 87, + 96,104,111,114,113,109,109,113,115,113,109,102, 96,101,110,117,118, +112,102, 88, 74, 68, 57, 40, 27, 25, 28, 28, 23, 34, 47, 56, 67, 71, + 67, 59, 50, 52, 60, 63, 61, 52, 37, 19, 3, 0, 0, 0, 0, 2, 6, + 6, 7, 8, 15, 22, 29, 38, 45, 48, 53, 58, 58, 54, 51, 50, 44, 33, + 18, 13, 3, 7, 16, 34, 50, 59, 62, 70, 76, 78, 81, 87, 90, 90, 86, + 85, 94,114,134,152,159,158,150,159,165,163,152,139,133,129,120,110, +100, 95, 91, 86, 81, 77, 74, 68, 60, 65, 71, 74, 76, 75, 73, 74, 78, + 79, 79, 76, 74, 70, 79, 88, 98,106,110,110,106,101, 94, 89, 86, 83, + 82, 84, 84, 80, 72, 59, 59, 58, 58, 59, 58, 58, 59, 59, 56, 60, 67, + 72, 74, 73, 69, 76, 81, 84, 84, 82, 82, 79, 77, 77, 76, 71, 59, 39, + 12, 0, 2, 25, 53, 75, 90,100,105,105,102, 99, 95, 90, 96,108,118, +124,129,145,161,171,171,158,139,120,105, 94, 84, 74, 63, 52, 41, 32, + 25, 20, 15, 9, 3, 0, 0, 0, 5, 25, 43, 55, 61, 60, 55, 44, 33, + 26, 23, 28, 36, 43, 48, 50, 47, 45, 45, 45, 45, 43, 37, 26, 9, 19, + 34, 44, 45, 37, 21, 3, 0, 12, 23, 32, 37, 39, 40, 46, 63, 74, 80, + 90,102,112,120,125,124,119,110,105,109,110,108,104,101,105,113,120, +122,117,107, 94, 83, 77, 65, 47, 33, 20, 23, 23, 18, 25, 39, 59, 71, + 75, 74, 67, 58, 50, 58, 62, 60, 53, 40, 22, 7, 0, 0, 0, 2, 11, + 15, 15, 14, 15, 19, 25, 32, 39, 45, 48, 50, 54, 53, 48, 50, 47, 41, + 31, 22, 19, 8, 2, 11, 28, 45, 56, 59, 65, 72, 75, 83, 88, 93, 94, + 90, 82, 92,111,130,147,156,156,154,163,163,156,143,142,139,133,124, +113,103, 97, 95, 92, 89, 87, 84, 81, 75, 67, 67, 71, 74, 74, 73, 76, + 79, 82, 84, 83, 82, 84, 92,102,113,121,125,125,121,114,107,102, 98, + 95, 96, 98, 97, 94, 86, 75, 67, 70, 73, 74, 73, 70, 68, 66, 61, 55, + 63, 70, 73, 73, 69, 77, 83, 85, 86, 84, 84, 82, 79, 79, 76, 70, 56, + 34, 7, 0, 3, 26, 53, 74, 90,102,110,113,113,109,104, 97, 90, 99, +106,111,121,134,149,158,158,147,129,113, 99, 87, 75, 63, 51, 39, 29, + 23, 19, 16, 14, 11, 5, 0, 0, 0, 2, 20, 36, 46, 51, 50, 43, 32, + 21, 15, 16, 22, 31, 37, 41, 43, 39, 37, 37, 39, 40, 39, 34, 25, 11, + 14, 29, 38, 38, 29, 14, 0, 2, 14, 23, 32, 36, 38, 40, 44, 61, 72, + 81, 94,106,117,127,132,132,127,118,110,102,102,103,103,102,107,114, +120,121,118,109, 96, 91, 84, 73, 54, 37, 22, 20, 19, 13, 15, 39, 61, + 74, 79, 78, 74, 66, 55, 54, 59, 58, 52, 39, 23, 8, 0, 0, 0, 7, + 18, 23, 25, 20, 21, 23, 27, 32, 38, 43, 46, 46, 45, 46, 48, 48, 45, + 38, 27, 27, 23, 13, 0, 3, 21, 38, 50, 54, 58, 65, 74, 82, 89, 94, + 96, 93, 87, 88,105,122,136,145,149,159,163,159,147,145,143,140,133, +123,113,104, 99, 97, 96, 93, 92, 92, 90, 86, 81, 74, 69, 69, 71, 74, + 78, 81, 84, 87, 88, 89, 94,103,113,124,134,139,139,134,127,119,113, +109,105,109,111,110,105, 97, 87, 75, 80, 85, 88, 89, 88, 86, 80, 71, + 58, 60, 68, 71, 71, 69, 77, 83, 86, 86, 85, 87, 84, 79, 78, 74, 65, + 50, 26, 1, 0, 6, 26, 51, 72, 88,102,112,119,120,117,111,103, 93, + 89, 94,100,112,122,133,140,139,130,117,102, 89, 76, 62, 47, 33, 22, + 18, 15, 16, 18, 13, 12, 7, 1, 0, 0, 0, 12, 26, 34, 38, 34, 27, + 16, 13, 5, 7, 15, 22, 28, 32, 31, 28, 26, 26, 29, 32, 33, 31, 23, + 12, 9, 25, 32, 32, 23, 8, 0, 2, 13, 22, 29, 34, 37, 39, 45, 56, + 71, 84, 96,108,119,128,133,133,128,122,114,109,104,102,101,103,107, +113,117,118,115,108,102, 98, 91, 77, 57, 39, 25, 15, 14, 7, 14, 41, + 62, 75, 81, 81, 77, 71, 61, 52, 56, 55, 48, 37, 21, 6, 1, 0, 0, + 11, 21, 28, 31, 26, 22, 23, 26, 29, 33, 38, 41, 43, 43, 45, 46, 46, + 41, 34, 28, 31, 27, 18, 5, 0, 11, 27, 40, 46, 51, 58, 68, 78, 86, + 92, 96, 95, 90, 90, 99,113,125,134,150,161,159,149,143,143,142,136, +128,123,117,110,106,102, 96, 96, 95, 96, 96, 95, 92, 88, 83, 78, 74, + 77, 79, 82, 85, 88, 90, 92, 99,109,120,130,140,145,147,142,133,127, +121,117,113,117,120,119,113,104, 93, 82, 89, 96,101,102,102, 98, 92, + 82, 70, 71, 69, 69, 68, 68, 77, 82, 85, 86, 86, 88, 87, 79, 76, 70, + 59, 41, 18, 0, 0, 8, 26, 47, 67, 84, 97,109,118,122,120,114,107, + 96, 84, 82, 90,100,109,118,122,121,113,102, 88, 75, 59, 43, 29, 25, + 20, 15, 13, 15, 16, 12, 12, 11, 5, 0, 0, 0, 6, 15, 21, 22, 18, + 11, 8, 6, 0, 0, 6, 13, 18, 20, 19, 14, 13, 14, 18, 22, 25, 25, + 20, 11, 8, 20, 28, 28, 20, 4, 0, 1, 11, 20, 29, 37, 39, 38, 45, + 57, 73, 85, 96,107,115,122,125,125,122,119,115,112,108,105,103,103, +108,112,113,114,113,112,108,103, 94, 79, 59, 41, 25, 12, 9, 3, 16, + 43, 63, 76, 81, 81, 78, 72, 62, 50, 53, 51, 44, 32, 16, 4, 3, 0, + 0, 9, 20, 28, 32, 28, 20, 18, 19, 21, 25, 29, 34, 37, 38, 41, 44, + 43, 38, 31, 26, 31, 29, 21, 8, 0, 0, 15, 27, 34, 39, 51, 60, 72, + 81, 88, 93, 93, 90, 92,101,112,123,137,150,156,150,137,140,140,140, +143,140,133,124,122,115,109,100, 96, 95, 96, 99,101,100, 96, 92, 87, + 82, 79, 80, 82, 84, 87, 89, 92, 98,108,118,127,136,140,142,139,133, +127,122,119,115,119,122,121,115,107, 96, 88, 97,105,111,112,110,105, + 98, 88, 84, 84, 81, 73, 66, 66, 75, 81, 84, 84, 87, 90, 89, 82, 74, + 66, 53, 32, 8, 3, 1, 9, 23, 43, 61, 77, 91,103,113,118,118,114, +108, 98, 87, 74, 79, 88, 96,102,105,103, 96, 86, 73, 56, 38, 27, 25, + 21, 16, 11, 8, 11, 12, 8, 13, 14, 9, 1, 0, 0, 2, 9, 12, 11, + 6, 2, 4, 0, 0, 0, 0, 5, 8, 8, 6, 2, 7, 11, 11, 12, 15, + 18, 14, 6, 5, 18, 26, 26, 18, 3, 0, 0, 6, 21, 33, 41, 45, 46, + 45, 57, 73, 84, 95,102,109,113,113,113,113,112,112,113,113,109,104, +102,109,115,120,122,120,115,111,104, 95, 80, 59, 43, 26, 9, 6, 3, + 19, 44, 63, 74, 79, 79, 74, 68, 59, 50, 51, 47, 39, 26, 11, 2, 2, + 1, 3, 6, 16, 25, 28, 26, 19, 9, 8, 11, 14, 18, 23, 26, 29, 34, + 38, 38, 34, 27, 21, 27, 27, 21, 11, 3, 4, 3, 13, 20, 29, 43, 51, + 61, 73, 82, 88, 88, 88, 90, 99,111,122,134,143,145,136,133,137,154, +163,165,156,143,143,137,127,115,107,100,100, 97,100,103,104,102, 98, + 92, 85, 81, 81, 81, 81, 83, 85, 88, 92,101,109,117,123,127,129,127, +124,120,118,115,113,114,118,117,112,103, 93, 95,103,112,115,115,113, +107, 99, 93, 97, 97, 92, 83, 68, 63, 74, 79, 83, 84, 87, 90, 89, 83, + 72, 63, 47, 28, 15, 13, 9, 9, 26, 50, 67, 79, 86, 96,106,112,113, +111,106, 99, 89, 78, 66, 74, 81, 87, 90, 88, 81, 70, 53, 33, 23, 29, + 32, 31, 25, 14, 3, 3, 4, 8, 15, 18, 15, 7, 0, 0, 0, 4, 6, + 4, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2, 7, 8, 5, + 6, 8, 6, 0, 4, 18, 25, 25, 16, 2, 0, 0, 6, 21, 33, 43, 47, + 48, 50, 54, 70, 81, 90, 96,100,102,102,101,101,108,115,121,121,117, +108,101,108,117,122,124,121,115,109,102, 92, 78, 60, 45, 26, 8, 4, + 1, 20, 45, 62, 72, 75, 74, 68, 60, 51, 50, 50, 45, 34, 20, 5, 0, + 0, 1, 6, 7, 9, 18, 22, 21, 14, 9, 12, 12, 13, 11, 8, 13, 16, + 23, 28, 31, 29, 23, 16, 20, 22, 18, 9, 7, 8, 6, 2, 6, 21, 36, + 46, 53, 62, 74, 80, 83, 84, 86, 94,105,115,125,132,130,123,137,161, +181,190,186,173,161,163,154,139,123,113,107,100, 97, 99,103,105,103, + 99, 92, 84, 82, 80, 82, 84, 84, 82, 82, 83, 89, 96,103,109,113,113, +113,113,111,110,110,110,108,109,108,103, 96, 91, 99,107,113,117,114, +110,104, 99,106,110,109,102, 90, 74, 61, 72, 78, 82, 83, 85, 89, 88, + 82, 72, 62, 46, 29, 22, 19, 14, 7, 32, 56, 74, 86, 94, 99,102,103, +106,106,103, 98, 91, 81, 70, 56, 65, 72, 75, 74, 66, 53, 34, 22, 29, + 34, 36, 32, 25, 13, 1, 0, 1, 11, 19, 21, 20, 12, 1, 0, 0, 1, + 3, 2, 0, 0, 0, 0, 0, 3, 9, 12, 8, 0, 0, 0, 0, 3, 5, + 4, 1, 1, 0, 0, 5, 18, 26, 26, 18, 3, 0, 0, 4, 19, 31, 39, + 44, 46, 46, 47, 63, 76, 84, 89, 92, 92, 92, 91, 96,105,113,121,123, +120,111, 97,107,115,120,121,117,111,105, 96, 88, 76, 64, 48, 27, 11, + 5, 0, 21, 44, 59, 67, 68, 64, 56, 47, 48, 50, 47, 41, 29, 14, 1, + 0, 0, 0, 4, 8, 9, 9, 14, 13, 19, 23, 26, 25, 20, 13, 4, 0, + 1, 8, 16, 22, 23, 21, 15, 9, 13, 12, 8, 11, 11, 8, 3, 0, 14, + 31, 43, 51, 57, 64, 73, 76, 84, 89, 92, 96,105,113,117,115,133,159, +188,206,212,208,194,173,175,167,150,132,120,112,102, 92, 96,101,102, +100, 94, 88, 84, 80, 85, 90, 92, 93, 92, 88, 82, 76, 84, 90, 95, 99, +102,102,102,102,102,104,105,105,102, 98, 93, 87, 92, 99,107,112,113, +110,104, 99,107,114,119,118,109, 95, 79, 60, 70, 77, 82, 84, 84, 86, + 85, 78, 74, 64, 46, 26, 23, 20, 15, 19, 39, 62, 79, 90, 99,105,109, +111,111,104,101, 98, 93, 86, 77, 65, 50, 52, 56, 55, 48, 40, 29, 26, + 33, 37, 37, 32, 22, 9, 1, 0, 1, 11, 19, 21, 19, 12, 2, 0, 0, + 2, 5, 5, 2, 0, 0, 0, 3, 12, 18, 16, 11, 0, 0, 0, 0, 0, + 3, 3, 1, 0, 0, 0, 3, 18, 26, 26, 19, 8, 1, 0, 1, 14, 25, + 33, 39, 50, 56, 57, 68, 74, 79, 84, 88, 90, 92, 95, 99,102,110,118, +121,119,111, 98,103,111,113,113,109,110,106, 98, 88, 77, 65, 48, 27, + 13, 6, 3, 22, 43, 55, 60, 59, 52, 48, 51, 51, 50, 46, 40, 37, 27, + 13, 0, 0, 0, 0, 5, 11, 14, 16, 21, 27, 33, 38, 38, 34, 28, 20, + 12, 1, 0, 4, 13, 19, 20, 18, 13, 13, 14, 14, 14, 13, 11, 5, 0, + 4, 21, 34, 46, 54, 61, 69, 79, 88, 93, 97,102,105,110,117,129,150, +183,210,226,232,228,216,196,179,173,159,142,127,114,105,101,100,101, +101, 99, 96, 90, 85, 89, 93, 98,101,101, 99, 95, 89, 84, 79, 82, 87, + 91, 96,100,103,105,103,100,102,102,101, 97, 93, 97,102,105,105,108, +108,105,100,102,110,118,122,121,112, 99, 84, 66, 67, 74, 79, 83, 84, + 84, 82, 79, 75, 65, 50, 31, 25, 19, 16, 33, 45, 65, 79, 91,100,107, +112,117,117,110, 99, 98, 96, 92, 84, 74, 60, 44, 45, 46, 43, 36, 25, + 32, 38, 40, 39, 33, 22, 9, 4, 2, 0, 6, 12, 15, 13, 7, 0, 0, + 1, 7, 11, 12, 9, 4, 0, 4, 12, 18, 19, 16, 9, 0, 0, 0, 0, + 6, 8, 7, 4, 0, 0, 0, 1, 12, 21, 25, 19, 14, 9, 9, 13, 18, + 25, 34, 45, 55, 61, 66, 77, 83, 87, 90, 92, 93, 95, 96, 98,101,105, +113,118,117,111, 99, 96,102,103,103,106,106,102, 93, 83, 73, 59, 43, + 21, 14, 7, 5, 23, 41, 51, 53, 48, 47, 55, 56, 53, 51, 51, 53, 50, + 40, 26, 8, 1, 0, 1, 5, 11, 16, 20, 25, 31, 38, 43, 45, 44, 40, + 33, 25, 13, 0, 0, 6, 15, 20, 23, 25, 25, 26, 26, 25, 20, 14, 11, + 4, 0, 6, 21, 34, 46, 56, 66, 77, 87, 94,100,105,110,117,125,140, +167,202,228,240,243,241,232,216,196,175,165,152,137,124,114,109,107, +106,105,102, 99, 93, 89, 95,100,104,107,108,105,100, 94, 88, 83, 79, + 79, 85, 91, 96,100,102,102, 99, 96, 98, 97, 96,100,106,111,113,110, +104,103,101, 96,103,110,118,122,121,113,102, 88, 74, 68, 71, 74, 79, + 81, 82, 79, 77, 73, 64, 50, 37, 34, 32, 31, 46, 47, 63, 76, 87, 96, +103,111,117,119,112,101, 97, 98, 96, 91, 82, 69, 53, 41, 43, 39, 32, + 29, 37, 43, 45, 41, 36, 25, 12, 3, 1, 0, 0, 3, 5, 4, 0, 0, + 2, 7, 13, 18, 19, 16, 11, 6, 9, 14, 16, 15, 13, 6, 0, 0, 0, + 7, 14, 16, 16, 13, 9, 4, 1, 4, 12, 18, 20, 22, 22, 21, 21, 22, + 25, 29, 38, 46, 55, 62, 74, 84, 89, 92, 94, 95, 95, 95, 96, 95, 96, + 99,106,112,113,109,100, 94, 96, 99,101,102, 99, 93, 84, 74, 62, 48, + 31, 15, 13, 7, 7, 27, 41, 48, 46, 44, 54, 59, 58, 53, 55, 58, 59, + 57, 50, 36, 19, 14, 8, 5, 11, 12, 18, 22, 25, 31, 37, 43, 47, 48, + 46, 40, 33, 19, 0, 0, 2, 13, 25, 31, 33, 34, 36, 36, 33, 29, 22, + 15, 11, 7, 7, 11, 22, 38, 53, 64, 74, 84, 92, 99,106,114,122,133, +150,179,212,235,245,247,246,240,226,208,183,171,161,147,133,122,114, +111,109,108,105,102, 97, 93, 98,103,108,111,112,109,102, 96, 90, 85, + 82, 79, 79, 81, 87, 92, 94, 94, 92, 92, 93, 93, 97,103,110,114,114, +109,101,100, 97, 96,103,109,113,118,117,113,104, 92, 79, 72, 74, 77, + 79, 81, 81, 79, 74, 68, 59, 47, 41, 43, 43, 44, 54, 53, 56, 68, 78, + 87, 96,104,113,115,111,101, 95, 98, 98, 94, 85, 73, 57, 41, 43, 39, + 32, 33, 40, 45, 46, 43, 37, 26, 14, 4, 0, 0, 0, 0, 0, 0, 2, + 4, 6, 12, 18, 22, 23, 21, 15, 9, 11, 13, 12, 9, 6, 0, 0, 0, + 4, 15, 21, 23, 23, 21, 16, 13, 9, 11, 16, 23, 29, 32, 32, 29, 27, + 27, 27, 29, 34, 41, 50, 61, 76, 87, 93, 96, 96, 96, 94, 93, 94, 94, + 92, 92, 98,103,106,104, 98, 93, 95, 96, 96, 95, 90, 82, 71, 58, 46, + 32, 16, 13, 12, 7, 13, 33, 44, 47, 43, 50, 57, 60, 56, 54, 58, 61, + 61, 59, 52, 40, 28, 23, 15, 9, 15, 18, 19, 21, 22, 26, 32, 38, 43, + 46, 45, 41, 34, 20, 0, 0, 2, 14, 26, 34, 37, 38, 39, 39, 38, 34, + 29, 21, 16, 15, 16, 20, 25, 37, 52, 64, 74, 84, 94,104,114,125,134, +140,156,186,218,238,246,248,246,241,228,208,179,173,167,156,140,127, +117,111,109,107,106,103,101, 96, 99,103,109,113,113,109,102, 96, 89, + 84, 82, 80, 80, 81, 81, 80, 83, 83, 82, 88, 89, 88, 93,101,109,113, +111,104, 96, 96, 94, 96,102,105,108,110,111,108,102, 94, 84, 74, 76, + 78, 79, 80, 79, 77, 73, 64, 54, 43, 41, 46, 50, 52, 57, 54, 46, 53, + 63, 74, 84, 93,103,108,106, 98, 92, 95, 96, 92, 84, 71, 55, 46, 46, + 43, 33, 33, 39, 44, 45, 41, 37, 28, 15, 6, 1, 0, 0, 0, 3, 6, + 7, 8, 11, 15, 21, 25, 26, 23, 16, 7, 6, 6, 3, 1, 0, 0, 0, + 0, 9, 21, 27, 28, 28, 27, 28, 22, 18, 19, 25, 31, 36, 37, 36, 32, + 28, 27, 25, 23, 26, 31, 40, 59, 75, 87, 93, 96, 96, 96, 93, 96, 96, + 92, 92, 89, 88, 93, 96, 97, 94, 90, 92, 92, 90, 86, 79, 67, 54, 45, + 28, 13, 6, 11, 12, 13, 21, 39, 47, 48, 45, 52, 56, 57, 52, 53, 55, + 57, 58, 55, 50, 38, 31, 26, 16, 16, 21, 22, 20, 20, 19, 20, 23, 28, + 34, 38, 38, 36, 28, 14, 0, 0, 5, 13, 25, 32, 34, 36, 37, 37, 37, + 36, 32, 27, 18, 20, 23, 27, 31, 34, 50, 63, 75, 86, 98,111,124,136, +147,154,158,186,216,235,243,246,243,234,218,194,171,175,171,159,142, +127,114,108,105,104,104,104,102,101, 97,102,107,111,111,107,101, 93, + 87, 85, 82, 79, 79, 80, 80, 79, 75, 70, 77, 83, 86, 85, 85, 94,102, +104,102, 96, 91, 91, 91, 96,100,101,101,102,102,101, 98, 93, 84, 73, + 75, 77, 78, 78, 77, 75, 72, 64, 52, 37, 38, 46, 52, 55, 54, 51, 40, + 32, 50, 62, 69, 80, 92, 98, 98, 94, 86, 89, 90, 87, 78, 64, 50, 52, + 52, 47, 38, 28, 36, 41, 47, 47, 43, 33, 21, 7, 2, 2, 2, 4, 7, + 11, 11, 9, 12, 15, 20, 25, 26, 23, 16, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 23, 28, 31, 32, 39, 41, 36, 23, 26, 31, 36, 39, 38, 34, + 29, 25, 21, 16, 14, 20, 21, 32, 52, 70, 83, 90, 94, 95, 94, 96, 99, + 98, 94, 92, 91, 88, 82, 85, 88, 87, 84, 85, 85, 82, 75, 64, 50, 38, + 28, 12, 0, 2, 8, 13, 25, 31, 45, 53, 52, 47, 51, 52, 50, 47, 47, + 48, 50, 50, 47, 41, 31, 27, 22, 14, 23, 28, 27, 22, 19, 15, 19, 16, + 19, 22, 26, 26, 23, 16, 4, 0, 1, 8, 13, 19, 23, 26, 27, 28, 28, + 29, 31, 31, 28, 21, 21, 26, 29, 33, 37, 45, 60, 74, 87,100,114,130, +145,158,163,163,179,208,228,236,238,231,216,194,169,165,171,167,156, +139,123,111,105,103,100,101,102,102,102, 97, 99,104,107,107,103, 96, + 88, 87, 87, 84, 79, 77, 78, 78, 75, 71, 74, 79, 82, 82, 81, 78, 82, + 90, 92, 90, 84, 84, 86, 90, 96, 97,104,108,107,102, 94, 92, 88, 82, + 71, 71, 73, 74, 74, 73, 71, 69, 63, 52, 36, 32, 41, 48, 53, 47, 43, + 43, 45, 53, 63, 69, 71, 78, 86, 89, 88, 83, 82, 81, 78, 69, 54, 56, + 58, 57, 52, 41, 33, 38, 46, 51, 50, 45, 36, 22, 8, 0, 1, 2, 4, + 8, 11, 9, 8, 8, 12, 18, 22, 23, 21, 14, 2, 0, 0, 0, 1, 3, + 1, 0, 0, 11, 21, 27, 29, 41, 50, 51, 45, 32, 31, 36, 39, 39, 37, + 31, 22, 18, 12, 14, 21, 26, 32, 39, 44, 62, 76, 85, 91, 92, 92, 96, + 99, 98, 93, 94, 93, 90, 84, 77, 75, 76, 75, 76, 75, 71, 61, 47, 28, + 18, 8, 0, 0, 0, 12, 27, 37, 41, 53, 59, 58, 52, 47, 45, 40, 40, + 39, 39, 39, 38, 36, 29, 19, 20, 14, 22, 31, 33, 32, 25, 20, 16, 21, + 20, 15, 11, 13, 13, 9, 3, 0, 0, 5, 12, 13, 12, 13, 13, 14, 15, + 22, 26, 26, 23, 25, 21, 16, 22, 28, 32, 36, 37, 53, 69, 84, 98,114, +130,147,159,163,161,173,198,214,221,218,204,183,161,147,156,161,159, +147,132,115,104,105,104,101, 96, 97, 99, 98, 95, 95,100,102,102, 98, + 92, 84, 83, 84, 80, 76, 74, 75, 74, 72, 68, 74, 80, 84, 84, 80, 74, + 68, 76, 79, 76, 76, 78, 84, 91, 95, 96,105,112,113,111,102, 90, 82, + 77, 66, 64, 66, 67, 66, 66, 64, 63, 59, 50, 34, 26, 33, 40, 45, 38, + 48, 55, 58, 55, 61, 65, 65, 64, 74, 79, 80, 78, 74, 72, 67, 57, 57, + 59, 61, 60, 58, 55, 50, 41, 48, 52, 50, 44, 33, 19, 4, 0, 0, 0, + 0, 5, 6, 5, 3, 3, 6, 12, 18, 21, 20, 13, 1, 0, 0, 0, 2, + 2, 0, 0, 0, 5, 15, 25, 38, 48, 54, 55, 50, 36, 38, 40, 41, 40, + 34, 25, 15, 8, 14, 21, 27, 31, 40, 46, 51, 54, 70, 80, 87, 91, 92, + 92, 95, 93, 96, 97, 96, 93, 87, 79, 70, 61, 61, 62, 61, 56, 45, 29, + 12, 0, 0, 0, 0, 13, 28, 40, 48, 50, 61, 66, 64, 58, 48, 38, 36, + 36, 40, 40, 37, 29, 25, 18, 9, 9, 19, 28, 37, 39, 36, 31, 23, 15, + 20, 19, 14, 7, 3, 2, 0, 0, 0, 0, 8, 13, 13, 8, 2, 1, 12, + 21, 29, 33, 34, 31, 23, 15, 9, 15, 21, 26, 28, 29, 41, 59, 76, 92, +108,123,139,150,154,150,169,188,198,200,190,169,147,129,134,142,147, +143,134,121,108, 97,100,100, 97, 94, 90, 92, 91, 87, 91, 96, 98, 98, + 94, 88, 79, 73, 74, 72, 70, 72, 73, 73, 70, 63, 71, 78, 83, 84, 81, + 75, 67, 58, 62, 65, 68, 77, 85, 92, 95, 94,103,112,115,113,106, 96, + 83, 70, 59, 55, 61, 65, 68, 67, 62, 54, 51, 43, 31, 23, 25, 31, 36, + 43, 53, 60, 63, 60, 52, 55, 55, 53, 63, 70, 72, 71, 67, 62, 57, 53, + 53, 56, 58, 65, 67, 65, 60, 54, 50, 51, 48, 40, 28, 20, 11, 0, 0, + 0, 0, 0, 2, 3, 5, 11, 12, 9, 15, 19, 18, 12, 0, 0, 0, 0, + 2, 1, 0, 0, 3, 11, 20, 31, 43, 52, 57, 57, 52, 38, 40, 41, 41, + 38, 29, 19, 12, 16, 21, 27, 31, 33, 41, 46, 50, 52, 64, 75, 84, 89, + 92, 95, 96, 98,101,102,100, 95, 88, 79, 68, 57, 46, 47, 47, 44, 36, + 23, 9, 0, 0, 7, 21, 34, 46, 55, 59, 59, 67, 70, 68, 61, 52, 39, + 40, 45, 48, 47, 43, 36, 25, 14, 7, 14, 22, 32, 39, 43, 43, 40, 32, + 22, 14, 13, 8, 4, 0, 0, 0, 0, 0, 0, 8, 12, 11, 6, 5, 12, + 16, 26, 33, 38, 39, 38, 33, 26, 18, 18, 21, 26, 29, 32, 41, 60, 74, + 84, 99,113,127,137,140,143,158,169,173,169,156,137,120,113,117,122, +125,125,119,110, 99, 90, 91, 92, 90, 88, 84, 83, 81, 81, 85, 89, 91, + 90, 87, 80, 73, 66, 63, 63, 66, 68, 69, 69, 66, 60, 64, 73, 78, 80, + 79, 74, 65, 58, 56, 61, 70, 79, 86, 92, 96, 95, 97,105,111,111,105, + 96, 84, 73, 60, 58, 63, 66, 68, 67, 62, 54, 41, 36, 26, 18, 22, 27, + 33, 40, 51, 58, 61, 59, 51, 40, 39, 39, 52, 59, 61, 60, 57, 52, 46, + 41, 41, 50, 60, 66, 70, 69, 66, 61, 57, 52, 46, 39, 32, 23, 13, 0, + 0, 0, 1, 5, 7, 7, 7, 12, 15, 14, 15, 18, 15, 8, 4, 3, 2, + 4, 4, 4, 0, 0, 4, 13, 22, 34, 46, 55, 59, 59, 53, 41, 36, 37, + 37, 32, 23, 12, 19, 23, 27, 31, 31, 29, 37, 40, 41, 45, 59, 71, 79, + 86, 91, 95, 98,101,103,103,100, 94, 85, 74, 61, 50, 38, 34, 37, 36, + 32, 25, 14, 16, 25, 37, 47, 57, 64, 68, 70, 70, 68, 70, 67, 60, 50, + 43, 45, 50, 52, 50, 44, 36, 25, 13, 7, 14, 22, 32, 39, 48, 52, 48, + 41, 32, 23, 14, 6, 5, 6, 5, 3, 1, 0, 0, 4, 6, 5, 12, 25, + 32, 34, 36, 34, 39, 40, 40, 38, 36, 28, 26, 29, 33, 34, 37, 39, 57, + 73, 83, 92,106,118,125,129,130,136,140,140,134,124,113,101, 96, 98, +102,105,105,102, 96, 89, 81, 79, 81, 80, 78, 74, 74, 71, 70, 74, 77, + 78, 77, 73, 66, 58, 52, 51, 53, 57, 60, 62, 61, 58, 53, 57, 65, 71, + 73, 72, 66, 59, 54, 54, 60, 70, 79, 88, 93, 97, 96, 93, 96,102,103, +100, 92, 83, 71, 59, 56, 60, 63, 64, 62, 57, 48, 37, 31, 22, 12, 18, + 23, 31, 32, 43, 52, 56, 54, 47, 33, 21, 27, 39, 46, 48, 47, 43, 38, + 36, 27, 33, 45, 55, 62, 66, 68, 67, 65, 62, 58, 52, 44, 34, 23, 11, + 0, 0, 0, 6, 9, 11, 11, 8, 11, 14, 14, 16, 18, 14, 14, 14, 12, + 7, 9, 8, 8, 4, 0, 5, 11, 21, 33, 46, 54, 59, 58, 54, 43, 28, + 28, 28, 23, 16, 13, 20, 26, 29, 29, 28, 22, 27, 28, 29, 39, 53, 63, + 73, 79, 86, 92, 96,101,102,102, 97, 89, 79, 66, 51, 38, 27, 21, 25, + 25, 25, 20, 31, 41, 52, 61, 69, 74, 78, 79, 79, 79, 76, 73, 67, 60, + 52, 52, 53, 52, 52, 48, 41, 32, 20, 9, 4, 11, 19, 28, 44, 54, 57, + 55, 48, 40, 31, 19, 11, 7, 11, 13, 12, 7, 0, 0, 0, 0, 12, 28, + 41, 48, 51, 48, 44, 37, 36, 38, 39, 38, 33, 31, 36, 37, 37, 37, 37, + 51, 67, 79, 88, 98,109,117,120,118,117,117,113,108,101, 92, 84, 79, + 79, 81, 84, 86, 86, 82, 77, 70, 65, 68, 67, 64, 62, 62, 59, 54, 56, + 58, 58, 56, 50, 43, 36, 31, 32, 37, 43, 48, 51, 51, 47, 40, 48, 56, + 61, 62, 60, 56, 50, 45, 47, 56, 68, 78, 87, 93, 96, 96, 93, 88, 93, + 96, 93, 87, 78, 66, 55, 53, 56, 58, 58, 55, 50, 41, 36, 28, 20, 11, + 9, 15, 22, 20, 32, 40, 46, 46, 40, 29, 14, 15, 26, 32, 33, 31, 26, + 25, 25, 27, 26, 34, 44, 53, 58, 62, 65, 66, 65, 61, 54, 45, 33, 20, + 6, 0, 0, 3, 11, 14, 14, 12, 8, 7, 12, 18, 20, 20, 19, 20, 20, + 18, 13, 15, 13, 13, 9, 3, 4, 9, 15, 28, 41, 51, 55, 55, 50, 40, + 27, 18, 18, 16, 9, 11, 18, 23, 26, 26, 22, 14, 13, 14, 19, 33, 44, + 53, 61, 70, 77, 84, 91, 96, 98, 96, 92, 82, 70, 54, 37, 23, 15, 13, + 11, 12, 13, 31, 48, 60, 70, 77, 83, 86, 88, 88, 86, 85, 84, 79, 74, + 67, 61, 63, 63, 60, 55, 51, 44, 34, 20, 4, 0, 4, 12, 31, 46, 55, + 58, 56, 52, 45, 36, 22, 13, 8, 14, 18, 16, 9, 0, 0, 0, 7, 26, + 43, 54, 59, 59, 56, 50, 40, 29, 27, 32, 34, 33, 32, 37, 38, 37, 36, + 36, 43, 60, 74, 83, 91,100,107,111,110,104, 97, 92, 86, 80, 77, 75, + 73, 67, 66, 63, 64, 66, 64, 60, 54, 48, 52, 51, 47, 48, 48, 45, 37, + 34, 41, 44, 43, 39, 34, 28, 27, 27, 32, 36, 36, 36, 36, 31, 27, 37, + 44, 48, 48, 45, 40, 36, 33, 38, 50, 63, 75, 84, 90, 93, 94, 92, 87, + 85, 88, 85, 79, 71, 60, 52, 54, 55, 55, 53, 50, 45, 40, 34, 27, 19, + 8, 1, 5, 12, 13, 23, 27, 33, 37, 33, 26, 16, 14, 12, 16, 18, 14, + 8, 11, 22, 26, 27, 29, 34, 39, 46, 53, 58, 63, 64, 61, 54, 43, 29, + 14, 0, 0, 0, 6, 14, 18, 16, 14, 15, 13, 18, 22, 25, 23, 20, 22, + 23, 20, 14, 19, 20, 18, 16, 11, 2, 9, 14, 19, 32, 41, 46, 46, 40, + 32, 21, 9, 8, 8, 5, 4, 9, 15, 19, 19, 14, 5, 0, 0, 12, 23, + 32, 39, 47, 58, 64, 73, 81, 88, 91, 88, 83, 72, 57, 39, 21, 9, 4, + 6, 6, 4, 19, 40, 58, 72, 80, 87, 92, 93, 93, 92, 90, 88, 88, 84, + 79, 71, 74, 74, 73, 69, 64, 59, 52, 41, 27, 8, 0, 0, 11, 29, 45, + 53, 56, 54, 50, 45, 36, 22, 11, 8, 15, 19, 16, 8, 0, 0, 0, 20, + 38, 51, 59, 62, 61, 56, 47, 38, 27, 16, 18, 25, 25, 28, 34, 36, 36, + 34, 38, 40, 53, 67, 78, 85, 91, 97,101,101, 96, 91, 84, 80, 81, 80, + 80, 79, 73, 64, 60, 53, 43, 41, 40, 34, 29, 33, 32, 28, 31, 32, 38, + 38, 34, 38, 39, 37, 33, 32, 33, 33, 29, 37, 41, 44, 41, 33, 21, 14, + 23, 29, 32, 31, 26, 21, 21, 21, 26, 40, 56, 68, 78, 84, 87, 87, 86, + 82, 79, 80, 78, 72, 65, 60, 53, 58, 60, 60, 57, 53, 47, 41, 34, 26, + 16, 7, 0, 0, 0, 5, 18, 23, 25, 26, 27, 22, 16, 15, 12, 4, 3, + 0, 0, 5, 16, 20, 23, 29, 37, 44, 48, 51, 50, 57, 60, 58, 51, 39, + 25, 15, 1, 0, 0, 8, 18, 21, 21, 18, 21, 20, 21, 26, 28, 28, 23, + 21, 21, 18, 13, 21, 26, 25, 22, 18, 11, 8, 15, 20, 22, 28, 32, 31, + 26, 18, 8, 0, 2, 8, 11, 7, 7, 13, 16, 14, 6, 0, 0, 0, 2, + 11, 15, 23, 41, 54, 61, 64, 67, 76, 79, 78, 71, 59, 43, 26, 15, 1, + 0, 2, 11, 27, 39, 46, 63, 77, 86, 92, 96, 96, 95, 92, 89, 88, 88, + 85, 79, 81, 83, 82, 80, 76, 72, 67, 60, 50, 33, 14, 0, 0, 7, 26, + 39, 47, 50, 47, 44, 39, 32, 20, 6, 8, 15, 16, 13, 3, 0, 0, 9, + 29, 45, 54, 59, 60, 58, 51, 40, 29, 19, 9, 2, 8, 12, 22, 31, 33, + 33, 37, 45, 47, 48, 62, 74, 81, 85, 87, 91, 91, 88, 84, 87, 90, 91, + 88, 84, 84, 79, 68, 57, 50, 38, 22, 16, 13, 12, 15, 15, 13, 25, 34, + 41, 41, 38, 37, 36, 36, 37, 38, 38, 37, 32, 40, 47, 50, 47, 39, 27, + 21, 21, 16, 13, 11, 7, 9, 12, 11, 13, 28, 45, 58, 67, 74, 76, 77, + 76, 74, 73, 74, 72, 65, 62, 58, 56, 61, 63, 63, 60, 55, 48, 40, 33, + 25, 15, 5, 0, 0, 0, 0, 7, 14, 18, 18, 21, 20, 14, 12, 11, 5, + 0, 0, 0, 0, 6, 11, 14, 23, 34, 43, 50, 52, 51, 50, 55, 54, 47, + 36, 25, 15, 1, 0, 0, 12, 22, 27, 27, 22, 23, 23, 20, 26, 31, 32, + 28, 21, 15, 11, 13, 23, 31, 33, 28, 22, 16, 8, 16, 22, 25, 21, 14, + 12, 6, 0, 0, 0, 5, 13, 18, 16, 11, 12, 16, 16, 11, 2, 0, 0, + 0, 0, 0, 15, 34, 50, 58, 60, 58, 61, 66, 65, 58, 45, 27, 19, 7, + 0, 0, 8, 31, 50, 60, 66, 67, 78, 87, 93, 96, 96, 92, 89, 86, 85, + 84, 82, 86, 88, 89, 88, 85, 81, 76, 72, 66, 56, 40, 20, 0, 0, 4, + 21, 32, 39, 40, 38, 34, 31, 26, 15, 3, 6, 11, 12, 6, 0, 0, 0, + 19, 37, 47, 53, 55, 54, 51, 47, 48, 45, 33, 15, 0, 0, 2, 16, 26, + 32, 34, 41, 50, 52, 50, 60, 72, 79, 83, 82, 80, 80, 79, 87, 94, 98, + 98, 95, 88, 86, 82, 72, 57, 47, 34, 19, 1, 0, 0, 4, 13, 18, 25, + 34, 40, 40, 38, 37, 37, 37, 38, 38, 38, 36, 33, 44, 52, 55, 53, 45, + 32, 25, 25, 20, 11, 0, 0, 0, 0, 0, 0, 14, 29, 44, 53, 58, 60, + 61, 62, 65, 70, 71, 68, 61, 57, 53, 57, 62, 64, 64, 61, 55, 47, 39, + 31, 22, 13, 7, 1, 0, 0, 0, 0, 0, 5, 13, 18, 19, 14, 5, 5, + 3, 0, 0, 0, 0, 0, 0, 3, 12, 23, 36, 45, 48, 48, 43, 50, 51, + 45, 33, 21, 12, 0, 0, 0, 16, 29, 36, 36, 32, 26, 21, 20, 22, 28, + 32, 32, 26, 16, 3, 13, 26, 36, 40, 38, 28, 18, 9, 19, 25, 25, 20, + 11, 0, 0, 0, 0, 0, 11, 20, 25, 25, 20, 13, 20, 22, 20, 14, 5, + 0, 0, 0, 0, 7, 27, 43, 52, 54, 52, 45, 52, 52, 45, 33, 18, 6, + 0, 0, 0, 25, 48, 65, 74, 78, 79, 77, 86, 91, 92, 91, 88, 83, 79, + 78, 82, 87, 91, 93, 94, 92, 88, 84, 79, 74, 69, 59, 45, 25, 2, 0, + 0, 14, 25, 29, 29, 27, 26, 22, 19, 13, 11, 8, 5, 3, 0, 0, 0, + 8, 26, 40, 48, 51, 48, 46, 54, 59, 59, 55, 43, 23, 1, 0, 0, 13, + 26, 34, 39, 43, 50, 52, 59, 66, 74, 80, 83, 82, 77, 69, 78, 88, 96, +102,103,100, 92, 84, 79, 70, 57, 46, 32, 15, 0, 0, 0, 8, 19, 25, + 27, 31, 37, 37, 41, 41, 39, 34, 33, 33, 34, 29, 39, 50, 56, 59, 57, + 50, 37, 23, 23, 19, 11, 1, 0, 0, 0, 0, 0, 1, 14, 26, 34, 38, + 40, 43, 54, 63, 69, 70, 68, 62, 53, 43, 53, 59, 61, 61, 57, 52, 43, + 33, 25, 20, 20, 19, 13, 5, 0, 0, 0, 0, 2, 9, 15, 18, 15, 8, + 0, 1, 5, 7, 8, 8, 9, 14, 16, 13, 18, 27, 38, 44, 45, 41, 41, + 44, 39, 29, 18, 7, 0, 0, 5, 25, 38, 46, 48, 46, 43, 39, 34, 25, + 21, 28, 32, 29, 23, 22, 18, 31, 41, 47, 47, 40, 28, 15, 19, 22, 22, + 15, 5, 0, 0, 3, 11, 18, 21, 26, 29, 29, 25, 19, 28, 33, 33, 31, + 25, 16, 9, 4, 2, 12, 23, 32, 39, 40, 37, 29, 37, 37, 32, 21, 7, + 0, 0, 0, 16, 41, 62, 75, 82, 84, 84, 82, 85, 89, 89, 87, 82, 83, + 91, 94, 94, 95, 96, 98,100,100,100, 96, 90, 79, 71, 61, 46, 26, 3, + 0, 0, 8, 18, 20, 21, 31, 36, 33, 27, 23, 21, 18, 12, 6, 0, 0, + 2, 12, 26, 37, 41, 41, 47, 56, 62, 65, 65, 59, 47, 27, 3, 0, 0, + 14, 29, 41, 51, 56, 61, 66, 73, 78, 82, 84, 84, 83, 79, 79, 79, 86, + 96,102,106,104, 97, 88, 79, 70, 60, 47, 31, 13, 0, 0, 0, 11, 21, + 28, 33, 36, 38, 43, 46, 46, 44, 45, 48, 52, 54, 56, 59, 62, 63, 63, + 60, 53, 40, 27, 18, 14, 8, 2, 0, 0, 0, 1, 5, 5, 4, 11, 16, + 19, 21, 36, 48, 59, 66, 69, 69, 64, 57, 54, 59, 62, 63, 63, 61, 58, + 54, 46, 34, 27, 25, 27, 27, 23, 13, 21, 5, 0, 0, 4, 11, 15, 16, + 13, 9, 7, 6, 11, 15, 19, 22, 27, 27, 23, 28, 29, 33, 41, 44, 41, + 38, 34, 29, 25, 15, 4, 0, 0, 11, 31, 46, 55, 59, 60, 58, 57, 53, + 43, 31, 29, 37, 40, 43, 43, 38, 38, 51, 58, 59, 55, 46, 33, 29, 31, + 27, 19, 9, 11, 20, 29, 38, 43, 44, 40, 32, 29, 25, 27, 37, 43, 46, + 46, 44, 40, 36, 31, 28, 23, 31, 37, 37, 32, 23, 18, 23, 25, 20, 13, + 5, 0, 0, 13, 36, 57, 73, 83, 88, 88, 87, 84, 87, 89, 88, 84, 87, + 96,105,109,110,112,114,117,118,117,114,109,100, 88, 75, 63, 47, 26, + 11, 0, 0, 3, 7, 12, 26, 38, 45, 44, 39, 36, 31, 26, 20, 14, 7, + 1, 3, 12, 19, 26, 29, 43, 57, 64, 68, 71, 69, 62, 50, 34, 20, 8, + 6, 16, 37, 52, 63, 71, 76, 80, 85, 88, 91, 92, 91, 89, 88, 88, 88, + 84, 93,102,106,106,102, 92, 83, 73, 60, 46, 29, 20, 15, 8, 5, 8, + 19, 27, 33, 37, 40, 44, 46, 52, 58, 63, 68, 72, 76, 79, 80, 82, 81, + 78, 71, 60, 47, 34, 25, 18, 13, 11, 12, 14, 18, 23, 27, 26, 18, 4, + 3, 5, 8, 25, 38, 51, 60, 65, 67, 65, 59, 61, 67, 70, 71, 70, 69, + 67, 63, 57, 48, 43, 33, 36, 41, 41, 36, 41, 23, 9, 0, 0, 3, 9, + 14, 19, 21, 21, 21, 20, 20, 25, 29, 34, 33, 34, 39, 40, 38, 39, 44, + 43, 39, 37, 32, 26, 15, 2, 0, 0, 14, 36, 52, 62, 67, 69, 69, 69, + 66, 57, 44, 38, 47, 55, 58, 59, 55, 48, 61, 69, 72, 69, 61, 52, 40, + 40, 37, 29, 23, 32, 41, 53, 59, 62, 61, 55, 45, 29, 23, 37, 45, 51, + 55, 57, 58, 57, 56, 54, 52, 46, 38, 38, 36, 28, 18, 20, 25, 25, 20, + 13, 5, 2, 14, 32, 53, 70, 81, 88, 91, 91, 88, 88, 90, 90, 88, 92, +100,109,119,124,128,132,137,140,140,137,130,121,109, 96, 84, 71, 56, + 39, 25, 11, 5, 2, 0, 14, 31, 44, 52, 51, 47, 43, 38, 32, 26, 20, + 14, 9, 7, 9, 14, 23, 36, 51, 61, 67, 70, 71, 71, 68, 61, 50, 37, + 25, 16, 22, 45, 63, 75, 84, 88, 92, 95, 96, 96, 96, 95, 94, 93, 94, + 93, 89, 90, 98,104,106,102, 93, 83, 71, 58, 43, 34, 34, 32, 27, 21, + 20, 26, 31, 36, 40, 46, 53, 59, 64, 70, 75, 81, 86, 92, 95, 96, 96, + 95, 89, 82, 73, 62, 52, 44, 37, 29, 21, 23, 29, 36, 43, 45, 41, 31, + 15, 0, 0, 0, 12, 25, 38, 51, 58, 62, 62, 59, 66, 70, 72, 73, 73, + 73, 72, 69, 64, 58, 54, 44, 41, 52, 56, 53, 54, 37, 20, 6, 0, 0, + 2, 12, 21, 27, 31, 33, 34, 34, 34, 34, 38, 41, 45, 47, 47, 45, 39, + 41, 41, 40, 38, 34, 27, 16, 2, 0, 0, 16, 38, 54, 64, 70, 74, 74, + 76, 74, 66, 54, 46, 57, 65, 69, 70, 66, 60, 73, 79, 82, 79, 74, 66, + 56, 48, 47, 41, 39, 47, 57, 67, 74, 76, 74, 67, 56, 41, 38, 44, 51, + 55, 59, 63, 65, 67, 69, 68, 67, 63, 55, 43, 32, 23, 12, 20, 25, 23, + 19, 12, 11, 19, 32, 50, 67, 79, 88, 92, 94, 92, 90, 93, 92, 91, 95, +106,117,125,133,142,149,158,165,171,169,159,147,130,115,102, 90, 79, + 65, 51, 36, 20, 11, 5, 5, 18, 33, 46, 53, 53, 50, 45, 39, 34, 28, + 23, 20, 16, 16, 20, 27, 38, 48, 59, 67, 72, 75, 78, 78, 76, 70, 61, + 50, 38, 27, 31, 56, 74, 86, 94, 99,102,102,102, 99, 97, 96, 95, 96, + 97, 96, 92, 85, 93, 99,101, 98, 90, 79, 65, 51, 38, 41, 45, 46, 46, + 43, 40, 38, 45, 51, 56, 61, 65, 69, 73, 77, 82, 88, 96,102,108,110, +108,103, 97, 88, 79, 71, 63, 58, 52, 44, 33, 31, 38, 46, 53, 55, 51, + 39, 23, 4, 0, 0, 0, 9, 23, 37, 47, 54, 55, 58, 65, 69, 70, 71, + 71, 71, 71, 70, 65, 61, 58, 50, 45, 57, 64, 63, 58, 41, 23, 7, 0, + 0, 0, 4, 18, 28, 36, 41, 44, 45, 45, 45, 47, 50, 53, 53, 52, 47, + 40, 36, 38, 38, 37, 34, 28, 16, 2, 0, 0, 16, 37, 53, 62, 69, 72, + 74, 76, 75, 69, 59, 53, 63, 71, 75, 76, 73, 73, 83, 88, 91, 88, 83, + 75, 67, 58, 57, 54, 48, 55, 65, 75, 82, 84, 82, 75, 65, 56, 58, 54, + 52, 55, 59, 63, 67, 71, 74, 75, 74, 73, 66, 57, 43, 25, 8, 19, 22, + 22, 23, 25, 29, 37, 48, 63, 78, 88, 94, 96, 96, 93, 98, 98, 96, 93, +106,120,136,150,161,163,169,183,196,200,196,183,161,139,121,106, 95, + 84, 72, 59, 44, 26, 15, 8, 21, 33, 43, 47, 50, 50, 45, 40, 36, 29, + 26, 22, 21, 20, 22, 27, 36, 45, 54, 63, 70, 74, 77, 79, 81, 79, 75, + 68, 58, 48, 36, 43, 67, 84, 96,103,107,108,107,102, 98, 95, 93, 93, + 95, 97, 96, 92, 85, 87, 91, 92, 90, 82, 69, 53, 37, 38, 44, 51, 56, + 59, 60, 58, 56, 58, 64, 69, 73, 74, 77, 84, 90, 92, 92,100,109,115, +118,114,108,100, 92, 83, 75, 70, 66, 61, 54, 44, 32, 39, 50, 56, 58, + 53, 43, 27, 9, 0, 0, 0, 0, 8, 22, 34, 41, 45, 53, 60, 63, 63, + 63, 63, 64, 65, 65, 61, 58, 57, 51, 46, 59, 66, 67, 57, 40, 21, 12, + 4, 0, 0, 0, 9, 23, 36, 43, 48, 50, 51, 51, 52, 54, 55, 54, 50, + 44, 37, 28, 31, 32, 33, 32, 27, 16, 7, 3, 0, 15, 34, 48, 58, 63, + 66, 68, 72, 72, 67, 60, 59, 68, 74, 77, 78, 74, 83, 92, 96, 97, 94, + 88, 81, 74, 67, 68, 67, 63, 58, 67, 78, 86, 88, 88, 83, 75, 72, 73, + 68, 58, 52, 55, 59, 64, 69, 74, 76, 77, 77, 74, 67, 57, 45, 31, 39, + 43, 44, 43, 44, 51, 59, 66, 74, 86, 95, 99,101, 99,102,105,102, 97, +102,115,134,156,177,190,192,190,206,219,223,218,202,175,145,130,115, + 99, 87, 76, 63, 47, 28, 19, 22, 37, 47, 53, 54, 51, 45, 37, 31, 26, + 21, 19, 18, 18, 19, 22, 28, 37, 45, 53, 60, 66, 70, 74, 77, 79, 79, + 77, 71, 62, 53, 41, 57, 78, 93,104,111,113,112,109,102,107,108,104, + 97, 92, 94, 94, 90, 84, 79, 81, 81, 77, 68, 53, 34, 37, 48, 54, 55, + 61, 68, 71, 71, 70, 69, 74, 79, 81, 81, 89, 97,104,107,106,102,110, +118,119,115,108, 99, 91, 82, 75, 72, 70, 67, 61, 53, 41, 36, 45, 53, + 54, 50, 39, 26, 11, 0, 0, 0, 0, 0, 8, 20, 28, 33, 44, 50, 52, + 57, 65, 66, 60, 54, 56, 53, 47, 50, 47, 45, 57, 64, 65, 50, 33, 32, + 28, 20, 8, 0, 0, 0, 15, 29, 40, 46, 50, 51, 51, 52, 53, 52, 48, + 43, 36, 27, 20, 22, 25, 27, 28, 23, 14, 13, 9, 3, 14, 31, 43, 51, + 55, 56, 58, 62, 64, 61, 57, 64, 70, 74, 77, 77, 81, 92, 99,102,102, + 96, 89, 83, 75, 77, 79, 79, 76, 73, 69, 79, 87, 92, 92, 89, 84, 84, + 82, 77, 67, 60, 58, 63, 65, 65, 70, 74, 76, 79, 79, 76, 71, 62, 53, + 56, 59, 60, 59, 62, 70, 77, 81, 83, 94,102,105,106,104,108,109,104, + 98,107,123,145,171,194,208,210,208,225,235,238,232,216,186,156,143, +124,106, 88, 78, 65, 48, 28, 26, 39, 51, 58, 60, 58, 52, 45, 36, 25, + 21, 16, 13, 8, 11, 13, 16, 23, 31, 39, 46, 53, 57, 61, 66, 72, 76, + 77, 76, 72, 65, 56, 50, 71, 88,102,112,117,117,113,111,121,127,127, +120,109, 96, 90, 90, 87, 80, 71, 69, 66, 60, 50, 33, 32, 46, 58, 64, + 66, 65, 73, 77, 78, 78, 79, 84, 87, 88, 89, 98,108,117,121,119,113, +107,113,115,113,105, 96, 88, 84, 76, 71, 70, 68, 64, 58, 48, 37, 36, + 44, 46, 41, 33, 21, 9, 0, 0, 0, 0, 0, 0, 8, 14, 21, 31, 37, + 46, 62, 71, 74, 69, 58, 43, 41, 32, 37, 39, 41, 52, 58, 58, 40, 41, + 43, 40, 36, 22, 5, 0, 0, 6, 21, 32, 39, 43, 43, 43, 43, 43, 41, + 37, 29, 22, 13, 12, 14, 16, 20, 21, 19, 14, 15, 13, 6, 14, 28, 38, + 44, 45, 46, 50, 51, 54, 56, 61, 67, 72, 74, 75, 76, 88, 97,103,105, +102, 96, 88, 80, 82, 85, 87, 88, 86, 83, 79, 79, 88, 95, 97, 97, 95, + 91, 89, 84, 82, 79, 75, 72, 74, 75, 74, 70, 74, 79, 83, 84, 82, 78, + 73, 67, 71, 72, 73, 79, 84, 88, 91, 92,102,110,113,114,112,110,110, +104,103,118,133,150,175,198,210,212,226,238,244,245,242,228,198,161, +149,129,110, 93, 79, 65, 48, 28, 40, 53, 62, 66, 66, 62, 54, 45, 45, + 44, 39, 31, 20, 13, 6, 3, 7, 13, 20, 28, 36, 41, 47, 53, 59, 66, + 72, 75, 75, 73, 67, 58, 63, 82, 98,111,120,123,121,117,129,142,149, +147,133,117,101, 88, 87, 82, 75, 65, 54, 48, 40, 26, 26, 29, 50, 64, + 72, 74, 74, 74, 79, 81, 83, 88, 92, 95, 95, 93,102,113,123,128,128, +123,115,109,112,108,102, 99, 95, 89, 80, 68, 68, 67, 65, 60, 53, 44, + 36, 32, 36, 32, 29, 25, 18, 8, 0, 0, 0, 0, 0, 1, 6, 12, 19, + 25, 46, 64, 74, 77, 74, 64, 50, 31, 22, 22, 29, 36, 44, 50, 48, 43, + 45, 47, 50, 46, 34, 16, 0, 1, 3, 12, 20, 27, 32, 41, 46, 47, 41, + 33, 23, 15, 8, 6, 15, 22, 22, 16, 16, 15, 13, 15, 15, 11, 18, 23, + 32, 36, 37, 45, 51, 53, 54, 57, 62, 67, 71, 73, 74, 78, 89, 98,103, +102, 98, 90, 87, 88, 90, 92, 95, 95, 94, 91, 88, 88, 88, 96,102,106, +106,104,102, 98, 96, 94, 90, 84, 80, 82, 82, 80, 76, 79, 86, 91, 93, + 92, 89, 86, 84, 85, 88, 92, 95, 98, 99, 99,108,115,120,121,119,115, +113,111,113,128,145,163,181,198,212,223,235,244,248,249,246,238,212, +177,145,128,112, 96, 80, 65, 50, 41, 53, 63, 70, 74, 72, 67, 58, 60, + 60, 58, 52, 43, 29, 16, 9, 5, 5, 8, 14, 22, 32, 43, 52, 59, 63, + 64, 70, 74, 75, 74, 68, 71, 74, 92,108,121,129,130,127,132,145,159, +165,159,142,122,103, 89, 81, 75, 67, 56, 43, 32, 21, 14, 13, 25, 47, + 64, 74, 79, 81, 82, 84, 87, 92, 96,100,102,102,100,101,111,120,127, +127,123,117,112,109,107,105,102, 98, 92, 83, 70, 63, 64, 62, 59, 54, + 46, 40, 38, 39, 38, 36, 31, 25, 15, 5, 0, 0, 0, 0, 0, 1, 4, + 12, 18, 40, 58, 70, 75, 74, 66, 54, 38, 22, 11, 20, 28, 36, 40, 39, + 39, 43, 52, 56, 54, 44, 28, 9, 11, 13, 15, 19, 23, 33, 43, 48, 48, + 43, 33, 22, 13, 5, 13, 21, 27, 27, 21, 16, 14, 11, 16, 19, 16, 16, + 16, 20, 25, 34, 43, 47, 48, 50, 53, 58, 63, 67, 70, 72, 77, 88, 96, + 99, 96, 91, 88, 94, 96, 96, 99,101,102,100, 98, 96, 96, 97, 99,106, +113,117,117,114,112,109,107,102, 96, 88, 87, 88, 88, 84, 79, 89, 97, +103,106,105,102,101,101,102,103,106,107,108,108,109,115,120,121,120, +118,114,113,117,130,147,163,181,196,210,221,234,244,249,250,249,242, +223,188,154,127,113, 99, 85, 72, 57, 52, 60, 70, 76, 79, 78, 73, 67, + 70, 69, 67, 61, 51, 36, 18, 7, 3, 3, 6, 13, 22, 33, 45, 55, 63, + 67, 68, 70, 74, 76, 76, 79, 83, 87,102,118,130,137,139,134,149,159, +169,173,163,145,125,107, 92, 79, 67, 55, 41, 28, 18, 5, 0, 0, 13, + 38, 59, 73, 81, 85, 88, 89, 92, 96,101,105,108,109,106,101,102,111, +117,119,117,113,109,106,105,103,102, 97, 92, 83, 70, 57, 59, 59, 58, + 54, 48, 43, 41, 41, 40, 37, 32, 25, 16, 6, 1, 0, 0, 0, 0, 0, + 0, 5, 12, 27, 46, 60, 67, 69, 65, 57, 45, 32, 18, 15, 23, 29, 31, + 33, 29, 43, 53, 59, 58, 51, 37, 19, 20, 22, 23, 26, 29, 33, 38, 44, + 45, 39, 29, 18, 6, 13, 19, 23, 29, 31, 26, 18, 15, 9, 16, 22, 22, + 21, 19, 18, 25, 32, 37, 39, 39, 40, 44, 50, 56, 60, 64, 67, 74, 85, + 92, 92, 89, 86, 93, 98,100,100,101,102,103,103,102,102,102,104,105, +109,119,125,128,127,122,119,115,112,106, 98, 90, 92, 92, 89, 83, 91, +102,113,118,120,117,114,113,113,113,113,114,114,114,114,115,118,120, +121,121,119,114,115,128,140,154,171,183,196,210,226,240,247,249,248, +243,225,194,158,128,113,102, 90, 77, 63, 58, 65, 74, 79, 82, 82, 79, + 74, 75, 75, 73, 66, 56, 39, 19, 3, 0, 0, 1, 8, 19, 32, 44, 55, + 62, 67, 69, 72, 77, 78, 83, 88, 91, 96,112,125,137,145,145,147,161, +167,173,171,161,143,125,109, 94, 81, 67, 58, 51, 43, 32, 19, 3, 0, + 4, 23, 48, 67, 79, 86, 89, 92, 94, 97,102,105,109,111,110,104, 97, + 99,104,107,107,104,102,101, 99, 99, 97, 94, 88, 80, 67, 50, 53, 55, + 58, 59, 55, 46, 40, 40, 38, 33, 27, 20, 12, 5, 2, 0, 0, 0, 0, + 0, 0, 0, 7, 13, 29, 45, 55, 60, 60, 56, 48, 39, 28, 14, 18, 22, + 22, 21, 22, 38, 51, 58, 58, 53, 40, 26, 27, 28, 28, 28, 29, 31, 33, + 33, 34, 29, 20, 7, 13, 18, 22, 26, 31, 33, 29, 21, 18, 12, 14, 22, + 26, 27, 27, 26, 22, 26, 28, 28, 26, 26, 31, 38, 45, 52, 57, 61, 67, + 79, 85, 86, 82, 87, 93, 98, 99, 97, 98,100,102,103,103,104,106,108, +108,112,124,134,139,137,130,125,121,117,112,105, 96, 91, 92, 89, 84, + 93,107,119,127,129,127,123,121,119,118,117,117,118,118,119,121,123, +124,125,125,123,120,118,128,142,156,169,177,177,188,206,228,241,245, +244,238,219,190,156,125,114,105, 94, 81, 67, 61, 67, 74, 80, 84, 86, + 85, 80, 79, 79, 76, 70, 58, 40, 18, 0, 0, 0, 2, 9, 16, 27, 40, + 52, 59, 63, 67, 76, 81, 81, 87, 92, 96,103,118,132,143,150,152,159, +169,171,167,161,150,136,121,107, 94, 81, 74, 69, 63, 56, 46, 34, 19, + 1, 13, 27, 38, 57, 73, 83, 88, 89, 91, 93, 96,101,104,108,108,104, + 98, 92, 92, 95, 96, 94, 93, 96, 93, 92, 92, 88, 84, 75, 61, 43, 46, + 54, 61, 62, 58, 50, 37, 34, 31, 25, 20, 18, 12, 8, 8, 7, 3, 1, + 0, 0, 0, 0, 2, 11, 16, 25, 37, 46, 51, 52, 50, 44, 37, 25, 11, + 14, 13, 9, 14, 31, 45, 53, 55, 51, 39, 28, 29, 29, 27, 25, 23, 23, + 25, 25, 21, 14, 5, 12, 18, 22, 25, 34, 37, 36, 34, 28, 20, 13, 8, + 20, 26, 29, 32, 31, 27, 19, 16, 14, 12, 18, 19, 23, 32, 40, 48, 53, + 58, 72, 78, 79, 76, 83, 89, 93, 93, 92, 94,101,106,108,108,103,104, +106,107,115,130,143,149,143,136,128,123,119,113,107, 99, 90, 88, 86, + 81, 95,109,120,128,130,128,124,121,118,115,113,114,115,117,119,122, +124,127,128,128,127,124,123,130,142,154,161,165,163,159,177,204,225, +234,234,226,206,177,145,122,118,109, 98, 85, 70, 62, 66, 74, 80, 86, + 89, 90, 88, 81, 81, 79, 73, 60, 41, 16, 0, 0, 0, 5, 12, 22, 29, + 36, 47, 55, 59, 73, 82, 87, 87, 87, 93, 97,106,120,133,143,152,158, +171,175,169,159,149,136,124,113,102, 92, 83, 80, 77, 72, 66, 58, 48, + 36, 16, 20, 33, 44, 50, 62, 75, 81, 83, 84, 85, 88, 92, 96,100,102, +100, 95, 89, 84, 83, 84, 84, 88, 91, 88, 84, 84, 81, 76, 67, 53, 36, + 44, 56, 62, 63, 58, 47, 34, 26, 20, 26, 28, 26, 19, 16, 15, 14, 11, + 5, 5, 3, 0, 0, 0, 5, 16, 25, 29, 31, 38, 44, 46, 45, 43, 34, + 20, 5, 4, 6, 6, 22, 37, 46, 48, 44, 33, 28, 28, 26, 22, 20, 22, + 19, 13, 13, 11, 2, 4, 13, 20, 25, 33, 43, 45, 41, 40, 37, 28, 16, + 8, 13, 22, 28, 32, 32, 28, 19, 7, 1, 3, 8, 9, 9, 18, 28, 37, + 44, 48, 62, 70, 71, 70, 76, 82, 84, 84, 89, 95,101,105,109,109,105, +100,102,104,121,139,152,154,149,137,134,124,117,111,104, 96, 88, 80, + 79, 82, 96,109,119,123,124,122,118,113,110,107,106,107,108,111,113, +117,121,123,125,127,128,127,128,130,139,147,150,150,147,139,150,173, +196,210,212,202,181,158,132,124,121,113,102, 88, 72, 61, 67, 74, 79, + 86, 92, 94, 93, 87, 84, 81, 75, 63, 44, 16, 0, 0, 2, 12, 16, 25, + 33, 39, 43, 50, 64, 79, 88, 93, 93, 89, 91, 95,106,118,130,142,150, +165,179,177,183,179,161,139,115,105, 96, 87, 84, 83, 80, 77, 72, 66, + 58, 48, 32, 32, 39, 48, 54, 55, 64, 72, 74, 80, 88, 91, 89, 85, 90, + 92, 92, 88, 84, 79, 75, 74, 75, 80, 84, 81, 74, 74, 73, 67, 57, 41, + 31, 45, 56, 62, 61, 54, 41, 27, 15, 26, 34, 37, 33, 26, 23, 21, 20, + 18, 14, 9, 7, 4, 0, 0, 9, 20, 28, 34, 37, 33, 33, 40, 44, 45, + 40, 29, 16, 4, 4, 1, 15, 29, 38, 39, 34, 27, 25, 23, 21, 25, 32, + 34, 29, 19, 3, 0, 2, 9, 14, 20, 27, 38, 47, 50, 45, 48, 47, 40, + 29, 15, 5, 14, 22, 28, 31, 27, 18, 5, 0, 0, 0, 0, 0, 8, 19, + 26, 32, 37, 53, 61, 65, 64, 66, 72, 78, 84, 87, 92, 96,102,104,105, +102, 94, 96,111,129,147,159,159,150,142,142,132,118,106, 99, 92, 82, + 70, 72, 85, 97,107,113,115,113,111,107,103,103,102, 99, 96, 98,101, +104,108,113,115,119,121,123,124,128,133,137,139,136,133,129,123,129, +147,167,181,183,173,154,136,124,127,125,118,105, 90, 74, 60, 72, 79, + 84, 88, 92, 96, 96, 91, 87, 85, 79, 67, 48, 21, 0, 0, 2, 12, 19, + 26, 36, 41, 44, 48, 68, 83, 94,100,101, 96, 88, 90,102,113,124,136, +156,175,186,183,194,192,179,154,125,103, 89, 80, 81, 82, 80, 78, 74, + 69, 63, 56, 43, 43, 44, 52, 56, 56, 53, 59, 68, 82, 90, 93, 92, 86, + 78, 82, 84, 85, 84, 82, 77, 73, 66, 71, 75, 73, 66, 65, 62, 57, 46, + 31, 31, 45, 54, 58, 56, 46, 32, 16, 19, 33, 40, 43, 38, 28, 25, 21, + 21, 21, 20, 16, 12, 7, 2, 0, 12, 27, 40, 48, 51, 47, 39, 38, 45, + 51, 52, 46, 34, 16, 2, 0, 12, 25, 32, 33, 29, 27, 22, 20, 29, 38, + 45, 45, 40, 28, 11, 0, 4, 14, 21, 26, 29, 36, 45, 47, 54, 58, 58, + 53, 44, 31, 15, 3, 12, 20, 25, 23, 18, 6, 0, 0, 0, 0, 2, 13, + 23, 29, 31, 25, 44, 55, 60, 61, 60, 66, 75, 80, 88, 94, 96, 97, 99, + 99, 96, 88,102,118,137,158,167,165,154,137,139,130,118,104, 94, 85, + 74, 61, 74, 87, 97,104,108,107,103,100,103,106,106,104,100, 95, 91, + 89, 96,101,105,107,108,111,113,115,121,127,133,136,134,128,119,107, +117,130,145,156,156,147,133,121,125,129,128,120,108, 92, 76, 58, 72, + 81, 87, 91, 92, 96, 97, 92, 92, 91, 85, 75, 57, 31, 2, 0, 0, 6, + 14, 27, 37, 43, 45, 50, 69, 85, 97,104,106,102, 95, 85, 97,109,120, +140,163,183,190,181,194,196,186,163,134,110, 90, 74, 76, 77, 79, 81, + 81, 77, 70, 56, 45, 44, 50, 56, 58, 57, 54, 46, 66, 81, 90, 94, 92, + 87, 79, 76, 81, 84, 83, 80, 80, 78, 71, 58, 63, 61, 59, 58, 55, 47, + 37, 21, 28, 40, 50, 52, 47, 37, 21, 5, 23, 37, 44, 45, 38, 27, 19, + 16, 20, 22, 22, 23, 22, 18, 9, 0, 11, 28, 45, 56, 60, 59, 54, 44, + 53, 59, 61, 57, 46, 29, 11, 6, 14, 22, 29, 32, 31, 27, 25, 33, 40, + 48, 53, 53, 47, 36, 18, 7, 7, 19, 28, 34, 38, 40, 47, 55, 61, 66, + 67, 64, 58, 47, 34, 22, 11, 6, 13, 15, 13, 8, 2, 0, 0, 0, 9, + 20, 28, 34, 36, 31, 39, 47, 55, 61, 67, 72, 78, 85, 93, 98,101,102, +101, 98, 96, 97,110,127,147,163,171,167,154,137,127,121,111,100, 88, + 78, 68, 66, 78, 88, 96,101,102, 99, 95,100,103,105,105,102, 98, 94, + 94, 98,102,103,105,107,108,109,114,120,122,121,128,133,132,127,118, +114,113,118,128,134,134,128,119,114,119,124,124,119,107, 93, 78, 62, + 69, 80, 88, 93, 96, 98, 98, 99,100, 98, 92, 82, 66, 43, 14, 0, 0, + 4, 16, 28, 37, 43, 46, 53, 70, 86, 98,107,111,108,100, 92, 98,110, +125,147,167,183,188,179,188,192,183,163,136,113, 94, 79, 79, 82, 84, + 87, 86, 83, 76, 64, 50, 47, 54, 58, 59, 57, 53, 56, 65, 78, 87, 91, + 90, 89, 86, 84, 84, 84, 85, 86, 85, 82, 75, 63, 53, 53, 54, 52, 48, + 41, 29, 15, 20, 31, 38, 39, 34, 22, 6, 12, 29, 43, 48, 46, 39, 27, + 13, 21, 26, 27, 25, 31, 33, 32, 25, 13, 12, 27, 46, 59, 67, 68, 64, + 56, 57, 64, 66, 63, 54, 39, 20, 19, 19, 25, 29, 31, 28, 27, 37, 43, + 47, 53, 56, 56, 51, 39, 29, 26, 25, 26, 34, 43, 46, 47, 50, 57, 64, + 69, 73, 72, 69, 62, 54, 45, 36, 22, 12, 16, 19, 18, 14, 9, 5, 5, + 16, 27, 37, 41, 43, 39, 39, 50, 58, 66, 72, 77, 82, 88, 94, 99,102, +103,103,102, 99,107,119,133,149,161,163,158,147,133,121,110,101, 92, + 84, 74, 66, 72, 81, 88, 94, 96, 96, 92, 93, 98,100,100, 99, 96, 93, + 90, 96,100,103,105,106,106,106,107,113,120,123,122,129,134,134,129, +120,120,120,117,111,115,115,112,105,102,106,112,113,111,102, 92, 78, + 64, 66, 79, 89, 96,102,104,105,105,105,102, 96, 85, 71, 50, 23, 1, + 0, 8, 19, 27, 33, 37, 45, 54, 72, 86, 99,108,113,111,105, 98,106, +117,132,150,167,177,177,169,179,183,175,156,132,112, 94, 83, 84, 87, + 88, 89, 88, 84, 78, 68, 55, 47, 53, 56, 56, 58, 62, 67, 74, 82, 88, + 93, 95, 95, 93, 92, 92, 92, 91, 90, 88, 83, 76, 65, 54, 46, 46, 45, + 41, 36, 25, 12, 9, 18, 22, 21, 14, 8, 3, 22, 39, 51, 54, 52, 44, + 31, 19, 20, 25, 25, 31, 39, 45, 45, 40, 29, 26, 26, 47, 63, 73, 75, + 74, 67, 58, 66, 70, 68, 61, 48, 33, 32, 25, 25, 27, 27, 32, 39, 45, + 48, 50, 53, 54, 54, 48, 48, 46, 44, 41, 41, 43, 50, 53, 54, 54, 56, + 63, 70, 74, 75, 75, 73, 68, 63, 56, 47, 36, 25, 21, 23, 23, 21, 19, + 19, 23, 34, 44, 48, 51, 48, 45, 50, 60, 69, 75, 80, 84, 88, 92, 96, + 98,100,102,101,107,114,124,133,142,147,147,140,132,123,113,102, 89, + 82, 75, 68, 66, 75, 82, 87, 89, 89, 88, 85, 91, 92, 93, 92, 91, 88, + 85, 86, 92, 97,100,101,102,102, 99,103,110,117,121,124,132,137,139, +133,123,124,124,120,112,102, 99, 96, 92, 89, 91, 96, 99, 99, 95, 87, + 76, 63, 64, 79, 89, 98,104,108,109,108,106,102, 94, 84, 69, 51, 28, + 8, 1, 11, 19, 23, 27, 33, 41, 58, 73, 86, 97,106,111,111,107,103, +111,122,136,150,161,163,159,161,169,169,159,140,122,105, 90, 85, 87, + 89, 90, 90, 88, 84, 77, 69, 57, 45, 51, 57, 62, 67, 72, 76, 81, 87, + 91, 95, 96, 97, 97, 97, 97, 96, 95, 92, 88, 81, 74, 64, 55, 50, 46, + 44, 39, 32, 22, 11, 0, 3, 7, 8, 6, 2, 13, 33, 48, 58, 61, 58, + 50, 38, 25, 22, 19, 26, 38, 47, 54, 56, 52, 40, 38, 34, 51, 68, 78, + 81, 80, 75, 65, 66, 71, 71, 66, 56, 44, 43, 38, 37, 38, 41, 45, 48, + 51, 48, 46, 47, 48, 50, 56, 59, 59, 57, 54, 53, 54, 56, 59, 62, 63, + 60, 60, 67, 72, 75, 77, 77, 75, 74, 70, 64, 57, 47, 37, 27, 28, 29, + 29, 31, 32, 38, 47, 53, 55, 55, 53, 51, 61, 71, 77, 81, 83, 84, 87, + 88, 91, 93, 95,103,112,118,123,127,128,128,124,121,114,110,102, 91, + 79, 68, 63, 57, 68, 76, 79, 81, 81, 80, 79, 83, 85, 85, 84, 83, 80, + 78, 76, 79, 86, 91, 96, 99,101, 99, 98,103,108,113,119,128,137,143, +143,137,127,125,124,121,113,102, 90, 81, 79, 78, 74, 79, 84, 86, 84, + 79, 71, 62, 62, 78, 89, 99,105,108,108,105,102, 95, 87, 76, 62, 45, + 27, 12, 3, 11, 16, 19, 22, 29, 46, 61, 74, 84, 93,100,104,106,104, +103,112,123,134,145,150,149,142,150,156,152,140,124,110, 96, 84, 86, + 88, 90, 90, 88, 85, 80, 74, 67, 58, 47, 52, 60, 67, 73, 78, 81, 84, + 88, 90, 92, 95, 96, 98,100,101, 99, 96, 91, 84, 78, 70, 61, 55, 52, + 51, 47, 43, 34, 23, 11, 0, 0, 0, 0, 0, 4, 21, 40, 55, 63, 65, + 62, 56, 45, 33, 25, 21, 29, 43, 54, 60, 62, 57, 46, 46, 40, 56, 73, + 82, 85, 84, 79, 70, 67, 72, 72, 69, 61, 52, 48, 46, 47, 50, 52, 53, + 54, 51, 45, 40, 38, 44, 53, 60, 65, 67, 65, 61, 60, 60, 63, 66, 69, + 71, 69, 63, 61, 67, 72, 74, 77, 78, 77, 76, 74, 70, 63, 55, 44, 33, + 33, 37, 39, 41, 40, 47, 53, 55, 57, 56, 54, 62, 71, 77, 79, 80, 79, + 79, 79, 80, 84, 93,102,110,114,114,113,111,107,103,101, 97, 94, 88, + 79, 64, 50, 45, 55, 66, 72, 73, 71, 69, 71, 76, 79, 79, 76, 74, 75, + 74, 73, 71, 74, 81, 88, 93, 96, 96, 95,100,105,110,113,121,130,140, +147,147,140,129,122,122,119,111,102, 90, 79, 67, 63, 62, 60, 67, 71, + 72, 69, 63, 60, 61, 76, 88, 96,102,102,101, 97, 92, 84, 74, 62, 48, + 34, 21, 9, 5, 7, 11, 13, 19, 31, 50, 63, 72, 79, 86, 92, 95, 97, + 96, 98,108,119,129,136,139,134,127,136,139,132,122,109, 96, 84, 81, + 84, 88, 89, 88, 86, 82, 76, 74, 69, 60, 52, 48, 59, 68, 74, 79, 83, + 84, 84, 85, 87, 88, 92, 95, 98, 99, 97, 92, 87, 79, 72, 67, 58, 54, + 53, 53, 50, 44, 34, 22, 8, 0, 0, 0, 0, 0, 7, 25, 43, 56, 64, + 66, 64, 59, 51, 41, 33, 29, 33, 47, 57, 62, 63, 58, 50, 47, 43, 63, + 77, 85, 88, 85, 79, 71, 67, 72, 72, 69, 63, 55, 50, 50, 52, 55, 57, + 57, 55, 50, 40, 32, 33, 43, 53, 61, 68, 71, 68, 64, 62, 67, 71, 71, + 74, 75, 74, 70, 65, 65, 65, 69, 72, 74, 76, 77, 78, 77, 73, 66, 56, + 44, 36, 39, 44, 46, 46, 43, 48, 51, 54, 55, 53, 63, 72, 76, 78, 75, + 72, 72, 72, 69, 78, 88, 96,103,105,102, 97, 92, 92, 89, 84, 79, 77, + 72, 61, 46, 26, 37, 50, 59, 62, 60, 56, 64, 71, 74, 74, 70, 67, 71, + 73, 73, 72, 70, 70, 76, 82, 87, 90, 90, 95,100,105,109,113,120,129, +139,145,145,139,128,117,117,113,107, 98, 88, 78, 68, 60, 53, 43, 46, + 53, 56, 56, 57, 57, 60, 75, 85, 92, 95, 94, 90, 85, 78, 68, 55, 41, + 28, 19, 11, 8, 5, 1, 3, 8, 15, 32, 51, 61, 68, 74, 78, 81, 84, + 85, 85, 89, 99,109,119,124,125,122,114,122,122,115,105, 94, 84, 74, + 78, 82, 85, 87, 85, 82, 77, 77, 75, 69, 61, 52, 44, 53, 63, 71, 77, + 79, 79, 79, 78, 82, 83, 84, 88, 92, 94, 92, 88, 80, 73, 71, 67, 58, + 52, 53, 54, 51, 45, 33, 20, 5, 0, 0, 0, 0, 0, 9, 22, 40, 53, + 61, 64, 63, 59, 54, 46, 39, 37, 37, 47, 57, 61, 60, 54, 47, 46, 53, + 70, 81, 87, 87, 84, 77, 68, 68, 71, 70, 67, 61, 54, 45, 47, 52, 56, + 58, 58, 54, 46, 34, 34, 41, 46, 52, 61, 69, 72, 69, 65, 72, 76, 78, + 77, 74, 76, 75, 73, 68, 67, 68, 67, 63, 67, 70, 74, 76, 78, 76, 71, + 61, 50, 36, 40, 46, 48, 47, 41, 38, 41, 46, 48, 54, 64, 72, 74, 74, + 70, 64, 68, 69, 72, 76, 79, 88, 93, 94, 88, 88, 91, 90, 86, 77, 64, + 55, 50, 39, 23, 22, 29, 39, 46, 47, 43, 55, 64, 69, 70, 66, 60, 57, + 63, 67, 68, 67, 66, 64, 67, 73, 78, 80, 86, 92, 96,101,104,109,114, +122,130,137,137,133,123,111,111,107,101, 93, 84, 74, 65, 59, 54, 45, + 41, 45, 50, 52, 53, 52, 60, 73, 81, 85, 85, 82, 76, 69, 59, 53, 51, + 45, 34, 19, 5, 5, 3, 0, 0, 5, 13, 29, 47, 57, 62, 69, 75, 78, + 77, 72, 71, 78, 88, 98,107,113,114,112,105,107,106,100, 92, 83, 78, + 80, 83, 79, 84, 84, 83, 79, 74, 74, 73, 66, 57, 48, 41, 41, 54, 63, + 69, 73, 72, 79, 87, 91, 92, 88, 82, 84, 86, 84, 80, 74, 69, 68, 63, + 55, 50, 53, 53, 51, 43, 29, 15, 0, 0, 0, 0, 3, 8, 12, 22, 32, + 46, 54, 58, 58, 56, 52, 46, 41, 38, 39, 45, 54, 57, 55, 47, 43, 45, + 63, 76, 84, 87, 84, 79, 71, 65, 68, 69, 67, 62, 55, 48, 34, 39, 45, + 52, 56, 56, 53, 44, 32, 40, 50, 56, 59, 62, 69, 72, 68, 74, 79, 83, + 84, 82, 79, 74, 71, 69, 66, 63, 66, 69, 69, 66, 61, 66, 72, 74, 74, + 69, 60, 47, 34, 40, 45, 46, 45, 39, 29, 27, 32, 44, 56, 66, 72, 74, + 71, 65, 56, 61, 62, 72, 77, 79, 80, 83, 81, 84, 87, 88, 84, 78, 66, + 48, 27, 22, 20, 22, 22, 21, 27, 31, 29, 40, 52, 60, 63, 63, 58, 50, + 41, 50, 54, 56, 57, 55, 54, 54, 57, 64, 72, 79, 84, 89, 92, 96, 98, +103,110,117,122,123,120,113,102,103,100, 94, 87, 78, 68, 60, 53, 48, + 43, 44, 45, 47, 47, 47, 44, 58, 69, 75, 77, 74, 67, 58, 48, 50, 52, + 51, 50, 44, 33, 19, 4, 2, 0, 0, 3, 14, 25, 39, 50, 62, 73, 79, + 82, 79, 74, 64, 63, 75, 87, 96,102,105,103, 98, 92, 91, 86, 80, 77, + 78, 83, 84, 79, 82, 83, 81, 77, 71, 66, 64, 57, 55, 48, 40, 29, 38, + 50, 58, 62, 74, 84, 92, 96, 96, 91, 84, 75, 78, 76, 72, 66, 62, 60, + 55, 46, 50, 53, 53, 48, 39, 25, 11, 3, 5, 4, 2, 13, 20, 23, 25, + 25, 34, 44, 48, 51, 50, 45, 40, 36, 34, 36, 39, 48, 52, 50, 41, 39, + 56, 71, 80, 85, 85, 80, 73, 63, 66, 67, 66, 61, 54, 45, 38, 37, 39, + 38, 47, 53, 54, 52, 45, 32, 41, 53, 60, 65, 68, 71, 74, 77, 81, 85, + 88, 88, 86, 83, 79, 76, 72, 64, 58, 58, 63, 66, 66, 63, 59, 65, 68, + 67, 62, 54, 41, 31, 37, 40, 41, 39, 33, 25, 19, 31, 43, 55, 64, 69, + 69, 65, 57, 47, 52, 58, 69, 75, 79, 79, 79, 79, 79, 80, 79, 74, 64, + 48, 28, 11, 12, 16, 18, 18, 16, 16, 18, 22, 31, 41, 48, 52, 50, 44, + 36, 28, 31, 37, 39, 40, 39, 47, 55, 59, 61, 64, 69, 74, 81, 88, 93, + 99,101, 99,102,107,109,107,101, 92, 93, 90, 85, 77, 69, 68, 65, 60, + 54, 51, 50, 48, 47, 46, 43, 39, 52, 60, 64, 62, 57, 47, 47, 54, 58, + 62, 64, 62, 56, 44, 28, 12, 3, 0, 0, 2, 15, 28, 41, 55, 67, 76, + 81, 82, 79, 72, 61, 61, 63, 75, 86, 93, 96, 96, 93, 88, 82, 78, 74, + 73, 79, 83, 82, 78, 80, 80, 79, 74, 70, 64, 60, 58, 57, 52, 45, 36, + 31, 40, 52, 63, 76, 86, 92, 96, 94, 88, 79, 71, 70, 69, 65, 60, 54, + 48, 46, 47, 51, 53, 52, 46, 34, 21, 16, 22, 22, 21, 15, 21, 31, 36, + 38, 38, 38, 44, 48, 50, 51, 51, 50, 47, 41, 34, 29, 40, 46, 45, 38, + 47, 62, 75, 83, 85, 83, 76, 65, 63, 65, 65, 61, 55, 46, 36, 31, 32, + 36, 34, 44, 51, 54, 52, 45, 33, 40, 54, 63, 70, 75, 79, 81, 84, 87, + 89, 90, 89, 86, 83, 79, 76, 74, 67, 57, 46, 51, 57, 59, 58, 53, 56, + 58, 57, 52, 44, 33, 23, 27, 31, 32, 31, 26, 20, 21, 23, 36, 48, 57, + 60, 60, 54, 45, 40, 45, 55, 66, 73, 75, 76, 74, 71, 70, 68, 65, 57, + 45, 29, 21, 12, 7, 11, 12, 11, 14, 16, 20, 25, 32, 37, 39, 36, 27, + 22, 15, 9, 14, 19, 21, 25, 36, 45, 52, 56, 59, 63, 68, 74, 81, 88, + 94, 97, 97, 94, 88, 93, 96, 95, 90, 83, 79, 76, 71, 66, 67, 69, 69, + 65, 60, 57, 55, 53, 50, 46, 41, 37, 39, 45, 45, 45, 45, 54, 64, 70, + 73, 74, 73, 69, 60, 48, 33, 16, 5, 0, 0, 0, 15, 31, 46, 59, 70, + 78, 80, 79, 75, 66, 54, 54, 56, 63, 74, 83, 88, 89, 88, 84, 79, 74, + 69, 74, 79, 81, 80, 75, 76, 75, 73, 69, 65, 61, 59, 59, 58, 54, 47, + 39, 31, 36, 48, 62, 75, 84, 91, 92, 88, 81, 72, 62, 63, 62, 59, 54, + 50, 46, 45, 48, 52, 53, 50, 40, 31, 25, 36, 40, 39, 36, 29, 25, 36, + 43, 47, 48, 45, 52, 55, 57, 58, 58, 58, 57, 53, 45, 38, 38, 41, 41, + 38, 52, 67, 78, 84, 85, 81, 72, 58, 60, 61, 60, 56, 48, 39, 28, 23, + 22, 27, 32, 41, 50, 53, 51, 45, 33, 38, 53, 64, 73, 79, 83, 85, 88, + 89, 90, 88, 86, 82, 78, 74, 73, 72, 67, 59, 48, 38, 40, 46, 47, 44, + 46, 45, 43, 38, 31, 23, 16, 14, 16, 19, 20, 19, 16, 18, 21, 23, 36, + 45, 48, 46, 40, 29, 34, 38, 52, 62, 68, 70, 69, 64, 59, 54, 50, 44, + 34, 29, 27, 21, 12, 5, 15, 22, 27, 28, 32, 33, 32, 38, 40, 40, 34, + 25, 12, 0, 0, 0, 2, 7, 18, 28, 38, 46, 50, 52, 57, 64, 72, 79, + 85, 90, 93, 91, 85, 77, 80, 84, 83, 79, 71, 63, 58, 57, 59, 62, 66, + 68, 66, 62, 60, 57, 54, 50, 44, 39, 34, 31, 27, 29, 43, 56, 68, 77, + 81, 82, 80, 77, 70, 60, 47, 33, 19, 7, 0, 0, 1, 11, 29, 46, 60, + 71, 76, 78, 74, 67, 56, 43, 43, 47, 50, 61, 70, 76, 79, 80, 78, 74, + 69, 70, 74, 78, 79, 78, 74, 69, 66, 62, 59, 56, 55, 55, 56, 55, 52, + 46, 38, 32, 26, 40, 56, 71, 80, 85, 85, 80, 71, 59, 56, 57, 55, 52, + 47, 44, 41, 43, 46, 51, 51, 45, 37, 32, 40, 52, 55, 52, 46, 38, 27, + 33, 43, 50, 53, 51, 54, 58, 60, 61, 61, 62, 62, 59, 52, 47, 45, 44, + 41, 40, 54, 68, 79, 84, 84, 79, 67, 52, 55, 55, 54, 50, 41, 32, 21, + 14, 9, 18, 28, 39, 47, 51, 50, 44, 33, 34, 51, 63, 73, 79, 83, 85, + 88, 88, 86, 83, 79, 74, 70, 66, 66, 66, 64, 59, 51, 38, 32, 32, 33, + 34, 32, 28, 25, 20, 26, 31, 32, 28, 21, 16, 8, 11, 13, 13, 15, 18, + 21, 29, 33, 31, 25, 22, 27, 31, 46, 57, 62, 62, 58, 51, 41, 33, 26, + 18, 20, 23, 23, 21, 23, 26, 33, 39, 41, 44, 46, 46, 40, 40, 40, 38, + 31, 20, 8, 1, 0, 0, 0, 0, 7, 19, 28, 36, 38, 40, 47, 55, 63, + 72, 79, 83, 84, 79, 71, 60, 66, 70, 69, 64, 55, 47, 43, 43, 45, 51, + 57, 61, 61, 59, 57, 54, 51, 45, 40, 34, 29, 27, 25, 38, 54, 67, 78, + 84, 88, 86, 82, 76, 67, 56, 44, 31, 21, 8, 2, 6, 9, 9, 23, 43, + 58, 68, 73, 72, 66, 56, 43, 28, 27, 34, 38, 44, 53, 61, 67, 70, 70, + 67, 63, 68, 72, 74, 74, 73, 70, 66, 60, 53, 47, 53, 57, 60, 61, 56, + 45, 38, 32, 26, 21, 26, 45, 62, 73, 77, 76, 70, 58, 44, 48, 48, 45, + 41, 38, 34, 33, 36, 40, 45, 44, 38, 37, 34, 51, 61, 62, 57, 50, 40, + 29, 22, 34, 44, 50, 47, 52, 56, 58, 59, 60, 60, 61, 60, 54, 53, 50, + 46, 43, 40, 53, 67, 78, 83, 82, 76, 63, 46, 46, 46, 45, 41, 34, 26, + 16, 9, 5, 14, 25, 36, 44, 47, 46, 40, 31, 29, 46, 59, 68, 75, 79, + 81, 82, 81, 78, 73, 67, 61, 57, 54, 55, 58, 58, 56, 50, 39, 25, 23, + 23, 21, 16, 13, 15, 25, 32, 40, 45, 45, 40, 31, 18, 9, 8, 12, 13, + 13, 15, 14, 19, 16, 11, 14, 18, 23, 38, 48, 53, 52, 45, 34, 22, 11, + 1, 4, 11, 18, 32, 40, 43, 44, 51, 54, 54, 56, 57, 56, 50, 41, 39, + 34, 26, 15, 12, 9, 6, 0, 0, 0, 0, 8, 16, 21, 22, 25, 32, 40, + 51, 59, 66, 70, 68, 61, 50, 45, 51, 53, 51, 44, 34, 26, 21, 21, 26, + 36, 46, 54, 56, 55, 52, 46, 40, 36, 31, 26, 22, 20, 26, 44, 60, 73, + 82, 87, 88, 86, 79, 71, 63, 59, 51, 39, 26, 9, 13, 21, 27, 28, 23, + 36, 53, 62, 65, 63, 55, 43, 27, 13, 11, 21, 28, 31, 32, 40, 48, 54, + 57, 56, 57, 61, 64, 66, 67, 66, 65, 63, 59, 61, 62, 59, 63, 69, 72, + 68, 58, 43, 22, 14, 11, 11, 28, 48, 60, 65, 64, 57, 44, 34, 39, 38, + 33, 29, 25, 21, 21, 23, 29, 34, 34, 32, 33, 41, 57, 65, 65, 58, 48, + 38, 26, 16, 18, 31, 38, 37, 43, 48, 51, 52, 52, 53, 55, 55, 56, 55, + 52, 46, 46, 41, 50, 65, 75, 81, 80, 74, 59, 40, 34, 34, 34, 37, 36, + 29, 20, 8, 0, 8, 19, 29, 38, 43, 41, 37, 28, 25, 39, 52, 61, 67, + 70, 71, 72, 70, 65, 58, 51, 45, 45, 43, 43, 48, 52, 52, 47, 38, 23, + 12, 11, 7, 5, 15, 31, 41, 46, 47, 55, 58, 55, 46, 33, 20, 12, 9, + 12, 8, 12, 11, 6, 6, 1, 4, 7, 14, 27, 37, 41, 39, 31, 18, 9, + 0, 0, 0, 12, 31, 46, 54, 56, 58, 64, 65, 63, 63, 63, 61, 55, 46, + 39, 32, 31, 33, 34, 32, 25, 13, 7, 6, 1, 0, 3, 6, 6, 15, 20, + 22, 32, 40, 47, 48, 45, 34, 23, 31, 34, 33, 27, 19, 9, 7, 9, 14, + 21, 31, 41, 50, 53, 54, 52, 47, 39, 27, 16, 13, 11, 9, 26, 46, 62, + 74, 82, 85, 85, 81, 74, 76, 75, 70, 59, 44, 27, 12, 25, 38, 46, 48, + 44, 36, 44, 54, 56, 52, 41, 28, 15, 7, 1, 8, 19, 25, 27, 23, 26, + 34, 40, 40, 45, 51, 53, 55, 56, 57, 58, 58, 64, 73, 76, 74, 68, 74, + 77, 74, 65, 51, 31, 11, 0, 0, 9, 29, 45, 52, 51, 44, 31, 23, 27, + 25, 25, 22, 19, 14, 8, 8, 15, 21, 22, 23, 28, 45, 59, 65, 63, 55, + 46, 43, 32, 14, 2, 11, 19, 19, 29, 34, 37, 38, 38, 40, 44, 51, 55, + 54, 51, 50, 51, 45, 45, 60, 72, 78, 77, 70, 56, 36, 21, 23, 32, 36, + 36, 31, 20, 8, 0, 0, 11, 21, 29, 34, 36, 31, 23, 26, 31, 43, 51, + 55, 56, 56, 56, 54, 48, 41, 43, 44, 44, 40, 32, 38, 44, 46, 43, 34, + 19, 3, 0, 0, 7, 28, 45, 55, 59, 58, 61, 65, 64, 57, 45, 29, 20, + 9, 7, 5, 11, 12, 8, 2, 0, 0, 0, 2, 14, 23, 27, 25, 16, 3, + 0, 0, 0, 4, 25, 44, 57, 64, 65, 71, 75, 74, 71, 66, 66, 63, 56, + 47, 44, 47, 51, 54, 54, 51, 41, 29, 29, 27, 20, 8, 0, 0, 0, 4, + 9, 11, 11, 18, 22, 22, 16, 6, 13, 16, 16, 12, 9, 5, 2, 1, 6, + 15, 21, 23, 31, 39, 44, 46, 45, 41, 34, 23, 11, 0, 0, 2, 21, 43, + 60, 72, 78, 80, 79, 75, 82, 84, 82, 74, 61, 44, 25, 21, 39, 54, 63, + 66, 62, 53, 48, 50, 46, 40, 29, 16, 11, 2, 0, 0, 11, 20, 25, 23, + 19, 13, 21, 23, 28, 34, 38, 45, 51, 50, 48, 55, 70, 81, 87, 87, 83, + 75, 79, 76, 68, 54, 36, 16, 2, 0, 0, 12, 27, 37, 38, 32, 21, 11, + 20, 25, 25, 22, 18, 12, 5, 0, 1, 7, 9, 14, 28, 45, 56, 61, 58, + 56, 60, 59, 51, 33, 11, 0, 0, 4, 14, 19, 21, 22, 22, 25, 37, 47, + 52, 52, 52, 56, 56, 50, 40, 56, 68, 74, 74, 66, 51, 29, 9, 19, 27, + 31, 31, 25, 14, 3, 0, 0, 0, 9, 20, 26, 27, 25, 19, 27, 32, 33, + 37, 39, 39, 40, 38, 45, 48, 54, 57, 56, 52, 43, 28, 33, 39, 41, 38, + 27, 12, 0, 0, 0, 18, 39, 55, 64, 68, 66, 62, 67, 67, 62, 53, 39, + 22, 12, 0, 7, 14, 18, 15, 9, 0, 0, 0, 0, 1, 9, 14, 13, 6, + 0, 0, 0, 5, 19, 38, 54, 65, 71, 71, 79, 82, 81, 76, 70, 63, 59, + 52, 60, 64, 65, 64, 65, 65, 61, 53, 53, 53, 51, 43, 29, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 8, 7, 3, 0, 0, 2, + 12, 21, 28, 33, 34, 34, 33, 36, 36, 32, 26, 16, 4, 0, 0, 0, 13, + 34, 53, 65, 72, 74, 72, 79, 84, 86, 83, 74, 58, 37, 16, 33, 53, 67, + 75, 78, 74, 66, 55, 57, 52, 40, 26, 9, 2, 0, 0, 0, 6, 16, 23, + 23, 19, 12, 6, 9, 11, 19, 31, 40, 45, 44, 34, 55, 73, 85, 93, 96, + 92, 86, 78, 76, 69, 56, 47, 33, 18, 0, 0, 0, 15, 26, 29, 27, 22, + 20, 19, 23, 23, 25, 25, 21, 15, 4, 0, 0, 1, 8, 27, 41, 52, 55, + 52, 61, 67, 67, 60, 46, 23, 0, 0, 0, 3, 7, 9, 16, 21, 31, 36, + 46, 51, 54, 59, 61, 59, 51, 36, 52, 63, 70, 69, 60, 45, 22, 4, 12, + 19, 22, 20, 12, 2, 0, 0, 3, 2, 2, 5, 14, 18, 18, 18, 27, 33, + 34, 33, 29, 36, 45, 52, 56, 61, 65, 66, 64, 58, 48, 44, 46, 45, 41, + 33, 20, 5, 0, 0, 4, 26, 47, 62, 71, 74, 73, 69, 64, 65, 62, 55, + 45, 32, 20, 11, 14, 21, 25, 25, 19, 11, 1, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 4, 18, 34, 51, 63, 72, 76, 77, 84, 85, 84, 79, 74, 66, + 60, 68, 74, 79, 80, 80, 79, 77, 75, 74, 74, 73, 69, 62, 50, 31, 11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 11, 11, 6, 7, 18, + 25, 29, 31, 37, 40, 43, 43, 43, 40, 37, 32, 23, 15, 6, 0, 0, 0, + 7, 28, 46, 58, 65, 66, 73, 79, 83, 83, 77, 65, 46, 23, 32, 47, 64, + 76, 84, 86, 83, 75, 66, 60, 56, 48, 36, 20, 7, 0, 0, 8, 22, 31, + 31, 25, 21, 14, 8, 7, 9, 14, 22, 31, 36, 36, 39, 57, 74, 88, 96, +101, 99, 93, 87, 80, 74, 66, 56, 43, 25, 5, 0, 0, 7, 20, 27, 32, + 33, 33, 32, 33, 36, 37, 36, 32, 25, 13, 0, 0, 4, 13, 23, 37, 45, + 48, 53, 63, 69, 70, 64, 52, 31, 6, 0, 0, 4, 12, 20, 29, 40, 48, + 52, 55, 60, 64, 67, 66, 62, 53, 38, 46, 58, 64, 63, 54, 37, 15, 0, + 2, 6, 7, 5, 3, 0, 0, 18, 23, 22, 15, 9, 11, 6, 8, 14, 26, + 33, 36, 36, 32, 43, 54, 62, 68, 73, 74, 74, 70, 62, 55, 57, 58, 57, + 52, 41, 27, 12, 7, 9, 14, 32, 52, 67, 75, 79, 78, 74, 68, 61, 58, + 53, 45, 36, 26, 19, 20, 27, 31, 31, 26, 18, 9, 3, 0, 0, 0, 0, + 1, 4, 7, 12, 20, 32, 46, 59, 70, 78, 82, 82, 86, 88, 85, 81, 75, + 69, 70, 76, 82, 86, 89, 91, 92, 91, 90, 89, 88, 86, 82, 74, 61, 44, + 22, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15, 18, 15, 16, 32, + 44, 51, 54, 55, 53, 47, 50, 50, 50, 50, 45, 39, 29, 19, 12, 12, 12, + 12, 9, 25, 41, 53, 58, 63, 72, 77, 79, 75, 66, 51, 29, 31, 46, 59, + 73, 83, 89, 91, 88, 83, 74, 65, 55, 47, 38, 27, 18, 9, 11, 27, 40, + 48, 48, 41, 27, 18, 13, 12, 13, 15, 20, 25, 31, 36, 44, 62, 79, 91, + 99,103,103, 99, 93, 88, 81, 73, 61, 46, 26, 6, 0, 0, 8, 20, 28, + 36, 39, 40, 41, 43, 44, 44, 41, 36, 26, 14, 1, 1, 8, 18, 25, 32, + 38, 44, 55, 64, 70, 71, 66, 54, 37, 25, 16, 16, 21, 29, 40, 51, 59, + 65, 68, 70, 73, 75, 75, 73, 67, 55, 39, 39, 51, 57, 55, 46, 31, 11, + 0, 0, 0, 1, 5, 4, 8, 14, 37, 43, 40, 34, 25, 19, 16, 8, 8, + 22, 32, 38, 39, 37, 45, 59, 69, 75, 80, 81, 79, 74, 64, 63, 66, 67, + 64, 58, 47, 34, 19, 19, 21, 23, 33, 53, 67, 76, 81, 80, 77, 71, 62, + 52, 48, 41, 34, 27, 22, 23, 31, 33, 32, 27, 21, 14, 11, 5, 0, 0, + 0, 7, 14, 20, 26, 32, 44, 57, 67, 74, 82, 87, 88, 88, 88, 86, 81, + 77, 72, 75, 80, 85, 89, 93, 96, 99,101,101,100, 98, 95, 89, 81, 68, + 51, 29, 12, 9, 6, 2, 0, 0, 0, 0, 0, 5, 14, 20, 22, 20, 37, + 53, 63, 69, 72, 72, 70, 66, 61, 57, 54, 54, 51, 43, 38, 33, 29, 27, + 27, 26, 23, 28, 39, 48, 52, 59, 67, 72, 71, 65, 53, 34, 22, 44, 57, + 66, 76, 84, 90, 92, 91, 86, 79, 71, 60, 46, 37, 29, 22, 18, 27, 43, + 55, 61, 60, 54, 40, 23, 15, 13, 13, 14, 18, 22, 28, 39, 53, 68, 81, + 92,100,104,105,102, 98, 92, 86, 76, 63, 46, 25, 6, 0, 0, 6, 15, + 25, 32, 37, 41, 45, 47, 47, 45, 40, 33, 22, 11, 0, 3, 12, 22, 29, + 34, 34, 44, 55, 63, 68, 69, 65, 59, 50, 40, 34, 34, 40, 48, 57, 66, + 73, 77, 79, 81, 84, 84, 83, 79, 71, 58, 39, 37, 41, 47, 46, 38, 23, + 6, 0, 0, 0, 1, 7, 16, 25, 32, 50, 56, 54, 48, 39, 31, 27, 21, + 20, 22, 29, 39, 43, 41, 43, 59, 72, 79, 84, 86, 84, 77, 66, 67, 71, + 71, 68, 61, 51, 38, 25, 27, 27, 28, 34, 51, 65, 74, 79, 79, 76, 69, + 60, 50, 39, 33, 27, 22, 20, 25, 29, 31, 28, 23, 19, 14, 13, 11, 5, + 0, 2, 13, 22, 31, 36, 41, 58, 72, 80, 86, 88, 92, 95, 96, 95, 92, + 89, 85, 80, 82, 85, 86, 90, 94, 99,102,105,106,105,102, 98, 92, 83, + 70, 53, 33, 19, 18, 14, 8, 3, 2, 5, 8, 8, 8, 18, 23, 26, 33, + 50, 64, 74, 79, 82, 82, 80, 77, 74, 72, 71, 70, 67, 63, 57, 52, 46, + 41, 40, 41, 44, 45, 44, 45, 47, 52, 59, 63, 60, 52, 37, 18, 34, 54, + 63, 69, 76, 82, 87, 89, 88, 85, 79, 72, 61, 47, 31, 26, 22, 23, 40, + 54, 64, 69, 68, 61, 48, 31, 15, 12, 11, 11, 20, 29, 39, 50, 61, 74, + 83, 91, 97,101,102,102, 99, 95, 88, 77, 62, 43, 22, 12, 2, 0, 1, + 7, 14, 21, 28, 36, 41, 45, 45, 41, 36, 25, 14, 3, 0, 5, 16, 28, + 36, 39, 38, 48, 58, 65, 70, 72, 70, 65, 57, 50, 45, 47, 53, 60, 68, + 74, 80, 84, 87, 88, 90, 91, 88, 84, 75, 61, 40, 34, 32, 37, 34, 27, + 15, 1, 2, 3, 6, 13, 20, 29, 38, 45, 58, 63, 62, 57, 50, 41, 40, + 36, 33, 38, 40, 38, 45, 47, 45, 55, 69, 79, 85, 88, 85, 79, 68, 66, + 70, 70, 66, 59, 50, 39, 31, 32, 31, 29, 34, 45, 59, 69, 74, 75, 71, + 63, 54, 44, 31, 22, 16, 12, 12, 20, 23, 23, 20, 15, 11, 8, 11, 12, + 9, 6, 4, 16, 27, 37, 43, 54, 72, 84, 92, 96, 97, 96,101,103,103, +102, 98, 93, 88, 87, 90, 92, 91, 92, 96,101,104,105,103,100, 96, 90, + 81, 69, 53, 33, 25, 23, 20, 14, 7, 6, 11, 14, 15, 13, 20, 27, 32, + 40, 55, 69, 79, 84, 87, 87, 85, 83, 80, 79, 80, 81, 79, 76, 71, 65, + 59, 59, 60, 60, 61, 60, 57, 50, 41, 40, 48, 51, 47, 37, 21, 19, 41, + 58, 65, 67, 72, 75, 79, 81, 81, 79, 75, 68, 58, 45, 28, 18, 16, 32, + 46, 58, 67, 71, 70, 63, 51, 32, 15, 7, 5, 16, 28, 38, 47, 56, 66, + 76, 83, 88, 92, 95, 96, 97, 96, 93, 86, 75, 59, 38, 23, 15, 5, 0, + 0, 0, 1, 7, 15, 25, 33, 38, 38, 34, 26, 15, 4, 0, 1, 8, 21, + 34, 43, 46, 45, 52, 61, 67, 72, 73, 70, 65, 58, 52, 50, 52, 58, 65, + 72, 78, 83, 86, 88, 91, 93, 93, 92, 86, 77, 63, 41, 32, 26, 25, 23, + 16, 6, 11, 15, 20, 22, 23, 31, 39, 46, 53, 62, 67, 67, 62, 56, 51, + 52, 48, 44, 51, 56, 57, 54, 51, 51, 47, 62, 74, 82, 86, 84, 79, 68, + 59, 64, 64, 60, 54, 46, 37, 33, 34, 31, 34, 33, 37, 51, 60, 66, 67, + 61, 52, 43, 34, 25, 14, 3, 0, 6, 14, 15, 14, 9, 4, 1, 0, 4, + 8, 9, 8, 8, 18, 31, 40, 46, 64, 81, 94,102,107,106,102,105,109, +111,110,105,100, 94, 89, 92, 95, 95, 92, 92, 96, 98, 98, 96, 93, 89, + 84, 77, 66, 51, 33, 27, 27, 22, 16, 9, 7, 13, 16, 18, 16, 22, 32, + 38, 41, 54, 67, 78, 84, 86, 86, 85, 84, 83, 83, 85, 88, 88, 85, 81, + 75, 70, 74, 74, 74, 74, 73, 68, 59, 45, 31, 33, 36, 31, 21, 8, 23, + 44, 58, 62, 61, 62, 64, 66, 68, 70, 69, 65, 58, 50, 38, 25, 9, 20, + 34, 47, 58, 66, 69, 67, 60, 47, 29, 12, 0, 9, 23, 36, 45, 53, 59, + 68, 75, 79, 83, 87, 89, 88, 90, 91, 88, 82, 71, 55, 34, 22, 14, 4, + 0, 0, 0, 0, 1, 2, 9, 20, 26, 27, 22, 14, 4, 0, 2, 6, 11, + 27, 41, 51, 55, 53, 52, 61, 67, 71, 71, 67, 62, 55, 48, 47, 52, 59, + 65, 72, 77, 81, 84, 86, 89, 92, 93, 92, 87, 78, 63, 41, 28, 20, 15, + 12, 5, 12, 19, 28, 36, 38, 39, 40, 44, 51, 58, 67, 69, 69, 66, 60, + 61, 62, 59, 53, 61, 68, 72, 72, 65, 53, 48, 53, 66, 77, 82, 81, 76, + 66, 53, 53, 53, 50, 44, 38, 33, 36, 34, 34, 40, 40, 38, 41, 51, 55, + 55, 47, 37, 29, 22, 15, 8, 0, 0, 0, 6, 6, 3, 0, 0, 0, 0, + 0, 2, 6, 7, 9, 19, 32, 43, 50, 70, 88,102,112,115,113,109,107, +113,115,114,111,104, 97, 92, 92, 94, 95, 94, 92, 88, 89, 89, 86, 84, + 80, 77, 71, 61, 50, 34, 27, 26, 23, 18, 12, 9, 14, 18, 16, 14, 26, + 37, 46, 52, 54, 61, 74, 79, 83, 84, 83, 83, 83, 84, 88, 91, 92, 92, + 88, 84, 80, 83, 83, 82, 81, 79, 75, 66, 52, 31, 18, 20, 15, 7, 9, + 26, 44, 54, 56, 52, 57, 60, 58, 54, 53, 53, 50, 44, 37, 28, 20, 23, + 25, 33, 43, 53, 60, 63, 61, 54, 40, 22, 5, 0, 14, 27, 38, 46, 53, + 58, 65, 72, 77, 87, 93, 96, 93, 87, 84, 82, 76, 66, 51, 31, 18, 9, + 1, 0, 0, 0, 1, 6, 6, 3, 6, 13, 14, 11, 4, 0, 0, 5, 8, + 15, 33, 48, 58, 62, 61, 56, 59, 65, 67, 66, 62, 56, 48, 43, 41, 51, + 60, 65, 67, 73, 76, 79, 81, 84, 88, 90, 90, 85, 77, 62, 39, 26, 14, + 7, 3, 7, 16, 27, 38, 47, 51, 51, 55, 59, 60, 61, 72, 75, 72, 69, + 67, 69, 70, 67, 60, 69, 77, 82, 83, 78, 68, 53, 40, 57, 69, 75, 76, + 72, 62, 48, 36, 37, 34, 31, 29, 33, 36, 36, 32, 39, 41, 39, 36, 40, + 44, 41, 32, 31, 22, 11, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 7, 19, 33, 45, 51, 72, 89,104,115,121,119,113, +107,113,117,115,112,105, 98, 94, 92, 94, 94, 92, 92, 88, 85, 80, 76, + 73, 71, 69, 65, 59, 48, 36, 22, 25, 25, 23, 21, 19, 19, 21, 20, 26, + 33, 43, 53, 59, 62, 65, 67, 75, 79, 82, 87, 91, 92, 92, 89, 93, 96, + 97, 96, 93, 90, 87, 85, 84, 84, 82, 77, 69, 54, 33, 8, 6, 4, 2, + 13, 25, 40, 48, 48, 56, 61, 63, 61, 55, 46, 37, 28, 23, 20, 26, 32, + 37, 39, 39, 38, 46, 54, 56, 54, 46, 32, 14, 0, 0, 14, 26, 36, 41, + 46, 51, 58, 71, 83, 92, 97, 98, 95, 88, 76, 75, 71, 61, 47, 29, 12, + 5, 1, 1, 3, 3, 5, 7, 6, 2, 0, 3, 4, 2, 0, 0, 0, 3, + 5, 21, 39, 55, 65, 69, 68, 63, 56, 62, 64, 62, 57, 50, 41, 36, 37, + 51, 60, 65, 66, 67, 71, 74, 76, 79, 84, 86, 86, 83, 74, 59, 36, 23, + 8, 2, 0, 12, 22, 36, 46, 55, 61, 68, 74, 76, 76, 75, 84, 81, 79, + 76, 74, 75, 75, 73, 68, 76, 84, 88, 89, 84, 75, 62, 46, 46, 56, 63, + 64, 61, 52, 40, 28, 22, 21, 23, 27, 31, 33, 34, 31, 34, 38, 37, 33, + 32, 31, 28, 28, 26, 16, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 18, 33, 45, 53, 69, 88,102,114,121,121, +117,111,111,113,112,108,102,103,104,103,101, 97, 99,100, 98, 96, 92, + 88, 81, 71, 69, 68, 65, 60, 51, 39, 27, 26, 27, 28, 29, 31, 33, 38, + 45, 51, 54, 56, 62, 66, 68, 72, 76, 83, 88, 94, 98,101,100, 97, 97, +102,103,104,102,101, 98, 97, 94, 86, 79, 75, 68, 55, 34, 9, 0, 0, + 2, 14, 25, 36, 43, 51, 57, 61, 61, 58, 52, 41, 32, 26, 28, 27, 34, + 41, 47, 51, 52, 50, 43, 43, 46, 44, 37, 23, 8, 0, 0, 6, 16, 25, + 29, 34, 51, 64, 75, 85, 92, 96, 96, 91, 83, 72, 71, 65, 57, 45, 29, + 19, 18, 15, 15, 15, 14, 12, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 12, 28, 46, 60, 69, 72, 71, 66, 60, 60, 62, 59, 54, 46, 46, 47, + 54, 61, 65, 68, 68, 65, 70, 77, 82, 83, 80, 84, 84, 80, 73, 57, 32, + 19, 1, 0, 3, 19, 40, 58, 67, 74, 79, 84, 88, 89, 88, 86, 95, 93, + 92, 91, 87, 81, 79, 74, 74, 81, 88, 92, 92, 88, 79, 67, 53, 48, 46, + 43, 44, 41, 34, 26, 18, 14, 14, 18, 21, 25, 27, 27, 25, 29, 34, 34, + 33, 29, 27, 26, 25, 21, 11, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 13, 29, 43, 50, 65, 84, 98,111,118, +120,117,113,113,112,112,112,113,113,113,112,109,103,108,111,113,112, +109,105, 97, 86, 73, 73, 73, 70, 64, 54, 44, 36, 29, 32, 38, 43, 48, + 55, 60, 65, 66, 65, 60, 63, 66, 71, 77, 84, 92, 97,102,104,104,101, +102,107,110,112,111,110,108,108,104, 94, 80, 72, 66, 55, 37, 13, 0, + 0, 0, 12, 23, 34, 43, 51, 56, 58, 57, 52, 43, 38, 39, 43, 45, 41, + 43, 51, 57, 60, 61, 60, 52, 37, 31, 29, 23, 14, 2, 0, 0, 0, 2, + 8, 19, 38, 56, 68, 77, 84, 88, 90, 88, 83, 74, 69, 67, 61, 53, 41, + 29, 31, 31, 29, 28, 27, 23, 16, 13, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 16, 32, 48, 61, 68, 71, 69, 64, 60, 60, 60, 57, 54, 54, 55, + 54, 65, 72, 74, 74, 74, 72, 74, 79, 84, 84, 81, 84, 84, 80, 73, 57, + 33, 8, 0, 0, 13, 39, 61, 76, 84, 88, 93, 96, 99, 99, 97, 94,104, +105,106,105,101, 93, 83, 76, 79, 85, 90, 93, 92, 87, 79, 68, 56, 47, + 40, 32, 21, 13, 11, 6, 3, 7, 11, 13, 14, 18, 19, 18, 15, 25, 31, + 32, 31, 28, 25, 22, 22, 19, 8, 0, 0, 0, 7, 12, 6, 0, 0, 0, + 0, 1, 3, 2, 0, 3, 2, 0, 0, 6, 22, 37, 45, 59, 79, 92,103, +111,113,114,114,115,118,120,121,122,121,119,117,113,110,117,122,127, +127,124,120,111, 98, 84, 75, 77, 76, 73, 65, 56, 48, 41, 36, 41, 51, + 59, 66, 72, 74, 74, 72, 65, 57, 60, 67, 75, 84, 92, 98,102,104,104, +102,107,111,113,115,115,114,113,113,110, 98, 84, 66, 61, 53, 38, 16, + 0, 0, 5, 16, 25, 31, 40, 48, 52, 52, 48, 43, 46, 48, 51, 54, 55, + 52, 48, 56, 62, 66, 67, 65, 57, 41, 23, 13, 9, 3, 0, 0, 0, 0, + 0, 8, 25, 41, 57, 67, 74, 79, 81, 80, 77, 71, 67, 66, 63, 56, 47, + 37, 36, 38, 39, 39, 38, 34, 28, 20, 14, 6, 0, 4, 4, 2, 0, 0, + 0, 0, 4, 16, 33, 47, 58, 63, 65, 63, 60, 58, 60, 59, 60, 60, 61, + 60, 62, 72, 77, 79, 79, 79, 77, 74, 79, 83, 82, 83, 85, 85, 81, 73, + 57, 33, 7, 0, 3, 31, 57, 76, 89, 96,101,103,106,107,106,104,102, +111,115,120,120,115,106, 94, 80, 81, 87, 90, 92, 89, 84, 76, 67, 57, + 46, 32, 18, 6, 0, 0, 0, 6, 14, 20, 22, 19, 9, 7, 6, 9, 20, + 26, 27, 26, 23, 21, 20, 20, 18, 8, 0, 0, 5, 16, 20, 14, 4, 0, + 0, 6, 12, 14, 13, 5, 7, 7, 2, 0, 0, 13, 27, 36, 53, 71, 84, + 94,102,106,109,112,117,122,127,129,128,125,122,118,113,117,125,133, +137,139,136,130,120,106, 91, 76, 79, 79, 77, 70, 62, 55, 50, 43, 41, + 53, 63, 72, 77, 79, 78, 74, 67, 58, 52, 61, 72, 82, 90, 97,102,103, +102,104,110,113,114,114,113,113,113,113,110, 99, 84, 64, 55, 48, 36, + 16, 7, 4, 12, 21, 27, 28, 34, 41, 45, 43, 41, 48, 53, 54, 56, 60, + 62, 58, 53, 57, 62, 65, 66, 63, 55, 40, 22, 4, 0, 3, 3, 0, 1, + 6, 12, 15, 27, 41, 53, 61, 67, 69, 69, 67, 63, 64, 65, 63, 58, 50, + 39, 33, 37, 40, 43, 44, 43, 38, 28, 22, 15, 7, 9, 15, 13, 8, 1, + 0, 0, 0, 7, 16, 27, 40, 51, 55, 56, 55, 53, 59, 60, 61, 64, 66, + 66, 64, 66, 74, 79, 83, 87, 87, 84, 78, 78, 80, 82, 85, 87, 86, 81, + 72, 56, 33, 7, 2, 18, 47, 71, 87, 99,107,110,111,111,111,110,108, +106,114,123,132,136,130,120,105, 90, 86, 86, 88, 88, 84, 79, 72, 63, + 56, 48, 38, 25, 8, 4, 1, 6, 11, 19, 27, 29, 26, 15, 0, 0, 3, + 13, 19, 21, 20, 18, 15, 14, 16, 15, 7, 3, 1, 12, 22, 26, 19, 7, + 1, 8, 16, 23, 27, 25, 19, 20, 14, 7, 0, 0, 3, 16, 25, 44, 62, + 75, 84, 91, 96,100,106,113,122,129,133,132,127,120,115,113,122,132, +139,142,140,136,130,122,108, 93, 79, 77, 77, 74, 69, 62, 57, 53, 48, + 43, 51, 62, 72, 77, 79, 78, 74, 66, 58, 54, 60, 68, 79, 88, 96,100, +101,100,106,110,111,111,110,107,106,105,106,103, 94, 80, 61, 46, 39, + 28, 20, 16, 13, 16, 25, 28, 28, 26, 33, 36, 36, 46, 52, 56, 57, 59, + 63, 65, 62, 54, 54, 56, 58, 57, 55, 46, 32, 15, 3, 0, 5, 8, 8, + 15, 23, 29, 33, 32, 36, 45, 51, 54, 55, 53, 59, 62, 63, 61, 58, 52, + 41, 29, 29, 32, 36, 39, 41, 40, 36, 27, 23, 19, 13, 22, 26, 22, 14, + 4, 0, 0, 0, 6, 16, 25, 29, 38, 43, 45, 45, 54, 59, 59, 63, 68, + 71, 71, 68, 68, 74, 81, 89, 92, 92, 89, 83, 82, 83, 85, 88, 88, 85, + 79, 69, 53, 29, 7, 7, 29, 58, 79, 94,106,113,114,114,119,121,117, +109,108,113,128,142,150,147,134,117,100, 94, 90, 84, 83, 79, 73, 68, + 63, 54, 50, 43, 34, 39, 38, 31, 21, 26, 31, 34, 36, 32, 21, 4, 0, + 0, 5, 11, 13, 12, 9, 7, 7, 9, 11, 6, 7, 6, 15, 25, 27, 19, + 7, 8, 18, 28, 36, 40, 38, 33, 34, 27, 14, 4, 3, 2, 5, 13, 34, + 52, 64, 73, 79, 85, 91, 98,108,119,128,132,130,123,115,111,117,125, +133,136,136,133,128,122,115,105, 92, 79, 73, 71, 67, 61, 55, 53, 51, + 50, 47, 43, 55, 66, 73, 75, 74, 70, 63, 58, 60, 59, 66, 77, 88, 94, + 98, 99, 99,104,107,107,104,101, 97, 95, 93, 95, 93, 87, 74, 57, 36, + 28, 27, 23, 19, 16, 18, 26, 31, 28, 21, 23, 25, 38, 48, 54, 57, 61, + 66, 64, 66, 64, 57, 46, 45, 44, 41, 38, 29, 18, 11, 9, 7, 7, 12, + 19, 28, 38, 44, 46, 43, 33, 32, 36, 37, 41, 53, 59, 61, 59, 55, 51, + 43, 31, 20, 27, 31, 28, 31, 34, 34, 29, 28, 27, 23, 26, 34, 36, 29, + 19, 20, 18, 9, 3, 2, 12, 19, 22, 22, 28, 32, 41, 53, 58, 56, 66, + 73, 75, 75, 71, 67, 73, 85, 93, 96, 96, 92, 86, 84, 85, 87, 88, 87, + 84, 76, 64, 47, 23, 8, 9, 37, 64, 84, 97,109,115,117,129,142,145, +137,122,108,111,127,145,158,159,147,128,110,100, 96, 88, 78, 73, 71, + 69, 63, 51, 48, 53, 62, 66, 65, 58, 47, 39, 45, 50, 48, 41, 26, 8, + 0, 0, 0, 2, 4, 3, 2, 3, 1, 5, 7, 6, 9, 9, 18, 25, 25, + 16, 8, 16, 28, 39, 48, 53, 52, 46, 47, 40, 26, 8, 6, 5, 0, 4, + 23, 40, 52, 61, 67, 76, 83, 89,101,113,122,127,125,119,111,106,115, +123,128,128,125,120,113,109,103, 96, 88, 77, 64, 59, 54, 46, 41, 41, + 44, 46, 47, 46, 44, 55, 63, 67, 68, 65, 60, 60, 61, 57, 63, 77, 88, + 95, 98, 98, 96,100,102, 99, 96, 91, 86, 82, 80, 81, 82, 77, 66, 51, + 29, 26, 26, 22, 18, 14, 16, 26, 29, 28, 21, 14, 26, 40, 50, 55, 60, + 68, 71, 67, 66, 65, 59, 48, 33, 23, 19, 14, 7, 7, 12, 15, 15, 14, + 15, 27, 38, 47, 54, 54, 48, 37, 21, 19, 26, 41, 52, 57, 57, 53, 47, + 40, 32, 25, 26, 33, 37, 34, 26, 23, 25, 28, 33, 34, 33, 37, 44, 44, + 37, 40, 43, 39, 31, 18, 5, 6, 13, 15, 15, 14, 20, 37, 48, 54, 58, + 69, 77, 80, 79, 75, 68, 74, 86, 94, 97, 96, 92, 86, 85, 85, 87, 87, + 84, 79, 71, 57, 38, 14, 7, 8, 38, 66, 84, 98,109,114,127,150,169, +173,159,139,118,107,123,142,159,165,156,137,118,102,100, 92, 84, 75, + 72, 69, 62, 48, 58, 69, 78, 82, 81, 74, 64, 51, 57, 60, 60, 53, 39, + 19, 0, 0, 0, 0, 0, 0, 6, 8, 8, 3, 7, 7, 9, 12, 18, 22, + 21, 12, 25, 34, 40, 51, 59, 64, 64, 56, 57, 51, 37, 19, 9, 7, 3, + 0, 14, 29, 40, 50, 62, 74, 79, 81, 92,104,114,120,119,113,107,100, +108,113,117,115,112,105, 99, 95, 91, 86, 79, 71, 61, 48, 37, 26, 22, + 23, 29, 36, 40, 44, 43, 41, 52, 58, 60, 59, 57, 54, 54, 50, 67, 81, + 91, 98,102,101, 97, 93, 93, 91, 88, 86, 84, 84, 79, 69, 70, 66, 58, + 50, 41, 32, 22, 16, 11, 8, 13, 22, 28, 27, 20, 8, 28, 43, 53, 58, + 62, 68, 69, 64, 66, 65, 59, 48, 33, 16, 2, 0, 0, 3, 12, 18, 22, + 26, 29, 31, 41, 51, 57, 57, 51, 39, 22, 6, 23, 39, 48, 52, 50, 44, + 36, 29, 31, 33, 36, 36, 37, 34, 25, 13, 25, 36, 43, 46, 48, 48, 51, + 51, 55, 60, 62, 60, 52, 38, 19, 1, 7, 11, 12, 8, 13, 27, 39, 46, + 61, 74, 83, 87, 86, 82, 75, 68, 81, 89, 92, 91, 87, 84, 83, 83, 84, + 83, 79, 74, 63, 47, 26, 4, 2, 4, 36, 64, 84, 98,108,114,137,165, +186,190,177,150,124,110,118,136,154,161,158,142,124,109,102, 96, 88, + 84, 79, 74, 69, 61, 70, 80, 88, 91, 89, 83, 73, 60, 65, 69, 69, 63, + 51, 31, 9, 0, 0, 0, 0, 4, 9, 13, 13, 8, 12, 14, 14, 15, 16, + 20, 19, 28, 40, 48, 54, 59, 68, 73, 72, 66, 66, 59, 46, 28, 16, 7, + 3, 0, 7, 21, 33, 41, 56, 67, 74, 74, 85, 96,105,114,121,121,114, +108,111,111,108,103, 99, 97, 97, 93, 86, 76, 69, 62, 58, 54, 50, 44, + 33, 20, 16, 20, 27, 32, 34, 34, 41, 50, 54, 55, 54, 52, 51, 53, 69, + 83, 92, 99,102,102, 98, 94, 91, 89, 88, 92, 96, 96, 91, 81, 69, 63, + 61, 56, 51, 43, 34, 27, 18, 5, 8, 19, 25, 23, 18, 13, 32, 46, 55, + 59, 62, 62, 62, 64, 65, 62, 55, 44, 27, 9, 3, 4, 5, 5, 13, 22, + 34, 44, 50, 51, 46, 51, 55, 55, 48, 37, 21, 15, 21, 34, 43, 45, 41, + 36, 31, 34, 37, 40, 43, 44, 41, 36, 29, 25, 34, 45, 54, 59, 62, 64, + 65, 67, 72, 75, 77, 74, 67, 55, 37, 18, 8, 8, 11, 12, 9, 21, 34, + 50, 65, 78, 87, 92, 92, 88, 83, 77, 74, 80, 84, 83, 83, 86, 88, 88, + 84, 79, 74, 66, 55, 41, 26, 12, 7, 2, 32, 61, 82, 96,105,119,143, +173,192,194,181,154,128,110,113,128,145,156,154,143,128,113,104,100, + 93, 88, 85, 80, 74, 71, 79, 88, 94, 96, 94, 87, 76, 67, 71, 74, 74, + 69, 59, 43, 22, 4, 0, 0, 1, 6, 9, 12, 12, 9, 18, 21, 22, 22, + 25, 25, 28, 38, 50, 57, 63, 67, 73, 75, 74, 76, 74, 68, 55, 38, 21, + 6, 0, 0, 5, 18, 28, 36, 46, 58, 64, 67, 79, 92,108,120,127,127, +121,118,122,121,115,108, 99, 99, 98, 94, 88, 79, 68, 67, 67, 66, 63, + 59, 50, 34, 18, 13, 9, 16, 20, 21, 33, 43, 48, 51, 52, 50, 48, 50, + 65, 78, 88, 93, 97, 97, 96, 92, 90, 88, 96,102,107,108,103, 94, 82, + 70, 63, 61, 58, 52, 44, 36, 25, 11, 9, 18, 22, 21, 15, 19, 36, 47, + 54, 58, 59, 60, 60, 61, 60, 56, 46, 32, 14, 13, 13, 14, 15, 14, 18, + 34, 50, 59, 66, 68, 64, 57, 51, 50, 44, 32, 27, 27, 28, 32, 39, 41, + 38, 32, 29, 36, 38, 41, 45, 46, 45, 43, 39, 37, 44, 55, 62, 68, 73, + 74, 76, 78, 81, 84, 86, 84, 79, 68, 53, 36, 19, 12, 13, 15, 16, 20, + 34, 51, 66, 78, 87, 92, 92, 90, 87, 83, 80, 79, 80, 83, 85, 88, 89, + 88, 85, 80, 74, 65, 55, 41, 23, 18, 13, 6, 33, 60, 79, 92,104,123, +147,173,188,188,171,147,124,111,112,121,134,145,147,140,129,115,107, +102, 96, 92, 88, 84, 78, 77, 84, 92, 98,100, 96, 88, 76, 70, 74, 76, + 76, 72, 63, 48, 31, 15, 8, 6, 3, 6, 7, 7, 7, 15, 22, 27, 31, + 33, 34, 33, 36, 44, 53, 61, 67, 73, 77, 80, 83, 84, 81, 74, 62, 46, + 27, 11, 0, 3, 7, 14, 25, 32, 38, 47, 54, 63, 82, 98,113,124,132, +132,127,134,137,134,127,117,106,106,105,102, 95, 85, 74, 72, 74, 74, + 74, 69, 59, 45, 28, 13, 7, 2, 4, 14, 27, 37, 44, 47, 47, 46, 44, + 43, 56, 68, 77, 84, 88, 89, 89, 88, 87, 93,102,111,117,119,114,105, + 93, 80, 67, 66, 63, 59, 51, 40, 28, 16, 18, 20, 22, 23, 26, 27, 37, + 45, 50, 53, 53, 53, 53, 54, 52, 45, 33, 21, 21, 21, 20, 21, 22, 28, + 33, 46, 60, 71, 78, 80, 79, 73, 65, 57, 50, 44, 40, 38, 37, 34, 36, + 38, 36, 31, 25, 32, 36, 40, 44, 45, 46, 46, 46, 47, 54, 62, 70, 75, + 79, 81, 83, 84, 86, 89, 91, 90, 85, 77, 65, 50, 31, 26, 19, 20, 22, + 21, 32, 47, 61, 73, 81, 86, 88, 88, 86, 84, 84, 84, 84, 85, 86, 87, + 87, 85, 83, 79, 73, 64, 53, 37, 25, 21, 15, 11, 36, 60, 77, 88,105, +124,145,163,173,169,154,133,115,110,113,113,124,133,137,136,127,115, +108,104, 99, 92, 88, 85, 79, 80, 87, 94, 99,100, 96, 86, 74, 70, 74, + 75, 74, 71, 62, 50, 34, 20, 13, 8, 3, 4, 4, 3, 9, 19, 26, 36, + 41, 46, 46, 44, 41, 46, 54, 62, 69, 76, 82, 87, 89, 89, 86, 78, 66, + 51, 34, 19, 6, 4, 8, 12, 21, 27, 33, 39, 51, 69, 87,102,117,128, +134,134,142,154,154,150,140,127,113,109,108,106, 99, 89, 78, 74, 76, + 78, 78, 74, 64, 50, 33, 19, 7, 1, 0, 8, 20, 31, 38, 43, 43, 41, + 39, 36, 41, 54, 62, 69, 74, 79, 81, 81, 84, 95,105,115,123,125,121, +112,100, 87, 73, 70, 68, 63, 54, 41, 27, 23, 26, 28, 32, 36, 38, 38, + 34, 40, 41, 43, 43, 43, 43, 43, 39, 29, 28, 29, 27, 26, 25, 27, 37, + 45, 51, 55, 68, 78, 85, 88, 88, 84, 78, 70, 62, 55, 48, 45, 43, 37, + 31, 33, 33, 29, 27, 27, 32, 36, 39, 41, 44, 47, 51, 56, 62, 69, 74, + 79, 83, 85, 87, 88, 88, 91, 92, 92, 89, 83, 74, 60, 44, 40, 36, 28, + 27, 25, 22, 37, 51, 61, 68, 74, 78, 79, 81, 83, 84, 84, 84, 84, 84, + 83, 82, 81, 79, 75, 70, 61, 48, 33, 27, 22, 19, 20, 39, 61, 75, 87, +103,120,136,147,150,143,130,117,105,107,113,113,115,123,128,128,122, +113,109,105,100, 93, 88, 85, 80, 81, 88, 94, 98, 98, 93, 84, 71, 67, + 70, 71, 69, 64, 56, 44, 31, 20, 13, 7, 0, 0, 0, 0, 11, 25, 37, + 46, 54, 57, 57, 55, 50, 52, 61, 68, 74, 78, 85, 90, 93, 92, 88, 79, + 67, 53, 37, 22, 11, 3, 6, 8, 16, 22, 28, 38, 54, 73, 90,105,119, +130,137,139,159,171,169,163,152,137,121,108,107,105,100, 91, 79, 74, + 76, 78, 77, 72, 62, 48, 33, 20, 11, 4, 0, 2, 13, 23, 32, 37, 38, + 37, 34, 37, 39, 54, 64, 71, 73, 72, 70, 73, 81, 93,104,114,122,124, +121,113,101, 88, 74, 73, 71, 64, 54, 39, 26, 29, 33, 37, 41, 45, 47, + 47, 40, 32, 31, 33, 33, 27, 27, 27, 31, 34, 36, 34, 32, 28, 27, 37, + 48, 58, 66, 70, 71, 80, 88, 92, 92, 90, 84, 78, 69, 60, 52, 46, 41, + 36, 27, 27, 28, 28, 28, 29, 33, 38, 44, 48, 53, 57, 60, 64, 71, 78, + 84, 88, 91, 93, 94, 94, 92, 91, 92, 93, 91, 86, 78, 68, 52, 53, 51, + 45, 34, 25, 18, 19, 31, 39, 47, 54, 60, 66, 72, 76, 79, 82, 82, 81, + 80, 79, 80, 79, 74, 72, 67, 58, 45, 37, 29, 28, 29, 32, 44, 62, 74, + 82, 97,112,122,127,125,119,110,101, 99,102,109,112,111,113,118,120, +115,108,108,104, 99, 92, 88, 84, 79, 79, 86, 92, 96, 95, 89, 79, 66, + 61, 63, 63, 60, 55, 45, 33, 22, 13, 7, 2, 0, 0, 0, 5, 20, 33, + 45, 56, 63, 67, 68, 65, 59, 59, 65, 73, 77, 79, 85, 92, 94, 92, 86, + 77, 68, 58, 43, 22, 12, 3, 1, 3, 11, 18, 23, 37, 55, 74, 91,106, +120,132,140,150,171,179,175,171,159,145,129,113,102,101, 96, 88, 79, + 70, 72, 73, 71, 65, 54, 41, 27, 19, 8, 3, 0, 0, 4, 14, 23, 29, + 33, 32, 32, 36, 39, 52, 64, 72, 74, 73, 67, 64, 76, 88,100,109,114, +115,113,106, 96, 85, 76, 76, 72, 64, 52, 34, 34, 43, 46, 45, 47, 52, + 54, 53, 45, 34, 36, 37, 34, 28, 21, 31, 38, 40, 40, 38, 33, 33, 44, + 50, 57, 69, 78, 82, 83, 80, 86, 92, 93, 91, 87, 79, 71, 60, 50, 41, + 36, 28, 20, 19, 22, 25, 27, 29, 36, 44, 51, 57, 62, 65, 67, 69, 77, + 84, 91, 95, 96, 97, 98, 98, 96, 91, 92, 92, 91, 87, 80, 72, 62, 65, + 64, 59, 48, 32, 14, 0, 4, 11, 18, 26, 36, 46, 57, 65, 73, 76, 77, + 77, 75, 77, 79, 79, 73, 69, 64, 56, 45, 39, 34, 37, 39, 41, 46, 63, + 74, 79, 88,100,107,109,105, 99, 92, 84, 93, 96,103,107,107,105,110, +112,110,102,104,102, 96, 90, 85, 83, 79, 76, 82, 88, 91, 90, 84, 74, + 60, 51, 53, 52, 48, 40, 31, 20, 9, 3, 0, 0, 0, 0, 3, 12, 27, + 40, 52, 62, 70, 75, 76, 74, 69, 68, 68, 75, 79, 82, 84, 90, 92, 90, + 83, 79, 74, 66, 51, 29, 9, 0, 0, 0, 4, 11, 19, 37, 52, 72, 89, +105,120,133,143,154,171,177,173,167,159,149,133,118,102, 94, 91, 84, + 76, 66, 64, 63, 61, 54, 43, 39, 33, 23, 11, 0, 0, 0, 0, 4, 14, + 22, 27, 28, 26, 31, 37, 45, 61, 70, 74, 72, 65, 56, 69, 82, 92,100, +104,104,101, 96, 88, 80, 80, 78, 73, 62, 47, 37, 48, 56, 58, 57, 53, + 56, 57, 55, 47, 50, 55, 56, 54, 47, 38, 37, 43, 45, 44, 40, 36, 41, + 54, 58, 64, 77, 86, 92, 93, 91, 85, 88, 90, 89, 84, 78, 68, 55, 43, + 32, 25, 18, 9, 8, 14, 19, 22, 26, 33, 43, 52, 60, 65, 68, 70, 71, + 80, 88, 94, 97, 99, 98, 97, 98, 97, 92, 90, 91, 90, 87, 81, 74, 72, + 74, 74, 69, 58, 43, 22, 4, 0, 0, 11, 23, 33, 39, 41, 55, 66, 70, + 70, 71, 70, 74, 77, 75, 69, 68, 64, 56, 50, 44, 37, 40, 44, 47, 47, + 62, 73, 77, 78, 87, 92, 92, 88, 82, 79, 83, 91, 89, 96,100,100, 98, +102,105,103, 97, 99, 96, 92, 85, 83, 81, 77, 70, 76, 82, 85, 84, 79, + 70, 55, 40, 41, 40, 36, 28, 19, 8, 0, 0, 0, 0, 0, 1, 11, 21, + 32, 43, 54, 66, 75, 81, 84, 84, 79, 73, 74, 75, 80, 84, 85, 87, 88, + 85, 86, 85, 81, 73, 59, 39, 15, 0, 0, 0, 0, 12, 23, 39, 55, 67, + 85,102,118,133,147,158,161,163,159,156,150,142,129,115,102, 88, 84, + 79, 72, 62, 55, 53, 50, 41, 44, 43, 38, 34, 26, 13, 0, 0, 0, 0, + 4, 15, 23, 27, 26, 21, 27, 36, 51, 62, 67, 66, 60, 51, 61, 74, 84, + 90, 92, 92, 88, 84, 83, 83, 82, 79, 72, 60, 47, 45, 57, 64, 67, 67, + 63, 59, 58, 56, 60, 66, 70, 71, 69, 62, 53, 43, 48, 50, 48, 44, 39, + 45, 55, 58, 71, 83, 92, 99,101, 97, 91, 84, 85, 84, 79, 72, 60, 46, + 31, 20, 12, 5, 0, 0, 3, 8, 12, 16, 23, 34, 46, 55, 62, 66, 68, + 69, 81, 89, 94, 96, 96, 94, 94, 93, 92, 91, 91, 91, 89, 86, 81, 74, + 79, 82, 81, 77, 66, 51, 29, 11, 3, 8, 23, 36, 44, 46, 47, 46, 58, + 63, 64, 65, 67, 67, 70, 68, 70, 71, 67, 60, 55, 51, 45, 39, 44, 48, + 51, 58, 68, 74, 74, 73, 77, 76, 74, 74, 71, 82, 89, 83, 88, 92, 92, + 92, 96, 99, 98, 93, 91, 89, 84, 81, 80, 79, 74, 71, 75, 84, 89, 91, + 88, 80, 70, 57, 51, 48, 41, 32, 19, 15, 12, 5, 0, 0, 0, 5, 21, + 37, 50, 61, 67, 69, 77, 84, 88, 89, 87, 82, 79, 76, 81, 85, 88, 88, + 88, 88, 89, 88, 84, 77, 64, 46, 23, 3, 0, 0, 7, 19, 28, 40, 55, + 67, 81, 98,114,133,150,165,175,177,167,149,133,127,118,107, 95, 83, + 79, 74, 65, 56, 47, 43, 39, 37, 40, 39, 45, 44, 38, 26, 14, 3, 0, + 0, 0, 8, 20, 27, 29, 25, 16, 21, 34, 48, 56, 56, 51, 52, 57, 66, + 74, 79, 79, 78, 83, 84, 84, 84, 83, 79, 71, 58, 58, 56, 61, 70, 74, + 75, 74, 73, 71, 71, 74, 79, 82, 82, 79, 73, 63, 54, 52, 53, 52, 48, + 45, 48, 57, 66, 77, 88, 96,102,102, 99, 92, 85, 79, 78, 73, 64, 52, + 43, 33, 22, 8, 0, 0, 0, 0, 2, 11, 19, 23, 23, 36, 46, 55, 60, + 63, 65, 77, 84, 88, 88, 90, 95, 98, 97, 95, 94, 93, 92, 90, 87, 81, + 78, 84, 88, 88, 83, 74, 59, 40, 33, 26, 19, 29, 41, 48, 51, 50, 48, + 48, 56, 59, 62, 67, 70, 72, 74, 76, 76, 73, 66, 61, 58, 54, 50, 44, + 44, 48, 50, 62, 69, 71, 69, 67, 67, 66, 64, 67, 78, 84, 75, 81, 83, + 83, 84, 89, 93, 93, 88, 83, 80, 79, 78, 77, 75, 75, 73, 77, 85, 92, + 95, 94, 88, 80, 72, 63, 54, 46, 39, 34, 28, 22, 14, 3, 0, 0, 7, + 28, 48, 64, 75, 82, 84, 83, 84, 90, 92, 90, 86, 86, 84, 82, 86, 88, + 88, 88, 88, 88, 87, 84, 77, 66, 51, 31, 11, 0, 3, 15, 25, 33, 37, + 51, 63, 78, 95,112,130,152,171,183,188,177,154,130,112,105, 96, 85, + 77, 74, 67, 58, 47, 37, 33, 31, 26, 28, 38, 45, 46, 43, 36, 26, 15, + 6, 5, 1, 11, 16, 27, 31, 28, 19, 7, 16, 32, 40, 43, 47, 57, 61, + 59, 63, 65, 63, 71, 80, 84, 84, 84, 83, 78, 69, 68, 67, 64, 64, 74, + 79, 83, 84, 84, 84, 84, 87, 90, 91, 89, 85, 79, 71, 62, 55, 53, 52, + 51, 48, 54, 63, 72, 80, 89, 96,100,100, 96, 92, 85, 79, 73, 65, 57, + 48, 39, 29, 19, 6, 0, 0, 0, 2, 11, 19, 25, 26, 22, 26, 38, 47, + 54, 58, 62, 67, 74, 79, 86, 92, 98,102,101, 98, 97, 96, 94, 92, 87, + 82, 80, 87, 92, 92, 89, 81, 70, 55, 50, 43, 37, 29, 41, 48, 52, 51, + 48, 45, 47, 53, 61, 69, 74, 78, 80, 82, 81, 78, 71, 63, 62, 61, 59, + 54, 44, 45, 48, 54, 64, 67, 66, 63, 61, 58, 54, 61, 71, 74, 66, 72, + 74, 72, 74, 81, 86, 87, 84, 79, 76, 74, 74, 73, 72, 74, 72, 77, 86, + 93, 96, 96, 93, 87, 81, 75, 68, 60, 53, 45, 37, 28, 19, 8, 4, 11, + 19, 32, 56, 74, 86, 92, 95, 95, 93, 91, 92, 91, 92, 93, 89, 84, 87, + 88, 88, 87, 85, 85, 83, 79, 73, 63, 50, 32, 15, 8, 12, 22, 31, 36, + 38, 41, 56, 75, 91,107,124,145,165,181,188,177,154,129,110, 95, 84, + 75, 71, 69, 61, 48, 37, 25, 23, 22, 19, 18, 32, 40, 44, 43, 38, 32, + 25, 15, 14, 8, 15, 23, 27, 29, 27, 18, 7, 7, 18, 27, 40, 53, 61, + 64, 61, 52, 47, 45, 62, 75, 79, 80, 81, 79, 74, 74, 74, 73, 70, 67, + 77, 84, 89, 92, 93, 93, 94, 96, 96, 96, 93, 88, 82, 75, 68, 61, 53, + 50, 48, 47, 54, 64, 73, 79, 84, 89, 92, 92, 90, 86, 83, 78, 72, 63, + 54, 44, 32, 21, 9, 0, 0, 0, 5, 14, 21, 26, 28, 27, 22, 28, 33, + 39, 46, 52, 56, 63, 70, 78, 85, 92, 97,101,101, 99, 98, 97, 95, 92, + 88, 83, 81, 89, 94, 96, 94, 88, 79, 67, 61, 54, 48, 41, 38, 46, 50, + 48, 46, 40, 39, 47, 60, 72, 79, 83, 84, 85, 84, 79, 73, 62, 62, 63, + 64, 61, 52, 40, 47, 50, 56, 61, 61, 57, 54, 50, 44, 53, 60, 62, 54, + 60, 61, 59, 61, 71, 76, 79, 76, 72, 70, 69, 68, 67, 67, 71, 69, 76, + 84, 92, 96, 96, 94, 91, 87, 83, 77, 69, 60, 51, 39, 28, 18, 11, 11, + 23, 33, 39, 59, 79, 92,100,104,104,102,100, 97, 96, 97, 98, 94, 87, + 88, 88, 86, 88, 86, 80, 77, 73, 65, 56, 44, 29, 14, 13, 18, 27, 33, + 37, 39, 41, 55, 72, 86,100,114,132,150,167,173,165,143,122,104, 89, + 79, 79, 79, 78, 70, 56, 34, 13, 13, 15, 12, 15, 22, 31, 36, 37, 37, + 33, 29, 21, 20, 14, 18, 28, 33, 29, 21, 12, 2, 6, 16, 29, 44, 57, + 64, 66, 62, 52, 33, 28, 48, 63, 70, 72, 73, 72, 73, 78, 78, 76, 74, + 70, 79, 88, 93, 97, 98, 99, 99,100, 99, 96, 92, 87, 81, 75, 71, 65, + 56, 44, 44, 43, 47, 59, 66, 71, 74, 77, 78, 79, 78, 77, 76, 73, 67, + 58, 47, 34, 21, 8, 0, 0, 0, 5, 16, 23, 28, 29, 29, 26, 26, 31, + 33, 33, 34, 40, 47, 56, 65, 74, 82, 88, 93, 96, 96, 96, 96, 96, 99, + 98, 92, 84, 81, 90, 96,100, 99, 95, 88, 78, 69, 62, 56, 50, 40, 38, + 43, 43, 39, 31, 31, 40, 59, 72, 79, 84, 86, 86, 83, 78, 70, 60, 56, + 60, 64, 64, 57, 44, 45, 50, 50, 52, 52, 47, 44, 38, 31, 40, 46, 47, + 38, 45, 46, 44, 45, 56, 64, 68, 66, 62, 61, 61, 61, 60, 61, 64, 64, + 74, 82, 88, 92, 93, 92, 91, 89, 87, 81, 74, 63, 50, 36, 22, 12, 8, + 18, 36, 47, 54, 59, 79, 93,103,109,110,109,106,102,100,101,101, 96, + 88, 88, 88, 88, 90, 88, 83, 74, 63, 55, 45, 33, 21, 11, 12, 20, 29, + 36, 39, 41, 53, 59, 67, 79, 91,103,115,130,143,150,143,128,111, 95, + 90, 88, 88, 88, 85, 78, 64, 43, 16, 3, 6, 5, 14, 23, 29, 29, 28, + 29, 29, 29, 23, 22, 18, 18, 28, 33, 31, 18, 1, 0, 3, 15, 29, 45, + 57, 64, 66, 62, 52, 36, 15, 27, 44, 52, 55, 58, 63, 74, 78, 79, 77, + 74, 71, 81, 89, 96, 98, 99, 99, 99, 99, 96, 92, 87, 82, 78, 74, 71, + 66, 57, 44, 34, 34, 33, 45, 52, 55, 56, 56, 57, 59, 61, 63, 65, 64, + 58, 50, 37, 22, 8, 0, 0, 0, 2, 16, 27, 33, 34, 32, 28, 25, 29, + 32, 32, 28, 22, 25, 34, 46, 56, 66, 74, 79, 84, 86, 87, 90, 98,104, +107,106,100, 91, 82, 91, 98,102,103,101, 95, 88, 80, 71, 61, 54, 45, + 33, 31, 31, 27, 19, 23, 39, 58, 71, 79, 83, 84, 83, 79, 72, 64, 55, + 46, 52, 59, 62, 58, 47, 41, 47, 50, 45, 37, 33, 29, 25, 19, 25, 29, + 29, 19, 25, 27, 26, 27, 39, 48, 54, 53, 51, 51, 52, 54, 54, 52, 56, + 56, 68, 76, 82, 85, 87, 87, 87, 88, 87, 82, 73, 60, 45, 27, 12, 2, + 3, 23, 45, 59, 67, 70, 77, 92,102,109,112,111,107,103,100,100,100, + 95, 90, 90, 88, 90, 92, 89, 84, 78, 69, 55, 36, 19, 8, 3, 5, 19, + 29, 37, 41, 51, 62, 67, 68, 72, 82, 91,101,112,121,125,122,111, 99, + 99, 97, 96, 95, 94, 92, 84, 71, 52, 25, 8, 0, 0, 11, 23, 32, 36, + 34, 28, 23, 26, 23, 22, 19, 16, 25, 28, 25, 12, 0, 0, 0, 9, 26, + 41, 54, 61, 62, 59, 52, 38, 18, 11, 18, 26, 31, 40, 58, 70, 76, 77, + 75, 73, 73, 83, 90, 95, 96, 96, 94, 93, 92, 88, 84, 79, 76, 74, 69, + 68, 64, 56, 43, 26, 21, 19, 23, 29, 29, 28, 27, 34, 40, 41, 43, 50, + 51, 47, 38, 25, 9, 5, 3, 0, 0, 11, 27, 37, 41, 40, 33, 26, 31, + 33, 32, 28, 22, 14, 7, 16, 29, 43, 53, 60, 66, 70, 74, 83, 91,100, +107,111,111,105, 96, 84, 92,100,105,107,105,100, 94, 88, 80, 65, 57, + 47, 34, 19, 15, 12, 4, 15, 38, 57, 69, 75, 78, 79, 76, 71, 63, 55, + 46, 39, 38, 50, 56, 55, 50, 39, 44, 46, 43, 33, 19, 14, 11, 6, 7, + 12, 12, 0, 4, 7, 8, 8, 20, 29, 36, 37, 36, 38, 43, 46, 48, 47, + 45, 47, 60, 68, 74, 77, 79, 84, 84, 83, 84, 79, 68, 54, 34, 18, 0, + 0, 1, 28, 53, 69, 77, 80, 80, 88, 98,105,108,108,104,100, 96, 95, + 94, 90, 92, 92, 91, 91, 91, 88, 84, 79, 72, 59, 41, 19, 0, 0, 1, + 16, 29, 39, 45, 58, 68, 73, 73, 79, 84, 92, 98, 99,103,107,104,102, +103,103,102,103,100, 99, 96, 88, 77, 59, 34, 12, 0, 0, 4, 19, 31, + 38, 40, 36, 27, 21, 22, 20, 19, 14, 18, 20, 15, 3, 0, 0, 4, 18, + 26, 36, 48, 55, 56, 55, 50, 39, 25, 6, 0, 0, 7, 31, 51, 64, 72, + 74, 72, 69, 75, 84, 89, 92, 92, 89, 86, 85, 84, 79, 83, 85, 84, 79, + 70, 63, 61, 54, 41, 26, 9, 5, 0, 2, 1, 7, 21, 32, 37, 36, 31, + 31, 34, 32, 25, 13, 5, 6, 5, 0, 3, 21, 38, 47, 50, 45, 37, 29, + 33, 33, 29, 22, 13, 3, 0, 0, 11, 22, 33, 41, 47, 59, 70, 79, 88, + 96,105,111,112,107, 99, 87, 93,102,108,110,108,103, 97, 92, 84, 72, + 59, 48, 34, 18, 0, 0, 0, 16, 38, 55, 65, 70, 71, 71, 68, 67, 70, + 68, 61, 48, 29, 37, 46, 51, 50, 44, 39, 41, 39, 31, 19, 9, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 12, 19, 20, 31, 36, 36, 39, 44, + 45, 41, 32, 46, 56, 62, 66, 74, 79, 80, 75, 76, 71, 60, 44, 31, 15, + 0, 0, 4, 33, 59, 75, 84, 87, 87, 85, 94,100,102,101, 96, 91, 86, + 85, 88, 92, 94, 96, 96, 93, 90, 85, 81, 76, 70, 59, 44, 22, 0, 0, + 0, 15, 31, 43, 52, 60, 70, 74, 83, 91, 97,101,102,102,100, 95,100, +104,108,113,117,117,112,102, 98, 92, 81, 66, 44, 19, 0, 0, 0, 9, + 25, 34, 39, 38, 32, 20, 22, 19, 16, 13, 11, 11, 5, 0, 3, 13, 21, + 27, 37, 41, 45, 46, 48, 50, 47, 40, 28, 13, 0, 0, 0, 21, 43, 58, + 67, 70, 69, 66, 76, 84, 88, 88, 86, 82, 79, 76, 79, 85, 89, 91, 88, + 82, 72, 58, 57, 51, 40, 26, 11, 0, 0, 0, 0, 2, 15, 25, 29, 28, + 21, 13, 19, 19, 14, 12, 6, 6, 3, 0, 15, 34, 50, 57, 58, 53, 41, + 29, 31, 29, 23, 14, 4, 0, 0, 0, 0, 3, 13, 26, 40, 53, 63, 72, + 81, 91,100,106,108,105, 97, 86, 97,105,111,112,109,103, 98, 91, 84, + 74, 63, 50, 33, 15, 0, 0, 0, 19, 39, 54, 62, 65, 65, 64, 72, 78, + 80, 79, 73, 61, 44, 26, 38, 45, 48, 48, 45, 39, 37, 28, 23, 14, 9, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 14, 26, 32, 33, 38, + 45, 48, 46, 38, 31, 37, 45, 55, 65, 70, 71, 66, 67, 62, 50, 33, 23, + 9, 0, 0, 8, 38, 62, 78, 86, 89, 88, 86, 92, 96, 96, 93, 88, 84, + 92, 95, 93, 92, 96,100,101,100, 97, 92, 84, 71, 64, 56, 44, 25, 4, + 0, 0, 13, 32, 47, 60, 70, 78, 86, 95,103,111,115,117,114,112,111, +113,118,124,130,134,133,125,113,100, 91, 82, 69, 51, 28, 6, 0, 0, + 1, 13, 25, 31, 32, 28, 21, 23, 22, 16, 12, 7, 3, 0, 4, 15, 26, + 36, 40, 45, 50, 52, 54, 53, 46, 44, 40, 32, 19, 4, 0, 0, 12, 33, + 51, 60, 64, 64, 67, 77, 83, 85, 88, 88, 87, 92, 94, 94, 95, 93, 92, + 88, 81, 71, 57, 50, 44, 36, 25, 18, 11, 5, 3, 4, 9, 14, 15, 20, + 18, 13, 5, 3, 11, 14, 15, 13, 9, 6, 9, 28, 47, 60, 66, 66, 60, + 51, 38, 26, 20, 13, 8, 8, 9, 7, 0, 0, 2, 11, 23, 38, 54, 68, + 79, 86, 89, 93,100,102,100, 94, 90,100,107,110,109,107,110,107, 99, + 89, 79, 67, 52, 32, 11, 0, 0, 2, 22, 40, 53, 58, 60, 61, 73, 79, + 85, 88, 86, 79, 69, 53, 36, 31, 40, 46, 50, 50, 47, 45, 44, 40, 31, + 19, 15, 13, 8, 3, 13, 11, 8, 1, 0, 0, 0, 0, 5, 19, 27, 29, + 39, 48, 54, 54, 48, 39, 31, 34, 45, 54, 58, 57, 58, 59, 53, 39, 23, + 14, 3, 0, 1, 13, 41, 64, 78, 85, 88, 86, 85, 91, 92, 92, 88, 82, + 89, 96, 99, 98, 96, 97,101,103,104,102, 99, 90, 76, 58, 53, 44, 29, + 13, 7, 1, 8, 31, 51, 66, 78, 87, 96,105,114,123,129,133,132,130, +129,132,139,147,154,158,154,143,128,113, 97, 82, 69, 54, 36, 16, 0, + 0, 0, 4, 9, 16, 20, 19, 21, 23, 21, 16, 11, 6, 4, 6, 13, 23, + 34, 43, 47, 48, 52, 54, 57, 57, 51, 43, 40, 36, 27, 20, 15, 9, 7, + 20, 39, 51, 56, 56, 68, 77, 84, 92, 95, 95, 98,109,113,113,113,111, +105, 96, 88, 76, 64, 52, 47, 45, 41, 38, 34, 32, 31, 31, 31, 27, 22, + 13, 8, 6, 5, 5, 5, 12, 16, 19, 19, 16, 22, 40, 55, 65, 71, 71, + 67, 59, 48, 37, 26, 20, 23, 27, 29, 27, 18, 11, 16, 26, 37, 50, 62, + 74, 85, 92, 96, 98, 97, 96, 96, 92, 92,100,104,105,108,114,118,115, +107, 96, 84, 71, 53, 29, 6, 4, 11, 16, 26, 40, 50, 54, 53, 65, 76, + 84, 90, 92, 91, 84, 74, 59, 43, 32, 34, 43, 47, 50, 51, 51, 53, 52, + 44, 33, 22, 23, 22, 19, 25, 27, 23, 15, 2, 0, 0, 0, 0, 11, 21, + 28, 41, 52, 59, 61, 58, 51, 43, 33, 36, 41, 44, 43, 51, 52, 44, 29, + 13, 6, 0, 1, 11, 19, 44, 63, 75, 82, 84, 82, 86, 89, 90, 88, 84, + 85, 90, 97,100, 99, 97, 96, 99,102,103,103,101, 93, 79, 60, 50, 44, + 33, 27, 22, 14, 6, 27, 51, 69, 82, 92,102,112,122,132,139,145,147, +147,147,150,159,167,173,175,171,159,142,123,107, 92, 76, 57, 39, 23, + 8, 1, 2, 2, 7, 11, 14, 16, 20, 21, 18, 13, 8, 6, 5, 9, 16, + 27, 37, 44, 48, 50, 48, 52, 55, 57, 53, 41, 40, 38, 34, 34, 32, 28, + 21, 19, 26, 39, 47, 56, 67, 82, 92,100,102,102,112,127,134,136,133, +127,118,108, 97, 88, 78, 69, 59, 54, 53, 53, 54, 54, 55, 54, 52, 47, + 38, 27, 14, 6, 8, 11, 11, 9, 18, 23, 26, 26, 32, 46, 58, 66, 71, + 73, 70, 65, 57, 47, 37, 33, 38, 41, 44, 41, 34, 23, 29, 38, 47, 57, + 68, 79, 89, 98,104,107,107,104,101, 97, 93, 96, 98,102,112,120,123, +121,112,100, 87, 72, 52, 27, 9, 16, 22, 27, 32, 39, 46, 47, 45, 63, + 76, 84, 91, 94, 92, 86, 76, 61, 46, 38, 36, 36, 41, 45, 48, 51, 55, + 56, 52, 44, 33, 29, 32, 29, 37, 41, 38, 28, 13, 3, 0, 0, 0, 6, + 16, 29, 41, 53, 61, 66, 64, 59, 53, 43, 31, 31, 31, 33, 41, 44, 36, + 20, 4, 0, 0, 8, 19, 25, 47, 63, 73, 77, 78, 81, 86, 87, 86, 84, + 81, 88, 92, 94, 96, 96, 96, 94, 93, 96, 98, 99, 98, 92, 79, 60, 45, + 41, 34, 36, 32, 23, 14, 27, 50, 69, 83, 93,102,112,121,130,139,147, +152,156,158,161,169,175,181,181,175,163,147,130,114,100, 85, 68, 47, + 28, 15, 5, 9, 13, 14, 14, 14, 15, 16, 15, 12, 6, 8, 7, 3, 8, + 16, 26, 34, 40, 44, 44, 44, 45, 51, 55, 53, 45, 38, 37, 39, 41, 43, + 40, 34, 31, 29, 34, 44, 58, 74, 88, 99,107,111,109,120,140,154,154, +149,139,128,115,104, 95, 88, 82, 74, 64, 60, 62, 64, 67, 69, 68, 65, + 58, 50, 37, 25, 13, 8, 13, 14, 12, 19, 27, 32, 32, 38, 48, 56, 63, + 68, 70, 70, 68, 62, 55, 45, 40, 45, 48, 50, 48, 43, 32, 38, 45, 53, + 62, 71, 81, 92,102,110,115,117,113,108,102, 96, 90, 94,104,113,120, +123,119,111, 99, 86, 71, 50, 23, 16, 23, 29, 33, 36, 34, 40, 41, 38, + 56, 71, 80, 88, 92, 91, 85, 75, 62, 47, 45, 41, 37, 32, 37, 40, 45, + 51, 55, 54, 48, 41, 34, 38, 38, 47, 53, 50, 39, 23, 12, 3, 2, 4, + 5, 15, 27, 39, 52, 60, 66, 67, 64, 58, 50, 38, 22, 21, 23, 33, 34, + 26, 11, 0, 0, 2, 14, 23, 31, 50, 62, 69, 73, 76, 82, 84, 83, 80, + 78, 80, 88, 93, 95, 93, 92, 93, 92, 90, 85, 88, 90, 90, 85, 74, 58, + 39, 38, 33, 39, 38, 29, 22, 38, 52, 65, 79, 89, 97,106,113,121,129, +137,145,152,156,159,165,169,173,171,165,154,142,128,115,104, 91, 77, + 59, 38, 21, 13, 16, 23, 27, 27, 25, 20, 18, 13, 7, 12, 14, 11, 6, + 3, 12, 21, 27, 32, 34, 34, 34, 37, 45, 52, 53, 50, 39, 32, 37, 41, + 45, 44, 40, 37, 37, 40, 48, 60, 75, 90,102,113,118,118,122,143,159, +163,156,143,130,118,108, 99, 94, 90, 84, 77, 67, 64, 68, 73, 74, 74, + 70, 62, 53, 41, 29, 19, 13, 13, 15, 14, 20, 29, 36, 34, 38, 45, 51, + 56, 60, 64, 66, 67, 64, 58, 48, 41, 45, 47, 48, 47, 44, 34, 40, 47, + 55, 62, 72, 81, 92,104,114,123,125,122,114,105, 96, 88, 92,102,112, +117,117,112,103, 93, 81, 66, 46, 22, 19, 25, 28, 32, 34, 33, 33, 34, + 31, 43, 60, 72, 81, 86, 86, 82, 73, 60, 47, 50, 47, 41, 34, 26, 27, + 32, 40, 48, 50, 47, 44, 40, 41, 44, 53, 59, 57, 47, 32, 16, 7, 3, + 7, 8, 12, 27, 38, 46, 56, 63, 65, 64, 60, 53, 41, 27, 15, 14, 23, + 25, 16, 4, 0, 0, 5, 16, 26, 36, 52, 61, 66, 69, 76, 80, 79, 76, + 74, 71, 78, 86, 92, 93, 92, 87, 89, 90, 88, 83, 75, 76, 78, 74, 66, + 52, 34, 33, 29, 37, 37, 29, 33, 47, 58, 64, 72, 81, 88, 96,102,108, +114,122,129,136,140,143,149,150,152,149,143,136,127,120,112,103, 94, + 82, 68, 50, 28, 20, 22, 31, 36, 36, 32, 25, 19, 11, 15, 21, 23, 19, + 9, 0, 4, 12, 18, 21, 22, 22, 27, 36, 40, 47, 53, 54, 47, 33, 29, + 36, 40, 41, 39, 37, 37, 39, 47, 58, 74, 88,102,114,123,125,122,137, +154,158,150,139,127,115,106,100, 96, 95, 92, 86, 78, 67, 66, 71, 74, + 73, 68, 60, 51, 39, 28, 20, 15, 11, 14, 14, 21, 31, 37, 34, 34, 39, + 46, 57, 61, 60, 60, 64, 64, 59, 50, 37, 37, 39, 39, 39, 37, 31, 37, + 45, 53, 61, 72, 80, 92,104,118,129,133,129,120,108, 97, 92, 92,100, +106,108,106,100, 92, 84, 74, 59, 41, 22, 16, 20, 22, 25, 28, 28, 25, + 26, 23, 23, 43, 57, 70, 77, 79, 76, 68, 56, 48, 52, 50, 45, 37, 28, + 21, 16, 26, 36, 40, 43, 41, 41, 43, 46, 55, 61, 60, 53, 38, 20, 9, + 1, 6, 7, 16, 29, 43, 50, 52, 55, 59, 59, 57, 52, 43, 29, 14, 8, + 14, 15, 8, 0, 0, 3, 8, 16, 25, 39, 53, 61, 64, 66, 72, 74, 72, + 67, 63, 60, 73, 81, 87, 88, 86, 81, 84, 87, 85, 81, 73, 61, 61, 59, + 53, 43, 29, 26, 23, 32, 32, 29, 43, 54, 61, 64, 65, 69, 76, 84, 89, + 96,102,107,113,120,123,125,127,128,128,125,121,117,111,107,103, 98, + 92, 84, 72, 56, 37, 27, 27, 37, 43, 41, 36, 26, 16, 14, 23, 29, 32, + 27, 16, 3, 0, 3, 7, 9, 11, 13, 23, 33, 38, 45, 54, 58, 55, 45, + 33, 25, 31, 33, 32, 31, 31, 39, 48, 55, 68, 84, 99,113,124,130,130, +125,139,143,139,129,119,112,107,100, 96, 96, 94, 91, 85, 77, 66, 64, + 68, 67, 61, 53, 43, 32, 23, 16, 14, 11, 11, 13, 23, 33, 37, 33, 29, + 40, 58, 70, 75, 75, 69, 59, 61, 57, 48, 34, 25, 26, 26, 26, 25, 21, + 29, 39, 51, 67, 79, 87, 91,104,120,133,139,133,122,108, 96, 96, 96, + 96, 99, 98, 93, 87, 79, 71, 61, 51, 37, 23, 12, 11, 11, 13, 18, 20, + 19, 16, 16, 13, 20, 38, 54, 65, 69, 68, 62, 52, 46, 51, 51, 45, 38, + 37, 33, 26, 18, 21, 28, 33, 36, 37, 43, 48, 51, 59, 59, 54, 43, 26, + 8, 0, 0, 12, 25, 34, 48, 56, 58, 55, 50, 51, 51, 47, 41, 31, 18, + 11, 5, 7, 2, 0, 0, 6, 11, 14, 21, 41, 54, 60, 63, 62, 62, 64, + 60, 61, 58, 56, 63, 72, 78, 79, 75, 77, 80, 82, 81, 76, 67, 55, 41, + 41, 38, 32, 23, 18, 16, 25, 25, 38, 50, 58, 62, 62, 59, 55, 63, 79, + 91,101,109,113,113,112,109,110,111,111,109,107,103,100, 96, 93, 92, + 90, 87, 80, 71, 57, 39, 37, 36, 41, 45, 43, 34, 23, 13, 20, 29, 37, + 38, 32, 21, 7, 0, 0, 0, 1, 3, 5, 16, 26, 29, 44, 56, 61, 61, + 54, 41, 31, 20, 22, 22, 23, 31, 40, 48, 55, 58, 76, 92,108,121,130, +133,130,124,129,125,120,119,115,111,102, 92, 93, 93, 92, 88, 81, 73, + 64, 58, 58, 53, 48, 44, 38, 29, 20, 9, 6, 6, 16, 27, 34, 37, 32, + 26, 47, 66, 79, 84, 85, 80, 71, 60, 56, 46, 32, 14, 18, 22, 21, 13, + 11, 22, 38, 57, 72, 83, 89, 92,106,121,134,140,134,122,107, 95, 97, + 98, 96, 91, 88, 81, 73, 64, 59, 51, 41, 33, 25, 15, 7, 0, 1, 5, + 9, 9, 6, 6, 4, 1, 18, 37, 51, 58, 60, 56, 47, 39, 46, 47, 44, + 41, 44, 43, 36, 23, 22, 16, 20, 25, 31, 41, 50, 53, 51, 54, 52, 44, + 31, 16, 2, 7, 20, 33, 43, 52, 58, 60, 58, 53, 43, 39, 38, 34, 27, + 18, 15, 11, 5, 1, 0, 1, 7, 12, 13, 20, 38, 50, 56, 59, 58, 56, + 55, 55, 56, 54, 55, 63, 69, 72, 74, 74, 75, 77, 77, 74, 67, 56, 43, + 28, 25, 23, 20, 15, 12, 11, 19, 27, 39, 50, 56, 58, 56, 52, 51, 65, + 80, 93,103,112,115,117,114,112,109,105,102,100, 98, 96, 95, 92, 88, + 82, 81, 79, 74, 66, 53, 47, 45, 43, 43, 44, 39, 28, 16, 20, 27, 34, + 40, 40, 36, 25, 12, 1, 0, 0, 0, 0, 1, 9, 20, 33, 48, 60, 66, + 66, 60, 46, 36, 25, 20, 20, 22, 28, 36, 45, 48, 52, 68, 85,100,113, +123,128,128,124,122,121,120,118,113,107, 99, 89, 89, 89, 88, 85, 80, + 74, 67, 63, 61, 59, 55, 51, 44, 36, 26, 15, 7, 11, 22, 32, 37, 37, + 32, 31, 53, 71, 84, 91, 92, 89, 82, 73, 63, 54, 43, 29, 20, 25, 26, + 19, 12, 27, 45, 62, 75, 84, 90, 93,107,122,134,139,133,120,106, 95, + 95, 96, 93, 88, 78, 70, 63, 63, 59, 52, 40, 28, 23, 19, 13, 7, 0, + 5, 9, 9, 6, 0, 0, 0, 0, 16, 33, 45, 51, 50, 45, 39, 37, 39, + 43, 47, 50, 50, 44, 33, 27, 20, 8, 14, 28, 41, 51, 54, 52, 46, 45, + 40, 32, 21, 9, 13, 26, 38, 47, 52, 57, 59, 57, 53, 43, 27, 25, 23, + 20, 19, 20, 15, 9, 4, 0, 0, 5, 11, 14, 14, 29, 40, 46, 48, 48, + 46, 45, 45, 45, 45, 51, 60, 67, 71, 74, 74, 73, 73, 72, 72, 66, 55, + 38, 20, 11, 11, 9, 6, 5, 6, 14, 22, 34, 45, 50, 51, 46, 45, 58, + 69, 83, 95,105,113,117,118,117,113,110,105,101, 96, 92, 89, 87, 84, + 81, 75, 74, 72, 68, 60, 53, 52, 50, 46, 39, 38, 31, 19, 20, 26, 31, + 37, 41, 41, 37, 28, 18, 8, 0, 0, 0, 0, 0, 11, 23, 38, 53, 64, + 71, 71, 65, 53, 37, 27, 20, 18, 18, 22, 34, 43, 47, 50, 59, 76, 91, +102,112,118,120,120,119,118,115,112,106, 99, 91, 84, 84, 84, 82, 79, + 75, 71, 66, 64, 63, 61, 57, 52, 44, 36, 26, 16, 9, 15, 26, 34, 38, + 37, 34, 37, 58, 76, 89, 97,101, 98, 92, 85, 78, 70, 60, 48, 34, 28, + 29, 25, 20, 37, 53, 68, 79, 86, 91, 93,106,120,132,134,129,119,106, + 96, 89, 89, 88, 82, 74, 61, 59, 60, 57, 51, 40, 29, 18, 16, 14, 11, + 4, 8, 13, 16, 16, 11, 5, 1, 0, 1, 12, 27, 37, 41, 41, 38, 38, + 40, 46, 51, 55, 56, 53, 44, 33, 26, 20, 15, 27, 41, 51, 51, 50, 45, + 37, 36, 29, 22, 14, 15, 28, 39, 46, 50, 51, 53, 52, 48, 40, 25, 11, + 11, 12, 21, 25, 20, 13, 6, 2, 0, 0, 6, 12, 14, 18, 26, 31, 32, + 33, 34, 32, 29, 31, 31, 43, 53, 61, 67, 70, 70, 67, 72, 75, 75, 70, + 59, 44, 26, 21, 13, 0, 0, 0, 3, 11, 15, 25, 36, 40, 40, 36, 48, + 61, 70, 84, 94,103,110,113,115,113,112,108,104,102, 99, 94, 88, 80, + 73, 70, 65, 66, 65, 61, 54, 52, 52, 51, 45, 36, 28, 20, 14, 25, 29, + 32, 36, 38, 38, 34, 29, 22, 14, 7, 0, 0, 0, 0, 11, 26, 40, 55, + 66, 73, 74, 68, 56, 37, 25, 16, 11, 8, 20, 32, 40, 50, 57, 64, 67, + 78, 88, 97,104,109,111,112,111,108,102, 96, 88, 81, 79, 76, 74, 71, + 68, 64, 61, 59, 59, 60, 59, 54, 47, 39, 29, 20, 12, 8, 20, 31, 38, + 40, 37, 40, 44, 62, 79, 92,102,106,106,102, 95, 89, 82, 74, 63, 50, + 32, 31, 26, 27, 44, 59, 72, 81, 88, 91, 92,104,115,125,128,123,114, +104, 96, 88, 82, 80, 76, 68, 57, 56, 54, 53, 48, 40, 31, 20, 11, 11, + 9, 6, 11, 16, 21, 23, 22, 18, 12, 4, 1, 7, 12, 20, 29, 34, 34, + 36, 40, 46, 52, 57, 59, 59, 54, 47, 41, 37, 32, 23, 37, 47, 43, 43, + 39, 33, 28, 25, 20, 14, 13, 25, 34, 39, 41, 39, 40, 40, 38, 32, 19, + 3, 0, 14, 25, 28, 25, 16, 18, 14, 6, 2, 4, 8, 14, 15, 18, 22, + 25, 26, 25, 22, 19, 15, 18, 31, 43, 53, 59, 62, 61, 63, 71, 75, 75, + 71, 61, 48, 36, 33, 23, 9, 0, 0, 0, 5, 11, 14, 23, 29, 29, 33, + 46, 58, 67, 80, 90, 97,103,106,108,107,106,105,109,108,105, 99, 92, + 85, 78, 69, 60, 59, 58, 55, 48, 46, 48, 46, 40, 28, 18, 9, 16, 25, + 27, 26, 27, 28, 28, 27, 25, 21, 18, 12, 3, 0, 0, 0, 8, 25, 40, + 54, 65, 73, 74, 68, 56, 37, 18, 8, 0, 6, 23, 39, 51, 59, 65, 69, + 70, 68, 71, 80, 88, 94, 98,101,101, 96, 91, 84, 75, 74, 71, 65, 60, + 54, 48, 45, 44, 45, 48, 52, 51, 46, 38, 28, 19, 11, 6, 9, 23, 36, + 43, 44, 39, 45, 48, 64, 80, 93,103,109,110,107,102, 96, 91, 83, 73, + 58, 40, 26, 22, 29, 46, 62, 74, 83, 88, 91, 91,100,111,118,119,115, +108,100, 93, 87, 79, 73, 69, 63, 54, 52, 50, 47, 44, 39, 32, 23, 15, + 8, 6, 7, 12, 18, 22, 27, 29, 29, 26, 18, 4, 5, 9, 9, 14, 22, + 26, 28, 34, 40, 48, 55, 60, 62, 60, 57, 54, 51, 45, 37, 29, 39, 31, + 32, 31, 27, 22, 18, 14, 11, 7, 18, 25, 28, 28, 26, 23, 23, 23, 20, + 8, 0, 2, 18, 28, 33, 29, 27, 32, 31, 22, 9, 5, 5, 13, 16, 16, + 16, 18, 18, 15, 12, 9, 7, 11, 18, 28, 39, 46, 48, 46, 56, 64, 71, + 72, 70, 62, 51, 45, 44, 36, 21, 4, 0, 0, 2, 5, 6, 13, 19, 20, + 25, 37, 50, 59, 73, 82, 88, 93, 96, 96, 96, 96,102,108,110,108,102, + 96, 92, 88, 80, 73, 62, 52, 50, 44, 37, 40, 38, 31, 19, 7, 4, 13, + 20, 20, 16, 14, 14, 14, 15, 15, 16, 15, 12, 5, 0, 0, 0, 4, 20, + 34, 50, 61, 68, 69, 64, 52, 31, 8, 0, 0, 14, 34, 50, 60, 66, 71, + 72, 70, 65, 59, 57, 68, 77, 84, 88, 88, 84, 78, 69, 64, 63, 58, 50, + 39, 29, 22, 19, 19, 23, 31, 37, 37, 33, 25, 16, 7, 5, 2, 12, 28, + 40, 48, 48, 44, 45, 50, 63, 79, 92,102,107,109,108,104,100, 94, 86, + 76, 61, 43, 21, 14, 25, 44, 61, 74, 82, 88, 89, 89, 96,104,109,109, +105, 99, 92, 88, 83, 75, 67, 62, 58, 51, 43, 41, 41, 39, 36, 31, 25, + 16, 9, 7, 8, 11, 15, 20, 26, 32, 37, 38, 34, 22, 8, 9, 8, 4, + 7, 13, 16, 22, 29, 39, 47, 55, 60, 62, 61, 61, 59, 54, 44, 31, 26, + 14, 18, 19, 20, 19, 14, 7, 5, 4, 7, 13, 14, 13, 9, 5, 5, 6, + 5, 0, 5, 9, 22, 33, 38, 34, 40, 46, 46, 40, 26, 7, 4, 8, 15, + 18, 16, 9, 6, 3, 0, 0, 4, 8, 9, 14, 21, 27, 29, 29, 41, 53, + 61, 66, 66, 61, 53, 52, 51, 44, 31, 13, 0, 0, 0, 1, 0, 3, 11, + 14, 13, 21, 33, 46, 61, 71, 77, 80, 83, 84, 84, 86, 96,103,107,107, +103, 98, 99, 95, 88, 82, 74, 61, 45, 38, 28, 28, 27, 20, 9, 0, 0, + 6, 9, 7, 2, 0, 0, 0, 0, 3, 5, 7, 6, 4, 3, 0, 0, 0, + 13, 27, 40, 53, 61, 62, 57, 44, 23, 1, 0, 1, 25, 44, 58, 66, 71, + 74, 74, 69, 61, 55, 47, 40, 55, 66, 72, 74, 70, 63, 53, 50, 47, 40, + 28, 14, 11, 7, 2, 0, 0, 9, 18, 20, 16, 11, 3, 2, 3, 8, 16, + 33, 47, 54, 55, 50, 43, 46, 60, 76, 88, 96,102,104,103,102, 98, 93, + 85, 74, 58, 38, 15, 1, 16, 37, 56, 71, 80, 85, 88, 86, 92, 97, 99, + 98, 94, 88, 82, 78, 74, 67, 58, 55, 53, 47, 38, 32, 33, 34, 33, 27, + 21, 14, 8, 5, 5, 11, 21, 28, 32, 32, 38, 45, 45, 38, 26, 15, 13, + 9, 2, 7, 9, 14, 21, 29, 38, 46, 54, 59, 61, 63, 62, 56, 46, 31, + 13, 0, 2, 7, 12, 14, 14, 11, 4, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 3, 12, 18, 28, 39, 43, 41, 51, 58, 60, 55, 43, 23, 3, 4, + 13, 16, 15, 9, 1, 0, 0, 0, 0, 4, 6, 6, 5, 8, 8, 8, 20, + 34, 47, 56, 60, 60, 55, 53, 54, 50, 38, 22, 7, 0, 0, 0, 0, 0, + 4, 9, 11, 8, 13, 32, 46, 56, 62, 66, 68, 70, 71, 74, 87, 96,102, +104,103,100,102, 99, 93, 88, 80, 71, 57, 37, 23, 15, 14, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, + 0, 5, 18, 31, 44, 52, 54, 48, 36, 15, 0, 0, 9, 32, 51, 61, 68, + 71, 73, 71, 68, 70, 67, 60, 48, 36, 43, 53, 56, 53, 46, 36, 29, 27, + 19, 5, 4, 4, 4, 1, 0, 0, 0, 1, 3, 2, 0, 0, 0, 5, 12, + 21, 39, 54, 60, 61, 56, 44, 41, 57, 73, 84, 92, 96, 97, 97, 96, 93, + 88, 79, 68, 51, 28, 5, 0, 7, 28, 50, 66, 76, 82, 84, 82, 87, 91, + 91, 88, 82, 74, 68, 64, 61, 54, 47, 48, 47, 43, 34, 22, 26, 29, 31, + 26, 18, 7, 1, 0, 0, 12, 26, 37, 43, 44, 40, 46, 50, 47, 40, 27, + 16, 12, 5, 2, 5, 14, 29, 43, 48, 51, 54, 58, 59, 59, 59, 54, 43, + 26, 8, 0, 0, 5, 6, 9, 13, 13, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 15, 22, 34, 45, 50, 47, 57, 65, 69, 65, 55, 38, 15, + 0, 7, 11, 11, 5, 0, 0, 0, 0, 0, 0, 1, 3, 2, 0, 0, 0, + 0, 15, 32, 45, 54, 57, 55, 48, 52, 50, 43, 31, 16, 4, 0, 0, 0, + 0, 1, 6, 7, 8, 13, 19, 33, 43, 51, 61, 67, 70, 68, 61, 76, 88, + 96,101,102,102, 99, 96, 92, 88, 83, 75, 64, 47, 26, 4, 4, 0, 0, + 0, 0, 0, 0, 0, 6, 13, 15, 14, 7, 0, 0, 0, 0, 0, 0, 0, + 2, 6, 9, 11, 23, 37, 45, 48, 43, 29, 9, 0, 0, 13, 34, 51, 60, + 64, 66, 67, 72, 75, 77, 74, 67, 55, 39, 21, 32, 37, 37, 31, 22, 12, + 5, 0, 2, 8, 14, 15, 11, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 11, 28, 47, 60, 66, 67, 61, 50, 34, 54, 69, 79, 87, 91, 92, 91, 89, + 86, 81, 72, 58, 39, 15, 0, 0, 0, 20, 43, 61, 73, 79, 79, 77, 84, + 85, 83, 77, 69, 58, 52, 45, 45, 43, 44, 41, 38, 33, 26, 15, 21, 26, + 28, 25, 16, 5, 0, 0, 0, 11, 27, 40, 47, 51, 48, 43, 51, 52, 48, + 40, 28, 15, 7, 0, 11, 28, 45, 57, 63, 65, 62, 58, 60, 61, 57, 48, + 34, 18, 1, 0, 0, 0, 2, 4, 8, 9, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 14, 26, 41, 53, 57, 56, 58, 66, 71, 69, 61, 46, + 27, 9, 7, 7, 6, 4, 1, 4, 8, 11, 8, 5, 7, 7, 5, 1, 0, + 0, 0, 0, 14, 31, 44, 51, 52, 48, 48, 50, 46, 39, 28, 19, 9, 5, + 5, 6, 7, 6, 13, 18, 22, 28, 37, 47, 58, 66, 72, 74, 73, 65, 63, + 78, 88, 97,102,104,104,102, 97, 88, 81, 76, 67, 53, 33, 12, 7, 2, + 0, 0, 0, 0, 2, 7, 18, 25, 28, 27, 21, 12, 2, 0, 0, 0, 0, + 3, 11, 18, 22, 23, 20, 28, 38, 40, 37, 25, 6, 0, 6, 16, 32, 46, + 54, 57, 67, 74, 79, 81, 81, 78, 70, 57, 39, 21, 22, 22, 22, 19, 13, + 9, 7, 7, 13, 19, 22, 25, 25, 19, 7, 0, 0, 0, 0, 0, 0, 2, + 11, 23, 40, 57, 68, 73, 73, 67, 56, 44, 50, 64, 75, 82, 86, 92, 96, + 94, 88, 79, 67, 52, 33, 13, 0, 0, 6, 18, 38, 57, 69, 75, 77, 74, + 78, 78, 74, 66, 54, 47, 52, 53, 51, 48, 46, 41, 34, 25, 15, 11, 18, + 22, 23, 20, 13, 3, 0, 0, 0, 6, 22, 37, 46, 51, 51, 47, 47, 52, + 52, 48, 40, 29, 20, 12, 23, 41, 57, 68, 74, 77, 75, 72, 64, 58, 56, + 47, 33, 14, 0, 0, 0, 0, 0, 0, 3, 4, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 12, 29, 45, 57, 63, 63, 56, 63, 68, 68, 62, + 51, 34, 21, 21, 20, 16, 13, 16, 23, 27, 27, 25, 20, 18, 19, 18, 14, + 8, 1, 0, 0, 4, 18, 28, 38, 44, 44, 48, 52, 52, 48, 44, 38, 32, + 28, 29, 31, 31, 29, 26, 31, 36, 40, 48, 58, 67, 74, 79, 80, 78, 72, + 60, 65, 81, 92,102,107,109,109,103, 92, 79, 76, 69, 57, 39, 18, 13, + 7, 1, 0, 0, 3, 8, 14, 26, 34, 39, 39, 33, 23, 13, 3, 0, 0, + 2, 11, 20, 27, 32, 33, 31, 23, 25, 28, 26, 16, 9, 5, 12, 20, 27, + 40, 47, 60, 74, 80, 84, 87, 86, 80, 71, 57, 38, 27, 28, 28, 27, 26, + 23, 21, 20, 22, 26, 32, 40, 43, 40, 32, 20, 7, 3, 1, 0, 0, 5, + 14, 26, 40, 55, 67, 76, 79, 79, 73, 63, 53, 59, 67, 75, 83, 90, 96, + 99, 98, 92, 85, 74, 61, 44, 23, 4, 2, 13, 22, 34, 55, 68, 74, 75, + 72, 69, 68, 63, 53, 43, 48, 56, 56, 53, 50, 44, 37, 28, 18, 4, 5, + 11, 15, 16, 14, 8, 1, 5, 6, 6, 7, 14, 29, 41, 47, 50, 48, 45, + 53, 56, 55, 51, 43, 33, 25, 37, 52, 65, 76, 83, 87, 88, 85, 79, 69, + 54, 47, 36, 19, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 27, 45, 58, 66, 67, 62, 57, 63, 64, + 60, 52, 40, 38, 36, 32, 27, 31, 36, 40, 43, 41, 37, 32, 28, 28, 27, + 22, 18, 12, 2, 8, 14, 20, 25, 33, 37, 40, 48, 53, 56, 57, 56, 54, + 52, 51, 51, 52, 52, 50, 46, 41, 45, 51, 58, 67, 74, 80, 84, 84, 82, + 76, 65, 53, 73, 88, 99,107,111,111,105, 94, 81, 76, 71, 59, 41, 21, + 18, 11, 5, 0, 2, 9, 16, 20, 31, 41, 47, 47, 43, 33, 21, 11, 6, + 3, 11, 19, 26, 32, 37, 39, 37, 31, 21, 13, 13, 18, 20, 19, 16, 22, + 28, 34, 50, 65, 77, 84, 88, 90, 88, 82, 72, 56, 37, 36, 36, 36, 36, + 34, 33, 32, 32, 36, 46, 54, 57, 56, 52, 43, 32, 20, 15, 12, 7, 5, + 14, 27, 40, 54, 66, 76, 82, 84, 83, 78, 70, 60, 65, 73, 79, 86, 92, + 96, 99, 99, 96, 89, 81, 70, 53, 32, 13, 9, 14, 22, 34, 55, 68, 74, + 74, 71, 64, 57, 51, 41, 40, 51, 56, 56, 52, 48, 41, 32, 20, 9, 0, + 0, 3, 6, 7, 7, 12, 19, 21, 19, 13, 12, 11, 18, 32, 41, 46, 47, + 44, 53, 60, 62, 59, 52, 43, 34, 46, 58, 70, 80, 88, 93, 96, 96, 92, + 83, 71, 54, 39, 27, 12, 11, 1, 0, 0, 0, 0, 0, 0, 0, 3, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 21, 39, 54, 63, 66, 63, 59, 57, + 59, 57, 52, 53, 53, 48, 44, 43, 48, 52, 54, 54, 50, 45, 40, 39, 37, + 33, 28, 23, 19, 12, 16, 25, 32, 38, 41, 45, 47, 48, 53, 57, 60, 62, + 64, 64, 63, 63, 64, 64, 62, 59, 54, 50, 55, 62, 71, 79, 84, 87, 87, + 84, 78, 67, 48, 64, 83, 95,103,108,108,102, 92, 79, 78, 73, 61, 44, + 23, 19, 13, 6, 2, 6, 16, 23, 25, 36, 46, 53, 54, 50, 41, 28, 16, + 12, 13, 18, 23, 28, 32, 36, 38, 38, 33, 27, 25, 21, 23, 29, 32, 32, + 32, 36, 40, 53, 66, 76, 84, 89, 91, 88, 82, 71, 54, 41, 44, 43, 41, + 40, 39, 38, 40, 47, 56, 64, 68, 68, 65, 59, 51, 41, 36, 28, 21, 15, + 12, 21, 36, 51, 63, 74, 81, 85, 86, 84, 79, 74, 66, 68, 75, 81, 87, + 91, 95, 97, 97, 96, 92, 86, 76, 60, 39, 20, 14, 11, 16, 36, 55, 68, + 74, 74, 70, 63, 55, 45, 34, 43, 50, 54, 54, 56, 54, 48, 38, 25, 8, + 0, 0, 0, 2, 15, 26, 34, 38, 36, 29, 20, 14, 13, 11, 21, 34, 41, + 44, 41, 55, 64, 67, 65, 58, 47, 44, 52, 61, 72, 81, 90, 97,102,105, +102, 94, 84, 69, 51, 34, 23, 23, 14, 7, 2, 0, 0, 0, 0, 0, 2, + 1, 0, 0, 1, 4, 1, 0, 0, 0, 4, 14, 28, 44, 55, 59, 59, 59, + 59, 60, 61, 63, 63, 62, 58, 52, 57, 62, 64, 64, 60, 55, 48, 50, 47, + 44, 38, 33, 27, 23, 20, 23, 33, 41, 47, 51, 52, 52, 52, 51, 55, 60, + 64, 67, 69, 70, 69, 70, 69, 68, 65, 60, 55, 53, 61, 70, 78, 83, 86, + 86, 83, 76, 65, 52, 57, 77, 89, 97,101,101, 95, 85, 80, 80, 74, 62, + 44, 25, 19, 12, 5, 3, 9, 21, 29, 32, 38, 50, 57, 59, 55, 46, 33, + 19, 21, 22, 23, 25, 26, 27, 29, 32, 33, 40, 44, 44, 39, 34, 38, 43, + 43, 44, 45, 47, 52, 62, 72, 79, 85, 88, 86, 80, 69, 53, 46, 47, 46, + 44, 41, 43, 47, 53, 60, 67, 74, 75, 74, 70, 63, 56, 54, 48, 41, 38, + 33, 26, 25, 40, 56, 68, 77, 83, 85, 85, 83, 79, 74, 69, 67, 74, 79, + 84, 88, 91, 93, 94, 95, 93, 88, 79, 64, 41, 23, 15, 6, 13, 38, 57, + 68, 73, 72, 67, 60, 52, 40, 34, 41, 47, 52, 57, 60, 59, 54, 44, 28, + 9, 0, 0, 0, 19, 36, 47, 53, 53, 47, 36, 23, 13, 12, 11, 9, 25, + 36, 39, 41, 58, 68, 72, 70, 61, 50, 51, 55, 61, 70, 79, 88, 97,105, +110,109,102, 92, 80, 63, 43, 33, 33, 26, 20, 13, 4, 0, 0, 0, 0, + 1, 1, 0, 0, 4, 7, 5, 0, 0, 0, 0, 6, 16, 28, 40, 47, 50, + 53, 57, 61, 65, 68, 69, 68, 63, 63, 69, 73, 74, 71, 65, 57, 58, 58, + 54, 48, 45, 43, 39, 29, 27, 29, 40, 50, 54, 56, 55, 54, 52, 48, 48, + 54, 60, 64, 68, 69, 69, 69, 68, 67, 64, 60, 56, 51, 54, 63, 72, 78, + 81, 81, 78, 72, 60, 57, 59, 71, 83, 89, 92, 90, 85, 80, 84, 84, 77, + 63, 43, 22, 16, 9, 2, 3, 12, 25, 33, 37, 40, 53, 60, 63, 60, 51, + 37, 27, 28, 27, 25, 21, 19, 18, 18, 22, 37, 50, 56, 58, 55, 48, 43, + 48, 51, 50, 50, 50, 51, 54, 62, 71, 78, 82, 81, 76, 67, 53, 46, 47, + 45, 46, 48, 51, 54, 59, 65, 72, 77, 77, 75, 71, 65, 67, 66, 58, 53, + 53, 51, 43, 41, 40, 56, 69, 77, 81, 83, 82, 79, 75, 71, 68, 62, 71, + 76, 80, 84, 88, 88, 90, 92, 93, 90, 81, 66, 41, 23, 13, 3, 18, 41, + 58, 67, 70, 68, 63, 57, 47, 36, 32, 39, 44, 52, 57, 61, 61, 57, 47, + 31, 9, 0, 0, 11, 33, 52, 62, 66, 63, 55, 40, 29, 13, 9, 8, 7, + 16, 29, 34, 45, 61, 72, 75, 73, 63, 55, 55, 60, 75, 86, 92, 96, 94, +103,110,111,107, 99, 88, 74, 54, 40, 38, 32, 27, 20, 12, 1, 0, 0, + 0, 0, 1, 0, 2, 8, 11, 8, 1, 0, 0, 0, 0, 5, 12, 20, 27, + 33, 40, 47, 56, 62, 67, 70, 69, 65, 73, 79, 81, 80, 76, 68, 62, 64, + 63, 58, 52, 50, 50, 46, 38, 36, 36, 46, 54, 57, 57, 55, 52, 48, 48, + 47, 52, 54, 57, 61, 63, 63, 63, 62, 61, 59, 55, 51, 47, 45, 52, 62, + 69, 74, 74, 70, 64, 56, 61, 62, 64, 75, 80, 81, 79, 76, 84, 88, 86, + 78, 63, 40, 18, 12, 5, 0, 3, 12, 23, 33, 37, 43, 56, 64, 67, 64, + 55, 41, 32, 31, 27, 21, 15, 12, 9, 5, 21, 39, 54, 64, 68, 66, 60, + 51, 51, 52, 51, 48, 46, 50, 52, 51, 59, 68, 74, 75, 73, 64, 53, 43, + 48, 50, 51, 52, 53, 55, 59, 65, 72, 76, 76, 74, 73, 75, 76, 73, 63, + 61, 63, 62, 57, 57, 56, 54, 67, 74, 78, 79, 78, 74, 71, 67, 65, 60, + 65, 71, 78, 87, 92, 92, 87, 91, 92, 90, 81, 66, 40, 21, 9, 11, 22, + 44, 58, 65, 66, 62, 57, 51, 43, 31, 27, 34, 40, 47, 53, 57, 59, 56, + 47, 31, 8, 0, 0, 18, 44, 62, 73, 74, 70, 59, 46, 38, 22, 11, 6, + 6, 12, 25, 31, 48, 65, 75, 79, 74, 64, 60, 59, 73, 88,100,108,111, +108,102,107,109,108,102, 93, 80, 63, 44, 36, 31, 27, 22, 15, 6, 1, + 0, 0, 0, 0, 0, 7, 13, 15, 13, 5, 8, 6, 1, 1, 0, 0, 2, + 6, 20, 31, 39, 45, 53, 60, 64, 64, 72, 79, 85, 88, 86, 80, 73, 63, + 65, 64, 60, 54, 52, 51, 48, 41, 46, 46, 51, 56, 57, 54, 50, 45, 51, + 55, 55, 57, 61, 61, 59, 55, 56, 56, 55, 54, 52, 48, 45, 40, 39, 39, + 50, 58, 63, 64, 61, 55, 60, 63, 62, 58, 68, 72, 71, 69, 79, 86, 89, + 87, 78, 61, 38, 12, 6, 0, 0, 3, 7, 20, 28, 32, 47, 60, 69, 72, + 69, 61, 47, 31, 26, 21, 16, 15, 14, 11, 15, 20, 40, 58, 70, 74, 74, + 69, 59, 48, 48, 46, 44, 47, 52, 53, 52, 47, 57, 65, 69, 69, 66, 60, + 53, 47, 50, 51, 55, 60, 65, 68, 70, 69, 74, 74, 76, 79, 80, 79, 74, + 64, 67, 70, 70, 66, 68, 68, 66, 64, 73, 75, 76, 74, 75, 74, 71, 62, + 57, 59, 69, 80, 88, 92, 92, 86, 90, 92, 90, 81, 65, 39, 21, 8, 16, + 22, 43, 55, 60, 60, 55, 50, 46, 38, 27, 21, 31, 38, 43, 43, 48, 52, + 51, 43, 27, 15, 1, 0, 21, 48, 67, 77, 79, 74, 63, 47, 39, 23, 12, + 7, 7, 11, 23, 29, 52, 68, 78, 80, 76, 68, 67, 65, 80, 96,110,120, +123,120,112,103,107,107,104, 96, 85, 71, 53, 44, 28, 25, 22, 16, 8, + 6, 3, 0, 0, 3, 7, 13, 18, 19, 15, 16, 21, 21, 15, 7, 0, 0, + 0, 1, 12, 23, 33, 40, 47, 53, 59, 66, 74, 83, 88, 89, 87, 82, 74, + 64, 63, 62, 59, 55, 51, 48, 48, 53, 57, 57, 55, 55, 52, 46, 44, 52, + 57, 59, 59, 57, 62, 63, 62, 60, 57, 55, 53, 52, 52, 51, 51, 48, 43, + 36, 39, 50, 55, 57, 54, 53, 57, 59, 57, 50, 59, 62, 61, 71, 79, 85, + 88, 84, 74, 56, 31, 6, 0, 0, 0, 3, 8, 15, 23, 37, 52, 64, 73, + 75, 73, 65, 52, 36, 21, 18, 16, 14, 11, 13, 22, 28, 45, 62, 74, 79, + 79, 74, 65, 55, 47, 45, 46, 47, 50, 50, 46, 41, 50, 59, 66, 69, 69, + 65, 61, 58, 58, 61, 65, 70, 74, 75, 76, 74, 74, 76, 78, 79, 79, 75, + 69, 68, 70, 73, 73, 69, 74, 74, 74, 73, 74, 77, 80, 82, 83, 82, 78, + 70, 59, 59, 71, 80, 88, 91, 89, 89, 92, 94, 91, 81, 64, 38, 19, 12, + 19, 21, 38, 48, 53, 52, 54, 58, 54, 44, 28, 18, 28, 39, 45, 46, 39, + 37, 38, 33, 31, 22, 8, 4, 19, 45, 65, 76, 79, 76, 66, 51, 32, 16, + 9, 9, 9, 14, 26, 34, 55, 71, 79, 82, 78, 74, 73, 71, 84, 99,113, +123,128,125,119,110,103,104,102, 96, 86, 74, 59, 44, 27, 22, 20, 15, + 11, 12, 11, 9, 9, 13, 16, 20, 21, 21, 20, 29, 36, 36, 32, 23, 13, + 3, 0, 0, 2, 12, 22, 31, 38, 45, 53, 62, 72, 79, 84, 85, 83, 77, + 69, 61, 62, 60, 58, 55, 54, 54, 56, 62, 67, 66, 60, 53, 45, 40, 52, + 58, 60, 61, 59, 54, 56, 58, 58, 57, 55, 54, 53, 53, 52, 52, 51, 48, + 43, 34, 34, 41, 48, 51, 50, 45, 50, 50, 45, 37, 46, 50, 62, 74, 79, + 84, 83, 77, 65, 46, 21, 2, 0, 0, 0, 0, 6, 14, 26, 39, 54, 65, + 73, 74, 72, 63, 51, 36, 21, 14, 12, 8, 5, 18, 26, 31, 51, 66, 76, + 80, 80, 76, 68, 58, 52, 47, 46, 45, 43, 40, 36, 31, 41, 52, 60, 65, + 67, 66, 64, 63, 65, 69, 72, 74, 76, 76, 75, 74, 73, 74, 74, 74, 72, + 68, 69, 70, 72, 74, 72, 70, 74, 76, 76, 77, 79, 84, 87, 88, 88, 86, + 81, 74, 63, 60, 71, 79, 85, 88, 90, 93, 96, 96, 90, 79, 61, 33, 15, + 13, 18, 16, 29, 39, 44, 54, 62, 65, 60, 47, 29, 25, 27, 39, 47, 50, + 43, 27, 22, 29, 32, 27, 18, 13, 18, 36, 56, 69, 74, 73, 65, 51, 33, + 16, 12, 11, 9, 20, 29, 41, 60, 74, 81, 82, 78, 75, 75, 75, 86, 98, +111,121,127,125,121,113,105, 97, 95, 89, 81, 71, 57, 37, 23, 18, 14, + 11, 9, 14, 16, 21, 23, 23, 25, 26, 26, 22, 29, 41, 47, 48, 45, 38, + 28, 19, 9, 5, 0, 0, 7, 16, 23, 32, 41, 52, 63, 71, 75, 76, 74, + 68, 61, 59, 59, 57, 55, 56, 59, 61, 64, 69, 74, 73, 65, 51, 38, 46, + 56, 61, 60, 58, 53, 46, 41, 46, 47, 48, 48, 48, 47, 47, 47, 45, 44, + 41, 37, 33, 33, 36, 43, 46, 45, 40, 37, 34, 27, 23, 36, 54, 67, 75, + 79, 79, 75, 66, 52, 31, 8, 0, 0, 0, 0, 0, 2, 11, 23, 39, 53, + 63, 69, 70, 65, 57, 45, 31, 18, 7, 4, 1, 9, 21, 28, 38, 55, 68, + 76, 79, 79, 74, 66, 57, 51, 46, 43, 39, 33, 27, 19, 20, 31, 41, 50, + 56, 59, 60, 61, 63, 67, 71, 74, 74, 74, 73, 70, 67, 68, 70, 69, 68, + 67, 67, 67, 68, 69, 69, 66, 67, 71, 74, 75, 78, 83, 88, 92, 93, 92, + 88, 83, 74, 66, 65, 74, 80, 86, 90, 94, 97, 99, 96, 88, 75, 55, 27, + 8, 13, 14, 9, 21, 34, 51, 63, 70, 71, 64, 50, 37, 34, 29, 38, 46, + 50, 43, 28, 12, 23, 31, 31, 25, 20, 26, 29, 41, 56, 63, 64, 59, 47, + 32, 15, 14, 13, 16, 28, 36, 51, 67, 77, 82, 82, 77, 75, 76, 79, 85, + 95,104,113,118,120,119,113,107, 97, 88, 79, 71, 61, 50, 26, 18, 11, + 5, 1, 5, 14, 26, 32, 36, 38, 36, 32, 29, 26, 38, 50, 56, 57, 54, + 48, 40, 32, 25, 20, 14, 4, 0, 0, 7, 15, 29, 37, 50, 58, 62, 63, + 60, 55, 55, 57, 55, 52, 56, 60, 64, 67, 70, 72, 78, 77, 69, 55, 40, + 48, 57, 59, 56, 50, 41, 33, 23, 27, 31, 34, 36, 37, 38, 38, 37, 36, + 32, 29, 29, 31, 31, 29, 37, 40, 39, 37, 31, 25, 19, 28, 46, 60, 71, + 76, 76, 73, 65, 53, 36, 20, 8, 0, 0, 0, 0, 2, 2, 5, 19, 36, + 50, 58, 62, 61, 56, 47, 34, 22, 12, 3, 0, 2, 14, 23, 28, 44, 58, + 67, 73, 74, 72, 66, 58, 51, 45, 40, 36, 29, 21, 11, 2, 7, 16, 26, + 33, 39, 44, 47, 52, 56, 61, 66, 69, 70, 68, 65, 60, 60, 64, 64, 64, + 62, 61, 60, 60, 61, 61, 58, 53, 59, 64, 68, 72, 77, 84, 91, 95, 96, + 95, 90, 83, 74, 71, 72, 79, 85, 91, 95, 97,100,100, 95, 85, 70, 48, + 20, 7, 11, 7, 4, 26, 46, 61, 71, 74, 74, 66, 51, 46, 45, 39, 32, + 40, 43, 38, 25, 16, 18, 26, 29, 28, 22, 31, 36, 38, 38, 47, 51, 48, + 39, 25, 13, 14, 15, 29, 39, 44, 59, 73, 80, 84, 82, 76, 72, 75, 79, + 84, 89, 96,102,107,110,112,110,105, 98, 89, 79, 67, 53, 38, 21, 11, + 1, 0, 0, 4, 19, 31, 40, 45, 48, 47, 40, 34, 29, 41, 54, 60, 61, + 59, 55, 48, 41, 36, 33, 29, 19, 4, 0, 0, 3, 18, 26, 32, 41, 45, + 46, 44, 48, 52, 52, 51, 55, 63, 68, 69, 68, 72, 73, 78, 78, 72, 59, + 53, 54, 53, 53, 46, 37, 26, 19, 18, 16, 21, 21, 20, 22, 25, 26, 25, + 22, 22, 23, 25, 26, 26, 23, 28, 32, 32, 31, 26, 22, 22, 40, 55, 65, + 72, 74, 72, 63, 52, 45, 37, 23, 8, 0, 0, 0, 9, 16, 16, 9, 12, + 28, 43, 51, 53, 51, 45, 34, 25, 15, 6, 0, 0, 7, 19, 26, 31, 47, + 58, 64, 66, 64, 59, 52, 44, 37, 33, 29, 23, 16, 7, 0, 0, 0, 4, + 8, 13, 18, 22, 27, 33, 40, 50, 56, 60, 60, 58, 53, 46, 53, 56, 56, + 54, 52, 50, 47, 47, 47, 46, 41, 40, 47, 53, 59, 65, 74, 84, 92, 97, +100, 97, 91, 83, 78, 75, 78, 85, 90, 95, 97,100,102,100, 92, 80, 63, + 40, 15, 5, 5, 0, 12, 37, 57, 69, 75, 76, 74, 65, 55, 56, 54, 46, + 33, 28, 31, 27, 20, 15, 12, 21, 26, 26, 22, 29, 38, 41, 41, 38, 32, + 32, 25, 15, 11, 14, 33, 45, 52, 54, 68, 79, 84, 84, 81, 74, 67, 73, + 79, 87, 94,100,103,102, 99,102,102,100, 96, 89, 79, 67, 52, 34, 11, + 0, 0, 0, 0, 6, 20, 34, 45, 52, 56, 57, 53, 44, 36, 40, 54, 60, + 62, 60, 57, 53, 54, 48, 44, 41, 33, 18, 1, 0, 0, 4, 12, 16, 22, + 27, 32, 38, 41, 44, 47, 53, 61, 71, 77, 77, 73, 69, 71, 76, 78, 74, + 64, 61, 61, 56, 44, 32, 20, 23, 25, 21, 13, 12, 9, 6, 8, 12, 12, + 11, 8, 12, 14, 15, 16, 16, 14, 20, 22, 23, 21, 20, 18, 33, 50, 61, + 68, 72, 71, 65, 57, 58, 54, 44, 27, 7, 0, 0, 8, 23, 31, 31, 23, + 12, 21, 33, 40, 43, 39, 32, 27, 23, 13, 1, 0, 8, 15, 23, 28, 31, + 46, 54, 57, 55, 50, 41, 32, 23, 18, 15, 13, 9, 7, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 8, 19, 29, 40, 46, 47, 44, 38, 33, 40, 43, + 41, 39, 34, 31, 28, 26, 26, 23, 18, 25, 32, 40, 52, 63, 71, 83, 92, + 99,102, 99, 92, 83, 80, 77, 84, 90, 95, 97, 99,100,100, 97, 88, 75, + 56, 34, 20, 3, 0, 0, 21, 46, 65, 74, 78, 76, 72, 62, 64, 65, 60, + 51, 34, 15, 13, 12, 11, 11, 8, 14, 19, 20, 20, 23, 33, 40, 41, 38, + 27, 13, 8, 2, 7, 31, 50, 60, 65, 65, 74, 84, 87, 86, 81, 74, 68, + 72, 76, 87, 96,104,109,107,102, 92, 92, 92, 90, 84, 76, 63, 46, 26, + 0, 0, 0, 0, 11, 19, 25, 37, 48, 57, 63, 66, 64, 58, 47, 39, 50, + 57, 59, 59, 60, 65, 66, 62, 53, 53, 45, 33, 16, 0, 0, 0, 0, 2, + 7, 14, 20, 26, 31, 38, 46, 52, 67, 78, 84, 84, 79, 69, 65, 74, 76, + 74, 68, 67, 65, 59, 46, 26, 26, 29, 29, 23, 13, 0, 0, 0, 0, 1, + 3, 1, 0, 0, 2, 3, 4, 3, 5, 8, 11, 12, 11, 9, 22, 40, 55, + 64, 70, 70, 67, 62, 67, 68, 62, 50, 29, 5, 0, 0, 18, 34, 44, 44, + 37, 23, 16, 26, 32, 33, 29, 25, 26, 21, 8, 1, 13, 23, 31, 31, 32, + 31, 39, 46, 45, 39, 29, 21, 13, 2, 0, 0, 3, 15, 22, 23, 18, 7, + 8, 11, 7, 0, 0, 0, 0, 0, 0, 7, 19, 27, 29, 28, 22, 15, 22, + 23, 22, 18, 13, 8, 4, 2, 1, 0, 4, 11, 22, 40, 55, 65, 69, 82, + 93,101,104,102, 94, 84, 80, 83, 89, 94, 97, 99, 99, 99, 98, 94, 84, + 70, 53, 39, 21, 2, 0, 0, 28, 54, 70, 78, 79, 75, 69, 69, 72, 71, + 63, 51, 31, 8, 0, 0, 0, 4, 7, 7, 9, 11, 12, 13, 25, 33, 37, + 33, 22, 5, 0, 0, 20, 46, 64, 74, 77, 76, 80, 88, 90, 88, 83, 79, + 77, 71, 72, 84, 96,105,110,108,101, 89, 81, 82, 81, 77, 68, 53, 33, 13 +}; diff --git a/examples/src/ssg/majik/image_map.h b/examples/src/ssg/majik/image_map.h new file mode 100644 index 0000000..e0048b8 --- /dev/null +++ b/examples/src/ssg/majik/image_map.h @@ -0,0 +1,13111 @@ + +unsigned char image_map [] = +{ + 30, 2,128, 87, 65,237,120,122, 70,124,150, 22,124,167, 6, + 122,175, 0,122,178, 0,121,180, 0,121,181, 0,119,187, 0, + 117,192, 0,115,194, 0,115,195, 0,116,193, 0,118,188, 0, + 121,182, 0,123,173, 1,121,179, 0,121,182, 0,120,184, 0, + 118,189, 0,116,192, 0,116,193, 0,117,191, 0,119,186, 0, + 119,186, 0,120,183, 0,122,176, 0,124,164, 9,123,139, 37, + 114,104,197, 87, 65,237, 30, 2,128, 69, 60,230,109, 92,251, + 115,107,129,118,114, 96,121,127, 58,124,147, 26,124,161, 11, + 123,169, 4,120,182, 0,117,192, 0,113,198, 0,111,202, 0, + 111,202, 0,113,199, 0,116,193, 0,120,184, 0,117,192, 0, + 115,194, 0,115,194, 0,116,193, 0,117,191, 0,118,189, 0, + 119,186, 0,122,177, 0,122,175, 0,123,172, 2,124,167, 6, + 124,161, 11,124,150, 22,121,127, 58, 99, 75,247, 30, 2,128, + 94, 70,242,117,110,109,121,127, 58,122,130, 53,121,125, 63, + 117,110,109,105, 84,252, 30, 43,203, 30, 37,192, 30, 43,203, + 30, 55,222, 99, 75,247,109, 92,251,111, 96,248,111, 96,248, + 109, 92,251,121,127, 58,124,155, 16,123,172, 2,120,183, 0, + 118,188, 0,117,191, 0,117,191, 0,118,188, 0,117,192, 0, + 115,194, 0,115,194, 0,118,189, 0,121,180, 0,124,160, 12, + 115,107,129, 30, 2,128,114,104,197,124,152, 20,123,171, 3, + 122,178, 0,122,178, 0,123,173, 1,124,162, 10,124,145, 29, + 124,152, 20,124,160, 12,124,161, 11,124,157, 15,124,150, 22, + 124,149, 24,123,141, 34,117,110,109,123,139, 37,124,160, 12, + 123,170, 3,122,175, 0,122,175, 0,123,170, 3,124,162, 10, + 124,157, 15,124,162, 10,124,158, 13,124,152, 20,124,147, 26, + 123,137, 41,118,117, 85, 99, 75,247, 30, 2,128,107, 89,252, + 123,139, 37,124,161, 11,123,170, 3,123,172, 2,123,168, 5, + 124,160, 12,124,147, 26,124,150, 22,124,143, 31,122,130, 53, + 121,125, 63,118,117, 85,113,100,242, 87, 65,237, 30, 10,132, + 30, 49,213,113,100,242,121,127, 58,123,137, 41,123,137, 41, + 121,127, 58,118,114, 96,113,100,242,114,104,197,111, 96,248, + 111, 96,248,111, 96,248,107, 89,252, 99, 75,247, 30, 43,203, + 30, 2,128, 30, 43,203,102, 80,250,120,122, 70,124,154, 18, + 123,171, 3,121,182, 0,118,188, 0,117,190, 0,112,199, 0, + 107,206, 0,104,210, 0,102,212, 0,103,211, 0,106,207, 0, + 110,202, 0,114,196, 0,111,202, 0,108,205, 0,106,207, 0, + 105,209, 0,103,211, 0,103,212, 0,104,210, 0,107,207, 0, + 109,204, 0,113,198, 0,117,191, 0,120,183, 0,123,171, 3, + 124,149, 24,113,100,242, 30, 2,128,117,110,109,124,164, 9, + 120,184, 0,116,193, 0,114,197, 0,114,197, 0,115,194, 0, + 118,189, 0,116,193, 0,115,194, 0,116,193, 0,119,187, 0, + 122,176, 0,124,154, 18,115,107,129, 30, 26,168, 30, 55,222, + 87, 65,237,109, 92,251,118,114, 96,120,122, 70,121,125, 63, + 120,122, 70,117,110,109,119,119, 77,122,130, 53,124,149, 24, + 124,155, 16,124,152, 20,123,137, 41,109, 92,251, 30, 2,128, + 115,107,129,124,162, 10,120,183, 0,115,194, 0,112,200, 0, + 111,202, 0,111,202, 0,111,201, 0,108,205, 0,108,206, 0, + 109,204, 0,110,202, 0,111,202, 0,112,200, 0,114,196, 0, + 118,189, 0,114,197, 0,109,204, 0,105,209, 0,103,211, 0, + 104,210, 0,107,206, 0,112,200, 0,117,191, 0,117,192, 0, + 117,191, 0,118,188, 0,121,180, 0,124,166, 7,123,137, 41, + 99, 75,247,109, 92,251,122,130, 53,124,157, 15,123,172, 2, + 121,182, 0,119,187, 0,118,189, 0,117,191, 0,117,191, 0, + 117,191, 0,114,197, 0,112,200, 0,111,202, 0,111,201, 0, + 113,198, 0,116,193, 0,118,188, 0,120,183, 0,119,185, 0, + 118,189, 0,116,193, 0,115,195, 0,114,196, 0,115,194, 0, + 117,191, 0,117,192, 0,117,191, 0,119,186, 0,121,179, 0, + 124,167, 6,124,150, 22,121,125, 63,114,104,197,105, 84,252, + 102, 80,250,111, 96,248,117,110,109,121,127, 58,124,145, 29, + 124,160, 12,123,173, 1,119,185, 0,115,194, 0,112,200, 0, + 110,203, 0,110,203, 0,111,201, 0,115,195, 0,118,189, 0, + 117,190, 0,115,194, 0,114,196, 0,115,195, 0,116,193, 0, + 117,192, 0,118,188, 0,119,186, 0,120,184, 0,120,182, 0, + 122,178, 0,123,169, 4,124,154, 18,121,127, 58,107, 89,252, + 109, 92,251,115,107,129,121,125, 63,123,139, 37,124,143, 31, + 123,139, 37,121,127, 58,115,107,129,102, 80,250, 30, 43,203, + 30, 43,203, 30, 43,203, 69, 60,230,105, 84,252,114,104,197, + 121,125, 63,124,143, 31,124,155, 16,124,162, 10,123,172, 2, + 120,183, 0,118,189, 0,117,192, 0,117,192, 0,116,193, 0, + 114,197, 0,113,198, 0,114,196, 0,117,191, 0,121,179, 0, + 124,157, 15,111, 96,248, 94, 70,242,118,114, 96,124,155, 16, + 123,173, 1,121,180, 0,121,181, 0,122,177, 0,124,167, 6, + 124,154, 18,124,150, 22,124,158, 13,124,161, 11,124,158, 13, + 124,154, 18,124,145, 29,122,132, 48,123,141, 34,124,160, 12, + 123,172, 2,121,181, 0,120,184, 0,120,184, 0,121,181, 0, + 123,174, 1,124,164, 9,124,152, 20,124,147, 26,123,141, 34, + 122,130, 53,118,114, 96, 99, 75,247, 30, 49,213, 94, 70,242, + 121,125, 63,124,160, 12,122,175, 0,120,183, 0,119,185, 0, + 120,184, 0,121,180, 0,123,174, 1,123,169, 4,124,167, 6, + 124,165, 8,124,161, 11,124,157, 15,124,147, 26,122,130, 53, + 113,100,242, 99, 75,247,109, 92,251,113,100,242,111, 96,248, + 114,104,197,111, 96,248,102, 80,250, 87, 65,237, 69, 60,230, + 99, 75,247,107, 89,252,113,100,242,115,107,129,114,104,197, + 114,104,197,115,107,129,118,114, 96,120,122, 70,122,132, 48, + 124,152, 20,123,169, 4,121,179, 0,119,185, 0,117,191, 0, + 112,200, 0,107,207, 0,103,212, 0,101,214, 0,101,213, 0, + 104,210, 0,108,206, 0,109,204, 0,107,206, 0,105,209, 0, + 103,212, 0,100,214, 0, 98,216, 0, 98,217, 0,100,215, 0, + 103,211, 0,107,206, 0,111,201, 0,116,193, 0,119,185, 0, + 123,171, 3,124,147, 26,109, 92,251, 30, 55,222,118,117, 85, + 124,164, 9,120,183, 0,116,192, 0,114,197, 0,114,197, 0, + 115,194, 0,117,190, 0,116,193, 0,116,193, 0,117,191, 0, + 120,184, 0,123,171, 3,124,145, 29,117,110,109,105, 84,252, + 105, 84,252,105, 84,252,107, 89,252,113,100,242,120,122, 70, + 122,135, 44,123,137, 41,122,130, 53,115,107,129,113,100,242, + 118,117, 85,122,130, 53,121,127, 58,115,107,129, 30, 49,213, + 30, 49,213,122,132, 48,123,170, 3,118,188, 0,113,198, 0, + 109,203, 0,108,206, 0,108,206, 0,108,205, 0,107,207, 0, + 106,207, 0,107,206, 0,108,206, 0,108,205, 0,110,202, 0, + 113,198, 0,116,193, 0,115,194, 0,112,200, 0,108,205, 0, + 107,207, 0,107,206, 0,111,202, 0,115,194, 0,119,185, 0, + 121,182, 0,121,179, 0,123,174, 1,124,161, 11,122,132, 48, + 94, 70,242, 30, 20,155,122,132, 48,124,152, 20,123,168, 5, + 121,179, 0,119,186, 0,117,191, 0,115,194, 0,115,195, 0, + 114,196, 0,114,196, 0,111,201, 0,109,204, 0,107,206, 0, + 107,207, 0,108,205, 0,111,202, 0,113,198, 0,116,193, 0, + 117,191, 0,115,194, 0,114,197, 0,112,199, 0,112,199, 0, + 114,197, 0,115,194, 0,114,196, 0,114,196, 0,115,194, 0, + 117,190, 0,120,184, 0,122,176, 0,124,166, 7,124,157, 15, + 124,147, 26,122,132, 48,121,127, 58,122,135, 44,124,143, 31, + 124,154, 18,124,166, 7,122,177, 0,118,188, 0,114,196, 0, + 110,202, 0,108,205, 0,108,206, 0,109,203, 0,113,199, 0, + 115,194, 0,114,196, 0,114,197, 0,114,197, 0,114,197, 0, + 114,197, 0,114,196, 0,115,194, 0,116,192, 0,117,191, 0, + 118,189, 0,119,186, 0,121,180, 0,123,170, 3,124,157, 15, + 123,137, 41,123,141, 34,124,149, 24,124,154, 18,124,155, 16, + 124,154, 18,124,149, 24,123,139, 37,121,127, 58,118,114, 96, + 115,107,129,114,104,197,115,107,129,118,117, 85,122,130, 53, + 124,145, 29,124,160, 12,123,169, 4,122,176, 0,121,179, 0, + 120,182, 0,119,185, 0,117,190, 0,116,193, 0,115,195, 0, + 113,198, 0,112,200, 0,111,201, 0,113,198, 0,117,191, 0, + 122,178, 0,124,152, 20,122,130, 53,121,125, 63,124,143, 31, + 124,158, 13,123,172, 2,121,179, 0,121,182, 0,122,178, 0, + 123,172, 2,124,164, 9,124,154, 18,124,155, 16,124,160, 12, + 124,161, 11,124,157, 15,124,150, 22,124,149, 24,124,157, 15, + 123,168, 5,122,178, 0,119,186, 0,118,189, 0,117,190, 0, + 118,188, 0,120,184, 0,122,177, 0,123,168, 5,124,158, 13, + 124,145, 29,122,130, 53,117,110,109,105, 84,252,109, 92,251, + 118,114, 96,124,150, 22,123,172, 2,120,184, 0,117,191, 0, + 115,194, 0,115,194, 0,116,192, 0,118,189, 0,119,186, 0, + 120,184, 0,120,183, 0,121,181, 0,122,177, 0,123,171, 3, + 124,162, 10,124,147, 26,119,119, 77,114,104,197,117,110,109, + 118,114, 96,115,107,129,111, 96,248,102, 80,250, 94, 70,242, + 105, 84,252,114,104,197,119,119, 77,122,135, 44,124,145, 29, + 124,154, 18,124,158, 13,124,160, 12,124,160, 12,124,161, 11, + 124,164, 9,124,165, 8,124,167, 6,123,174, 1,120,182, 0, + 117,191, 0,112,199, 0,107,207, 0,103,212, 0,100,215, 0, + 100,215, 0,101,213, 0,103,211, 0,103,212, 0,102,213, 0, + 100,215, 0, 98,217, 0, 96,219, 0, 94,220, 0, 94,221, 0, + 95,219, 0, 99,215, 0,104,210, 0,109,204, 0,114,197, 0, + 119,186, 0,123,171, 3,124,143, 31,121,125, 63,117,110,109, + 122,130, 53,124,158, 13,121,179, 0,118,189, 0,115,194, 0, + 114,196, 0,115,194, 0,117,192, 0,117,190, 0,117,191, 0, + 118,188, 0,121,181, 0,124,166, 7,124,147, 26,123,137, 41, + 120,122, 70,111, 96,248,111, 96,248,107, 89,252,102, 80,250, + 119,119, 77,123,139, 37,124,147, 26,124,147, 26,122,132, 48, + 113,100,242, 99, 75,247,107, 89,252,105, 84,252, 87, 65,237, + 30, 55,222,107, 89,252,124,147, 26,122,176, 0,117,191, 0, + 112,199, 0,109,204, 0,107,207, 0,106,207, 0,106,207, 0, + 106,208, 0,105,209, 0,105,209, 0,105,209, 0,107,207, 0, + 109,204, 0,113,199, 0,113,198, 0,113,198, 0,113,198, 0, + 112,200, 0,110,202, 0,111,201, 0,114,196, 0,118,189, 0, + 121,179, 0,124,165, 8,124,158, 13,124,147, 26,119,119, 77, + 87, 65,237, 30, 31,180,111, 96,248,124,149, 24,124,161, 11, + 123,172, 2,121,181, 0,119,187, 0,117,191, 0,115,194, 0, + 114,196, 0,114,196, 0,113,198, 0,110,203, 0,107,207, 0, + 105,209, 0,104,210, 0,105,209, 0,107,207, 0,110,203, 0, + 112,199, 0,114,196, 0,112,199, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,114,197, 0,112,200, 0,111,201, 0, + 112,200, 0,113,198, 0,116,193, 0,118,189, 0,120,183, 0, + 122,178, 0,123,173, 1,124,164, 9,124,147, 26,124,149, 24, + 124,154, 18,124,161, 11,123,170, 3,121,180, 0,117,190, 0, + 113,198, 0,109,204, 0,107,207, 0,106,208, 0,108,206, 0, + 111,202, 0,113,198, 0,112,200, 0,112,200, 0,111,201, 0, + 112,200, 0,111,201, 0,111,201, 0,112,199, 0,113,198, 0, + 114,196, 0,115,194, 0,116,192, 0,118,188, 0,120,183, 0, + 122,176, 0,124,167, 6,124,166, 7,123,169, 4,123,172, 2, + 123,174, 1,123,172, 2,123,169, 4,124,162, 10,124,152, 20, + 124,143, 31,122,135, 44,122,130, 53,122,132, 48,123,141, 34, + 124,152, 20,124,165, 8,122,175, 0,120,182, 0,119,187, 0, + 118,189, 0,117,191, 0,116,192, 0,115,194, 0,114,196, 0, + 113,199, 0,111,202, 0,109,203, 0,110,202, 0,112,199, 0, + 117,191, 0,122,178, 0,124,164, 9,124,157, 15,124,154, 18, + 124,162, 10,123,170, 3,122,176, 0,121,179, 0,121,180, 0, + 121,179, 0,122,175, 0,123,170, 3,124,164, 9,124,157, 15, + 124,161, 11,124,164, 9,124,162, 10,124,161, 11,124,161, 11, + 124,164, 9,123,172, 2,121,181, 0,119,187, 0,117,192, 0, + 116,193, 0,116,193, 0,118,189, 0,120,184, 0,122,177, 0, + 124,167, 6,124,154, 18,123,137, 41,118,117, 85,109, 92,251, + 118,117, 85,124,145, 29,124,166, 7,121,181, 0,118,189, 0, + 114,196, 0,112,199, 0,111,201, 0,112,200, 0,113,198, 0, + 114,197, 0,115,195, 0,116,193, 0,117,191, 0,118,188, 0, + 120,183, 0,122,177, 0,124,167, 6,124,154, 18,122,132, 48, + 115,107,129,118,114, 96,118,114, 96,117,110,109,115,107,129, + 115,107,129,119,119, 77,123,137, 41,124,154, 18,124,165, 8, + 123,172, 2,122,178, 0,121,180, 0,121,180, 0,121,180, 0, + 122,178, 0,122,178, 0,122,178, 0,122,178, 0,122,178, 0, + 121,182, 0,118,189, 0,113,198, 0,108,205, 0,103,211, 0, + 100,214, 0, 99,216, 0,100,215, 0, 98,216, 0, 98,217, 0, + 97,218, 0, 95,219, 0, 94,221, 0, 92,222, 0, 92,223, 0, + 91,223, 0, 93,222, 0, 96,219, 0,101,213, 0,107,207, 0, + 113,198, 0,119,187, 0,123,170, 3,124,158, 13,124,154, 18, + 124,147, 26,124,149, 24,124,161, 11,123,171, 3,120,184, 0, + 117,190, 0,116,193, 0,115,194, 0,116,193, 0,117,190, 0, + 119,187, 0,120,184, 0,122,176, 0,124,166, 7,124,160, 12, + 124,152, 20,124,143, 31,121,127, 58,111, 96,248,105, 84,252, + 87, 65,237,115,107,129,123,139, 37,124,152, 20,124,155, 16, + 124,145, 29,121,125, 63,107, 89,252, 30, 43,203, 30, 49,213, + 30, 49,213, 69, 60,230,118,117, 85,124,158, 13,122,178, 0, + 117,191, 0,113,199, 0,109,203, 0,108,206, 0,106,207, 0, + 106,208, 0,104,210, 0,103,211, 0,102,212, 0,103,211, 0, + 105,209, 0,108,205, 0,110,202, 0,111,202, 0,111,202, 0, + 111,201, 0,113,198, 0,115,195, 0,116,193, 0,118,189, 0, + 121,182, 0,123,171, 3,124,157, 15,122,135, 44,111, 96,248, + 30, 43,203, 30, 26,168,109, 92,251,122,130, 53,124,155, 16, + 124,162, 10,123,170, 3,122,178, 0,120,184, 0,118,188, 0, + 117,191, 0,116,193, 0,114,196, 0,112,200, 0,109,204, 0, + 106,207, 0,104,210, 0,104,210, 0,104,210, 0,106,208, 0, + 108,205, 0,111,202, 0,112,200, 0,110,202, 0,109,204, 0, + 108,205, 0,108,205, 0,109,203, 0,112,200, 0,110,202, 0, + 109,204, 0,109,204, 0,110,202, 0,112,200, 0,114,196, 0, + 116,192, 0,118,189, 0,120,184, 0,122,177, 0,124,164, 9, + 124,157, 15,124,160, 12,124,166, 7,123,173, 1,120,182, 0, + 117,192, 0,112,200, 0,108,205, 0,106,208, 0,105,209, 0, + 106,207, 0,109,204, 0,111,201, 0,110,202, 0,110,202, 0, + 110,202, 0,110,203, 0,108,205, 0,108,205, 0,109,204, 0, + 110,202, 0,111,201, 0,112,199, 0,114,197, 0,115,195, 0, + 117,192, 0,118,188, 0,120,184, 0,121,180, 0,120,182, 0, + 120,184, 0,119,185, 0,120,184, 0,121,180, 0,122,175, 0, + 124,167, 6,124,158, 13,124,154, 18,124,152, 20,124,147, 26, + 124,152, 20,124,162, 10,123,172, 2,121,182, 0,118,188, 0, + 116,193, 0,115,195, 0,114,196, 0,114,197, 0,113,198, 0, + 112,199, 0,111,201, 0,109,204, 0,108,205, 0,109,204, 0, + 112,200, 0,117,192, 0,121,179, 0,122,176, 0,123,170, 3, + 124,167, 6,123,171, 3,122,176, 0,121,180, 0,120,182, 0, + 120,182, 0,121,181, 0,122,178, 0,123,173, 1,123,168, 5, + 124,162, 10,124,165, 8,123,168, 5,123,169, 4,123,169, 4, + 124,167, 6,123,168, 5,123,173, 1,121,180, 0,119,186, 0, + 117,191, 0,115,194, 0,115,194, 0,116,193, 0,118,189, 0, + 121,182, 0,123,172, 2,124,160, 12,124,145, 29,123,139, 37, + 122,132, 48,124,149, 24,124,164, 9,122,176, 0,119,186, 0, + 116,193, 0,113,198, 0,110,202, 0,109,204, 0,108,205, 0, + 109,204, 0,110,202, 0,111,201, 0,112,199, 0,114,197, 0, + 116,193, 0,118,189, 0,120,184, 0,122,177, 0,123,168, 5, + 124,157, 15,124,143, 31,121,127, 58,117,110,109,117,110,109, + 118,117, 85,121,125, 63,123,139, 37,124,157, 15,123,170, 3, + 121,179, 0,119,185, 0,118,188, 0,117,190, 0,117,191, 0, + 118,189, 0,118,188, 0,119,186, 0,119,186, 0,118,189, 0, + 117,190, 0,117,191, 0,116,192, 0,114,196, 0,110,203, 0, + 105,209, 0,101,213, 0, 99,215, 0, 97,218, 0, 95,220, 0, + 94,220, 0, 94,221, 0, 93,222, 0, 92,222, 0, 91,223, 0, + 91,224, 0, 91,224, 0, 92,222, 0, 95,220, 0,100,215, 0, + 106,207, 0,113,198, 0,119,186, 0,122,176, 0,123,174, 1, + 123,172, 2,123,169, 4,124,167, 6,124,167, 6,123,172, 2, + 122,178, 0,120,184, 0,118,189, 0,116,192, 0,116,193, 0, + 117,192, 0,118,188, 0,120,183, 0,122,178, 0,123,171, 3, + 124,165, 8,124,158, 13,124,152, 20,123,141, 34,119,119, 77, + 102, 80,250, 30, 55,222,105, 84,252,122,130, 53,124,150, 22, + 124,157, 15,124,150, 22,122,135, 44,115,107,129, 94, 70,242, + 30, 37,192, 30, 55,222,109, 92,251,122,132, 48,124,162, 10, + 122,178, 0,118,189, 0,114,196, 0,112,200, 0,110,203, 0, + 108,205, 0,106,208, 0,104,210, 0,102,213, 0,101,214, 0, + 102,213, 0,104,210, 0,108,206, 0,109,204, 0,109,204, 0, + 109,203, 0,111,202, 0,113,198, 0,116,193, 0,119,185, 0, + 122,178, 0,123,170, 3,124,160, 12,124,147, 26,121,125, 63, + 109, 92,251, 87, 65,237,114,104,197,122,130, 53,124,147, 26, + 124,157, 15,124,161, 11,123,169, 4,122,177, 0,121,182, 0, + 119,186, 0,118,189, 0,116,193, 0,114,196, 0,112,200, 0, + 109,203, 0,107,206, 0,106,208, 0,105,209, 0,106,208, 0, + 107,206, 0,109,203, 0,111,201, 0,112,200, 0,109,203, 0, + 108,206, 0,107,207, 0,107,207, 0,108,205, 0,110,202, 0, + 109,204, 0,107,206, 0,107,207, 0,108,205, 0,110,203, 0, + 112,200, 0,114,196, 0,116,193, 0,118,189, 0,121,182, 0, + 123,170, 3,124,161, 11,124,162, 10,123,168, 5,122,175, 0, + 120,184, 0,116,193, 0,111,201, 0,107,206, 0,105,209, 0, + 105,209, 0,106,208, 0,109,204, 0,111,201, 0,110,203, 0, + 109,203, 0,109,203, 0,107,207, 0,105,209, 0,105,209, 0, + 106,208, 0,107,207, 0,107,206, 0,109,204, 0,111,202, 0, + 111,201, 0,112,199, 0,113,198, 0,114,196, 0,116,193, 0, + 117,191, 0,116,192, 0,116,193, 0,117,191, 0,118,188, 0, + 120,183, 0,122,176, 0,123,173, 1,123,172, 2,123,171, 3, + 123,168, 5,124,164, 9,124,165, 8,122,175, 0,120,184, 0, + 117,191, 0,115,195, 0,114,197, 0,113,199, 0,112,199, 0, + 112,200, 0,110,202, 0,109,203, 0,108,205, 0,107,207, 0, + 108,205, 0,111,201, 0,116,193, 0,119,186, 0,120,182, 0, + 122,176, 0,123,172, 2,123,174, 1,122,177, 0,121,179, 0, + 121,182, 0,121,182, 0,121,181, 0,121,179, 0,122,176, 0, + 123,170, 3,124,165, 8,123,171, 3,122,175, 0,122,177, 0, + 122,175, 0,123,172, 2,123,170, 3,123,172, 2,122,178, 0, + 120,184, 0,118,189, 0,116,193, 0,115,194, 0,116,193, 0, + 117,190, 0,120,184, 0,123,174, 1,124,167, 6,124,167, 6, + 124,165, 8,124,161, 11,124,166, 7,123,174, 1,121,182, 0, + 118,188, 0,115,194, 0,113,199, 0,110,202, 0,108,205, 0, + 107,206, 0,107,206, 0,108,205, 0,109,204, 0,110,202, 0, + 112,199, 0,114,196, 0,117,191, 0,119,186, 0,121,180, 0, + 123,174, 1,124,167, 6,124,160, 12,124,150, 22,123,141, 34, + 122,132, 48,121,125, 63,122,130, 53,124,149, 24,124,166, 7, + 122,178, 0,119,186, 0,117,191, 0,115,194, 0,114,196, 0, + 114,196, 0,115,194, 0,117,192, 0,116,193, 0,114,197, 0, + 113,199, 0,113,199, 0,113,198, 0,114,197, 0,113,198, 0, + 111,202, 0,107,207, 0,103,212, 0,100,215, 0, 96,219, 0, + 94,221, 0, 92,222, 0, 92,222, 0, 92,222, 0, 92,222, 0, + 92,223, 0, 92,223, 0, 92,223, 0, 93,222, 0, 95,219, 0, + 100,214, 0,107,207, 0,114,197, 0,119,186, 0,119,185, 0, + 120,184, 0,120,183, 0,120,182, 0,120,182, 0,120,184, 0, + 120,184, 0,120,184, 0,120,183, 0,119,185, 0,118,188, 0, + 117,191, 0,117,191, 0,118,189, 0,120,184, 0,122,178, 0, + 123,172, 2,124,165, 8,124,158, 13,124,154, 18,124,147, 26, + 122,132, 48,113,100,242, 30, 55,222, 30, 43,203,115,107,129, + 123,139, 37,124,149, 24,124,147, 26,122,132, 48,117,110,109, + 105, 84,252, 99, 75,247,105, 84,252,117,110,109,123,139, 37, + 124,162, 10,122,176, 0,120,184, 0,117,191, 0,115,195, 0, + 113,199, 0,110,203, 0,107,206, 0,104,210, 0,101,213, 0, + 100,215, 0,100,214, 0,103,212, 0,107,207, 0,108,205, 0, + 108,205, 0,109,203, 0,111,201, 0,114,196, 0,117,190, 0, + 121,182, 0,123,170, 3,124,155, 16,123,141, 34,122,130, 53, + 118,114, 96,113,100,242,120,122, 70,123,139, 37,124,149, 24, + 124,154, 18,124,157, 15,124,167, 6,122,175, 0,121,180, 0, + 120,184, 0,119,187, 0,118,189, 0,117,192, 0,115,194, 0, + 114,197, 0,111,201, 0,110,203, 0,109,204, 0,109,204, 0, + 110,203, 0,111,201, 0,113,199, 0,114,196, 0,112,199, 0, + 110,202, 0,108,206, 0,107,207, 0,106,207, 0,107,206, 0, + 109,204, 0,108,205, 0,107,207, 0,106,207, 0,107,206, 0, + 109,203, 0,112,200, 0,114,196, 0,116,193, 0,118,189, 0, + 120,182, 0,123,171, 3,124,161, 11,124,167, 6,122,177, 0, + 121,182, 0,119,185, 0,115,194, 0,111,201, 0,107,206, 0, + 105,209, 0,105,209, 0,107,207, 0,109,204, 0,112,200, 0, + 110,202, 0,110,203, 0,107,207, 0,103,211, 0,101,213, 0, + 101,213, 0,102,212, 0,103,212, 0,102,212, 0,103,211, 0, + 105,209, 0,108,206, 0,108,205, 0,109,204, 0,109,203, 0, + 110,202, 0,112,199, 0,112,199, 0,112,199, 0,113,198, 0, + 115,194, 0,118,189, 0,120,184, 0,120,184, 0,120,183, 0, + 120,182, 0,121,181, 0,122,178, 0,123,173, 1,123,173, 1, + 120,182, 0,118,189, 0,115,194, 0,114,197, 0,113,199, 0, + 112,200, 0,110,202, 0,108,206, 0,107,207, 0,107,206, 0, + 106,207, 0,107,206, 0,110,202, 0,115,194, 0,118,189, 0, + 119,185, 0,122,178, 0,123,172, 2,123,172, 2,123,173, 1, + 122,176, 0,122,178, 0,122,178, 0,122,178, 0,122,177, 0, + 123,174, 1,123,169, 4,123,170, 3,122,178, 0,120,182, 0, + 120,182, 0,121,180, 0,122,176, 0,122,175, 0,122,178, 0, + 122,178, 0,121,181, 0,119,186, 0,118,189, 0,117,192, 0, + 117,192, 0,118,189, 0,120,183, 0,122,177, 0,121,180, 0, + 121,182, 0,121,180, 0,122,177, 0,122,175, 0,121,179, 0, + 120,184, 0,118,189, 0,116,193, 0,114,197, 0,111,201, 0, + 109,203, 0,108,205, 0,107,206, 0,108,205, 0,109,204, 0, + 110,202, 0,112,199, 0,115,195, 0,117,190, 0,119,185, 0, + 121,179, 0,123,174, 1,123,169, 4,124,165, 8,124,160, 12, + 124,155, 16,124,149, 24,123,141, 34,122,135, 44,124,150, 22, + 123,169, 4,121,182, 0,118,189, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,114,196, 0,114,197, 0,110,202, 0, + 108,205, 0,107,206, 0,108,205, 0,110,203, 0,111,201, 0, + 110,203, 0,109,203, 0,107,206, 0,104,210, 0,100,214, 0, + 97,218, 0, 94,221, 0, 92,222, 0, 92,222, 0, 93,221, 0, + 91,223, 0, 91,223, 0, 93,221, 0, 94,220, 0, 95,220, 0, + 98,217, 0,102,212, 0,108,205, 0,115,194, 0,116,192, 0, + 117,191, 0,117,190, 0,117,190, 0,117,190, 0,117,192, 0, + 115,194, 0,115,195, 0,115,195, 0,116,193, 0,117,190, 0, + 118,189, 0,117,191, 0,117,190, 0,119,187, 0,120,182, 0, + 122,177, 0,123,169, 4,124,161, 11,124,154, 18,124,149, 24, + 124,147, 26,123,137, 41,118,117, 85, 99, 75,247, 30, 43,203, + 87, 65,237,118,114, 96,122,132, 48,122,132, 48,120,122, 70, + 115,107,129,111, 96,248,107, 89,252,109, 92,251,118,114, 96, + 123,137, 41,124,158, 13,123,170, 3,122,178, 0,120,184, 0, + 118,189, 0,114,197, 0,110,202, 0,109,204, 0,105,209, 0, + 102,213, 0,100,215, 0,100,215, 0,102,212, 0,106,207, 0, + 109,203, 0,110,203, 0,111,201, 0,113,198, 0,117,192, 0, + 120,184, 0,122,175, 0,124,162, 10,124,145, 29,122,132, 48, + 121,125, 63,119,119, 77,122,132, 48,124,149, 24,124,157, 15, + 124,158, 13,124,158, 13,124,158, 13,123,168, 5,122,175, 0, + 121,180, 0,120,183, 0,120,184, 0,119,186, 0,119,187, 0, + 118,189, 0,116,192, 0,115,195, 0,114,197, 0,113,198, 0, + 114,197, 0,114,196, 0,116,193, 0,117,191, 0,117,191, 0, + 114,196, 0,112,200, 0,109,204, 0,107,206, 0,107,207, 0, + 107,206, 0,109,204, 0,108,206, 0,107,207, 0,107,207, 0, + 108,205, 0,110,202, 0,113,198, 0,116,193, 0,117,190, 0, + 119,186, 0,121,179, 0,124,167, 6,124,167, 6,122,178, 0, + 120,184, 0,119,187, 0,118,188, 0,115,195, 0,111,202, 0, + 107,206, 0,106,208, 0,106,208, 0,108,206, 0,110,202, 0, + 113,198, 0,112,200, 0,108,205, 0,103,212, 0, 99,215, 0, + 97,217, 0, 98,217, 0, 99,215, 0,100,215, 0, 98,217, 0, + 98,216, 0,100,215, 0,102,212, 0,105,209, 0,105,209, 0, + 105,209, 0,106,207, 0,108,205, 0,108,206, 0,108,205, 0, + 109,204, 0,111,201, 0,114,196, 0,118,189, 0,118,189, 0, + 118,188, 0,118,188, 0,118,188, 0,119,186, 0,120,184, 0, + 121,180, 0,122,178, 0,119,187, 0,116,192, 0,114,196, 0, + 113,198, 0,111,201, 0,108,206, 0,105,209, 0,105,209, 0, + 107,207, 0,106,208, 0,107,207, 0,110,203, 0,114,197, 0, + 118,189, 0,120,184, 0,122,176, 0,123,168, 5,124,166, 7, + 124,167, 6,123,168, 5,123,170, 3,123,172, 2,123,173, 1, + 123,172, 2,123,169, 4,124,167, 6,122,178, 0,119,185, 0, + 118,188, 0,118,188, 0,119,185, 0,121,180, 0,122,178, 0, + 121,182, 0,120,182, 0,121,182, 0,120,182, 0,119,186, 0, + 118,189, 0,118,189, 0,119,187, 0,120,182, 0,119,187, 0, + 118,189, 0,117,191, 0,118,189, 0,119,186, 0,121,182, 0, + 120,182, 0,119,185, 0,118,189, 0,115,194, 0,113,198, 0, + 113,199, 0,111,201, 0,110,203, 0,109,204, 0,109,203, 0, + 110,202, 0,112,200, 0,114,197, 0,116,192, 0,119,187, 0, + 121,181, 0,123,174, 1,123,169, 4,124,166, 7,124,164, 9, + 124,161, 11,124,158, 13,124,152, 20,124,145, 29,123,139, 37, + 124,152, 20,123,170, 3,120,182, 0,117,190, 0,115,194, 0, + 114,196, 0,114,196, 0,114,197, 0,114,197, 0,109,204, 0, + 105,209, 0,103,211, 0,103,212, 0,105,209, 0,107,207, 0, + 109,204, 0,107,207, 0,106,208, 0,106,208, 0,105,209, 0, + 102,213, 0, 98,217, 0, 95,219, 0, 94,221, 0, 94,221, 0, + 89,225, 0, 86,228, 0, 86,228, 0, 88,226, 0, 93,222, 0, + 98,217, 0,100,214, 0,105,209, 0,110,202, 0,114,197, 0, + 114,196, 0,114,197, 0,113,198, 0,114,197, 0,114,196, 0, + 113,198, 0,112,200, 0,110,202, 0,110,202, 0,112,200, 0, + 114,196, 0,117,191, 0,117,191, 0,117,191, 0,118,188, 0, + 121,182, 0,123,173, 1,123,168, 5,124,160, 12,124,145, 29, + 123,141, 34,123,141, 34,123,139, 37,121,127, 58,113,100,242, + 94, 70,242, 30, 55,222, 94, 70,242,114,104,197,117,110,109, + 115,107,129,113,100,242,107, 89,252,105, 84,252,107, 89,252, + 114,104,197,121,127, 58,124,147, 26,124,160, 12,124,167, 6, + 122,178, 0,118,188, 0,114,196, 0,112,200, 0,111,201, 0, + 107,206, 0,103,211, 0,100,214, 0,100,215, 0,102,212, 0, + 106,208, 0,111,201, 0,112,199, 0,114,197, 0,116,192, 0, + 119,186, 0,122,176, 0,124,164, 9,124,147, 26,121,127, 58, + 118,114, 96,118,114, 96,120,122, 70,124,149, 24,124,161, 11, + 124,165, 8,124,165, 8,124,160, 12,124,154, 18,124,164, 9, + 123,170, 3,123,174, 1,122,176, 0,122,177, 0,122,177, 0, + 122,177, 0,121,179, 0,120,183, 0,119,186, 0,118,188, 0, + 118,189, 0,118,188, 0,119,186, 0,120,184, 0,120,184, 0, + 119,186, 0,117,192, 0,114,197, 0,111,201, 0,109,204, 0, + 108,205, 0,108,205, 0,110,202, 0,108,206, 0,107,207, 0, + 108,206, 0,110,203, 0,112,199, 0,115,194, 0,117,190, 0, + 119,185, 0,121,182, 0,122,176, 0,123,172, 2,122,175, 0, + 120,182, 0,119,187, 0,118,189, 0,117,190, 0,113,198, 0, + 109,203, 0,107,207, 0,106,208, 0,106,207, 0,109,204, 0, + 112,200, 0,114,196, 0,110,202, 0,104,210, 0, 99,215, 0, + 95,219, 0, 94,221, 0, 94,221, 0, 95,219, 0, 98,217, 0, + 96,219, 0, 95,219, 0, 97,218, 0, 98,217, 0,101,214, 0, + 103,212, 0,103,211, 0,104,210, 0,103,211, 0,102,212, 0, + 103,212, 0,105,209, 0,107,207, 0,111,202, 0,114,196, 0, + 117,190, 0,118,189, 0,117,191, 0,117,191, 0,117,191, 0, + 118,189, 0,118,188, 0,119,185, 0,120,184, 0,117,190, 0, + 115,194, 0,113,198, 0,109,203, 0,106,208, 0,104,210, 0, + 104,210, 0,106,208, 0,105,209, 0,106,208, 0,108,205, 0, + 112,199, 0,117,191, 0,121,182, 0,123,171, 3,124,160, 12, + 124,167, 6,123,168, 5,124,167, 6,124,164, 9,124,164, 9, + 124,165, 8,124,165, 8,124,164, 9,122,176, 0,119,185, 0, + 117,191, 0,116,193, 0,116,193, 0,118,189, 0,120,184, 0, + 122,178, 0,121,182, 0,120,183, 0,120,183, 0,120,182, 0, + 120,184, 0,119,186, 0,119,187, 0,120,184, 0,118,189, 0, + 116,193, 0,114,196, 0,114,196, 0,115,194, 0,117,191, 0, + 119,186, 0,120,183, 0,117,190, 0,114,197, 0,111,201, 0, + 109,204, 0,108,205, 0,108,205, 0,109,203, 0,111,202, 0, + 111,201, 0,112,200, 0,114,197, 0,116,193, 0,118,188, 0, + 120,182, 0,123,174, 1,124,166, 7,124,160, 12,124,162, 10, + 124,165, 8,124,164, 9,124,160, 12,124,149, 24,123,137, 41, + 122,130, 53,124,154, 18,123,171, 3,120,182, 0,118,189, 0, + 116,193, 0,115,194, 0,115,194, 0,115,194, 0,110,202, 0, + 105,209, 0,102,213, 0,100,215, 0,100,215, 0,102,212, 0, + 105,209, 0,108,206, 0,105,209, 0,104,210, 0,103,211, 0, + 103,212, 0,102,212, 0,100,215, 0, 97,218, 0, 95,220, 0, + 89,225, 0, 85,229, 0, 82,231, 0, 82,231, 0, 84,229, 0, + 89,225, 0, 96,219, 0,103,212, 0,107,207, 0,111,201, 0, + 111,201, 0,110,203, 0,108,205, 0,108,205, 0,109,203, 0, + 113,199, 0,111,201, 0,109,203, 0,108,205, 0,108,206, 0, + 109,204, 0,112,200, 0,115,195, 0,118,189, 0,118,189, 0, + 119,186, 0,121,181, 0,122,178, 0,122,175, 0,123,168, 5, + 124,157, 15,122,132, 48,123,137, 41,123,137, 41,122,132, 48, + 118,117, 85,113,100,242,111, 96,248,105, 84,252, 99, 75,247, + 109, 92,251,109, 92,251,111, 96,248,114,104,197,117,110,109, + 117,110,109,117,110,109,115,107,129,121,127, 58,123,141, 34, + 124,160, 12,123,174, 1,120,184, 0,117,191, 0,115,194, 0, + 114,196, 0,110,203, 0,105,209, 0,102,212, 0,101,214, 0, + 103,212, 0,106,207, 0,111,201, 0,116,193, 0,117,190, 0, + 120,184, 0,122,176, 0,124,164, 9,124,155, 16,123,141, 34, + 120,122, 70,105, 84,252,107, 89,252,122,130, 53,124,154, 18, + 124,166, 7,123,170, 3,123,168, 5,124,164, 9,124,157, 15, + 124,150, 22,124,157, 15,124,160, 12,124,161, 11,124,161, 11, + 123,169, 4,122,175, 0,122,178, 0,122,178, 0,122,178, 0, + 122,178, 0,122,178, 0,121,180, 0,120,182, 0,119,185, 0, + 118,188, 0,118,189, 0,118,188, 0,115,194, 0,113,199, 0, + 110,202, 0,109,203, 0,110,203, 0,111,201, 0,109,203, 0, + 109,204, 0,110,203, 0,112,200, 0,114,196, 0,116,193, 0, + 117,191, 0,119,186, 0,121,182, 0,122,178, 0,123,173, 1, + 122,178, 0,120,184, 0,118,188, 0,117,191, 0,116,192, 0, + 112,199, 0,108,205, 0,106,208, 0,105,209, 0,106,207, 0, + 109,204, 0,112,200, 0,111,201, 0,108,206, 0,102,213, 0, + 96,219, 0, 92,222, 0, 91,223, 0, 91,223, 0, 93,222, 0, + 95,220, 0, 94,220, 0, 94,221, 0, 94,221, 0, 95,220, 0, + 96,219, 0, 98,217, 0, 99,216, 0, 98,216, 0, 97,217, 0, + 97,217, 0, 98,217, 0,100,215, 0,103,211, 0,107,207, 0, + 111,201, 0,115,195, 0,117,190, 0,117,191, 0,116,192, 0, + 116,193, 0,116,193, 0,117,192, 0,117,190, 0,118,189, 0, + 117,191, 0,114,196, 0,111,201, 0,108,206, 0,105,209, 0, + 103,211, 0,104,210, 0,106,208, 0,105,209, 0,105,209, 0, + 108,206, 0,112,200, 0,116,192, 0,120,183, 0,123,170, 3, + 124,164, 9,123,170, 3,123,171, 3,123,170, 3,123,168, 5, + 124,166, 7,124,165, 8,124,165, 8,122,176, 0,119,185, 0, + 116,192, 0,114,197, 0,113,199, 0,113,198, 0,115,194, 0, + 118,189, 0,120,182, 0,121,181, 0,120,182, 0,120,183, 0, + 120,184, 0,120,184, 0,119,185, 0,119,187, 0,117,191, 0, + 115,195, 0,113,198, 0,112,200, 0,112,200, 0,113,198, 0, + 115,194, 0,117,190, 0,116,193, 0,114,196, 0,111,202, 0, + 107,206, 0,105,209, 0,104,210, 0,104,210, 0,105,209, 0, + 106,207, 0,108,205, 0,110,202, 0,113,199, 0,115,194, 0, + 117,190, 0,119,185, 0,121,180, 0,122,177, 0,123,173, 1, + 124,167, 6,124,165, 8,124,165, 8,124,162, 10,124,154, 18, + 123,137, 41,121,127, 58,124,152, 20,123,169, 4,121,180, 0, + 119,186, 0,118,189, 0,117,191, 0,117,191, 0,113,198, 0, + 108,205, 0,103,211, 0,100,215, 0, 98,217, 0, 98,217, 0, + 100,215, 0,103,211, 0,106,208, 0,106,208, 0,104,210, 0, + 103,211, 0,102,212, 0,101,214, 0, 99,215, 0, 97,218, 0, + 93,221, 0, 88,226, 0, 83,230, 0, 80,233, 0, 80,233, 0, + 82,231, 0, 87,227, 0, 94,221, 0,100,214, 0,106,207, 0, + 107,206, 0,106,208, 0,105,209, 0,103,211, 0,104,210, 0, + 105,209, 0,109,204, 0,111,201, 0,109,203, 0,108,206, 0, + 107,207, 0,108,205, 0,110,202, 0,113,198, 0,116,192, 0, + 118,188, 0,119,185, 0,120,183, 0,121,180, 0,122,176, 0, + 123,170, 3,124,158, 13,123,137, 41,123,137, 41,123,139, 37, + 122,135, 44,120,122, 70,119,119, 77,121,125, 63,120,122, 70, + 119,119, 77,120,122, 70,121,127, 58,122,130, 53,122,130, 53, + 122,130, 53,121,125, 63,118,117, 85,117,110,109,118,114, 96, + 122,132, 48,124,150, 22,124,165, 8,122,175, 0,121,182, 0, + 119,185, 0,117,192, 0,112,200, 0,107,206, 0,104,210, 0, + 103,211, 0,105,209, 0,108,205, 0,113,199, 0,117,191, 0, + 120,183, 0,122,176, 0,123,169, 4,124,162, 10,124,154, 18, + 124,143, 31,121,127, 58,109, 92,251,107, 89,252,122,130, 53, + 124,155, 16,124,167, 6,123,171, 3,123,171, 3,124,166, 7, + 124,158, 13,124,150, 22,124,149, 24,124,155, 16,124,162, 10, + 123,170, 3,122,177, 0,121,181, 0,120,184, 0,120,184, 0, + 119,185, 0,119,185, 0,119,186, 0,119,186, 0,119,186, 0, + 118,188, 0,118,189, 0,118,189, 0,118,188, 0,117,191, 0, + 114,197, 0,112,200, 0,111,202, 0,111,202, 0,112,200, 0, + 113,199, 0,112,199, 0,113,198, 0,115,195, 0,117,192, 0, + 117,190, 0,118,188, 0,120,183, 0,122,178, 0,123,172, 2, + 123,168, 5,122,177, 0,120,184, 0,118,188, 0,117,191, 0, + 116,193, 0,112,199, 0,109,204, 0,106,207, 0,106,208, 0, + 106,207, 0,108,205, 0,111,202, 0,110,203, 0,106,207, 0, + 100,214, 0, 95,220, 0, 92,223, 0, 90,225, 0, 90,224, 0, + 92,223, 0, 94,221, 0, 92,222, 0, 91,223, 0, 91,223, 0, + 92,223, 0, 93,222, 0, 94,221, 0, 94,220, 0, 94,221, 0, + 93,222, 0, 93,222, 0, 94,220, 0, 97,217, 0,101,213, 0, + 106,208, 0,110,202, 0,114,197, 0,117,191, 0,116,192, 0, + 115,194, 0,115,195, 0,115,195, 0,115,194, 0,116,193, 0, + 116,193, 0,115,194, 0,113,198, 0,110,203, 0,107,207, 0, + 104,210, 0,103,212, 0,103,211, 0,105,209, 0,105,209, 0, + 106,208, 0,109,204, 0,112,199, 0,117,192, 0,121,182, 0, + 123,169, 4,124,160, 12,124,166, 7,123,169, 4,123,169, 4, + 123,169, 4,123,168, 5,123,169, 4,122,177, 0,119,186, 0, + 116,193, 0,112,199, 0,110,202, 0,109,204, 0,110,202, 0, + 113,198, 0,116,192, 0,118,189, 0,119,187, 0,119,187, 0, + 118,189, 0,118,189, 0,118,189, 0,117,191, 0,116,193, 0, + 114,197, 0,112,200, 0,110,202, 0,110,203, 0,110,202, 0, + 112,200, 0,114,197, 0,113,198, 0,111,201, 0,110,202, 0, + 108,205, 0,105,209, 0,102,212, 0,100,214, 0,100,215, 0, + 100,214, 0,102,213, 0,104,210, 0,107,207, 0,110,203, 0, + 113,199, 0,115,194, 0,118,189, 0,119,186, 0,119,186, 0, + 120,184, 0,121,180, 0,123,173, 1,124,167, 6,124,166, 7, + 124,161, 11,124,149, 24,123,139, 37,123,141, 34,124,161, 11, + 123,173, 1,121,181, 0,120,184, 0,119,185, 0,118,189, 0, + 112,199, 0,107,207, 0,102,212, 0, 98,216, 0, 97,218, 0, + 97,218, 0, 98,216, 0,101,213, 0,104,210, 0,105,209, 0, + 105,209, 0,103,211, 0,100,214, 0, 98,217, 0, 97,218, 0, + 96,219, 0, 93,221, 0, 88,226, 0, 83,230, 0, 81,232, 0, + 81,232, 0, 83,230, 0, 87,227, 0, 92,222, 0, 98,216, 0, + 104,210, 0,103,212, 0,101,213, 0,100,214, 0, 99,215, 0, + 100,215, 0,102,213, 0,105,209, 0,110,202, 0,111,201, 0, + 109,203, 0,108,205, 0,108,205, 0,110,203, 0,112,199, 0, + 115,194, 0,118,189, 0,119,186, 0,121,182, 0,122,178, 0, + 123,172, 2,124,165, 8,124,154, 18,123,141, 34,123,139, 37, + 123,137, 41,122,132, 48,120,122, 70,122,132, 48,123,141, 34, + 124,145, 29,124,149, 24,124,150, 22,124,150, 22,124,149, 24, + 124,145, 29,123,137, 41,121,127, 58,118,114, 96,113,100,242, + 115,107,129,118,114, 96,122,132, 48,124,149, 24,124,160, 12, + 124,167, 6,122,178, 0,118,188, 0,114,196, 0,110,202, 0, + 107,206, 0,107,207, 0,108,205, 0,112,200, 0,116,193, 0, + 120,184, 0,122,175, 0,124,167, 6,124,160, 12,124,155, 16, + 124,149, 24,123,141, 34,121,125, 63,120,122, 70,118,114, 96, + 121,127, 58,124,152, 20,124,165, 8,123,170, 3,123,170, 3, + 124,166, 7,124,157, 15,124,157, 15,124,161, 11,124,167, 6, + 123,172, 2,122,178, 0,120,182, 0,119,186, 0,118,188, 0, + 118,189, 0,117,191, 0,117,191, 0,117,191, 0,118,189, 0, + 118,188, 0,118,188, 0,119,187, 0,119,187, 0,119,185, 0, + 118,189, 0,115,194, 0,113,198, 0,112,199, 0,112,199, 0, + 113,198, 0,115,195, 0,116,193, 0,117,191, 0,118,189, 0, + 119,186, 0,120,184, 0,121,180, 0,123,174, 1,124,167, 6, + 124,161, 11,124,162, 10,123,172, 2,121,181, 0,119,187, 0, + 117,192, 0,115,194, 0,113,198, 0,110,202, 0,107,206, 0, + 106,207, 0,106,207, 0,108,206, 0,110,203, 0,109,203, 0, + 106,207, 0,100,214, 0, 95,220, 0, 92,223, 0, 90,224, 0, + 91,224, 0, 92,222, 0, 93,222, 0, 91,223, 0, 90,225, 0, + 90,225, 0, 90,224, 0, 91,223, 0, 92,223, 0, 92,222, 0, + 91,223, 0, 90,224, 0, 91,223, 0, 93,221, 0, 97,218, 0, + 101,213, 0,106,207, 0,111,202, 0,114,196, 0,116,193, 0, + 116,193, 0,114,197, 0,112,200, 0,111,201, 0,112,199, 0, + 114,196, 0,115,195, 0,114,196, 0,112,200, 0,109,203, 0, + 106,207, 0,104,210, 0,102,212, 0,103,212, 0,105,209, 0, + 107,207, 0,108,206, 0,110,202, 0,114,197, 0,118,189, 0, + 122,178, 0,124,164, 9,124,147, 26,124,155, 16,124,160, 12, + 124,162, 10,124,164, 9,124,166, 7,123,174, 1,119,185, 0, + 116,192, 0,112,199, 0,109,204, 0,107,207, 0,106,207, 0, + 108,206, 0,111,202, 0,114,196, 0,116,193, 0,117,192, 0, + 117,192, 0,115,195, 0,114,197, 0,113,198, 0,113,198, 0, + 113,198, 0,111,201, 0,109,203, 0,108,205, 0,108,205, 0, + 109,203, 0,111,201, 0,112,200, 0,109,203, 0,108,205, 0, + 107,206, 0,107,207, 0,103,211, 0,100,214, 0, 98,217, 0, + 97,218, 0, 97,218, 0, 98,217, 0,101,214, 0,104,210, 0, + 108,205, 0,111,201, 0,114,196, 0,117,191, 0,117,191, 0, + 117,192, 0,117,191, 0,118,188, 0,120,182, 0,122,175, 0, + 123,170, 3,123,168, 5,124,162, 10,124,149, 24,124,149, 24, + 124,149, 24,124,162, 10,123,171, 3,122,175, 0,122,176, 0, + 118,188, 0,113,198, 0,108,206, 0,103,212, 0, 99,215, 0, + 97,217, 0, 97,218, 0, 98,216, 0,101,214, 0,102,212, 0, + 103,212, 0,103,212, 0, 99,215, 0, 96,219, 0, 94,220, 0, + 94,221, 0, 94,220, 0, 93,222, 0, 89,225, 0, 85,228, 0, + 83,230, 0, 83,230, 0, 85,229, 0, 88,226, 0, 92,222, 0, + 97,217, 0,101,214, 0,100,215, 0, 98,217, 0, 97,218, 0, + 97,218, 0, 97,217, 0, 99,215, 0,103,212, 0,108,206, 0, + 113,198, 0,112,200, 0,110,202, 0,110,203, 0,111,202, 0, + 113,199, 0,115,194, 0,118,188, 0,120,183, 0,122,178, 0, + 123,171, 3,124,165, 8,124,158, 13,124,150, 22,124,143, 31, + 123,137, 41,122,130, 53,120,122, 70,118,117, 85,123,139, 37, + 124,152, 20,124,160, 12,124,164, 9,124,166, 7,124,165, 8, + 124,161, 11,124,152, 20,123,139, 37,120,122, 70,111, 96,248, + 109, 92,251,115,107,129,118,117, 85,118,117, 85,119,119, 77, + 123,137, 41,124,157, 15,123,172, 2,120,183, 0,117,191, 0, + 114,197, 0,111,201, 0,111,201, 0,113,199, 0,116,193, 0, + 120,184, 0,123,172, 2,124,160, 12,124,149, 24,124,143, 31, + 123,139, 37,122,135, 44,122,135, 44,123,141, 34,123,139, 37, + 122,130, 53,121,125, 63,124,149, 24,124,161, 11,124,167, 6, + 124,167, 6,124,164, 9,124,155, 16,124,162, 10,123,168, 5, + 123,172, 2,122,177, 0,121,180, 0,120,184, 0,119,187, 0, + 118,189, 0,117,191, 0,116,193, 0,116,193, 0,116,192, 0, + 117,191, 0,118,188, 0,120,184, 0,120,183, 0,121,182, 0, + 121,181, 0,119,187, 0,117,192, 0,115,194, 0,114,196, 0, + 114,196, 0,115,194, 0,116,192, 0,118,189, 0,119,187, 0, + 120,183, 0,122,178, 0,123,173, 1,124,167, 6,124,162, 10, + 124,158, 13,124,150, 22,124,160, 12,123,168, 5,122,176, 0, + 120,184, 0,117,191, 0,114,196, 0,113,198, 0,111,201, 0, + 109,204, 0,107,207, 0,106,207, 0,107,207, 0,108,205, 0, + 109,203, 0,107,207, 0,101,214, 0, 96,219, 0, 92,222, 0, + 91,223, 0, 91,223, 0, 93,221, 0, 91,223, 0, 90,225, 0, + 89,225, 0, 90,225, 0, 91,224, 0, 91,223, 0, 92,222, 0, + 92,222, 0, 91,223, 0, 90,224, 0, 91,223, 0, 94,221, 0, + 98,217, 0,103,212, 0,108,205, 0,112,199, 0,115,195, 0, + 115,194, 0,113,198, 0,110,202, 0,108,205, 0,108,205, 0, + 109,203, 0,113,199, 0,114,196, 0,114,196, 0,112,200, 0, + 109,203, 0,106,207, 0,104,210, 0,102,212, 0,103,212, 0, + 105,209, 0,109,204, 0,109,203, 0,112,200, 0,116,193, 0, + 120,184, 0,123,173, 1,124,161, 11,123,139, 37,123,139, 37, + 123,141, 34,124,147, 26,124,152, 20,124,162, 10,121,179, 0, + 118,189, 0,114,197, 0,110,202, 0,106,207, 0,104,210, 0, + 104,210, 0,106,208, 0,109,203, 0,114,197, 0,114,196, 0, + 115,195, 0,114,196, 0,112,200, 0,110,203, 0,109,204, 0, + 109,204, 0,109,204, 0,109,203, 0,108,205, 0,108,205, 0, + 108,205, 0,109,203, 0,111,202, 0,109,204, 0,107,207, 0, + 106,208, 0,106,207, 0,106,208, 0,103,211, 0,100,215, 0, + 97,218, 0, 95,220, 0, 95,220, 0, 96,219, 0, 99,215, 0, + 103,211, 0,108,206, 0,112,200, 0,115,194, 0,117,191, 0, + 116,192, 0,115,194, 0,116,193, 0,117,191, 0,119,186, 0, + 121,180, 0,123,174, 1,122,175, 0,123,171, 3,124,161, 11, + 124,157, 15,124,157, 15,124,150, 22,124,157, 15,124,164, 9, + 124,166, 7,120,183, 0,115,195, 0,110,203, 0,105,209, 0, + 102,213, 0,100,215, 0, 99,215, 0,100,214, 0,102,213, 0, + 102,212, 0,102,212, 0, 99,215, 0, 95,219, 0, 92,222, 0, + 91,223, 0, 91,224, 0, 91,223, 0, 92,223, 0, 90,224, 0, + 88,226, 0, 86,228, 0, 86,228, 0, 87,227, 0, 90,225, 0, + 93,221, 0, 97,218, 0, 99,215, 0, 98,217, 0, 97,218, 0, + 95,219, 0, 95,219, 0, 97,218, 0, 99,216, 0,102,212, 0, + 107,207, 0,113,199, 0,114,196, 0,113,198, 0,112,199, 0, + 113,198, 0,114,196, 0,117,191, 0,120,184, 0,122,177, 0, + 123,171, 3,123,170, 3,124,167, 6,124,161, 11,124,152, 20, + 123,141, 34,121,127, 58,118,117, 85,113,100,242,118,117, 85, + 123,141, 34,124,155, 16,124,165, 8,123,171, 3,123,173, 1, + 123,171, 3,124,166, 7,124,155, 16,123,137, 41,117,110,109, + 99, 75,247,107, 89,252,115,107,129,118,114, 96,118,117, 85, + 117,110,109,118,114, 96,123,141, 34,124,161, 11,123,173, 1, + 120,183, 0,118,189, 0,116,192, 0,116,192, 0,118,189, 0, + 121,182, 0,123,169, 4,124,149, 24,123,137, 41,118,114, 96, + 115,107,129,115,107,129,121,127, 58,123,141, 34,124,147, 26, + 124,145, 29,123,139, 37,121,127, 58,124,145, 29,124,155, 16, + 124,160, 12,124,160, 12,124,157, 15,124,152, 20,124,164, 9, + 123,169, 4,123,174, 1,122,177, 0,121,179, 0,121,182, 0, + 120,184, 0,119,186, 0,118,189, 0,117,192, 0,116,193, 0, + 117,192, 0,118,189, 0,120,184, 0,122,178, 0,123,174, 1, + 123,171, 3,122,178, 0,120,184, 0,118,188, 0,117,190, 0, + 117,191, 0,118,189, 0,118,188, 0,119,187, 0,120,184, 0, + 121,179, 0,123,174, 1,124,166, 7,124,160, 12,124,155, 16, + 124,149, 24,124,143, 31,124,147, 26,124,160, 12,123,169, 4, + 122,176, 0,121,182, 0,118,189, 0,114,196, 0,112,200, 0, + 110,202, 0,110,203, 0,108,205, 0,107,207, 0,106,208, 0, + 107,207, 0,108,205, 0,107,207, 0,101,214, 0, 96,219, 0, + 93,221, 0, 92,223, 0, 92,222, 0, 94,221, 0, 91,223, 0, + 90,224, 0, 90,224, 0, 91,223, 0, 92,222, 0, 94,221, 0, + 95,220, 0, 95,220, 0, 94,221, 0, 93,222, 0, 94,221, 0, + 97,218, 0,101,214, 0,105,209, 0,110,202, 0,113,198, 0, + 114,197, 0,112,200, 0,109,204, 0,107,207, 0,105,209, 0, + 105,209, 0,107,206, 0,111,202, 0,115,195, 0,114,196, 0, + 113,199, 0,110,202, 0,107,207, 0,105,209, 0,103,211, 0, + 103,211, 0,106,208, 0,110,203, 0,111,201, 0,114,197, 0, + 117,190, 0,121,179, 0,123,168, 5,124,155, 16,122,132, 48, + 122,130, 53,121,127, 58,119,119, 77,122,135, 44,124,165, 8, + 121,182, 0,117,191, 0,113,199, 0,109,204, 0,106,208, 0, + 103,211, 0,103,211, 0,105,209, 0,109,204, 0,113,198, 0, + 114,197, 0,114,196, 0,112,200, 0,109,204, 0,107,207, 0, + 106,208, 0,106,208, 0,106,207, 0,107,206, 0,109,204, 0, + 109,203, 0,110,202, 0,111,202, 0,109,203, 0,107,207, 0, + 105,209, 0,106,208, 0,106,207, 0,107,207, 0,105,209, 0, + 101,214, 0, 98,217, 0, 95,219, 0, 95,220, 0, 96,219, 0, + 100,215, 0,104,210, 0,109,204, 0,113,198, 0,117,192, 0, + 118,188, 0,117,191, 0,116,193, 0,116,193, 0,117,191, 0, + 118,188, 0,120,182, 0,122,178, 0,121,179, 0,122,177, 0, + 123,170, 3,124,162, 10,124,164, 9,124,161, 11,124,150, 22, + 124,149, 24,124,152, 20,123,173, 1,118,188, 0,114,197, 0, + 109,204, 0,106,208, 0,104,210, 0,103,211, 0,103,211, 0, + 104,210, 0,104,210, 0,101,214, 0, 97,218, 0, 93,222, 0, + 90,225, 0, 88,226, 0, 88,226, 0, 89,225, 0, 91,223, 0, + 89,226, 0, 87,227, 0, 87,227, 0, 87,227, 0, 89,225, 0, + 92,223, 0, 94,220, 0, 97,218, 0, 99,216, 0, 97,217, 0, + 96,219, 0, 96,219, 0, 97,218, 0, 98,217, 0,101,214, 0, + 104,210, 0,108,205, 0,113,198, 0,117,191, 0,116,193, 0, + 115,194, 0,116,193, 0,117,191, 0,119,185, 0,122,177, 0, + 123,168, 5,123,170, 3,123,170, 3,124,167, 6,124,161, 11, + 124,149, 24,122,132, 48,117,110,109,107, 89,252, 94, 70,242, + 117,110,109,123,137, 41,124,155, 16,124,166, 7,123,173, 1, + 122,176, 0,123,174, 1,123,168, 5,124,155, 16,122,132, 48, + 111, 96,248,102, 80,250,111, 96,248,117,110,109,118,114, 96, + 118,117, 85,117,110,109,117,110,109,117,110,109,123,137, 41, + 124,155, 16,123,168, 5,122,177, 0,121,180, 0,121,180, 0, + 122,175, 0,124,164, 9,123,141, 34,119,119, 77,113,100,242, + 102, 80,250,102, 80,250,115,107,129,121,127, 58,123,141, 34, + 124,147, 26,124,145, 29,123,137, 41,122,132, 48,124,143, 31, + 124,149, 24,124,152, 20,124,150, 22,124,147, 26,124,145, 29, + 124,160, 12,124,167, 6,123,170, 3,123,172, 2,123,173, 1, + 123,174, 1,122,176, 0,121,179, 0,120,184, 0,118,188, 0, + 118,189, 0,118,188, 0,120,184, 0,122,178, 0,123,168, 5, + 124,160, 12,124,166, 7,123,173, 1,122,178, 0,121,182, 0, + 120,182, 0,121,182, 0,121,180, 0,122,178, 0,122,178, 0, + 123,174, 1,123,168, 5,124,160, 12,124,145, 29,123,137, 41, + 122,132, 48,120,122, 70,120,122, 70,123,141, 34,124,157, 15, + 123,168, 5,122,177, 0,121,182, 0,119,186, 0,115,194, 0, + 112,200, 0,110,203, 0,109,204, 0,109,204, 0,107,206, 0, + 106,208, 0,106,208, 0,107,207, 0,107,207, 0,101,214, 0, + 97,218, 0, 94,221, 0, 92,222, 0, 93,222, 0, 94,221, 0, + 92,223, 0, 91,223, 0, 92,222, 0, 94,221, 0, 96,219, 0, + 98,217, 0, 99,215, 0,100,215, 0, 98,216, 0, 97,217, 0, + 98,217, 0,100,214, 0,104,210, 0,108,205, 0,113,199, 0, + 112,200, 0,110,203, 0,108,205, 0,106,208, 0,104,210, 0, + 103,212, 0,103,211, 0,106,208, 0,110,203, 0,115,195, 0, + 114,196, 0,112,199, 0,111,201, 0,108,205, 0,106,208, 0, + 104,210, 0,105,209, 0,107,206, 0,111,201, 0,113,199, 0, + 115,194, 0,119,187, 0,123,174, 1,124,160, 12,124,143, 31, + 118,117, 85,117,110,109,117,110,109,119,119, 77,124,145, 29, + 124,166, 7,121,182, 0,117,191, 0,113,198, 0,109,203, 0, + 106,208, 0,103,211, 0,103,211, 0,106,208, 0,109,203, 0, + 114,197, 0,114,196, 0,114,196, 0,110,202, 0,107,207, 0, + 105,209, 0,104,210, 0,105,209, 0,106,208, 0,107,206, 0, + 109,204, 0,112,200, 0,112,199, 0,110,202, 0,107,206, 0, + 105,209, 0,105,209, 0,106,207, 0,107,207, 0,108,206, 0, + 106,208, 0,102,212, 0,100,215, 0, 97,217, 0, 96,219, 0, + 98,217, 0,101,213, 0,106,207, 0,111,201, 0,113,198, 0, + 116,192, 0,120,184, 0,119,185, 0,118,188, 0,118,189, 0, + 118,189, 0,119,186, 0,121,182, 0,122,178, 0,121,181, 0, + 121,180, 0,122,175, 0,124,167, 6,123,169, 4,123,169, 4, + 124,164, 9,124,150, 22,123,139, 37,124,155, 16,122,178, 0, + 118,189, 0,114,196, 0,111,201, 0,109,203, 0,108,205, 0, + 108,205, 0,108,205, 0,105,209, 0,101,214, 0, 96,219, 0, + 92,223, 0, 89,225, 0, 87,227, 0, 87,227, 0, 88,226, 0, + 91,223, 0, 88,226, 0, 85,228, 0, 84,229, 0, 84,229, 0, + 85,228, 0, 89,226, 0, 93,222, 0, 97,218, 0, 98,216, 0, + 98,217, 0, 98,217, 0, 98,217, 0,100,215, 0,102,213, 0, + 104,210, 0,107,207, 0,111,202, 0,115,194, 0,119,186, 0, + 119,187, 0,118,188, 0,119,186, 0,120,182, 0,122,175, 0, + 124,164, 9,124,166, 7,123,169, 4,123,169, 4,124,165, 8, + 124,155, 16,123,137, 41,118,114, 96,102, 80,250, 30, 31,180, + 94, 70,242,118,114, 96,122,132, 48,124,150, 22,124,165, 8, + 123,173, 1,122,177, 0,122,175, 0,123,168, 5,124,154, 18, + 121,127, 58,113,100,242,105, 84,252,111, 96,248,115,107,129, + 117,110,109,119,119, 77,118,117, 85,113,100,242,111, 96,248, + 111, 96,248,119,119, 77,123,139, 37,124,154, 18,124,160, 12, + 124,160, 12,124,150, 22,123,137, 41,118,114, 96,115,107,129, + 115,107,129,109, 92,251, 99, 75,247,114,104,197,121,125, 63, + 123,137, 41,124,143, 31,123,139, 37,122,132, 48,123,137, 41, + 124,143, 31,124,145, 29,124,143, 31,123,137, 41,122,132, 48, + 122,130, 53,124,150, 22,124,160, 12,124,162, 10,124,162, 10, + 124,162, 10,124,164, 9,124,162, 10,124,167, 6,123,174, 1, + 121,179, 0,120,182, 0,121,182, 0,122,176, 0,124,166, 7, + 124,150, 22,123,139, 37,124,154, 18,124,164, 9,123,168, 5, + 123,170, 3,123,169, 4,124,166, 7,124,162, 10,124,161, 11, + 124,160, 12,124,155, 16,124,147, 26,122,135, 44,118,114, 96, + 109, 92,251,105, 84,252,102, 80,250,113,100,242,121,127, 58, + 124,150, 22,124,166, 7,122,176, 0,121,182, 0,119,185, 0, + 117,192, 0,113,199, 0,110,203, 0,108,205, 0,108,205, 0, + 108,205, 0,107,207, 0,106,207, 0,107,206, 0,106,208, 0, + 100,214, 0, 96,219, 0, 93,222, 0, 92,222, 0, 93,222, 0, + 95,220, 0, 94,221, 0, 94,221, 0, 95,219, 0, 98,217, 0, + 101,214, 0,103,212, 0,105,209, 0,105,209, 0,104,210, 0, + 102,212, 0,103,212, 0,105,209, 0,107,206, 0,111,202, 0, + 111,201, 0,109,204, 0,107,207, 0,106,208, 0,104,210, 0, + 102,212, 0,102,212, 0,103,212, 0,106,208, 0,110,203, 0, + 114,196, 0,113,199, 0,110,202, 0,109,204, 0,109,204, 0, + 107,207, 0,106,208, 0,106,208, 0,109,204, 0,112,200, 0, + 113,198, 0,116,193, 0,120,184, 0,123,170, 3,124,145, 29, + 120,122, 70,105, 84,252, 94, 70,242,114,104,197,124,143, 31, + 124,164, 9,122,175, 0,121,181, 0,118,189, 0,114,197, 0, + 110,202, 0,107,207, 0,105,209, 0,104,210, 0,106,207, 0, + 110,203, 0,114,197, 0,116,193, 0,113,198, 0,109,204, 0, + 106,208, 0,104,210, 0,104,210, 0,105,209, 0,107,207, 0, + 109,204, 0,111,202, 0,113,198, 0,111,201, 0,108,205, 0, + 106,208, 0,105,209, 0,106,208, 0,108,206, 0,106,208, 0, + 104,210, 0,102,212, 0,102,213, 0,101,214, 0, 99,215, 0, + 98,216, 0,100,215, 0,104,210, 0,106,208, 0,107,207, 0, + 109,204, 0,112,199, 0,117,191, 0,121,182, 0,121,181, 0, + 120,183, 0,120,184, 0,120,182, 0,121,179, 0,122,177, 0, + 121,179, 0,121,179, 0,122,175, 0,123,171, 3,122,175, 0, + 122,176, 0,123,173, 1,124,166, 7,124,152, 20,122,132, 48, + 124,160, 12,122,177, 0,119,186, 0,116,192, 0,114,196, 0, + 114,197, 0,113,198, 0,111,202, 0,106,207, 0,101,213, 0, + 97,218, 0, 92,222, 0, 90,225, 0, 88,226, 0, 88,226, 0, + 89,225, 0, 91,223, 0, 89,226, 0, 85,228, 0, 83,230, 0, + 82,231, 0, 83,230, 0, 86,228, 0, 91,224, 0, 96,219, 0, + 97,217, 0,100,215, 0,100,215, 0,101,214, 0,103,211, 0, + 106,208, 0,109,204, 0,111,201, 0,114,196, 0,118,189, 0, + 120,183, 0,120,182, 0,121,181, 0,122,178, 0,123,172, 2, + 124,161, 11,124,155, 16,124,162, 10,124,166, 7,124,165, 8, + 124,157, 15,123,141, 34,118,114, 96, 94, 70,242, 30, 26,168, + 105, 84,252,122,132, 48,124,154, 18,124,164, 9,124,166, 7, + 124,164, 9,123,172, 2,122,176, 0,123,174, 1,123,168, 5, + 124,154, 18,122,130, 53,115,107,129,107, 89,252, 99, 75,247, + 105, 84,252,115,107,129,119,119, 77,119,119, 77,115,107,129, + 99, 75,247, 30, 55,222, 30, 49,213,105, 84,252,120,122, 70, + 123,141, 34,124,149, 24,124,149, 24,123,139, 37,119,119, 77, + 117,110,109,115,107,129,118,114, 96,117,110,109,121,127, 58, + 123,139, 37,123,141, 34,123,137, 41,122,132, 48,123,137, 41, + 124,145, 29,124,147, 26,124,143, 31,124,145, 29,124,143, 31, + 122,130, 53,115,107,129,122,135, 44,124,145, 29,124,149, 24, + 124,145, 29,124,152, 20,124,157, 15,124,155, 16,124,143, 31, + 124,155, 16,124,166, 7,123,170, 3,123,169, 4,124,162, 10, + 124,147, 26,121,127, 58,111, 96,248,120,122, 70,123,137, 41, + 124,145, 29,124,145, 29,123,141, 34,122,132, 48,121,125, 63, + 120,122, 70,120,122, 70,119,119, 77,117,110,109,111, 96,248, + 94, 70,242, 30, 37,192, 30, 20,155, 30, 20,155, 30, 55,222, + 113,100,242,123,139, 37,124,161, 11,123,173, 1,121,181, 0, + 119,185, 0,118,188, 0,114,196, 0,111,201, 0,109,204, 0, + 108,205, 0,108,205, 0,108,205, 0,108,205, 0,109,203, 0, + 103,211, 0, 98,217, 0, 94,221, 0, 91,223, 0, 91,224, 0, + 91,223, 0, 94,221, 0, 97,218, 0, 97,217, 0, 99,215, 0, + 101,213, 0,102,212, 0,103,211, 0,103,211, 0,105,209, 0, + 109,204, 0,107,206, 0,107,207, 0,108,206, 0,108,206, 0, + 108,206, 0,108,205, 0,108,206, 0,107,207, 0,106,208, 0, + 104,210, 0,103,212, 0,102,212, 0,103,211, 0,106,208, 0, + 110,203, 0,114,196, 0,111,201, 0,109,204, 0,107,206, 0, + 107,206, 0,107,206, 0,107,207, 0,107,206, 0,111,202, 0, + 111,201, 0,113,198, 0,116,193, 0,120,184, 0,123,170, 3, + 124,143, 31,109, 92,251, 30, 20,155, 30, 49,213,118,117, 85, + 124,152, 20,123,172, 2,120,183, 0,118,189, 0,116,193, 0, + 115,195, 0,111,201, 0,108,206, 0,105,209, 0,105,209, 0, + 106,207, 0,110,203, 0,114,197, 0,118,189, 0,113,198, 0, + 109,204, 0,106,207, 0,105,209, 0,106,208, 0,107,206, 0, + 109,203, 0,111,202, 0,110,202, 0,109,204, 0,108,206, 0, + 107,207, 0,106,208, 0,106,208, 0,107,207, 0,109,203, 0, + 105,209, 0,101,214, 0, 98,216, 0, 98,217, 0, 98,217, 0, + 99,215, 0,100,215, 0,101,213, 0,101,213, 0,101,214, 0, + 102,212, 0,105,209, 0,109,204, 0,114,196, 0,119,185, 0, + 123,169, 4,123,174, 1,122,176, 0,122,175, 0,123,174, 1, + 123,171, 3,123,173, 1,123,173, 1,123,169, 4,123,174, 1, + 121,179, 0,121,181, 0,121,180, 0,122,175, 0,124,165, 8, + 124,150, 22,122,132, 48,124,162, 10,122,175, 0,120,183, 0, + 119,187, 0,118,189, 0,116,193, 0,113,199, 0,109,204, 0, + 103,211, 0, 99,216, 0, 95,220, 0, 92,222, 0, 91,223, 0, + 91,223, 0, 92,222, 0, 94,220, 0, 91,224, 0, 87,227, 0, + 84,229, 0, 82,231, 0, 83,230, 0, 85,229, 0, 89,225, 0, + 95,220, 0, 95,219, 0, 98,217, 0,101,213, 0,104,210, 0, + 107,207, 0,110,202, 0,113,199, 0,116,193, 0,116,193, 0, + 117,192, 0,117,191, 0,118,189, 0,119,185, 0,121,179, 0, + 123,168, 5,124,147, 26,124,150, 22,124,157, 15,124,158, 13, + 124,154, 18,123,141, 34,117,110,109, 30, 55,222, 30, 5,128, + 102, 80,250,122,135, 44,124,161, 11,123,173, 1,121,179, 0, + 121,180, 0,122,178, 0,123,172, 2,122,176, 0,122,175, 0, + 123,170, 3,124,160, 12,123,139, 37,115,107,129, 99, 75,247, + 30, 20,155, 99, 75,247,115,107,129,120,122, 70,120,122, 70, + 118,114, 96,113,100,242,102, 80,250, 30, 49,213, 99, 75,247, + 118,117, 85,123,141, 34,124,152, 20,124,154, 18,124,149, 24, + 122,135, 44,115,107,129,122,135, 44,123,141, 34,124,143, 31, + 124,150, 22,124,154, 18,124,152, 20,124,143, 31,122,130, 53, + 124,147, 26,124,152, 20,124,152, 20,124,147, 26,124,150, 22, + 124,150, 22,123,139, 37,118,117, 85,114,104,197,118,117, 85, + 118,117, 85,122,130, 53,123,139, 37,124,143, 31,123,141, 34, + 121,125, 63,118,117, 85,122,135, 44,124,145, 29,124,154, 18, + 124,157, 15,124,152, 20,123,139, 37,118,117, 85,107, 89,252, + 109, 92,251,105, 84,252,102, 80,250, 94, 70,242,107, 89,252, + 111, 96,248,109, 92,251, 99, 75,247, 30, 43,203, 30, 43,203, + 30, 31,180, 30, 43,203, 87, 65,237,107, 89,252,114,104,197, + 115,107,129,117,110,109,122,130, 53,124,155, 16,123,172, 2, + 121,182, 0,119,187, 0,117,191, 0,116,193, 0,113,198, 0, + 111,202, 0,109,204, 0,108,205, 0,108,205, 0,108,205, 0, + 108,206, 0,102,213, 0, 96,219, 0, 92,222, 0, 89,225, 0, + 89,226, 0, 89,225, 0, 92,223, 0, 95,220, 0, 97,217, 0, + 99,216, 0,100,214, 0,100,214, 0,100,215, 0,100,215, 0, + 102,213, 0,105,209, 0,108,205, 0,108,206, 0,107,207, 0, + 106,208, 0,106,208, 0,105,209, 0,105,209, 0,104,210, 0, + 104,210, 0,105,209, 0,104,210, 0,103,212, 0,103,211, 0, + 105,209, 0,109,204, 0,113,199, 0,111,201, 0,108,205, 0, + 107,207, 0,106,207, 0,107,207, 0,107,206, 0,108,205, 0, + 109,204, 0,110,203, 0,112,200, 0,115,195, 0,119,187, 0, + 123,174, 1,124,150, 22,118,114, 96,111, 96,248,109, 92,251, + 120,122, 70,124,157, 15,122,177, 0,118,188, 0,115,195, 0, + 112,199, 0,111,201, 0,111,201, 0,109,203, 0,107,207, 0, + 106,208, 0,107,207, 0,110,202, 0,114,196, 0,116,192, 0, + 114,197, 0,110,203, 0,107,206, 0,107,207, 0,108,205, 0, + 110,202, 0,108,205, 0,107,207, 0,106,207, 0,105,209, 0, + 104,210, 0,103,211, 0,104,210, 0,105,209, 0,106,208, 0, + 106,208, 0,103,212, 0, 99,216, 0, 96,219, 0, 94,220, 0, + 94,221, 0, 94,220, 0, 95,219, 0, 96,219, 0, 97,218, 0, + 97,217, 0, 99,216, 0,102,212, 0,107,207, 0,112,199, 0, + 118,189, 0,122,175, 0,124,162, 10,124,164, 9,124,165, 8, + 124,166, 7,124,165, 8,124,165, 8,124,166, 7,123,169, 4, + 122,175, 0,121,180, 0,120,182, 0,121,182, 0,122,178, 0, + 123,169, 4,124,157, 15,124,143, 31,124,150, 22,124,165, 8, + 123,174, 1,121,181, 0,119,186, 0,115,194, 0,110,202, 0, + 106,208, 0,103,212, 0,100,215, 0, 97,217, 0, 95,220, 0, + 93,222, 0, 91,223, 0, 91,223, 0, 91,223, 0, 88,226, 0, + 87,227, 0, 87,227, 0, 84,229, 0, 84,229, 0, 85,228, 0, + 89,225, 0, 93,221, 0, 94,221, 0, 97,218, 0,100,215, 0, + 104,210, 0,109,204, 0,111,201, 0,112,200, 0,113,199, 0, + 113,198, 0,114,197, 0,115,195, 0,116,192, 0,118,188, 0, + 121,182, 0,123,170, 3,124,161, 11,124,160, 12,124,157, 15, + 124,150, 22,122,135, 44,114,104,197, 30, 55,222, 30, 37,192, + 113,100,242,123,139, 37,124,164, 9,122,178, 0,119,186, 0, + 118,189, 0,118,189, 0,118,188, 0,120,184, 0,122,178, 0, + 122,177, 0,123,173, 1,124,165, 8,124,152, 20,122,130, 53, + 114,104,197,102, 80,250,105, 84,252,117,110,109,122,130, 53, + 123,139, 37,123,141, 34,122,135, 44,121,125, 63,118,114, 96, + 111, 96,248,118,117, 85,124,143, 31,124,157, 15,124,162, 10, + 124,161, 11,124,155, 16,124,157, 15,124,161, 11,124,166, 7, + 123,169, 4,123,170, 3,123,169, 4,124,165, 8,124,155, 16, + 123,137, 41,124,150, 22,124,157, 15,124,155, 16,124,150, 22, + 124,155, 16,124,154, 18,124,145, 29,122,130, 53,109, 92,251, + 102, 80,250,113,100,242,118,117, 85,121,125, 63,122,130, 53, + 121,125, 63,114,104,197, 30, 49,213, 99, 75,247,120,122, 70, + 124,147, 26,124,157, 15,124,157, 15,124,150, 22,122,135, 44, + 118,117, 85,113,100,242,107, 89,252,102, 80,250,102, 80,250, + 105, 84,252,102, 80,250, 94, 70,242, 30, 49,213, 30, 31,180, + 30, 43,203, 99, 75,247,111, 96,248,117,110,109,120,122, 70, + 122,132, 48,123,137, 41,121,127, 58,124,143, 31,124,155, 16, + 123,172, 2,120,184, 0,117,190, 0,115,194, 0,114,197, 0, + 113,199, 0,111,201, 0,110,202, 0,108,205, 0,108,206, 0, + 107,206, 0,107,206, 0,102,212, 0, 97,217, 0, 93,222, 0, + 90,225, 0, 88,226, 0, 89,226, 0, 91,224, 0, 93,222, 0, + 95,219, 0, 97,217, 0, 98,217, 0, 97,218, 0, 97,218, 0, + 97,217, 0,100,215, 0,103,211, 0,107,206, 0,108,206, 0, + 107,207, 0,106,208, 0,105,209, 0,105,209, 0,104,210, 0, + 103,212, 0,102,212, 0,104,210, 0,105,209, 0,103,211, 0, + 103,211, 0,105,209, 0,107,206, 0,110,202, 0,110,202, 0, + 110,203, 0,108,206, 0,106,207, 0,106,208, 0,106,208, 0, + 106,207, 0,107,207, 0,108,205, 0,110,203, 0,113,198, 0, + 117,191, 0,121,179, 0,124,160, 12,124,145, 29,122,132, 48, + 120,122, 70,123,137, 41,124,160, 12,121,181, 0,116,192, 0, + 112,200, 0,109,204, 0,107,207, 0,108,206, 0,110,202, 0, + 109,203, 0,108,205, 0,109,204, 0,112,200, 0,115,194, 0, + 115,194, 0,113,198, 0,110,202, 0,109,204, 0,109,204, 0, + 110,202, 0,107,206, 0,104,210, 0,102,212, 0,102,212, 0, + 101,213, 0,101,214, 0,101,214, 0,101,213, 0,102,212, 0, + 102,212, 0,102,212, 0,101,213, 0, 98,217, 0, 94,221, 0, + 92,223, 0, 90,224, 0, 90,224, 0, 91,224, 0, 92,222, 0, + 93,221, 0, 95,220, 0, 98,217, 0,101,213, 0,106,207, 0, + 112,200, 0,117,190, 0,122,178, 0,124,164, 9,124,147, 26, + 124,149, 24,124,152, 20,124,157, 15,124,161, 11,124,161, 11, + 124,165, 8,123,171, 3,122,176, 0,122,178, 0,122,178, 0, + 123,174, 1,124,167, 6,124,155, 16,124,145, 29,124,157, 15, + 124,167, 6,122,175, 0,121,182, 0,119,186, 0,114,196, 0, + 109,204, 0,105,209, 0,101,214, 0, 98,217, 0, 95,220, 0, + 92,223, 0, 90,225, 0, 88,226, 0, 87,227, 0, 88,226, 0, + 85,229, 0, 85,229, 0, 86,228, 0, 87,227, 0, 86,228, 0, + 87,227, 0, 89,225, 0, 93,221, 0, 94,221, 0, 96,219, 0, + 99,215, 0,103,211, 0,108,205, 0,111,201, 0,111,201, 0, + 112,200, 0,112,199, 0,113,198, 0,114,197, 0,116,193, 0, + 118,189, 0,121,182, 0,123,172, 2,123,171, 3,123,169, 4, + 124,166, 7,124,155, 16,123,137, 41,113,100,242, 99, 75,247, + 121,127, 58,124,154, 18,123,169, 4,121,181, 0,118,189, 0, + 115,194, 0,113,198, 0,113,198, 0,114,196, 0,116,193, 0, + 118,189, 0,120,182, 0,123,173, 1,123,168, 5,124,161, 11, + 124,149, 24,122,135, 44,119,119, 77,118,117, 85,122,130, 53, + 124,145, 29,124,152, 20,124,155, 16,124,152, 20,124,149, 24, + 124,147, 26,124,143, 31,123,137, 41,124,157, 15,123,168, 5, + 122,175, 0,122,178, 0,121,181, 0,120,182, 0,120,184, 0, + 119,185, 0,119,186, 0,119,185, 0,120,182, 0,122,178, 0, + 123,168, 5,124,152, 20,124,147, 26,124,154, 18,124,155, 16, + 124,152, 20,124,157, 15,124,157, 15,124,150, 22,123,137, 41, + 118,114, 96,111, 96,248,107, 89,252,111, 96,248,115,107,129, + 115,107,129,114,104,197,113,100,242,105, 84,252, 87, 65,237, + 120,122, 70,124,149, 24,124,162, 10,124,167, 6,124,166, 7, + 124,160, 12,124,152, 20,124,145, 29,123,139, 37,122,135, 44, + 122,132, 48,122,130, 53,121,125, 63,118,117, 85,115,107,129, + 111, 96,248,114,104,197,118,117, 85,121,125, 63,122,132, 48, + 123,139, 37,124,147, 26,124,149, 24,124,143, 31,124,152, 20, + 124,161, 11,123,172, 2,120,184, 0,116,192, 0,113,198, 0, + 111,201, 0,110,202, 0,110,203, 0,109,203, 0,109,204, 0, + 108,206, 0,107,207, 0,107,206, 0,105,209, 0,101,214, 0, + 96,219, 0, 93,222, 0, 91,224, 0, 90,225, 0, 91,224, 0, + 92,222, 0, 95,220, 0, 97,218, 0, 96,219, 0, 95,219, 0, + 95,219, 0, 97,218, 0,100,215, 0,103,211, 0,107,206, 0, + 109,204, 0,108,206, 0,107,207, 0,106,207, 0,106,208, 0, + 105,209, 0,103,211, 0,103,211, 0,104,210, 0,106,207, 0, + 105,209, 0,103,211, 0,104,210, 0,106,208, 0,108,205, 0, + 109,204, 0,109,204, 0,109,204, 0,108,206, 0,106,207, 0, + 106,208, 0,106,208, 0,106,208, 0,107,207, 0,109,204, 0, + 112,200, 0,116,193, 0,120,183, 0,124,167, 6,124,158, 13, + 124,147, 26,122,132, 48,123,141, 34,124,161, 11,120,183, 0, + 115,195, 0,110,202, 0,106,207, 0,105,209, 0,105,209, 0, + 108,205, 0,111,201, 0,111,202, 0,111,201, 0,113,198, 0, + 116,193, 0,115,194, 0,113,198, 0,110,202, 0,110,203, 0, + 110,202, 0,109,203, 0,105,209, 0,101,214, 0, 99,215, 0, + 99,215, 0, 99,215, 0,100,215, 0,100,215, 0,100,214, 0, + 101,214, 0,100,214, 0, 99,215, 0, 98,217, 0, 97,218, 0, + 93,221, 0, 90,225, 0, 88,226, 0, 87,227, 0, 87,227, 0, + 89,225, 0, 92,223, 0, 95,220, 0, 99,216, 0,103,211, 0, + 108,205, 0,114,197, 0,118,188, 0,122,178, 0,124,166, 7, + 124,149, 24,124,150, 22,124,152, 20,124,157, 15,124,158, 13, + 124,160, 12,124,158, 13,124,164, 9,123,168, 5,123,170, 3, + 123,170, 3,124,166, 7,124,160, 12,124,149, 24,124,152, 20, + 124,161, 11,123,170, 3,122,178, 0,120,183, 0,119,187, 0, + 114,196, 0,109,204, 0,104,210, 0,100,214, 0, 96,219, 0, + 93,222, 0, 90,225, 0, 87,227, 0, 86,228, 0, 85,229, 0, + 85,228, 0, 83,230, 0, 83,230, 0, 85,229, 0, 88,226, 0, + 88,226, 0, 88,226, 0, 91,224, 0, 95,220, 0, 95,220, 0, + 97,218, 0, 99,215, 0,103,211, 0,107,206, 0,111,201, 0, + 113,199, 0,113,198, 0,113,198, 0,114,197, 0,114,196, 0, + 116,193, 0,118,188, 0,121,179, 0,122,177, 0,122,176, 0, + 122,175, 0,123,170, 3,124,160, 12,123,137, 41,111, 96,248, + 122,135, 44,124,164, 9,122,176, 0,120,184, 0,117,191, 0, + 114,197, 0,111,201, 0,109,203, 0,109,204, 0,110,203, 0, + 111,201, 0,114,197, 0,117,192, 0,120,183, 0,123,170, 3, + 124,165, 8,124,158, 13,124,149, 24,124,149, 24,124,147, 26, + 124,145, 29,124,152, 20,124,158, 13,124,161, 11,124,162, 10, + 124,162, 10,124,166, 7,124,166, 7,124,161, 11,123,171, 3, + 121,181, 0,118,188, 0,116,192, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,114,196, 0,115,195, 0,117,192, 0, + 119,187, 0,121,179, 0,124,166, 7,124,145, 29,124,150, 22, + 124,154, 18,124,152, 20,124,155, 16,124,155, 16,124,152, 20, + 123,141, 34,121,125, 63,118,117, 85,117,110,109,118,117, 85, + 121,127, 58,122,135, 44,123,141, 34,124,143, 31,123,141, 34, + 122,130, 53,123,137, 41,124,160, 12,123,172, 2,122,178, 0, + 121,180, 0,122,178, 0,122,175, 0,123,172, 2,123,170, 3, + 124,167, 6,124,165, 8,124,160, 12,124,155, 16,124,147, 26, + 123,137, 41,122,135, 44,123,137, 41,123,139, 37,123,141, 34, + 124,143, 31,124,145, 29,124,150, 22,124,155, 16,124,152, 20, + 124,157, 15,124,162, 10,123,170, 3,120,184, 0,116,193, 0, + 112,199, 0,110,202, 0,109,204, 0,108,205, 0,108,205, 0, + 109,204, 0,108,206, 0,107,206, 0,108,206, 0,108,205, 0, + 105,209, 0,101,214, 0, 97,217, 0, 95,220, 0, 93,221, 0, + 93,222, 0, 94,220, 0, 96,219, 0, 97,218, 0, 96,219, 0, + 96,219, 0, 97,218, 0, 98,216, 0,101,213, 0,105,209, 0, + 109,204, 0,110,202, 0,110,203, 0,109,204, 0,109,204, 0, + 108,205, 0,107,206, 0,106,208, 0,105,209, 0,105,209, 0, + 107,206, 0,106,208, 0,105,209, 0,104,210, 0,105,209, 0, + 108,205, 0,108,205, 0,108,205, 0,109,204, 0,110,203, 0, + 108,205, 0,107,207, 0,106,208, 0,106,207, 0,107,207, 0, + 108,205, 0,111,201, 0,115,195, 0,119,185, 0,123,171, 3, + 124,162, 10,124,149, 24,122,130, 53,123,137, 41,124,164, 9, + 120,184, 0,114,196, 0,110,203, 0,106,207, 0,105,209, 0, + 105,209, 0,108,206, 0,111,202, 0,113,198, 0,113,198, 0, + 115,195, 0,117,192, 0,116,193, 0,113,198, 0,111,202, 0, + 110,202, 0,111,201, 0,108,205, 0,103,211, 0,100,215, 0, + 98,217, 0, 98,216, 0, 99,215, 0,100,214, 0,101,214, 0, + 101,213, 0,101,214, 0,100,215, 0, 97,217, 0, 95,219, 0, + 95,220, 0, 93,221, 0, 90,225, 0, 87,227, 0, 86,228, 0, + 87,227, 0, 89,225, 0, 93,221, 0, 98,217, 0,102,212, 0, + 108,206, 0,112,199, 0,117,192, 0,120,184, 0,122,175, 0, + 124,165, 8,124,149, 24,124,150, 22,124,154, 18,124,155, 16, + 124,158, 13,124,158, 13,124,158, 13,124,157, 15,124,157, 15, + 124,158, 13,124,158, 13,124,154, 18,124,149, 24,124,154, 18, + 124,158, 13,124,165, 8,123,172, 2,121,179, 0,120,184, 0, + 118,189, 0,116,193, 0,110,202, 0,105,209, 0,100,214, 0, + 96,219, 0, 92,222, 0, 89,225, 0, 87,227, 0, 85,229, 0, + 85,229, 0, 85,229, 0, 84,229, 0, 84,229, 0, 86,228, 0, + 89,225, 0, 90,224, 0, 90,225, 0, 92,223, 0, 96,219, 0, + 97,218, 0, 98,217, 0,100,215, 0,103,211, 0,107,207, 0, + 111,202, 0,114,197, 0,115,194, 0,115,194, 0,115,194, 0, + 116,193, 0,117,191, 0,119,186, 0,122,177, 0,122,177, 0, + 122,178, 0,122,176, 0,123,170, 3,124,160, 12,123,137, 41, + 122,130, 53,124,162, 10,121,179, 0,118,188, 0,116,192, 0, + 114,197, 0,111,202, 0,108,205, 0,107,207, 0,106,207, 0, + 106,207, 0,107,206, 0,110,203, 0,113,198, 0,117,191, 0, + 122,178, 0,124,166, 7,124,161, 11,124,160, 12,124,161, 11, + 124,160, 12,124,157, 15,124,154, 18,124,160, 12,124,164, 9, + 124,167, 6,123,170, 3,122,176, 0,121,179, 0,122,178, 0, + 121,182, 0,117,190, 0,114,197, 0,111,201, 0,109,204, 0, + 108,205, 0,108,205, 0,108,205, 0,109,204, 0,110,202, 0, + 113,199, 0,115,194, 0,119,187, 0,122,176, 0,124,157, 15, + 124,145, 29,124,154, 18,124,154, 18,124,149, 24,124,150, 22, + 124,150, 22,123,139, 37,122,130, 53,121,127, 58,123,139, 37, + 124,145, 29,124,152, 20,124,155, 16,124,158, 13,124,161, 11, + 124,162, 10,124,160, 12,124,152, 20,124,166, 7,122,177, 0, + 120,183, 0,119,186, 0,119,186, 0,119,185, 0,120,184, 0, + 120,184, 0,120,182, 0,121,179, 0,122,176, 0,123,170, 3, + 124,162, 10,124,160, 12,124,160, 12,124,160, 12,124,155, 16, + 124,152, 20,124,152, 20,124,155, 16,124,157, 15,124,157, 15, + 124,157, 15,124,155, 16,124,158, 13,124,167, 6,120,182, 0, + 116,192, 0,112,199, 0,110,203, 0,108,205, 0,108,206, 0, + 108,206, 0,108,205, 0,108,205, 0,108,206, 0,109,204, 0, + 109,204, 0,109,204, 0,107,207, 0,104,210, 0,101,214, 0, + 98,216, 0, 98,217, 0, 98,216, 0, 99,215, 0, 99,216, 0, + 98,217, 0, 98,217, 0, 99,215, 0,101,213, 0,105,209, 0, + 109,204, 0,112,200, 0,112,200, 0,112,200, 0,112,200, 0, + 112,200, 0,112,200, 0,111,201, 0,109,203, 0,108,206, 0, + 107,206, 0,108,205, 0,108,205, 0,107,207, 0,106,208, 0, + 106,208, 0,108,206, 0,109,204, 0,109,204, 0,109,203, 0, + 110,202, 0,111,202, 0,109,204, 0,108,205, 0,108,205, 0, + 109,204, 0,110,203, 0,112,200, 0,115,194, 0,119,185, 0, + 123,170, 3,124,158, 13,123,141, 34,118,117, 85,120,122, 70, + 124,165, 8,120,184, 0,115,194, 0,111,201, 0,108,205, 0, + 107,207, 0,107,207, 0,109,203, 0,113,199, 0,114,196, 0, + 114,196, 0,116,192, 0,118,188, 0,117,191, 0,114,197, 0, + 111,201, 0,111,201, 0,112,200, 0,109,204, 0,104,210, 0, + 101,214, 0,100,215, 0,100,214, 0,102,213, 0,103,211, 0, + 104,210, 0,104,210, 0,103,211, 0,101,214, 0, 97,218, 0, + 94,220, 0, 93,221, 0, 94,221, 0, 91,223, 0, 89,225, 0, + 88,226, 0, 89,226, 0, 92,222, 0, 97,218, 0,102,212, 0, + 108,205, 0,113,198, 0,117,190, 0,120,182, 0,123,174, 1, + 124,167, 6,124,157, 15,124,147, 26,124,149, 24,124,152, 20, + 124,154, 18,124,155, 16,124,157, 15,124,164, 9,124,167, 6, + 123,168, 5,124,166, 7,124,161, 11,124,157, 15,124,157, 15, + 124,158, 13,124,160, 12,124,165, 8,123,172, 2,121,180, 0, + 119,186, 0,117,190, 0,115,194, 0,113,199, 0,107,207, 0, + 102,213, 0, 97,218, 0, 93,222, 0, 90,225, 0, 87,227, 0, + 86,228, 0, 86,228, 0, 87,227, 0, 88,226, 0, 87,227, 0, + 89,225, 0, 92,222, 0, 92,222, 0, 92,223, 0, 94,221, 0, + 98,217, 0, 99,216, 0,100,215, 0,101,214, 0,104,210, 0, + 107,206, 0,111,201, 0,114,196, 0,116,193, 0,117,191, 0, + 117,191, 0,117,190, 0,118,188, 0,120,183, 0,123,174, 1, + 123,172, 2,123,174, 1,123,173, 1,123,168, 5,124,157, 15, + 123,137, 41,124,154, 18,122,175, 0,119,187, 0,116,193, 0, + 114,196, 0,112,200, 0,110,202, 0,108,205, 0,106,207, 0, + 105,209, 0,105,209, 0,105,209, 0,107,207, 0,110,202, 0, + 114,196, 0,119,186, 0,123,168, 5,124,160, 12,124,162, 10, + 124,165, 8,124,164, 9,124,160, 12,124,152, 20,124,157, 15, + 124,162, 10,123,168, 5,122,175, 0,120,182, 0,118,188, 0, + 118,189, 0,118,189, 0,114,197, 0,110,202, 0,107,207, 0, + 105,209, 0,104,210, 0,104,210, 0,105,209, 0,105,209, 0, + 107,207, 0,109,203, 0,113,199, 0,116,192, 0,120,182, 0, + 124,166, 7,124,143, 31,124,154, 18,124,157, 15,124,154, 18, + 124,143, 31,124,143, 31,122,130, 53,122,132, 48,124,145, 29, + 124,154, 18,124,160, 12,124,162, 10,124,164, 9,124,165, 8, + 123,169, 4,123,171, 3,123,171, 3,123,170, 3,123,170, 3, + 121,179, 0,119,185, 0,118,188, 0,118,189, 0,118,189, 0, + 117,191, 0,117,191, 0,117,190, 0,118,188, 0,119,185, 0, + 121,179, 0,123,173, 1,122,175, 0,122,175, 0,123,171, 3, + 124,166, 7,124,160, 12,124,162, 10,124,164, 9,124,164, 9, + 124,158, 13,124,160, 12,124,160, 12,124,155, 16,124,161, 11, + 122,178, 0,118,189, 0,114,197, 0,111,202, 0,109,204, 0, + 108,205, 0,108,205, 0,108,205, 0,108,205, 0,108,205, 0, + 108,206, 0,107,206, 0,108,206, 0,109,204, 0,109,204, 0, + 107,207, 0,105,209, 0,103,211, 0,103,211, 0,103,211, 0, + 103,212, 0,102,212, 0,102,212, 0,103,211, 0,106,208, 0, + 109,204, 0,113,199, 0,114,196, 0,114,196, 0,114,196, 0, + 115,194, 0,115,195, 0,113,198, 0,113,199, 0,113,198, 0, + 111,202, 0,110,203, 0,109,203, 0,110,202, 0,110,203, 0, + 108,205, 0,108,206, 0,109,204, 0,111,202, 0,111,202, 0, + 111,202, 0,112,200, 0,113,198, 0,113,199, 0,112,200, 0, + 112,200, 0,112,200, 0,112,200, 0,114,197, 0,117,192, 0, + 120,183, 0,124,167, 6,124,147, 26,120,122, 70,105, 84,252, + 121,127, 58,124,166, 7,120,182, 0,117,192, 0,114,197, 0, + 112,200, 0,111,202, 0,111,201, 0,113,198, 0,116,193, 0, + 115,195, 0,115,194, 0,117,191, 0,120,184, 0,118,189, 0, + 114,196, 0,112,200, 0,111,201, 0,112,200, 0,110,202, 0, + 107,207, 0,104,210, 0,103,211, 0,105,209, 0,106,207, 0, + 108,205, 0,106,207, 0,105,209, 0,104,210, 0,103,211, 0, + 98,216, 0, 95,220, 0, 93,221, 0, 94,221, 0, 94,221, 0, + 92,222, 0, 92,223, 0, 93,221, 0, 97,218, 0,103,212, 0, + 109,204, 0,114,196, 0,117,191, 0,121,182, 0,124,167, 6, + 124,157, 15,124,149, 24,123,139, 37,123,139, 37,124,143, 31, + 124,147, 26,124,149, 24,124,157, 15,124,165, 8,123,170, 3, + 122,175, 0,122,177, 0,122,176, 0,123,172, 2,124,166, 7, + 124,158, 13,124,157, 15,124,157, 15,124,161, 11,123,170, 3, + 122,178, 0,119,186, 0,117,191, 0,115,195, 0,113,198, 0, + 109,203, 0,104,210, 0, 99,216, 0, 95,220, 0, 92,222, 0, + 90,224, 0, 90,225, 0, 90,225, 0, 91,224, 0, 92,222, 0, + 92,222, 0, 94,221, 0, 95,219, 0, 94,221, 0, 94,221, 0, + 95,219, 0,100,215, 0,101,213, 0,101,213, 0,103,212, 0, + 105,209, 0,108,205, 0,111,201, 0,115,195, 0,118,189, 0, + 119,186, 0,119,186, 0,119,186, 0,120,184, 0,121,180, 0, + 123,171, 3,124,162, 10,124,166, 7,124,166, 7,124,161, 11, + 124,150, 22,124,152, 20,124,165, 8,121,180, 0,118,189, 0, + 115,194, 0,114,197, 0,112,199, 0,111,202, 0,109,204, 0, + 107,207, 0,106,208, 0,105,209, 0,105,209, 0,106,208, 0, + 109,204, 0,112,199, 0,117,191, 0,122,177, 0,124,152, 20, + 124,157, 15,124,161, 11,124,161, 11,124,155, 16,124,145, 29, + 124,150, 22,124,160, 12,123,169, 4,122,178, 0,119,187, 0, + 116,193, 0,114,197, 0,113,198, 0,111,201, 0,107,206, 0, + 104,210, 0,102,213, 0,101,213, 0,102,213, 0,102,212, 0, + 103,211, 0,105,209, 0,107,206, 0,110,202, 0,114,196, 0, + 119,186, 0,123,170, 3,124,155, 16,124,155, 16,124,160, 12, + 124,158, 13,124,149, 24,122,130, 53,118,117, 85,123,141, 34, + 124,154, 18,124,161, 11,124,165, 8,124,166, 7,124,166, 7, + 124,167, 6,123,170, 3,123,174, 1,122,177, 0,122,178, 0, + 122,178, 0,121,179, 0,120,184, 0,118,188, 0,118,189, 0, + 117,191, 0,116,193, 0,115,194, 0,115,195, 0,115,194, 0, + 117,191, 0,119,186, 0,120,183, 0,120,184, 0,120,183, 0, + 121,179, 0,123,173, 1,124,165, 8,124,166, 7,124,166, 7, + 124,164, 9,124,158, 13,124,162, 10,124,164, 9,124,162, 10, + 123,171, 3,122,177, 0,119,185, 0,116,193, 0,113,199, 0, + 111,202, 0,110,202, 0,110,203, 0,109,204, 0,108,205, 0, + 108,205, 0,108,205, 0,107,207, 0,107,207, 0,107,206, 0, + 109,204, 0,110,202, 0,109,204, 0,108,205, 0,108,205, 0, + 109,204, 0,108,205, 0,108,206, 0,108,206, 0,108,205, 0, + 110,202, 0,113,198, 0,117,192, 0,116,193, 0,115,194, 0, + 116,193, 0,117,192, 0,116,192, 0,115,194, 0,115,194, 0, + 115,194, 0,114,197, 0,112,200, 0,111,202, 0,110,202, 0, + 111,201, 0,112,200, 0,111,202, 0,111,202, 0,112,200, 0, + 113,198, 0,113,198, 0,113,198, 0,114,197, 0,115,194, 0, + 116,193, 0,115,194, 0,115,194, 0,115,194, 0,116,193, 0, + 118,188, 0,121,180, 0,124,162, 10,122,130, 53,109, 92,251, + 94, 70,242,122,132, 48,124,165, 8,121,179, 0,119,187, 0, + 117,191, 0,116,193, 0,115,194, 0,115,194, 0,117,192, 0, + 115,194, 0,114,196, 0,116,193, 0,118,189, 0,120,183, 0, + 119,187, 0,115,194, 0,113,198, 0,112,200, 0,112,199, 0, + 113,198, 0,110,202, 0,109,204, 0,108,205, 0,110,203, 0, + 109,203, 0,106,207, 0,104,210, 0,103,211, 0,103,212, 0, + 103,211, 0,101,214, 0, 97,218, 0, 95,220, 0, 95,220, 0, + 97,218, 0, 97,218, 0, 97,218, 0, 99,216, 0,103,211, 0, + 109,203, 0,111,201, 0,114,197, 0,117,191, 0,121,182, 0, + 124,165, 8,122,132, 48,118,117, 85,118,117, 85,121,125, 63, + 122,132, 48,123,139, 37,124,154, 18,124,162, 10,123,169, 4, + 123,174, 1,122,178, 0,121,181, 0,121,181, 0,122,178, 0, + 123,170, 3,124,158, 13,124,149, 24,124,145, 29,124,150, 22, + 124,164, 9,123,174, 1,120,184, 0,117,190, 0,115,194, 0, + 113,198, 0,112,200, 0,106,208, 0,101,214, 0, 97,217, 0, + 95,220, 0, 94,221, 0, 94,221, 0, 94,220, 0, 95,219, 0, + 97,218, 0, 98,217, 0, 99,216, 0, 97,218, 0, 95,219, 0, + 96,219, 0, 98,216, 0,103,212, 0,104,210, 0,104,210, 0, + 105,209, 0,107,206, 0,109,204, 0,112,200, 0,116,193, 0, + 119,187, 0,122,178, 0,121,182, 0,120,182, 0,121,181, 0, + 122,177, 0,124,167, 6,124,152, 20,124,149, 24,124,150, 22, + 124,147, 26,124,155, 16,124,162, 10,123,169, 4,120,182, 0, + 117,190, 0,115,194, 0,114,196, 0,113,198, 0,110,202, 0, + 108,206, 0,107,207, 0,107,207, 0,106,208, 0,106,208, 0, + 107,207, 0,108,205, 0,111,201, 0,116,193, 0,120,182, 0, + 124,164, 9,124,147, 26,124,152, 20,124,152, 20,124,145, 29, + 122,132, 48,124,143, 31,124,167, 6,121,180, 0,119,187, 0, + 117,191, 0,113,199, 0,110,203, 0,108,205, 0,108,206, 0, + 105,209, 0,102,212, 0,101,214, 0,100,214, 0,101,214, 0, + 102,213, 0,103,212, 0,104,210, 0,107,207, 0,110,203, 0, + 114,197, 0,118,188, 0,123,174, 1,123,168, 5,124,158, 13, + 124,164, 9,124,161, 11,124,152, 20,122,132, 48,118,114, 96, + 123,141, 34,124,157, 15,124,164, 9,124,166, 7,124,166, 7, + 124,166, 7,124,166, 7,123,169, 4,123,174, 1,122,178, 0, + 121,182, 0,120,183, 0,120,184, 0,119,186, 0,119,186, 0, + 119,187, 0,118,189, 0,116,193, 0,114,196, 0,113,198, 0, + 113,198, 0,114,197, 0,116,193, 0,118,188, 0,118,188, 0, + 119,187, 0,120,183, 0,122,178, 0,123,170, 3,124,164, 9, + 124,161, 11,124,155, 16,124,161, 11,124,166, 7,123,168, 5, + 123,172, 2,121,180, 0,119,185, 0,118,188, 0,118,189, 0, + 115,195, 0,113,198, 0,112,200, 0,110,203, 0,108,205, 0, + 108,206, 0,108,205, 0,110,203, 0,108,205, 0,107,206, 0, + 107,207, 0,108,205, 0,109,203, 0,111,201, 0,113,199, 0, + 113,198, 0,114,197, 0,113,198, 0,113,199, 0,113,199, 0, + 113,198, 0,114,196, 0,116,192, 0,118,188, 0,117,191, 0, + 116,192, 0,116,192, 0,117,191, 0,117,190, 0,116,192, 0, + 116,192, 0,117,191, 0,115,194, 0,114,197, 0,112,200, 0, + 111,201, 0,111,201, 0,112,200, 0,113,198, 0,114,197, 0, + 114,197, 0,114,196, 0,115,195, 0,115,194, 0,115,194, 0, + 116,193, 0,117,191, 0,119,186, 0,118,188, 0,118,189, 0, + 118,188, 0,119,185, 0,122,177, 0,124,160, 12,120,122, 70, + 30, 49,213, 99, 75,247,122,132, 48,124,161, 11,123,174, 1, + 121,181, 0,120,183, 0,120,184, 0,119,186, 0,118,189, 0, + 116,193, 0,115,195, 0,114,196, 0,116,193, 0,118,188, 0, + 119,185, 0,120,184, 0,116,192, 0,114,196, 0,113,199, 0, + 113,199, 0,113,198, 0,114,197, 0,113,198, 0,114,197, 0, + 111,201, 0,109,204, 0,106,208, 0,104,210, 0,103,211, 0, + 104,210, 0,105,209, 0,105,209, 0,100,214, 0, 98,217, 0, + 97,217, 0, 98,216, 0,101,214, 0,102,212, 0,105,209, 0, + 109,203, 0,111,201, 0,112,199, 0,115,195, 0,118,189, 0, + 122,178, 0,124,158, 13,120,122, 70,102, 80,250,109, 92,251, + 114,104,197,121,125, 63,124,145, 29,124,157, 15,124,164, 9, + 123,169, 4,123,174, 1,122,178, 0,121,181, 0,121,180, 0, + 122,177, 0,123,168, 5,124,154, 18,121,127, 58,120,122, 70, + 122,130, 53,124,149, 24,124,166, 7,122,178, 0,119,186, 0, + 117,192, 0,114,196, 0,113,198, 0,107,206, 0,103,212, 0, + 100,215, 0, 98,217, 0, 97,217, 0, 98,217, 0, 98,217, 0, + 100,215, 0,101,213, 0,102,212, 0,100,215, 0, 98,217, 0, + 98,217, 0, 99,216, 0,102,213, 0,106,208, 0,106,208, 0, + 106,207, 0,108,206, 0,109,204, 0,111,201, 0,114,196, 0, + 118,189, 0,120,182, 0,121,181, 0,121,180, 0,122,178, 0, + 122,177, 0,123,172, 2,124,162, 10,124,143, 31,118,117, 85, + 121,125, 63,123,141, 34,124,161, 11,123,170, 3,122,178, 0, + 120,182, 0,118,189, 0,116,192, 0,113,199, 0,108,205, 0, + 105,209, 0,102,212, 0,101,213, 0,102,212, 0,104,210, 0, + 107,206, 0,108,205, 0,109,204, 0,111,201, 0,115,194, 0, + 119,185, 0,123,170, 3,124,158, 13,123,141, 34,123,141, 34, + 122,135, 44,121,127, 58,124,158, 13,122,177, 0,119,187, 0, + 116,193, 0,114,196, 0,109,203, 0,106,208, 0,103,211, 0, + 102,212, 0,102,212, 0,102,213, 0,100,214, 0,100,214, 0, + 99,216, 0, 99,215, 0,101,214, 0,104,210, 0,107,207, 0, + 109,203, 0,114,197, 0,118,188, 0,120,183, 0,122,178, 0, + 123,170, 3,124,165, 8,124,162, 10,124,154, 18,122,132, 48, + 121,127, 58,123,137, 41,124,154, 18,124,161, 11,124,165, 8, + 124,165, 8,123,172, 2,122,177, 0,121,179, 0,122,178, 0, + 122,178, 0,120,182, 0,119,186, 0,118,189, 0,117,191, 0, + 116,192, 0,115,194, 0,115,194, 0,116,192, 0,114,196, 0, + 112,200, 0,111,202, 0,111,202, 0,112,199, 0,115,195, 0, + 118,189, 0,119,186, 0,120,184, 0,121,179, 0,123,174, 1, + 124,167, 6,124,162, 10,124,162, 10,124,166, 7,123,169, 4, + 123,171, 3,121,181, 0,119,186, 0,118,189, 0,117,191, 0, + 117,192, 0,115,194, 0,113,198, 0,111,201, 0,109,203, 0, + 108,205, 0,108,206, 0,108,205, 0,110,202, 0,110,202, 0, + 109,204, 0,108,205, 0,108,205, 0,109,204, 0,110,202, 0, + 112,200, 0,113,199, 0,114,197, 0,117,192, 0,117,190, 0, + 117,191, 0,116,193, 0,116,193, 0,117,191, 0,119,186, 0, + 119,186, 0,118,188, 0,118,189, 0,118,189, 0,118,188, 0, + 118,189, 0,118,189, 0,116,192, 0,114,196, 0,113,198, 0, + 112,199, 0,112,200, 0,111,201, 0,112,200, 0,112,200, 0, + 112,200, 0,113,198, 0,115,195, 0,115,195, 0,115,194, 0, + 116,193, 0,116,193, 0,117,192, 0,118,189, 0,120,182, 0, + 121,182, 0,120,183, 0,121,181, 0,123,174, 1,124,160, 12, + 121,127, 58, 69, 60,230, 30, 55,222,118,117, 85,124,149, 24, + 124,164, 9,123,170, 3,123,173, 1,120,182, 0,118,188, 0, + 117,191, 0,116,193, 0,115,194, 0,116,193, 0,117,190, 0, + 120,184, 0,120,184, 0,120,184, 0,117,191, 0,115,195, 0, + 114,197, 0,113,198, 0,114,197, 0,114,197, 0,114,196, 0, + 114,197, 0,112,200, 0,110,203, 0,108,206, 0,107,207, 0, + 107,207, 0,107,206, 0,108,205, 0,108,205, 0,104,210, 0, + 101,213, 0,101,214, 0,102,213, 0,104,210, 0,106,207, 0, + 109,203, 0,111,201, 0,113,198, 0,114,196, 0,117,191, 0, + 120,183, 0,123,169, 4,124,143, 31,119,119, 77, 99, 75,247, + 69, 60,230,114,104,197,122,132, 48,124,149, 24,124,158, 13, + 123,171, 3,121,179, 0,120,182, 0,120,182, 0,121,180, 0, + 122,178, 0,123,173, 1,124,165, 8,124,150, 22,122,132, 48, + 120,122, 70,121,125, 63,121,127, 58,124,154, 18,123,170, 3, + 121,181, 0,118,188, 0,116,193, 0,114,196, 0,109,203, 0, + 105,209, 0,102,212, 0,101,214, 0,100,214, 0, 98,217, 0, + 97,218, 0, 98,216, 0,101,213, 0,103,211, 0,101,213, 0, + 101,214, 0,101,214, 0,103,212, 0,106,208, 0,107,207, 0, + 107,206, 0,108,205, 0,111,202, 0,112,200, 0,114,196, 0, + 117,190, 0,120,184, 0,119,185, 0,120,184, 0,120,182, 0, + 121,179, 0,123,172, 2,124,162, 10,124,147, 26,120,122, 70, + 107, 89,252,117,110,109,124,147, 26,124,165, 8,122,176, 0, + 120,184, 0,118,189, 0,115,194, 0,112,199, 0,108,205, 0, + 105,209, 0,101,214, 0, 99,216, 0, 98,217, 0,100,215, 0, + 102,212, 0,105,209, 0,109,204, 0,110,202, 0,112,200, 0, + 115,195, 0,119,187, 0,123,174, 1,124,166, 7,124,154, 18, + 124,145, 29,123,141, 34,124,143, 31,123,168, 5,121,182, 0, + 117,190, 0,115,195, 0,111,201, 0,106,208, 0,102,213, 0, + 100,215, 0, 98,217, 0, 98,217, 0, 98,217, 0, 98,217, 0, + 97,218, 0, 95,220, 0, 95,219, 0, 97,217, 0,101,214, 0, + 104,210, 0,108,205, 0,112,199, 0,115,194, 0,118,189, 0, + 120,184, 0,122,178, 0,124,165, 8,124,160, 12,124,150, 22, + 122,130, 53,122,135, 44,124,145, 29,124,154, 18,124,162, 10, + 123,169, 4,122,176, 0,121,180, 0,120,183, 0,120,183, 0, + 121,182, 0,122,178, 0,120,184, 0,118,188, 0,116,192, 0, + 115,195, 0,114,197, 0,112,199, 0,112,200, 0,113,198, 0, + 115,195, 0,112,200, 0,109,203, 0,108,205, 0,108,205, 0, + 111,202, 0,114,196, 0,117,190, 0,120,183, 0,122,178, 0, + 123,174, 1,123,170, 3,124,167, 6,124,167, 6,123,169, 4, + 123,170, 3,122,178, 0,120,184, 0,118,188, 0,117,190, 0, + 117,191, 0,117,192, 0,115,194, 0,114,197, 0,112,200, 0, + 110,202, 0,110,203, 0,110,203, 0,110,202, 0,112,200, 0, + 112,200, 0,110,202, 0,109,203, 0,109,204, 0,109,203, 0, + 110,202, 0,111,201, 0,112,199, 0,114,197, 0,117,191, 0, + 120,183, 0,120,184, 0,118,188, 0,117,190, 0,118,189, 0, + 119,186, 0,120,182, 0,121,182, 0,120,182, 0,120,184, 0, + 119,185, 0,119,187, 0,118,189, 0,116,193, 0,114,197, 0, + 113,199, 0,112,199, 0,113,199, 0,113,198, 0,113,198, 0, + 113,199, 0,112,200, 0,112,199, 0,114,197, 0,115,195, 0, + 114,196, 0,114,196, 0,115,195, 0,116,193, 0,117,192, 0, + 119,186, 0,122,178, 0,123,174, 1,123,174, 1,123,170, 3, + 124,160, 12,122,135, 44,102, 80,250, 30, 20,155,105, 84,252, + 121,125, 63,124,145, 29,124,162, 10,122,177, 0,119,186, 0, + 118,189, 0,117,192, 0,116,192, 0,116,192, 0,118,189, 0, + 120,184, 0,121,179, 0,121,180, 0,120,184, 0,117,191, 0, + 115,194, 0,114,196, 0,114,196, 0,115,195, 0,115,194, 0, + 115,194, 0,115,195, 0,114,197, 0,112,200, 0,111,201, 0, + 111,202, 0,111,201, 0,113,199, 0,114,197, 0,112,200, 0, + 108,206, 0,106,208, 0,105,209, 0,106,208, 0,108,206, 0, + 110,203, 0,112,200, 0,114,197, 0,115,194, 0,117,190, 0, + 120,184, 0,123,173, 1,124,152, 20,122,130, 53,113,100,242, + 87, 65,237,109, 92,251,120,122, 70,123,139, 37,124,155, 16, + 123,170, 3,121,179, 0,119,185, 0,119,186, 0,119,186, 0, + 120,183, 0,121,179, 0,123,173, 1,124,166, 7,124,154, 18, + 122,135, 44,117,110,109,117,110,109,117,110,109,124,143, 31, + 124,165, 8,122,178, 0,119,186, 0,117,191, 0,115,194, 0, + 113,199, 0,109,204, 0,106,207, 0,102,212, 0, 99,215, 0, + 97,218, 0, 96,219, 0, 98,217, 0,101,214, 0,103,211, 0, + 103,211, 0,103,211, 0,105,209, 0,107,207, 0,107,206, 0, + 107,206, 0,108,205, 0,110,202, 0,113,198, 0,115,194, 0, + 118,189, 0,120,183, 0,119,186, 0,119,186, 0,120,184, 0, + 121,182, 0,122,177, 0,124,167, 6,124,150, 22,118,114, 96, + 94, 70,242, 69, 60,230,118,117, 85,124,150, 22,123,168, 5, + 121,180, 0,118,188, 0,115,194, 0,112,199, 0,109,204, 0, + 105,209, 0,101,213, 0, 98,216, 0, 97,218, 0, 97,218, 0, + 99,216, 0,101,213, 0,105,209, 0,109,204, 0,110,202, 0, + 112,199, 0,115,194, 0,118,188, 0,122,176, 0,123,171, 3, + 124,164, 9,124,158, 13,124,154, 18,124,160, 12,122,176, 0, + 119,186, 0,117,192, 0,113,198, 0,107,206, 0,102,213, 0, + 98,217, 0, 96,219, 0, 95,220, 0, 95,220, 0, 95,219, 0, + 95,219, 0, 94,220, 0, 93,221, 0, 94,221, 0, 95,219, 0, + 99,216, 0,103,212, 0,107,207, 0,111,202, 0,114,197, 0, + 116,192, 0,118,188, 0,121,181, 0,123,168, 5,124,152, 20, + 123,141, 34,121,125, 63,123,137, 41,124,152, 20,124,165, 8, + 123,172, 2,122,178, 0,120,182, 0,120,184, 0,119,185, 0, + 120,184, 0,121,181, 0,122,178, 0,120,184, 0,118,189, 0, + 116,193, 0,114,197, 0,112,199, 0,111,202, 0,110,202, 0, + 111,201, 0,113,198, 0,113,198, 0,109,204, 0,107,207, 0, + 106,207, 0,108,205, 0,111,201, 0,115,194, 0,119,187, 0, + 122,176, 0,123,171, 3,123,168, 5,124,167, 6,124,167, 6, + 123,168, 5,122,175, 0,120,182, 0,119,186, 0,118,188, 0, + 118,188, 0,118,188, 0,118,189, 0,117,192, 0,115,194, 0, + 114,197, 0,113,199, 0,112,199, 0,113,199, 0,114,197, 0, + 115,194, 0,113,198, 0,112,200, 0,111,202, 0,111,202, 0, + 111,202, 0,111,201, 0,112,199, 0,113,198, 0,115,195, 0, + 118,189, 0,121,180, 0,123,174, 1,121,182, 0,119,186, 0, + 119,187, 0,119,185, 0,121,182, 0,121,181, 0,121,182, 0, + 120,182, 0,120,184, 0,120,184, 0,118,188, 0,116,192, 0, + 114,196, 0,113,198, 0,113,198, 0,114,197, 0,115,194, 0, + 115,194, 0,115,195, 0,113,198, 0,113,199, 0,114,197, 0, + 115,194, 0,114,197, 0,113,198, 0,113,198, 0,114,196, 0, + 116,193, 0,118,189, 0,120,182, 0,123,172, 2,124,166, 7, + 124,165, 8,124,158, 13,123,141, 34,114,104,197, 30, 31,180, + 87, 65,237,119,119, 77,124,152, 20,123,170, 3,121,182, 0, + 118,188, 0,117,191, 0,117,191, 0,117,190, 0,118,188, 0, + 120,184, 0,122,176, 0,123,172, 2,122,178, 0,119,185, 0, + 118,189, 0,117,192, 0,116,192, 0,117,192, 0,117,191, 0, + 117,190, 0,117,190, 0,117,191, 0,116,192, 0,115,194, 0, + 115,194, 0,116,193, 0,117,191, 0,118,188, 0,119,186, 0, + 115,194, 0,112,200, 0,110,202, 0,110,202, 0,111,201, 0, + 112,199, 0,114,197, 0,115,194, 0,117,191, 0,118,188, 0, + 120,183, 0,123,173, 1,124,157, 15,121,127, 58,115,107,129, + 109, 92,251,111, 96,248,119,119, 77,122,135, 44,124,154, 18, + 123,168, 5,122,178, 0,120,184, 0,118,188, 0,118,188, 0, + 119,186, 0,120,183, 0,121,179, 0,123,174, 1,124,165, 8, + 124,150, 22,121,127, 58,105, 84,252,102, 80,250,113,100,242, + 123,137, 41,124,160, 12,123,174, 1,120,184, 0,118,189, 0, + 116,193, 0,113,198, 0,110,202, 0,106,208, 0,102,213, 0, + 98,216, 0, 96,219, 0, 96,219, 0, 98,217, 0,102,212, 0, + 105,209, 0,106,208, 0,107,207, 0,108,205, 0,108,206, 0, + 108,206, 0,108,205, 0,109,203, 0,112,199, 0,116,193, 0, + 118,188, 0,121,180, 0,120,182, 0,119,186, 0,119,185, 0, + 121,182, 0,122,176, 0,123,168, 5,124,154, 18,121,127, 58, + 102, 80,250, 30, 37,192,102, 80,250,121,127, 58,124,154, 18, + 123,169, 4,121,181, 0,118,189, 0,114,196, 0,111,202, 0, + 107,207, 0,103,211, 0,100,215, 0, 98,217, 0, 97,218, 0, + 98,217, 0,100,215, 0,103,212, 0,106,208, 0,109,203, 0, + 111,201, 0,113,198, 0,116,193, 0,119,187, 0,122,177, 0, + 123,172, 2,123,168, 5,124,164, 9,124,160, 12,123,171, 3, + 120,182, 0,118,189, 0,116,193, 0,111,202, 0,104,210, 0, + 99,216, 0, 95,219, 0, 94,221, 0, 94,221, 0, 94,220, 0, + 95,220, 0, 95,220, 0, 94,221, 0, 93,222, 0, 94,221, 0, + 95,219, 0, 98,216, 0,102,212, 0,106,207, 0,110,202, 0, + 113,198, 0,116,193, 0,118,189, 0,121,182, 0,123,169, 4, + 124,147, 26,121,125, 63,118,117, 85,122,132, 48,124,155, 16, + 123,169, 4,122,178, 0,120,184, 0,119,186, 0,119,186, 0, + 120,184, 0,121,182, 0,122,177, 0,122,178, 0,120,184, 0, + 118,189, 0,116,192, 0,115,195, 0,113,198, 0,111,201, 0, + 111,202, 0,111,201, 0,113,198, 0,114,196, 0,110,202, 0, + 107,207, 0,106,208, 0,107,207, 0,110,202, 0,114,197, 0, + 118,189, 0,122,178, 0,124,164, 9,124,161, 11,124,161, 11, + 124,161, 11,124,167, 6,122,177, 0,120,182, 0,120,184, 0, + 119,185, 0,120,184, 0,120,184, 0,119,185, 0,118,188, 0, + 117,191, 0,116,193, 0,115,194, 0,115,194, 0,116,192, 0, + 117,190, 0,117,191, 0,115,194, 0,114,197, 0,113,198, 0, + 113,198, 0,114,197, 0,114,196, 0,115,195, 0,115,194, 0, + 117,192, 0,119,185, 0,122,175, 0,124,167, 6,123,172, 2, + 121,179, 0,120,182, 0,121,181, 0,122,178, 0,122,178, 0, + 121,179, 0,121,181, 0,121,182, 0,121,181, 0,120,184, 0, + 117,191, 0,115,194, 0,114,196, 0,114,196, 0,115,194, 0, + 117,191, 0,119,187, 0,117,190, 0,116,193, 0,114,196, 0, + 114,196, 0,116,193, 0,114,196, 0,113,198, 0,113,199, 0, + 113,198, 0,115,194, 0,118,189, 0,120,184, 0,122,175, 0, + 124,164, 9,124,158, 13,124,154, 18,124,143, 31,118,117, 85, + 99, 75,247,109, 92,251,123,139, 37,124,164, 9,122,177, 0, + 120,184, 0,118,188, 0,118,189, 0,118,189, 0,119,186, 0, + 121,182, 0,122,175, 0,124,165, 8,123,169, 4,122,178, 0, + 120,184, 0,118,188, 0,118,188, 0,119,187, 0,119,185, 0, + 120,183, 0,121,182, 0,121,182, 0,120,183, 0,120,184, 0, + 120,184, 0,120,183, 0,121,180, 0,122,175, 0,123,170, 3, + 122,177, 0,119,186, 0,117,191, 0,116,193, 0,116,193, 0, + 117,191, 0,118,189, 0,119,187, 0,119,185, 0,120,182, 0, + 122,178, 0,123,171, 3,124,157, 15,121,125, 63,111, 96,248, + 114,104,197,115,107,129,122,130, 53,123,137, 41,124,150, 22, + 124,166, 7,122,177, 0,120,183, 0,119,187, 0,118,188, 0, + 118,188, 0,119,185, 0,120,183, 0,121,182, 0,122,177, 0, + 124,167, 6,124,150, 22,118,117, 85, 30, 55,222, 30, 49,213, + 115,107,129,124,145, 29,124,165, 8,122,175, 0,121,181, 0, + 118,188, 0,117,192, 0,114,197, 0,110,202, 0,106,208, 0, + 102,213, 0, 98,216, 0, 97,218, 0, 97,217, 0,100,214, 0, + 104,210, 0,108,205, 0,109,204, 0,110,203, 0,109,204, 0, + 108,205, 0,108,205, 0,109,204, 0,111,201, 0,115,195, 0, + 119,186, 0,122,178, 0,123,173, 1,121,179, 0,120,182, 0, + 121,180, 0,123,174, 1,124,165, 8,124,149, 24,121,125, 63, + 105, 84,252, 99, 75,247,114,104,197,120,122, 70,122,132, 48, + 124,154, 18,123,168, 5,122,178, 0,119,187, 0,115,194, 0, + 111,201, 0,107,207, 0,103,211, 0,101,214, 0, 99,216, 0, + 99,216, 0,100,215, 0,102,212, 0,105,209, 0,107,206, 0, + 110,203, 0,112,200, 0,114,196, 0,117,191, 0,120,184, 0, + 123,173, 1,123,168, 5,124,166, 7,124,164, 9,124,165, 8, + 122,178, 0,119,186, 0,117,192, 0,115,195, 0,109,204, 0, + 102,212, 0, 97,217, 0, 95,220, 0, 94,221, 0, 94,220, 0, + 95,219, 0, 97,218, 0, 97,218, 0, 96,219, 0, 95,220, 0, + 95,220, 0, 97,218, 0,100,215, 0,103,211, 0,107,207, 0, + 109,203, 0,113,198, 0,116,193, 0,118,188, 0,121,181, 0, + 123,168, 5,124,145, 29,114,104,197,111, 96,248,118,117, 85, + 124,152, 20,123,171, 3,121,181, 0,119,186, 0,119,186, 0, + 119,185, 0,121,181, 0,122,176, 0,123,171, 3,122,178, 0, + 120,182, 0,119,186, 0,118,188, 0,117,190, 0,116,192, 0, + 114,196, 0,113,198, 0,113,198, 0,114,196, 0,116,193, 0, + 112,199, 0,109,204, 0,108,206, 0,108,205, 0,111,201, 0, + 114,196, 0,118,189, 0,122,178, 0,124,158, 13,124,147, 26, + 124,145, 29,124,147, 26,124,164, 9,123,173, 1,122,178, 0, + 121,180, 0,121,180, 0,122,178, 0,121,181, 0,120,184, 0, + 120,183, 0,119,186, 0,118,188, 0,118,188, 0,119,187, 0, + 120,184, 0,120,182, 0,119,186, 0,118,189, 0,117,191, 0, + 117,192, 0,117,191, 0,117,190, 0,118,189, 0,118,188, 0, + 119,187, 0,120,184, 0,122,178, 0,124,167, 6,124,157, 15, + 124,157, 15,123,168, 5,123,173, 1,123,172, 2,123,170, 3, + 123,171, 3,123,174, 1,122,177, 0,122,178, 0,122,178, 0, + 122,178, 0,119,186, 0,117,191, 0,116,193, 0,116,192, 0, + 118,189, 0,119,186, 0,121,180, 0,120,182, 0,118,188, 0, + 116,192, 0,116,193, 0,117,192, 0,116,193, 0,114,196, 0, + 114,197, 0,114,196, 0,116,193, 0,119,186, 0,121,182, 0, + 122,175, 0,124,166, 7,124,150, 22,124,147, 26,123,139, 37, + 118,117, 85,111, 96,248,118,117, 85,124,154, 18,123,171, 3, + 121,181, 0,119,186, 0,118,188, 0,118,188, 0,119,185, 0, + 121,181, 0,123,174, 1,123,173, 1,123,171, 3,123,171, 3, + 122,178, 0,121,182, 0,120,183, 0,121,182, 0,122,178, 0, + 123,173, 1,123,169, 4,124,167, 6,124,167, 6,123,168, 5, + 123,170, 3,123,168, 5,124,165, 8,124,157, 15,124,147, 26, + 123,141, 34,124,162, 10,123,174, 1,121,179, 0,121,180, 0, + 122,178, 0,121,179, 0,122,177, 0,123,172, 2,123,171, 3, + 123,169, 4,124,165, 8,124,152, 20,121,125, 63,105, 84,252, + 113,100,242,115,107,129,122,132, 48,124,145, 29,124,147, 26, + 124,160, 12,123,172, 2,121,182, 0,119,186, 0,118,188, 0, + 118,188, 0,119,186, 0,120,184, 0,119,185, 0,120,184, 0, + 122,178, 0,123,169, 4,124,152, 20,119,119, 77, 69, 60,230, + 69, 60,230,121,125, 63,124,155, 16,123,170, 3,122,178, 0, + 120,183, 0,120,184, 0,118,189, 0,115,195, 0,111,202, 0, + 107,207, 0,103,212, 0,100,214, 0, 99,215, 0,100,214, 0, + 103,211, 0,108,205, 0,111,201, 0,111,201, 0,110,203, 0, + 109,204, 0,109,204, 0,110,202, 0,111,201, 0,113,198, 0, + 117,190, 0,122,178, 0,124,165, 8,124,167, 6,123,172, 2, + 123,173, 1,123,169, 4,124,158, 13,123,139, 37,115,107,129, + 113,100,242,118,117, 85,122,135, 44,124,147, 26,124,154, 18, + 124,158, 13,124,158, 13,124,164, 9,123,173, 1,120,183, 0, + 117,191, 0,113,198, 0,109,204, 0,105,209, 0,103,212, 0, + 101,213, 0,101,213, 0,103,212, 0,105,209, 0,106,208, 0, + 107,206, 0,110,202, 0,113,198, 0,116,193, 0,119,186, 0, + 122,178, 0,124,166, 7,124,155, 16,124,155, 16,124,155, 16, + 123,168, 5,121,182, 0,118,189, 0,115,194, 0,114,197, 0, + 108,205, 0,102,212, 0, 98,217, 0, 96,219, 0, 96,219, 0, + 97,218, 0, 98,217, 0,100,215, 0,100,215, 0, 99,216, 0, + 98,217, 0, 98,217, 0, 99,215, 0,102,213, 0,105,209, 0, + 106,207, 0,108,205, 0,112,199, 0,116,193, 0,118,188, 0, + 121,179, 0,124,166, 7,124,143, 31,113,100,242, 30, 55,222, + 109, 92,251,124,145, 29,123,169, 4,121,180, 0,119,185, 0, + 119,186, 0,120,182, 0,122,176, 0,124,167, 6,123,170, 3, + 123,174, 1,121,179, 0,120,182, 0,120,183, 0,121,182, 0, + 120,182, 0,119,186, 0,118,189, 0,117,192, 0,116,192, 0, + 117,191, 0,115,194, 0,113,199, 0,111,201, 0,112,200, 0, + 114,197, 0,116,193, 0,119,186, 0,122,176, 0,124,158, 13, + 120,122, 70,118,117, 85,121,125, 63,124,152, 20,124,165, 8, + 123,170, 3,123,172, 2,123,171, 3,123,172, 2,122,178, 0, + 121,180, 0,122,178, 0,122,178, 0,121,181, 0,121,181, 0, + 122,178, 0,122,175, 0,123,172, 2,122,178, 0,121,182, 0, + 120,182, 0,120,182, 0,121,181, 0,122,178, 0,122,177, 0, + 122,175, 0,122,175, 0,123,173, 1,124,166, 7,124,152, 20, + 123,139, 37,122,130, 53,124,145, 29,124,154, 18,124,154, 18, + 124,152, 20,124,158, 13,124,165, 8,123,170, 3,123,173, 1, + 123,174, 1,123,173, 1,121,180, 0,119,186, 0,118,188, 0, + 118,188, 0,120,184, 0,121,180, 0,122,177, 0,122,177, 0, + 121,181, 0,118,188, 0,117,191, 0,117,191, 0,118,189, 0, + 117,191, 0,117,192, 0,117,192, 0,118,188, 0,120,182, 0, + 122,175, 0,123,171, 3,124,164, 9,124,150, 22,122,135, 44, + 121,125, 63,114,104,197,114,104,197,122,130, 53,124,162, 10, + 122,177, 0,120,184, 0,119,186, 0,119,186, 0,120,184, 0, + 121,181, 0,122,178, 0,121,181, 0,121,180, 0,122,177, 0, + 123,170, 3,123,173, 1,122,175, 0,122,175, 0,123,169, 4, + 124,161, 11,124,160, 12,124,154, 18,123,139, 37,122,135, 44, + 123,139, 37,123,141, 34,123,137, 41,121,127, 58,117,110,109, + 107, 89,252,113,100,242,122,132, 48,124,150, 22,124,157, 15, + 124,157, 15,124,167, 6,123,171, 3,123,169, 4,124,161, 11, + 124,147, 26,124,147, 26,123,141, 34,119,119, 77,115,107,129, + 107, 89,252,113,100,242,121,125, 63,124,147, 26,124,152, 20, + 124,149, 24,124,165, 8,122,177, 0,120,184, 0,118,188, 0, + 118,188, 0,119,186, 0,120,182, 0,119,186, 0,119,186, 0, + 120,184, 0,121,179, 0,123,169, 4,124,150, 22,118,117, 85, + 30, 55,222,107, 89,252,123,137, 41,124,160, 12,123,172, 2, + 121,179, 0,120,183, 0,119,185, 0,119,186, 0,116,192, 0, + 112,199, 0,108,205, 0,105,209, 0,103,212, 0,102,212, 0, + 104,210, 0,108,206, 0,112,199, 0,113,199, 0,111,202, 0, + 110,203, 0,110,203, 0,111,202, 0,112,200, 0,114,197, 0, + 116,193, 0,120,184, 0,123,169, 4,124,149, 24,124,154, 18, + 124,158, 13,124,157, 15,124,147, 26,120,122, 70,114,104,197, + 117,110,109,123,137, 41,124,154, 18,124,162, 10,124,166, 7, + 123,169, 4,123,171, 3,123,170, 3,122,176, 0,122,178, 0, + 121,182, 0,119,185, 0,116,193, 0,112,200, 0,108,205, 0, + 106,208, 0,105,209, 0,105,209, 0,106,208, 0,107,207, 0, + 106,208, 0,107,206, 0,111,202, 0,114,196, 0,118,188, 0, + 121,179, 0,123,168, 5,124,150, 22,121,127, 58,122,132, 48, + 123,141, 34,123,168, 5,120,182, 0,117,191, 0,114,196, 0, + 113,198, 0,109,203, 0,103,211, 0,100,215, 0, 98,216, 0, + 99,216, 0,100,214, 0,102,213, 0,102,213, 0,102,212, 0, + 103,212, 0,101,213, 0,101,214, 0,102,212, 0,104,210, 0, + 106,207, 0,107,207, 0,108,205, 0,111,201, 0,115,194, 0, + 119,187, 0,122,178, 0,124,164, 9,123,139, 37,111, 96,248, + 30, 31,180, 30, 43,203,122,130, 53,124,164, 9,122,178, 0, + 120,183, 0,120,183, 0,122,178, 0,123,169, 4,124,158, 13, + 124,165, 8,122,175, 0,121,180, 0,121,182, 0,121,181, 0, + 122,177, 0,123,170, 3,123,170, 3,122,177, 0,120,182, 0, + 119,186, 0,118,188, 0,118,188, 0,117,191, 0,116,193, 0, + 116,193, 0,117,191, 0,119,187, 0,120,182, 0,123,173, 1, + 124,157, 15,121,125, 63, 87, 65,237,102, 80,250,121,125, 63, + 124,147, 26,124,155, 16,124,160, 12,124,160, 12,124,166, 7, + 123,171, 3,123,171, 3,124,166, 7,123,170, 3,123,173, 1, + 123,172, 2,123,169, 4,124,162, 10,124,154, 18,124,162, 10, + 124,167, 6,124,167, 6,124,166, 7,124,162, 10,124,158, 13, + 124,155, 16,124,152, 20,124,154, 18,124,154, 18,124,149, 24, + 123,137, 41,118,114, 96,109, 92,251,111, 96,248,115,107,129, + 115,107,129,117,110,109,121,125, 63,124,143, 31,124,157, 15, + 124,165, 8,123,168, 5,123,168, 5,123,170, 3,121,179, 0, + 120,182, 0,120,182, 0,121,179, 0,122,177, 0,122,177, 0, + 122,175, 0,122,175, 0,120,182, 0,118,188, 0,117,190, 0, + 118,189, 0,119,186, 0,120,184, 0,120,184, 0,121,181, 0, + 122,175, 0,124,167, 6,124,162, 10,124,157, 15,124,145, 29, + 121,125, 63,111, 96,248, 94, 70,242,107, 89,252,123,141, 34, + 124,167, 6,121,180, 0,119,186, 0,119,187, 0,119,186, 0, + 120,182, 0,120,182, 0,119,186, 0,118,188, 0,119,186, 0, + 121,181, 0,123,172, 2,124,162, 10,124,162, 10,124,160, 12, + 124,154, 18,124,157, 15,124,157, 15,124,149, 24,122,130, 53, + 109, 92,251,105, 84,252,114,104,197,117,110,109,114,104,197, + 105, 84,252, 30, 55,222, 30, 43,203, 87, 65,237,113,100,242, + 117,110,109,123,139, 37,124,157, 15,124,164, 9,124,162, 10, + 124,152, 20,121,127, 58,117,110,109,118,117, 85,119,119, 77, + 118,114, 96,113,100,242,113,100,242,122,132, 48,124,147, 26, + 124,149, 24,124,152, 20,123,168, 5,121,179, 0,119,185, 0, + 118,188, 0,119,187, 0,120,184, 0,121,182, 0,119,185, 0, + 119,186, 0,120,184, 0,122,178, 0,124,167, 6,124,145, 29, + 115,107,129, 30, 37,192,111, 96,248,123,139, 37,124,161, 11, + 123,172, 2,121,179, 0,120,182, 0,120,184, 0,119,185, 0, + 118,188, 0,115,195, 0,111,202, 0,108,206, 0,106,208, 0, + 106,208, 0,108,205, 0,112,199, 0,114,197, 0,111,201, 0, + 110,203, 0,110,203, 0,111,202, 0,112,199, 0,114,196, 0, + 116,192, 0,119,187, 0,122,177, 0,124,160, 12,124,150, 22, + 123,137, 41,122,130, 53,120,122, 70,109, 92,251, 99, 75,247, + 114,104,197,123,141, 34,124,160, 12,123,168, 5,123,173, 1, + 122,175, 0,122,177, 0,122,178, 0,120,184, 0,117,190, 0, + 116,192, 0,116,192, 0,118,189, 0,118,188, 0,115,194, 0, + 112,200, 0,109,203, 0,108,206, 0,107,207, 0,108,206, 0, + 107,207, 0,107,207, 0,108,205, 0,112,200, 0,114,196, 0, + 117,191, 0,120,182, 0,124,166, 7,122,132, 48,109, 92,251, + 102, 80,250,121,125, 63,124,162, 10,121,181, 0,117,191, 0, + 114,196, 0,112,200, 0,111,201, 0,106,207, 0,103,211, 0, + 102,212, 0,103,212, 0,103,211, 0,103,212, 0,103,212, 0, + 104,210, 0,107,207, 0,105,209, 0,104,210, 0,105,209, 0, + 106,208, 0,108,206, 0,107,206, 0,108,205, 0,111,201, 0, + 115,195, 0,118,189, 0,120,183, 0,123,173, 1,124,158, 13, + 121,127, 58, 69, 60,230, 30, 2,128,115,107,129,124,154, 18, + 123,172, 2,121,179, 0,121,179, 0,123,174, 1,124,164, 9, + 124,147, 26,124,162, 10,123,172, 2,122,177, 0,122,177, 0, + 123,174, 1,123,168, 5,124,158, 13,124,143, 31,124,158, 13, + 123,169, 4,122,178, 0,120,182, 0,119,185, 0,119,186, 0, + 119,185, 0,120,183, 0,121,181, 0,121,179, 0,122,175, 0, + 124,167, 6,124,155, 16,122,132, 48,105, 84,252, 30, 2,128, + 99, 75,247,118,114, 96,122,132, 48,123,141, 34,124,143, 31, + 124,147, 26,124,152, 20,124,149, 24,124,155, 16,124,161, 11, + 124,164, 9,124,162, 10,124,157, 15,124,147, 26,123,139, 37, + 121,125, 63,122,135, 44,123,137, 41,123,137, 41,124,143, 31, + 124,147, 26,124,145, 29,123,139, 37,120,122, 70,121,127, 58, + 121,127, 58,119,119, 77,114,104,197,102, 80,250, 30, 43,203, + 30, 10,132, 30, 2,128, 30, 10,132, 30, 49,213,109, 92,251, + 121,127, 58,124,145, 29,124,155, 16,124,160, 12,124,158, 13, + 123,171, 3,122,177, 0,122,178, 0,122,176, 0,123,174, 1, + 122,175, 0,123,172, 2,124,164, 9,122,178, 0,119,186, 0, + 118,189, 0,117,190, 0,118,188, 0,120,184, 0,122,178, 0, + 123,171, 3,124,165, 8,124,160, 12,124,150, 22,124,145, 29, + 122,135, 44,118,117, 85, 99, 75,247, 30, 2,128,109, 92,251, + 124,147, 26,123,171, 3,120,183, 0,118,188, 0,118,189, 0, + 119,187, 0,120,184, 0,118,189, 0,117,192, 0,117,192, 0, + 118,189, 0,120,183, 0,123,172, 2,124,155, 16,123,137, 41, + 124,143, 31,124,150, 22,124,152, 20,124,149, 24,123,137, 41, + 120,122, 70,113,100,242, 87, 65,237,107, 89,252,114,104,197, + 118,117, 85,118,117, 85,114,104,197, 99, 75,247, 30, 49,213, + 30, 2,128, 99, 75,247,121,125, 63,124,147, 26,124,157, 15, + 124,155, 16,124,145, 29,121,127, 58,115,107,129,121,127, 58, + 122,132, 48,122,132, 48,121,127, 58,119,119, 77,121,125, 63, + 122,135, 44,122,135, 44,124,154, 18,123,169, 4,121,179, 0, + 119,185, 0,119,187, 0,119,186, 0,120,182, 0,122,178, 0, + 121,182, 0,120,182, 0,121,179, 0,123,172, 2,124,158, 13, + 122,130, 53, 99, 75,247, 30, 2,128,105, 84,252,122,135, 44, + 124,157, 15,123,169, 4,122,176, 0,121,179, 0,121,181, 0, + 120,182, 0,120,184, 0,117,190, 0,114,197, 0,111,202, 0, + 109,203, 0,110,203, 0,112,200, 0,116,193, 0,112,199, 0, + 110,202, 0,109,203, 0,110,203, 0,112,200, 0,114,196, 0, + 116,193, 0,118,188, 0,121,182, 0,123,173, 1,124,165, 8, + 124,154, 18,122,135, 44,115,107,129, 69, 60,230, 30, 2,128, + 87, 65,237,120,122, 70,124,150, 22,124,165, 8,123,172, 2, + 122,175, 0,122,177, 0,122,178, 0,118,188, 0,115,194, 0, + 112,200, 0,110,202, 0,110,202, 0,112,200, 0,114,196, 0, + 117,190, 0,114,196, 0,111,201, 0,109,203, 0,108,205, 0, + 108,205, 0,108,205, 0,108,205, 0,110,202, 0,111,202, 0, + 112,199, 0,115,195, 0,118,188, 0,122,176, 0,124,152, 20, + 111, 96,248, 30, 2,128,113,100,242,124,152, 20,122,177, 0, + 118,189, 0,114,196, 0,112,200, 0,111,202, 0,110,202, 0, + 107,206, 0,106,207, 0,106,207, 0,105,209, 0,105,209, 0, + 105,209, 0,107,207, 0,109,203, 0,108,206, 0,107,207, 0, + 106,207, 0,107,207, 0,108,205, 0,110,203, 0,110,202, 0, + 113,199, 0,114,196, 0,117,191, 0,120,184, 0,122,178, 0, + 124,167, 6,124,147, 26,113,100,242, 30, 31,180,102, 80,250, + 123,141, 34,124,165, 8,123,172, 2,123,173, 1,124,167, 6, + 124,154, 18,124,143, 31,124,155, 16,124,166, 7,123,170, 3, + 123,170, 3,124,167, 6,124,164, 9,124,161, 11,124,149, 24, + 123,141, 34,124,152, 20,124,167, 6,122,176, 0,121,182, 0, + 119,185, 0,119,186, 0,120,184, 0,121,179, 0,123,173, 1, + 122,175, 0,123,174, 1,123,169, 4,124,157, 15,122,132, 48, + 102, 80,250, 30, 55,222,105, 84,252,111, 96,248,115,107,129, + 118,117, 85,119,119, 77,120,122, 70,122,130, 53,123,139, 37, + 124,147, 26,124,152, 20,124,154, 18,124,158, 13,124,158, 13, + 124,152, 20,123,141, 34,122,130, 53,120,122, 70,120,122, 70, + 122,132, 48,123,137, 41,123,139, 37,122,132, 48,119,119, 77, + 111, 96,248,109, 92,251,109, 92,251,105, 84,252,107, 89,252, + 107, 89,252,107, 89,252,111, 96,248,107, 89,252, 30, 55,222, + 99, 75,247,115,107,129,119,119, 77,123,139, 37,124,147, 26, + 124,150, 22,124,162, 10,123,172, 2,122,176, 0,122,177, 0, + 122,175, 0,123,173, 1,123,171, 3,123,171, 3,123,174, 1, + 120,182, 0,119,187, 0,118,188, 0,118,188, 0,120,184, 0, + 121,181, 0,122,176, 0,123,170, 3,124,157, 15,124,147, 26, + 124,143, 31,122,135, 44,120,122, 70,113,100,242,102, 80,250, + 113,100,242,124,147, 26,123,171, 3,120,183, 0,118,189, 0, + 117,190, 0,118,189, 0,117,191, 0,115,194, 0,114,197, 0, + 114,196, 0,117,191, 0,120,184, 0,123,172, 2,124,164, 9, + 124,157, 15,124,152, 20,124,152, 20,124,152, 20,124,152, 20, + 124,147, 26,123,137, 41,119,119, 77,109, 92,251,115,107,129, + 122,130, 53,124,143, 31,124,145, 29,123,141, 34,122,130, 53, + 118,114, 96,107, 89,252,102, 80,250,118,117, 85,123,141, 34, + 124,152, 20,124,157, 15,124,154, 18,124,147, 26,122,135, 44, + 122,132, 48,123,141, 34,124,145, 29,124,145, 29,124,143, 31, + 123,141, 34,124,147, 26,124,157, 15,123,168, 5,122,176, 0, + 121,179, 0,120,184, 0,119,186, 0,119,186, 0,120,182, 0, + 122,178, 0,123,174, 1,122,176, 0,123,172, 2,124,164, 9, + 124,147, 26,122,135, 44,117,110,109, 94, 70,242,114,104,197, + 122,135, 44,124,152, 20,124,165, 8,123,174, 1,121,181, 0, + 119,186, 0,118,189, 0,117,191, 0,118,189, 0,117,191, 0, + 114,196, 0,113,199, 0,113,199, 0,114,196, 0,114,196, 0, + 111,202, 0,109,204, 0,108,205, 0,110,203, 0,112,200, 0, + 113,198, 0,114,197, 0,116,192, 0,119,186, 0,121,180, 0, + 123,172, 2,124,161, 11,124,145, 29,118,117, 85,107, 89,252, + 102, 80,250,118,117, 85,123,141, 34,124,152, 20,124,162, 10, + 123,170, 3,123,174, 1,122,177, 0,119,186, 0,115,194, 0, + 111,201, 0,108,206, 0,106,208, 0,105,209, 0,106,207, 0, + 109,204, 0,112,200, 0,115,194, 0,114,197, 0,111,201, 0, + 109,204, 0,108,205, 0,107,206, 0,108,206, 0,108,205, 0, + 109,204, 0,110,202, 0,113,199, 0,117,192, 0,121,181, 0, + 124,161, 11,120,122, 70, 94, 70,242,102, 80,250,123,139, 37, + 123,170, 3,119,186, 0,115,194, 0,112,199, 0,111,202, 0, + 110,202, 0,109,203, 0,108,205, 0,108,206, 0,107,206, 0, + 108,206, 0,108,206, 0,107,207, 0,105,209, 0,103,211, 0, + 104,210, 0,106,207, 0,107,206, 0,108,205, 0,109,203, 0, + 110,202, 0,111,202, 0,112,200, 0,115,194, 0,119,186, 0, + 122,178, 0,123,169, 4,124,154, 18,119,119, 77, 99, 75,247, + 99, 75,247,120,122, 70,124,149, 24,124,160, 12,124,160, 12, + 124,152, 20,124,145, 29,124,145, 29,124,149, 24,124,160, 12, + 124,165, 8,124,166, 7,124,164, 9,124,165, 8,124,161, 11, + 124,150, 22,119,119, 77,121,125, 63,124,150, 22,124,167, 6, + 122,178, 0,120,183, 0,119,185, 0,118,189, 0,118,189, 0, + 119,186, 0,122,178, 0,121,181, 0,121,179, 0,123,172, 2, + 124,158, 13,122,132, 48,111, 96,248,107, 89,252,113,100,242, + 113,100,242,109, 92,251,102, 80,250, 94, 70,242,105, 84,252, + 114,104,197,119,119, 77,124,145, 29,124,158, 13,124,165, 8, + 124,165, 8,124,160, 12,124,150, 22,123,141, 34,122,132, 48, + 121,127, 58,119,119, 77,121,127, 58,122,132, 48,122,130, 53, + 120,122, 70,114,104,197,111, 96,248,109, 92,251,113,100,242, + 118,114, 96,120,122, 70,121,127, 58,122,132, 48,121,127, 58, + 114,104,197,114,104,197,119,119, 77,122,132, 48,123,137, 41, + 123,139, 37,124,145, 29,124,150, 22,124,167, 6,122,175, 0, + 121,179, 0,121,181, 0,121,181, 0,121,180, 0,121,180, 0, + 121,181, 0,121,181, 0,121,182, 0,120,184, 0,120,184, 0, + 120,183, 0,121,181, 0,121,179, 0,122,175, 0,124,164, 9, + 124,145, 29,124,145, 29,124,145, 29,124,143, 31,122,132, 48, + 121,125, 63,122,135, 44,123,141, 34,124,167, 6,121,181, 0, + 119,187, 0,118,189, 0,118,189, 0,114,196, 0,112,200, 0, + 112,200, 0,113,199, 0,115,194, 0,119,186, 0,122,176, 0, + 122,175, 0,123,170, 3,124,165, 8,124,162, 10,124,161, 11, + 124,160, 12,124,155, 16,124,149, 24,123,139, 37,123,141, 34, + 123,139, 37,124,143, 31,124,155, 16,124,160, 12,124,160, 12, + 124,155, 16,124,145, 29,122,130, 53,118,117, 85,120,122, 70, + 124,143, 31,124,155, 16,124,161, 11,124,162, 10,124,160, 12, + 124,154, 18,124,149, 24,124,147, 26,124,145, 29,124,149, 24, + 124,150, 22,124,154, 18,124,158, 13,124,165, 8,123,174, 1, + 121,180, 0,120,182, 0,121,182, 0,120,184, 0,119,185, 0, + 120,183, 0,121,179, 0,122,175, 0,123,170, 3,124,167, 6, + 124,164, 9,124,158, 13,124,152, 20,123,137, 41,121,125, 63, + 121,125, 63,124,149, 24,124,164, 9,123,173, 1,121,181, 0, + 119,186, 0,117,191, 0,115,195, 0,114,197, 0,115,195, 0, + 116,193, 0,117,192, 0,115,195, 0,114,197, 0,114,197, 0, + 113,199, 0,109,203, 0,107,206, 0,107,207, 0,108,205, 0, + 111,201, 0,111,201, 0,112,200, 0,114,197, 0,117,192, 0, + 119,186, 0,121,180, 0,123,171, 3,124,157, 15,124,145, 29, + 123,139, 37,122,132, 48,123,141, 34,124,155, 16,124,162, 10, + 124,164, 9,124,167, 6,123,174, 1,121,181, 0,117,191, 0, + 113,198, 0,109,204, 0,105,209, 0,102,212, 0,101,214, 0, + 102,213, 0,103,211, 0,106,208, 0,109,203, 0,113,198, 0, + 114,196, 0,112,200, 0,110,203, 0,108,205, 0,107,207, 0, + 107,207, 0,107,206, 0,109,204, 0,112,200, 0,116,193, 0, + 120,183, 0,124,165, 8,122,130, 53,118,114, 96,115,107,129, + 122,132, 48,124,161, 11,121,180, 0,117,191, 0,114,197, 0, + 112,200, 0,111,202, 0,110,202, 0,109,203, 0,109,204, 0, + 110,203, 0,107,206, 0,105,209, 0,104,210, 0,100,215, 0, + 98,217, 0, 98,216, 0,101,214, 0,105,209, 0,109,204, 0, + 109,204, 0,109,203, 0,109,203, 0,110,202, 0,113,198, 0, + 117,191, 0,121,182, 0,123,169, 4,124,157, 15,122,130, 53, + 118,114, 96,113,100,242,113,100,242,120,122, 70,122,135, 44, + 123,137, 41,123,139, 37,123,141, 34,124,143, 31,124,150, 22, + 124,155, 16,124,161, 11,124,162, 10,124,164, 9,124,162, 10, + 124,158, 13,124,147, 26,118,117, 85, 99, 75,247,121,125, 63, + 124,154, 18,123,169, 4,120,184, 0,117,192, 0,114,196, 0, + 114,197, 0,116,193, 0,118,188, 0,119,186, 0,119,186, 0, + 120,183, 0,122,175, 0,124,158, 13,124,143, 31,122,135, 44, + 119,119, 77,114,104,197,109, 92,251,102, 80,250, 87, 65,237, + 94, 70,242,113,100,242,122,130, 53,124,149, 24,124,158, 13, + 124,165, 8,124,166, 7,124,162, 10,124,154, 18,124,143, 31, + 122,132, 48,120,122, 70,118,117, 85,119,119, 77,122,135, 44, + 124,143, 31,124,145, 29,124,145, 29,123,141, 34,123,137, 41, + 122,132, 48,122,130, 53,122,132, 48,123,139, 37,124,145, 29, + 123,141, 34,122,135, 44,122,135, 44,122,135, 44,124,147, 26, + 124,150, 22,124,145, 29,123,141, 34,124,145, 29,124,164, 9, + 122,176, 0,120,182, 0,119,186, 0,119,187, 0,119,187, 0, + 119,187, 0,119,186, 0,119,185, 0,120,182, 0,121,180, 0, + 122,177, 0,122,178, 0,122,178, 0,121,179, 0,122,177, 0, + 123,168, 5,124,150, 22,124,147, 26,124,152, 20,124,154, 18, + 124,150, 22,123,141, 34,124,149, 24,124,154, 18,124,161, 11, + 122,177, 0,120,184, 0,119,186, 0,116,193, 0,112,199, 0, + 110,202, 0,110,203, 0,111,201, 0,114,196, 0,118,188, 0, + 120,183, 0,120,182, 0,122,178, 0,123,171, 3,124,167, 6, + 124,165, 8,124,162, 10,124,160, 12,124,157, 15,124,162, 10, + 124,166, 7,124,166, 7,124,164, 9,124,162, 10,123,168, 5, + 123,169, 4,124,167, 6,124,161, 11,124,147, 26,122,132, 48, + 120,122, 70,123,137, 41,124,150, 22,124,160, 12,124,164, 9, + 124,166, 7,124,165, 8,124,162, 10,124,161, 11,124,160, 12, + 124,158, 13,124,158, 13,124,160, 12,124,162, 10,124,167, 6, + 123,174, 1,121,180, 0,120,182, 0,120,182, 0,121,181, 0, + 120,183, 0,120,182, 0,121,180, 0,122,176, 0,123,172, 2, + 123,169, 4,124,167, 6,124,165, 8,124,162, 10,124,154, 18, + 124,152, 20,124,152, 20,124,154, 18,124,167, 6,122,176, 0, + 120,183, 0,118,189, 0,116,193, 0,113,198, 0,112,200, 0, + 112,199, 0,114,197, 0,115,194, 0,114,196, 0,113,199, 0, + 112,200, 0,111,201, 0,108,206, 0,105,209, 0,105,209, 0, + 106,208, 0,109,204, 0,110,202, 0,110,202, 0,112,200, 0, + 114,196, 0,116,192, 0,119,187, 0,121,180, 0,123,169, 4, + 123,168, 5,124,165, 8,124,161, 11,124,155, 16,124,162, 10, + 124,167, 6,123,168, 5,124,165, 8,122,175, 0,120,183, 0, + 116,193, 0,112,200, 0,108,206, 0,103,211, 0,101,214, 0, + 99,216, 0, 98,217, 0, 99,216, 0,100,214, 0,103,211, 0, + 107,207, 0,111,201, 0,115,195, 0,113,198, 0,110,202, 0, + 108,205, 0,107,207, 0,107,206, 0,109,204, 0,112,199, 0, + 116,192, 0,121,181, 0,124,162, 10,122,135, 44,122,132, 48, + 121,127, 58,124,149, 24,124,161, 11,123,172, 2,119,185, 0, + 116,193, 0,113,198, 0,112,200, 0,111,201, 0,111,202, 0, + 111,201, 0,107,206, 0,104,210, 0,102,212, 0, 99,215, 0, + 95,220, 0, 93,222, 0, 94,221, 0, 96,219, 0,100,215, 0, + 105,209, 0,109,203, 0,110,203, 0,109,204, 0,109,204, 0, + 111,201, 0,115,194, 0,119,186, 0,122,175, 0,124,158, 13, + 123,137, 41,123,139, 37,122,130, 53,120,122, 70,117,110,109, + 118,114, 96,120,122, 70,121,127, 58,122,132, 48,124,143, 31, + 124,152, 20,124,158, 13,124,161, 11,124,162, 10,124,161, 11, + 124,158, 13,124,152, 20,123,141, 34,118,114, 96, 69, 60,230, + 99, 75,247,123,141, 34,123,171, 3,119,186, 0,115,194, 0, + 113,199, 0,112,200, 0,114,197, 0,116,192, 0,116,192, 0, + 116,193, 0,117,191, 0,119,185, 0,123,174, 1,123,169, 4, + 124,164, 9,124,157, 15,124,145, 29,122,135, 44,120,122, 70, + 119,119, 77,121,125, 63,123,141, 34,124,158, 13,123,169, 4, + 122,176, 0,122,178, 0,122,178, 0,123,173, 1,124,166, 7, + 124,158, 13,124,150, 22,124,143, 31,123,139, 37,123,139, 37, + 124,149, 24,124,157, 15,124,161, 11,124,162, 10,124,161, 11, + 124,158, 13,124,155, 16,124,154, 18,124,150, 22,124,150, 22, + 124,150, 22,124,152, 20,124,154, 18,124,154, 18,124,147, 26, + 124,155, 16,124,158, 13,124,155, 16,124,145, 29,123,141, 34, + 124,161, 11,122,177, 0,119,186, 0,117,190, 0,117,192, 0, + 116,192, 0,117,191, 0,118,189, 0,119,187, 0,120,183, 0, + 121,179, 0,122,177, 0,123,174, 1,123,173, 1,122,177, 0, + 122,176, 0,123,169, 4,124,154, 18,124,147, 26,124,155, 16, + 124,160, 12,124,157, 15,124,145, 29,124,149, 24,124,152, 20, + 124,158, 13,123,172, 2,121,180, 0,119,186, 0,115,194, 0, + 112,200, 0,109,203, 0,109,203, 0,111,201, 0,114,197, 0, + 117,190, 0,118,188, 0,119,186, 0,121,182, 0,123,174, 1, + 123,168, 5,124,164, 9,124,161, 11,124,160, 12,124,166, 7, + 123,173, 1,122,178, 0,122,178, 0,122,177, 0,123,173, 1, + 123,172, 2,123,174, 1,123,173, 1,124,167, 6,124,154, 18, + 123,137, 41,119,119, 77,118,117, 85,123,137, 41,124,150, 22, + 124,161, 11,124,166, 7,123,168, 5,123,168, 5,124,167, 6, + 124,165, 8,124,164, 9,124,164, 9,124,164, 9,124,166, 7, + 123,169, 4,123,171, 3,122,177, 0,121,180, 0,121,181, 0, + 122,178, 0,121,179, 0,121,180, 0,122,178, 0,122,175, 0, + 123,171, 3,123,168, 5,124,167, 6,124,167, 6,124,166, 7, + 124,162, 10,124,164, 9,124,165, 8,124,167, 6,123,169, 4, + 123,173, 1,121,181, 0,119,187, 0,116,192, 0,113,198, 0, + 111,201, 0,111,201, 0,113,199, 0,114,197, 0,114,197, 0, + 112,200, 0,111,202, 0,110,202, 0,106,207, 0,103,211, 0, + 102,212, 0,103,212, 0,106,208, 0,110,203, 0,109,203, 0, + 110,202, 0,112,200, 0,114,197, 0,116,192, 0,119,186, 0, + 120,182, 0,121,181, 0,122,178, 0,122,175, 0,123,171, 3, + 124,167, 6,123,169, 4,123,169, 4,124,167, 6,122,177, 0, + 119,186, 0,115,194, 0,111,201, 0,107,206, 0,103,211, 0, + 100,214, 0, 98,217, 0, 96,219, 0, 95,219, 0, 96,219, 0, + 98,217, 0,101,214, 0,106,208, 0,111,201, 0,116,193, 0, + 114,197, 0,111,202, 0,109,204, 0,109,204, 0,111,202, 0, + 114,196, 0,118,188, 0,122,176, 0,124,154, 18,123,137, 41, + 123,139, 37,123,141, 34,124,160, 12,123,171, 3,122,178, 0, + 121,180, 0,118,188, 0,115,194, 0,114,197, 0,113,199, 0, + 112,200, 0,108,205, 0,103,211, 0,100,214, 0, 99,216, 0, + 95,220, 0, 91,224, 0, 89,226, 0, 89,225, 0, 92,222, 0, + 97,218, 0,101,213, 0,106,207, 0,110,202, 0,110,202, 0, + 110,203, 0,111,201, 0,114,196, 0,118,189, 0,121,180, 0, + 123,168, 5,124,154, 18,124,155, 16,124,147, 26,123,139, 37, + 121,127, 58,118,114, 96,114,104,197,117,110,109,120,122, 70, + 123,139, 37,124,152, 20,124,158, 13,124,161, 11,124,161, 11, + 124,160, 12,124,158, 13,124,152, 20,122,135, 44,115,107,129, + 109, 92,251,105, 84,252,123,137, 41,123,169, 4,120,184, 0, + 116,193, 0,113,199, 0,112,200, 0,113,198, 0,115,194, 0, + 113,198, 0,112,199, 0,114,197, 0,116,192, 0,119,185, 0, + 120,184, 0,121,181, 0,122,175, 0,123,168, 5,124,161, 11, + 124,160, 12,124,158, 13,124,154, 18,124,164, 9,123,173, 1, + 121,180, 0,120,184, 0,119,186, 0,120,184, 0,121,180, 0, + 123,174, 1,124,167, 6,124,160, 12,124,152, 20,124,147, 26, + 124,145, 29,124,155, 16,124,164, 9,124,167, 6,123,169, 4, + 123,169, 4,123,168, 5,124,166, 7,124,165, 8,124,162, 10, + 124,161, 11,124,161, 11,124,164, 9,124,165, 8,124,164, 9, + 124,157, 15,124,161, 11,124,162, 10,124,158, 13,124,149, 24, + 122,135, 44,124,160, 12,122,178, 0,118,188, 0,116,193, 0, + 115,195, 0,115,194, 0,116,193, 0,117,191, 0,119,186, 0, + 121,182, 0,122,177, 0,123,173, 1,123,172, 2,123,173, 1, + 123,173, 1,123,172, 2,123,168, 5,124,157, 15,124,147, 26, + 124,155, 16,124,160, 12,124,157, 15,124,145, 29,123,137, 41, + 123,141, 34,124,150, 22,123,168, 5,122,177, 0,120,182, 0, + 117,191, 0,113,198, 0,111,202, 0,110,202, 0,112,200, 0, + 114,196, 0,117,191, 0,117,190, 0,118,188, 0,120,182, 0, + 123,174, 1,123,169, 4,124,166, 7,124,160, 12,124,158, 13, + 123,169, 4,122,178, 0,120,184, 0,119,185, 0,120,184, 0, + 121,180, 0,122,175, 0,122,177, 0,122,175, 0,123,169, 4, + 124,155, 16,122,132, 48,115,107,129,105, 84,252,117,110,109, + 122,132, 48,124,150, 22,124,161, 11,124,167, 6,123,168, 5, + 124,167, 6,124,166, 7,124,164, 9,124,164, 9,124,164, 9, + 124,167, 6,123,171, 3,123,172, 2,123,172, 2,122,175, 0, + 122,177, 0,123,174, 1,123,173, 1,122,176, 0,122,175, 0, + 123,171, 3,123,168, 5,123,170, 3,124,167, 6,124,164, 9, + 124,165, 8,124,164, 9,124,166, 7,123,169, 4,123,171, 3, + 123,172, 2,123,174, 1,122,176, 0,121,181, 0,118,188, 0, + 115,194, 0,113,198, 0,112,199, 0,113,198, 0,114,197, 0, + 113,198, 0,111,202, 0,109,203, 0,110,203, 0,106,208, 0, + 102,213, 0,100,215, 0,100,215, 0,102,212, 0,106,207, 0, + 109,203, 0,110,203, 0,111,201, 0,112,199, 0,114,196, 0, + 117,190, 0,118,189, 0,118,188, 0,119,185, 0,120,182, 0, + 121,180, 0,122,176, 0,123,171, 3,123,169, 4,124,167, 6, + 122,178, 0,118,189, 0,113,198, 0,108,205, 0,103,211, 0, + 100,214, 0, 98,217, 0, 97,217, 0, 96,219, 0, 94,221, 0, + 93,221, 0, 94,221, 0, 96,219, 0,100,215, 0,106,208, 0, + 112,199, 0,117,190, 0,114,196, 0,112,200, 0,112,200, 0, + 114,197, 0,117,191, 0,121,182, 0,124,166, 7,123,137, 41, + 122,132, 48,123,139, 37,124,147, 26,124,167, 6,122,178, 0, + 120,184, 0,119,186, 0,119,185, 0,118,189, 0,116,193, 0, + 114,196, 0,111,201, 0,105,209, 0,100,215, 0, 97,218, 0, + 95,219, 0, 92,222, 0, 87,227, 0, 86,228, 0, 87,227, 0, + 90,224, 0, 95,220, 0,100,215, 0,105,209, 0,109,204, 0, + 112,200, 0,111,201, 0,112,199, 0,114,196, 0,117,190, 0, + 120,183, 0,123,174, 1,124,164, 9,124,162, 10,124,158, 13, + 124,149, 24,123,139, 37,121,125, 63,118,114, 96,119,119, 77, + 121,127, 58,122,135, 44,124,150, 22,124,158, 13,124,161, 11, + 124,158, 13,124,157, 15,124,157, 15,124,157, 15,124,152, 20, + 124,143, 31,122,135, 44,121,127, 58,121,127, 58,124,162, 10, + 121,180, 0,118,189, 0,115,195, 0,114,197, 0,114,196, 0, + 112,200, 0,109,204, 0,108,205, 0,110,203, 0,113,198, 0, + 115,194, 0,116,193, 0,117,191, 0,119,187, 0,121,182, 0, + 121,179, 0,121,179, 0,122,178, 0,123,174, 1,122,177, 0, + 120,183, 0,118,188, 0,117,190, 0,118,189, 0,119,187, 0, + 120,182, 0,122,176, 0,123,168, 5,124,160, 12,124,150, 22, + 124,145, 29,124,145, 29,124,155, 16,124,164, 9,123,168, 5, + 123,170, 3,123,171, 3,123,171, 3,123,170, 3,123,168, 5, + 124,167, 6,124,166, 7,124,167, 6,123,168, 5,123,169, 4, + 123,168, 5,124,162, 10,124,162, 10,124,161, 11,124,157, 15, + 124,147, 26,122,130, 53,124,160, 12,121,179, 0,118,189, 0, + 115,194, 0,114,196, 0,115,195, 0,116,193, 0,118,189, 0, + 119,186, 0,118,188, 0,118,188, 0,119,185, 0,122,178, 0, + 123,170, 3,123,172, 2,123,170, 3,124,165, 8,124,157, 15, + 124,145, 29,124,152, 20,124,155, 16,124,152, 20,122,135, 44, + 114,104,197,117,110,109,124,149, 24,124,166, 7,123,174, 1, + 122,177, 0,119,185, 0,116,193, 0,114,197, 0,113,198, 0, + 114,197, 0,115,194, 0,117,191, 0,117,191, 0,118,188, 0, + 120,182, 0,121,180, 0,121,180, 0,121,179, 0,122,176, 0, + 123,174, 1,123,172, 2,121,179, 0,119,186, 0,118,188, 0, + 119,187, 0,120,184, 0,122,178, 0,122,178, 0,122,176, 0, + 123,168, 5,124,152, 20,122,135, 44,121,125, 63,117,110,109, + 115,107,129,114,104,197,122,130, 53,124,149, 24,124,158, 13, + 124,162, 10,124,162, 10,124,160, 12,124,158, 13,124,158, 13, + 124,160, 12,124,164, 9,123,170, 3,123,173, 1,123,169, 4, + 123,169, 4,123,170, 3,123,169, 4,124,165, 8,123,169, 4, + 123,168, 5,124,164, 9,123,172, 2,122,177, 0,122,176, 0, + 123,168, 5,124,158, 13,124,160, 12,124,162, 10,124,167, 6, + 123,169, 4,123,170, 3,123,171, 3,123,172, 2,123,173, 1, + 122,178, 0,119,187, 0,116,192, 0,115,194, 0,115,195, 0, + 115,195, 0,113,199, 0,110,202, 0,109,204, 0,109,204, 0, + 105,209, 0,101,214, 0, 98,217, 0, 97,218, 0, 99,216, 0, + 103,212, 0,108,205, 0,110,203, 0,110,202, 0,112,200, 0, + 114,197, 0,116,192, 0,116,192, 0,117,191, 0,118,188, 0, + 119,185, 0,120,183, 0,121,180, 0,122,176, 0,123,170, 3, + 124,166, 7,121,180, 0,117,192, 0,111,201, 0,105,209, 0, + 100,215, 0, 95,220, 0, 92,222, 0, 91,223, 0, 92,222, 0, + 94,221, 0, 92,222, 0, 92,223, 0, 93,222, 0, 96,219, 0, + 101,214, 0,108,206, 0,115,194, 0,118,189, 0,116,193, 0, + 116,193, 0,117,191, 0,120,184, 0,123,171, 3,124,149, 24, + 119,119, 77,119,119, 77,122,130, 53,124,145, 29,123,171, 3, + 120,184, 0,118,189, 0,117,191, 0,118,189, 0,120,184, 0, + 118,188, 0,117,192, 0,110,203, 0,102,212, 0, 97,218, 0, + 93,221, 0, 92,222, 0, 91,223, 0, 87,227, 0, 85,228, 0, + 87,227, 0, 90,224, 0, 95,220, 0,100,215, 0,105,209, 0, + 109,204, 0,112,200, 0,114,196, 0,114,196, 0,116,193, 0, + 118,189, 0,120,183, 0,122,176, 0,123,168, 5,124,166, 7, + 124,162, 10,124,155, 16,124,147, 26,123,141, 34,124,149, 24, + 124,154, 18,124,154, 18,124,149, 24,124,158, 13,124,164, 9, + 124,165, 8,124,164, 9,124,162, 10,124,167, 6,123,169, 4, + 124,166, 7,124,160, 12,124,154, 18,124,147, 26,123,139, 37, + 124,152, 20,123,172, 2,120,184, 0,118,189, 0,117,192, 0, + 113,199, 0,108,205, 0,105,209, 0,105,209, 0,106,207, 0, + 110,203, 0,112,200, 0,112,200, 0,113,198, 0,115,194, 0, + 117,191, 0,117,190, 0,117,190, 0,118,189, 0,119,186, 0, + 119,186, 0,117,190, 0,116,193, 0,116,193, 0,116,192, 0, + 118,188, 0,120,182, 0,123,174, 1,124,166, 7,124,155, 16, + 124,150, 22,124,145, 29,124,143, 31,124,152, 20,124,160, 12, + 124,165, 8,124,167, 6,123,168, 5,123,169, 4,123,168, 5, + 124,167, 6,124,166, 7,124,165, 8,124,166, 7,123,168, 5, + 123,169, 4,123,168, 5,124,162, 10,124,160, 12,124,157, 15, + 124,150, 22,123,139, 37,122,135, 44,124,161, 11,121,179, 0, + 118,189, 0,116,193, 0,115,195, 0,115,194, 0,117,192, 0, + 118,189, 0,115,194, 0,114,197, 0,114,197, 0,115,194, 0, + 118,188, 0,122,178, 0,123,171, 3,123,169, 4,124,162, 10, + 124,158, 13,124,147, 26,124,147, 26,124,147, 26,123,141, 34, + 119,119, 77, 99, 75,247,117,110,109,124,147, 26,124,164, 9, + 123,171, 3,123,173, 1,123,174, 1,120,184, 0,118,189, 0, + 117,192, 0,117,191, 0,118,189, 0,117,191, 0,117,191, 0, + 118,188, 0,120,182, 0,120,184, 0,119,186, 0,119,186, 0, + 119,186, 0,119,186, 0,120,184, 0,121,180, 0,119,185, 0, + 118,188, 0,119,187, 0,120,184, 0,121,179, 0,121,179, 0, + 122,175, 0,124,167, 6,124,147, 26,124,143, 31,123,139, 37, + 122,132, 48,122,132, 48,122,130, 53,120,122, 70,121,127, 58, + 124,143, 31,124,147, 26,124,149, 24,124,147, 26,124,145, 29, + 124,145, 29,124,149, 24,124,155, 16,124,165, 8,123,170, 3, + 123,168, 5,124,164, 9,124,165, 8,124,162, 10,124,155, 16, + 124,160, 12,124,158, 13,124,165, 8,122,176, 0,121,182, 0, + 120,183, 0,122,178, 0,123,168, 5,124,150, 22,124,154, 18, + 124,160, 12,124,164, 9,124,164, 9,124,164, 9,124,164, 9, + 124,165, 8,124,166, 7,122,175, 0,120,184, 0,118,188, 0, + 117,190, 0,116,193, 0,112,199, 0,110,203, 0,109,204, 0, + 109,204, 0,105,209, 0,100,214, 0, 97,218, 0, 95,220, 0, + 97,218, 0,100,214, 0,106,208, 0,110,202, 0,110,202, 0, + 111,201, 0,113,198, 0,115,194, 0,116,193, 0,117,191, 0, + 119,186, 0,120,184, 0,121,182, 0,121,179, 0,122,176, 0, + 123,171, 3,124,166, 7,121,181, 0,116,192, 0,110,202, 0, + 104,210, 0, 97,217, 0, 92,222, 0, 89,226, 0, 87,227, 0, + 89,226, 0, 91,223, 0, 92,222, 0, 91,223, 0, 91,223, 0, + 94,221, 0, 98,217, 0,104,210, 0,112,200, 0,119,187, 0, + 119,186, 0,119,186, 0,120,183, 0,123,174, 1,124,158, 13, + 122,130, 53,119,119, 77,118,117, 85,119,119, 77,124,145, 29, + 123,173, 1,119,187, 0,116,193, 0,115,194, 0,117,192, 0, + 119,186, 0,120,184, 0,117,191, 0,109,204, 0,101,214, 0, + 94,220, 0, 90,224, 0, 89,225, 0, 90,225, 0, 87,227, 0, + 87,227, 0, 88,226, 0, 91,223, 0, 96,219, 0,101,214, 0, + 106,208, 0,109,203, 0,112,199, 0,115,195, 0,117,191, 0, + 118,189, 0,119,186, 0,121,181, 0,122,175, 0,123,169, 4, + 124,165, 8,124,164, 9,124,160, 12,124,157, 15,124,162, 10, + 124,166, 7,123,168, 5,124,167, 6,124,162, 10,123,171, 3, + 122,175, 0,122,176, 0,122,175, 0,123,172, 2,123,172, 2, + 123,173, 1,123,171, 3,123,173, 1,123,170, 3,124,164, 9, + 124,150, 22,123,141, 34,124,162, 10,122,176, 0,120,184, 0, + 115,194, 0,110,203, 0,105,209, 0,102,212, 0,101,213, 0, + 103,211, 0,106,207, 0,110,202, 0,110,203, 0,110,202, 0, + 112,200, 0,113,198, 0,114,197, 0,114,197, 0,114,196, 0, + 116,192, 0,115,195, 0,113,198, 0,113,199, 0,113,198, 0, + 115,194, 0,118,189, 0,121,182, 0,123,173, 1,123,172, 2, + 123,171, 3,123,170, 3,123,171, 3,123,171, 3,123,168, 5, + 124,165, 8,124,161, 11,124,162, 10,124,164, 9,124,164, 9, + 124,162, 10,124,160, 12,124,158, 13,124,157, 15,124,158, 13, + 124,161, 11,124,164, 9,124,162, 10,124,157, 15,124,154, 18, + 124,149, 24,123,139, 37,120,122, 70,123,139, 37,124,161, 11, + 122,178, 0,118,188, 0,117,192, 0,116,193, 0,117,191, 0, + 118,189, 0,115,195, 0,112,200, 0,110,202, 0,110,202, 0, + 112,200, 0,116,193, 0,120,184, 0,123,171, 3,123,170, 3, + 124,166, 7,124,160, 12,124,154, 18,124,143, 31,123,141, 34, + 122,130, 53,113,100,242,115,107,129,119,119, 77,123,139, 37, + 124,158, 13,124,166, 7,123,169, 4,123,169, 4,123,170, 3, + 122,178, 0,120,182, 0,120,184, 0,118,188, 0,117,190, 0, + 117,190, 0,118,188, 0,121,181, 0,120,184, 0,119,187, 0, + 118,189, 0,117,191, 0,117,192, 0,117,190, 0,119,186, 0, + 120,182, 0,119,185, 0,120,184, 0,121,182, 0,121,182, 0, + 121,180, 0,122,175, 0,124,166, 7,124,143, 31,123,141, 34, + 123,139, 37,123,137, 41,123,141, 34,123,141, 34,122,135, 44, + 120,122, 70,118,114, 96,120,122, 70,120,122, 70,121,127, 58, + 123,137, 41,124,143, 31,124,147, 26,124,149, 24,124,150, 22, + 124,160, 12,124,160, 12,124,161, 11,124,161, 11,124,158, 13, + 124,149, 24,124,147, 26,124,145, 29,124,166, 7,122,178, 0, + 120,184, 0,119,186, 0,120,184, 0,122,178, 0,124,164, 9, + 123,141, 34,124,150, 22,124,154, 18,124,155, 16,124,154, 18, + 124,161, 11,124,164, 9,124,164, 9,124,160, 12,123,173, 1, + 121,180, 0,119,186, 0,116,193, 0,113,198, 0,111,202, 0, + 110,203, 0,110,202, 0,106,208, 0,100,214, 0, 97,218, 0, + 95,220, 0, 96,219, 0,100,215, 0,105,209, 0,110,202, 0, + 110,202, 0,111,201, 0,113,198, 0,116,193, 0,117,191, 0, + 118,188, 0,120,182, 0,122,178, 0,122,178, 0,122,175, 0, + 123,171, 3,123,168, 5,123,170, 3,119,185, 0,115,194, 0, + 111,201, 0,105,209, 0, 98,217, 0, 92,223, 0, 88,226, 0, + 86,228, 0, 87,227, 0, 90,224, 0, 94,221, 0, 92,222, 0, + 92,223, 0, 93,221, 0, 97,218, 0,103,212, 0,110,202, 0, + 117,190, 0,121,179, 0,121,180, 0,122,176, 0,124,166, 7, + 124,150, 22,122,132, 48,121,125, 63,119,119, 77,115,107,129, + 124,147, 26,122,175, 0,118,188, 0,115,194, 0,114,196, 0, + 116,193, 0,118,188, 0,121,180, 0,117,191, 0,108,205, 0, + 100,215, 0, 93,222, 0, 89,226, 0, 87,227, 0, 88,226, 0, + 90,224, 0, 89,225, 0, 91,224, 0, 94,221, 0, 97,218, 0, + 99,215, 0,103,212, 0,107,206, 0,113,198, 0,115,194, 0, + 118,188, 0,120,184, 0,121,181, 0,122,177, 0,122,177, 0, + 123,173, 1,123,172, 2,124,167, 6,124,166, 7,123,168, 5, + 123,171, 3,123,174, 1,122,176, 0,122,175, 0,123,171, 3, + 122,178, 0,121,182, 0,120,183, 0,120,183, 0,121,182, 0, + 121,181, 0,121,181, 0,120,183, 0,119,185, 0,120,183, 0, + 121,179, 0,123,173, 1,124,165, 8,124,162, 10,122,177, 0, + 118,188, 0,113,198, 0,108,206, 0,103,212, 0,100,215, 0, + 99,216, 0,100,214, 0,103,212, 0,106,207, 0,109,204, 0, + 109,204, 0,109,203, 0,110,202, 0,111,202, 0,111,201, 0, + 111,201, 0,111,202, 0,109,203, 0,108,205, 0,109,204, 0, + 110,202, 0,113,198, 0,117,191, 0,120,182, 0,120,183, 0, + 120,182, 0,121,182, 0,120,184, 0,119,185, 0,120,184, 0, + 121,182, 0,122,178, 0,123,174, 1,123,170, 3,124,167, 6, + 124,164, 9,124,161, 11,124,161, 11,124,162, 10,124,162, 10, + 124,162, 10,124,161, 11,124,154, 18,124,154, 18,124,149, 24, + 123,141, 34,122,130, 53,118,117, 85,122,130, 53,124,143, 31, + 124,161, 11,122,178, 0,119,186, 0,117,190, 0,117,191, 0, + 118,189, 0,117,192, 0,113,198, 0,110,202, 0,108,205, 0, + 108,206, 0,110,202, 0,114,197, 0,118,189, 0,122,178, 0, + 123,170, 3,124,167, 6,124,160, 12,124,157, 15,124,150, 22, + 123,139, 37,121,127, 58,118,117, 85,119,119, 77,121,127, 58, + 123,139, 37,124,147, 26,124,158, 13,124,162, 10,124,165, 8, + 124,166, 7,123,170, 3,122,176, 0,121,182, 0,119,186, 0, + 118,188, 0,118,189, 0,119,186, 0,121,180, 0,122,178, 0, + 120,183, 0,118,188, 0,116,192, 0,115,194, 0,116,193, 0, + 117,190, 0,119,187, 0,119,185, 0,120,184, 0,120,184, 0, + 120,183, 0,121,181, 0,122,176, 0,124,166, 7,124,143, 31, + 120,122, 70,121,125, 63,121,127, 58,123,139, 37,123,141, 34, + 122,135, 44,121,125, 63,118,117, 85,118,114, 96,118,117, 85, + 120,122, 70,122,130, 53,122,135, 44,123,141, 34,124,143, 31, + 124,147, 26,124,150, 22,124,154, 18,124,157, 15,124,157, 15, + 124,152, 20,124,143, 31,122,130, 53,124,143, 31,124,164, 9, + 122,177, 0,120,184, 0,119,186, 0,119,185, 0,121,180, 0, + 123,169, 4,124,150, 22,124,143, 31,124,147, 26,124,154, 18, + 124,161, 11,124,167, 6,123,169, 4,123,169, 4,124,165, 8, + 124,166, 7,122,175, 0,120,183, 0,118,189, 0,115,194, 0, + 114,197, 0,113,199, 0,111,202, 0,106,208, 0,101,213, 0, + 98,217, 0, 96,219, 0, 97,218, 0,100,214, 0,105,209, 0, + 110,202, 0,111,202, 0,112,200, 0,114,196, 0,117,191, 0, + 118,188, 0,120,183, 0,122,177, 0,121,180, 0,122,178, 0, + 123,174, 1,124,166, 7,124,164, 9,122,176, 0,117,190, 0, + 112,199, 0,108,205, 0,106,208, 0,101,214, 0, 95,220, 0, + 90,224, 0, 88,226, 0, 88,226, 0, 90,224, 0, 94,221, 0, + 95,220, 0, 94,220, 0, 95,220, 0, 98,216, 0,103,211, 0, + 110,202, 0,117,191, 0,121,179, 0,122,175, 0,123,170, 3, + 124,160, 12,124,149, 24,122,132, 48,120,122, 70,118,117, 85, + 114,104,197,124,145, 29,123,174, 1,119,187, 0,116,193, 0, + 115,194, 0,117,191, 0,119,186, 0,121,180, 0,117,192, 0, + 108,205, 0,100,215, 0, 93,222, 0, 88,226, 0, 87,227, 0, + 87,227, 0, 89,225, 0, 91,223, 0, 92,222, 0, 94,220, 0, + 96,219, 0, 98,216, 0,102,213, 0,106,207, 0,112,200, 0, + 115,194, 0,118,188, 0,121,179, 0,122,177, 0,122,178, 0, + 122,178, 0,122,177, 0,123,173, 1,123,170, 3,123,171, 3, + 123,173, 1,122,176, 0,122,178, 0,121,181, 0,121,180, 0, + 122,178, 0,121,180, 0,120,184, 0,119,186, 0,119,187, 0, + 118,188, 0,118,188, 0,118,189, 0,117,191, 0,116,192, 0, + 117,191, 0,118,189, 0,119,185, 0,121,180, 0,122,176, 0, + 120,182, 0,117,192, 0,111,201, 0,106,207, 0,102,213, 0, + 99,216, 0, 98,217, 0, 98,216, 0,101,214, 0,103,211, 0, + 105,209, 0,107,207, 0,108,205, 0,108,206, 0,107,207, 0, + 106,207, 0,105,209, 0,105,209, 0,103,211, 0,103,211, 0, + 105,209, 0,107,206, 0,111,201, 0,116,192, 0,118,189, 0, + 117,190, 0,118,189, 0,117,192, 0,116,193, 0,116,193, 0, + 117,191, 0,118,188, 0,120,184, 0,121,181, 0,122,178, 0, + 123,174, 1,123,171, 3,123,171, 3,123,171, 3,123,171, 3, + 123,170, 3,124,167, 6,124,162, 10,124,155, 16,124,147, 26, + 123,141, 34,120,122, 70,119,119, 77,121,125, 63,122,135, 44, + 124,145, 29,124,162, 10,122,178, 0,119,185, 0,118,188, 0, + 118,188, 0,119,186, 0,117,192, 0,113,199, 0,109,204, 0, + 107,207, 0,106,207, 0,108,205, 0,112,200, 0,116,193, 0, + 120,184, 0,122,175, 0,124,165, 8,124,161, 11,124,160, 12, + 124,155, 16,124,149, 24,123,137, 41,121,125, 63,122,130, 53, + 122,135, 44,123,139, 37,124,145, 29,124,149, 24,124,152, 20, + 124,157, 15,124,161, 11,124,166, 7,123,171, 3,122,177, 0, + 121,181, 0,120,184, 0,120,184, 0,120,183, 0,122,178, 0, + 123,168, 5,122,176, 0,119,185, 0,117,191, 0,115,194, 0, + 115,194, 0,116,193, 0,117,190, 0,118,188, 0,119,186, 0, + 120,184, 0,120,182, 0,121,179, 0,123,173, 1,124,164, 9, + 124,143, 31,113,100,242,105, 84,252,115,107,129,120,122, 70, + 121,127, 58,121,125, 63,118,114, 96,114,104,197,111, 96,248, + 111, 96,248,113,100,242,115,107,129,118,114, 96,119,119, 77, + 121,125, 63,122,130, 53,123,137, 41,123,141, 34,124,145, 29, + 124,145, 29,123,141, 34,122,130, 53,120,122, 70,123,141, 34, + 124,160, 12,123,172, 2,121,179, 0,120,183, 0,120,182, 0, + 122,178, 0,123,168, 5,124,152, 20,123,139, 37,124,145, 29, + 124,154, 18,124,164, 9,123,169, 4,123,173, 1,123,173, 1, + 123,169, 4,124,162, 10,123,169, 4,122,176, 0,120,182, 0, + 119,186, 0,117,191, 0,114,197, 0,111,201, 0,107,207, 0, + 103,211, 0,100,215, 0, 98,216, 0,100,215, 0,102,212, 0, + 106,207, 0,111,202, 0,112,199, 0,114,197, 0,116,193, 0, + 119,186, 0,120,184, 0,122,178, 0,123,174, 1,122,176, 0, + 123,174, 1,123,168, 5,124,157, 15,124,160, 12,121,182, 0, + 115,194, 0,109,203, 0,105,209, 0,102,212, 0,102,213, 0, + 99,215, 0, 94,220, 0, 91,223, 0, 91,224, 0, 92,223, 0, + 94,221, 0, 99,216, 0, 99,216, 0, 99,215, 0,102,213, 0, + 106,208, 0,112,200, 0,118,189, 0,121,181, 0,123,174, 1, + 123,168, 5,124,161, 11,124,152, 20,122,135, 44,119,119, 77, + 118,117, 85,118,114, 96,123,139, 37,123,169, 4,120,183, 0, + 118,189, 0,117,190, 0,118,188, 0,121,182, 0,120,182, 0, + 116,193, 0,108,205, 0,101,214, 0, 94,220, 0, 90,224, 0, + 88,226, 0, 88,226, 0, 89,225, 0, 91,223, 0, 93,222, 0, + 95,220, 0, 97,218, 0,100,215, 0,103,212, 0,107,207, 0, + 111,201, 0,115,194, 0,118,189, 0,121,181, 0,122,175, 0, + 122,177, 0,122,178, 0,122,177, 0,123,172, 2,122,175, 0, + 122,177, 0,122,177, 0,122,178, 0,121,181, 0,120,183, 0, + 120,184, 0,120,183, 0,121,180, 0,120,183, 0,119,186, 0, + 118,188, 0,117,190, 0,117,192, 0,116,193, 0,114,196, 0, + 114,197, 0,114,196, 0,116,193, 0,117,191, 0,118,188, 0, + 119,186, 0,119,185, 0,115,194, 0,111,202, 0,106,208, 0, + 102,212, 0,100,215, 0, 98,216, 0, 99,216, 0,100,215, 0, + 101,213, 0,103,212, 0,104,210, 0,106,208, 0,106,207, 0, + 104,210, 0,102,212, 0,100,214, 0, 99,215, 0, 99,216, 0, + 99,215, 0,101,213, 0,105,209, 0,109,203, 0,115,195, 0, + 115,194, 0,115,194, 0,114,197, 0,112,199, 0,112,200, 0, + 113,198, 0,114,196, 0,117,192, 0,118,188, 0,120,184, 0, + 121,182, 0,122,178, 0,122,178, 0,122,177, 0,122,176, 0, + 122,175, 0,123,172, 2,123,169, 4,124,162, 10,124,152, 20, + 124,147, 26,123,141, 34,123,137, 41,122,132, 48,121,127, 58, + 122,132, 48,123,141, 34,124,161, 11,122,176, 0,120,183, 0, + 119,186, 0,119,186, 0,120,184, 0,117,190, 0,113,198, 0, + 109,203, 0,107,207, 0,106,207, 0,107,206, 0,110,202, 0, + 114,196, 0,118,189, 0,121,181, 0,123,171, 3,124,161, 11, + 124,162, 10,124,161, 11,124,155, 16,124,145, 29,123,141, 34, + 124,145, 29,124,149, 24,124,152, 20,124,150, 22,124,145, 29, + 124,149, 24,124,150, 22,124,150, 22,124,155, 16,124,161, 11, + 124,166, 7,123,170, 3,123,174, 1,122,176, 0,122,176, 0, + 123,172, 2,124,167, 6,124,167, 6,122,178, 0,119,186, 0, + 117,191, 0,116,193, 0,116,193, 0,117,191, 0,118,188, 0, + 119,185, 0,120,182, 0,122,178, 0,123,174, 1,123,168, 5, + 124,158, 13,123,139, 37,113,100,242, 69, 60,230, 94, 70,242, + 109, 92,251,114,104,197,113,100,242,109, 92,251, 99, 75,247, + 69, 60,230, 30, 55,222, 94, 70,242,107, 89,252,113,100,242, + 114,104,197,113,100,242,113,100,242,119,119, 77,122,135, 44, + 123,141, 34,124,145, 29,124,143, 31,123,141, 34,122,135, 44, + 122,132, 48,124,150, 22,124,162, 10,123,170, 3,123,174, 1, + 123,174, 1,123,171, 3,124,162, 10,124,147, 26,123,137, 41, + 122,135, 44,124,149, 24,124,161, 11,123,169, 4,123,174, 1, + 122,175, 0,123,171, 3,124,162, 10,124,158, 13,124,165, 8, + 122,175, 0,120,184, 0,117,190, 0,115,195, 0,112,200, 0, + 109,204, 0,106,208, 0,104,210, 0,103,212, 0,103,211, 0, + 105,209, 0,108,205, 0,111,201, 0,114,196, 0,116,192, 0, + 118,188, 0,121,181, 0,122,177, 0,123,171, 3,124,166, 7, + 124,166, 7,124,165, 8,124,157, 15,124,147, 26,124,166, 7, + 119,185, 0,113,198, 0,107,206, 0,103,212, 0,100,214, 0, + 100,215, 0,101,214, 0,100,215, 0, 96,219, 0, 95,220, 0, + 94,220, 0, 96,219, 0,100,215, 0,105,209, 0,105,209, 0, + 106,207, 0,110,203, 0,114,196, 0,118,188, 0,120,182, 0, + 122,176, 0,123,169, 4,124,162, 10,124,154, 18,123,137, 41, + 118,114, 96,117,110,109,121,127, 58,123,137, 41,124,162, 10, + 122,177, 0,120,184, 0,119,185, 0,120,182, 0,120,182, 0, + 119,186, 0,116,192, 0,110,202, 0,104,210, 0, 98,217, 0, + 94,221, 0, 92,223, 0, 91,223, 0, 92,223, 0, 93,222, 0, + 95,220, 0, 97,218, 0, 99,215, 0,102,212, 0,105,209, 0, + 109,204, 0,112,199, 0,115,195, 0,118,189, 0,120,183, 0, + 122,176, 0,123,172, 2,123,173, 1,123,172, 2,122,175, 0, + 122,178, 0,121,182, 0,120,182, 0,120,182, 0,121,182, 0, + 120,184, 0,119,186, 0,119,186, 0,120,184, 0,121,181, 0, + 120,184, 0,119,186, 0,118,189, 0,116,192, 0,115,194, 0, + 114,196, 0,114,197, 0,114,196, 0,115,194, 0,116,192, 0, + 117,191, 0,117,190, 0,117,190, 0,116,193, 0,112,200, 0, + 108,206, 0,105,209, 0,102,212, 0,101,213, 0,101,214, 0, + 101,213, 0,102,213, 0,102,212, 0,103,211, 0,104,210, 0, + 106,208, 0,103,212, 0,100,215, 0, 97,217, 0, 96,219, 0, + 96,219, 0, 97,218, 0, 99,216, 0,102,212, 0,107,206, 0, + 113,198, 0,114,196, 0,113,198, 0,110,202, 0,109,203, 0, + 109,203, 0,111,202, 0,113,198, 0,115,194, 0,118,189, 0, + 119,186, 0,120,183, 0,121,180, 0,121,181, 0,121,180, 0, + 122,178, 0,122,176, 0,123,171, 3,124,167, 6,124,161, 11, + 124,152, 20,124,145, 29,123,141, 34,124,143, 31,124,143, 31, + 123,141, 34,123,137, 41,122,130, 53,124,155, 16,123,171, 3, + 121,179, 0,120,182, 0,120,182, 0,121,181, 0,119,187, 0, + 115,195, 0,111,201, 0,108,205, 0,107,207, 0,108,205, 0, + 111,202, 0,114,197, 0,117,191, 0,120,184, 0,122,175, 0, + 124,164, 9,124,166, 7,124,166, 7,124,161, 11,124,154, 18, + 124,150, 22,124,152, 20,124,155, 16,124,158, 13,124,161, 11, + 124,160, 12,124,154, 18,124,147, 26,124,149, 24,124,147, 26, + 124,147, 26,124,149, 24,124,152, 20,124,158, 13,124,164, 9, + 124,167, 6,124,167, 6,124,164, 9,124,155, 16,123,168, 5, + 122,178, 0,119,186, 0,118,189, 0,117,191, 0,118,189, 0, + 119,185, 0,121,181, 0,122,176, 0,123,170, 3,124,165, 8, + 124,157, 15,124,147, 26,121,127, 58,109, 92,251,107, 89,252, + 102, 80,250, 87, 65,237, 69, 60,230, 69, 60,230, 30, 49,213, + 30, 37,192, 30, 31,180, 30, 55,222, 99, 75,247,107, 89,252, + 111, 96,248,111, 96,248,109, 92,251,105, 84,252,117,110,109, + 122,130, 53,124,143, 31,124,149, 24,124,150, 22,124,149, 24, + 124,143, 31,122,135, 44,122,132, 48,124,145, 29,124,154, 18, + 124,160, 12,124,161, 11,124,158, 13,124,152, 20,124,147, 26, + 123,141, 34,120,122, 70,123,137, 41,124,154, 18,124,165, 8, + 123,171, 3,123,172, 2,123,169, 4,124,160, 12,124,145, 29, + 124,154, 18,123,171, 3,121,181, 0,119,187, 0,117,192, 0, + 114,196, 0,112,199, 0,110,202, 0,109,204, 0,108,205, 0, + 108,205, 0,109,204, 0,110,202, 0,112,200, 0,114,196, 0, + 118,189, 0,120,182, 0,122,175, 0,123,168, 5,124,164, 9, + 124,155, 16,124,149, 24,124,145, 29,123,137, 41,123,137, 41, + 123,169, 4,119,186, 0,113,198, 0,107,207, 0,102,212, 0, + 100,214, 0,100,215, 0,101,214, 0,103,212, 0,102,212, 0, + 100,215, 0, 99,216, 0, 99,215, 0,102,212, 0,108,205, 0, + 110,202, 0,112,200, 0,114,197, 0,117,190, 0,118,188, 0, + 120,183, 0,122,177, 0,123,169, 4,124,162, 10,124,152, 20, + 123,139, 37,117,110,109,115,107,129,122,135, 44,124,147, 26, + 124,155, 16,123,170, 3,122,177, 0,121,179, 0,120,182, 0, + 119,186, 0,118,188, 0,117,190, 0,113,198, 0,108,205, 0, + 104,210, 0,100,215, 0, 97,217, 0, 96,219, 0, 96,219, 0, + 97,218, 0, 98,217, 0,100,214, 0,103,212, 0,106,208, 0, + 109,204, 0,111,201, 0,113,198, 0,115,194, 0,118,189, 0, + 120,184, 0,122,178, 0,123,170, 3,124,167, 6,123,169, 4, + 122,176, 0,121,181, 0,120,184, 0,119,186, 0,119,186, 0, + 119,186, 0,120,183, 0,119,186, 0,119,187, 0,119,186, 0, + 120,183, 0,121,179, 0,120,182, 0,119,186, 0,118,189, 0, + 117,192, 0,116,193, 0,116,193, 0,116,192, 0,117,191, 0, + 118,189, 0,118,189, 0,117,190, 0,117,191, 0,116,193, 0, + 114,197, 0,111,201, 0,109,204, 0,107,207, 0,106,208, 0, + 105,209, 0,105,209, 0,104,210, 0,103,211, 0,103,211, 0, + 104,210, 0,105,209, 0,103,212, 0, 99,215, 0, 96,219, 0, + 95,220, 0, 95,220, 0, 95,219, 0, 97,217, 0,101,214, 0, + 105,209, 0,110,202, 0,113,198, 0,111,201, 0,108,205, 0, + 107,206, 0,108,206, 0,110,203, 0,112,199, 0,115,194, 0, + 117,190, 0,119,186, 0,120,184, 0,121,182, 0,120,182, 0, + 121,182, 0,121,179, 0,122,175, 0,123,174, 1,123,171, 3, + 124,165, 8,124,154, 18,124,149, 24,123,141, 34,124,143, 31, + 124,145, 29,124,147, 26,124,143, 31,124,147, 26,124,160, 12, + 124,166, 7,123,173, 1,122,178, 0,122,178, 0,122,178, 0, + 121,182, 0,117,191, 0,113,198, 0,110,202, 0,109,203, 0, + 110,202, 0,112,200, 0,115,195, 0,117,190, 0,120,184, 0, + 122,175, 0,124,167, 6,123,171, 3,123,171, 3,124,166, 7, + 124,158, 13,124,155, 16,124,154, 18,124,157, 15,124,160, 12, + 124,164, 9,124,165, 8,124,164, 9,124,157, 15,124,143, 31, + 122,132, 48,121,127, 58,121,125, 63,121,127, 58,122,135, 44, + 124,143, 31,124,152, 20,124,157, 15,124,155, 16,124,150, 22, + 124,150, 22,124,166, 7,122,176, 0,120,182, 0,120,184, 0, + 120,183, 0,122,178, 0,123,171, 3,124,162, 10,124,154, 18, + 124,143, 31,122,135, 44,121,125, 63,115,107,129,111, 96,248, + 114,104,197,113,100,242,111, 96,248,107, 89,252, 94, 70,242, + 30, 55,222, 30, 37,192, 30, 31,180, 87, 65,237,107, 89,252, + 113,100,242,115,107,129,115,107,129,113,100,242,107, 89,252, + 111, 96,248,121,125, 63,123,141, 34,124,150, 22,124,155, 16, + 124,154, 18,124,149, 24,123,139, 37,121,125, 63,117,110,109, + 120,122, 70,122,132, 48,123,137, 41,123,139, 37,123,141, 34, + 124,145, 29,123,141, 34,120,122, 70,118,117, 85,123,141, 34, + 124,155, 16,124,164, 9,124,166, 7,124,162, 10,124,150, 22, + 123,141, 34,124,152, 20,124,165, 8,122,176, 0,121,182, 0, + 119,186, 0,118,189, 0,117,191, 0,116,193, 0,115,195, 0, + 114,196, 0,114,197, 0,113,198, 0,113,198, 0,114,197, 0, + 115,194, 0,118,189, 0,121,181, 0,123,168, 5,124,155, 16, + 124,152, 20,124,143, 31,122,132, 48,118,117, 85,115,107,129, + 124,143, 31,123,169, 4,119,185, 0,114,196, 0,109,204, 0, + 105,209, 0,102,212, 0,101,213, 0,102,212, 0,104,210, 0, + 107,207, 0,106,207, 0,105,209, 0,105,209, 0,107,207, 0, + 111,201, 0,116,193, 0,117,191, 0,118,188, 0,117,190, 0, + 118,189, 0,120,184, 0,122,176, 0,123,169, 4,124,161, 11, + 124,150, 22,122,135, 44,115,107,129,114,104,197,122,135, 44, + 124,149, 24,124,160, 12,123,168, 5,122,176, 0,121,182, 0, + 119,186, 0,118,189, 0,118,189, 0,118,188, 0,117,192, 0, + 114,197, 0,110,202, 0,107,206, 0,105,209, 0,102,212, 0, + 101,213, 0,102,212, 0,103,211, 0,105,209, 0,108,206, 0, + 110,202, 0,112,200, 0,112,200, 0,114,197, 0,116,193, 0, + 118,189, 0,119,186, 0,121,181, 0,123,173, 1,124,165, 8, + 123,169, 4,123,174, 1,121,180, 0,120,184, 0,118,188, 0, + 118,189, 0,118,188, 0,119,186, 0,120,184, 0,119,186, 0, + 119,186, 0,120,184, 0,122,178, 0,122,176, 0,121,179, 0, + 120,182, 0,119,185, 0,119,186, 0,119,186, 0,120,184, 0, + 120,183, 0,120,183, 0,120,184, 0,119,186, 0,118,189, 0, + 117,192, 0,116,192, 0,116,193, 0,114,196, 0,113,198, 0, + 112,200, 0,111,201, 0,110,203, 0,108,205, 0,106,207, 0, + 105,209, 0,105,209, 0,105,209, 0,105,209, 0,101,214, 0, + 97,217, 0, 96,219, 0, 96,219, 0, 96,219, 0, 97,218, 0, + 99,215, 0,103,212, 0,107,206, 0,113,199, 0,110,202, 0, + 107,206, 0,106,207, 0,107,206, 0,109,203, 0,112,199, 0, + 114,196, 0,116,193, 0,118,188, 0,119,185, 0,120,182, 0, + 120,183, 0,121,182, 0,122,178, 0,123,173, 1,123,174, 1, + 123,171, 3,124,164, 9,124,154, 18,124,154, 18,124,150, 22, + 123,139, 37,124,143, 31,124,143, 31,123,141, 34,124,155, 16, + 123,168, 5,123,174, 1,122,175, 0,123,171, 3,123,174, 1, + 123,174, 1,123,174, 1,119,186, 0,116,193, 0,113,198, 0, + 112,199, 0,113,198, 0,115,195, 0,117,191, 0,119,186, 0, + 121,180, 0,122,175, 0,122,175, 0,122,178, 0,122,177, 0, + 123,171, 3,124,162, 10,124,157, 15,124,161, 11,124,161, 11, + 124,158, 13,124,162, 10,124,165, 8,124,166, 7,124,164, 9, + 124,154, 18,122,130, 53,107, 89,252,109, 92,251,119,119, 77, + 122,130, 53,122,130, 53,121,125, 63,123,137, 41,123,141, 34, + 123,139, 37,122,132, 48,124,145, 29,124,161, 11,123,169, 4, + 123,174, 1,123,172, 2,124,165, 8,124,154, 18,124,155, 16, + 124,154, 18,123,141, 34,117,110,109,105, 84,252, 87, 65,237, + 111, 96,248,119,119, 77,121,127, 58,121,127, 58,119,119, 77, + 115,107,129,107, 89,252, 99, 75,247, 94, 70,242,109, 92,251, + 115,107,129,117,110,109,117,110,109,115,107,129,111, 96,248, + 102, 80,250,102, 80,250,118,117, 85,123,139, 37,124,152, 20, + 124,157, 15,124,157, 15,124,150, 22,123,139, 37,120,122, 70, + 117,110,109,113,100,242,115,107,129,119,119, 77,121,127, 58, + 122,135, 44,123,139, 37,122,135, 44,119,119, 77,117,110,109, + 119,119, 77,123,139, 37,124,150, 22,124,154, 18,124,150, 22, + 123,137, 41,124,149, 24,124,160, 12,124,164, 9,124,166, 7, + 123,172, 2,122,175, 0,122,177, 0,121,179, 0,121,181, 0, + 120,182, 0,119,187, 0,117,191, 0,117,191, 0,117,192, 0, + 116,193, 0,116,192, 0,118,188, 0,121,180, 0,124,167, 6, + 124,147, 26,122,135, 44,121,127, 58,117,110,109,105, 84,252, + 118,117, 85,124,150, 22,124,166, 7,121,181, 0,117,191, 0, + 112,199, 0,109,204, 0,106,208, 0,105,209, 0,105,209, 0, + 107,207, 0,109,203, 0,113,199, 0,112,200, 0,111,202, 0, + 112,200, 0,115,194, 0,119,185, 0,120,183, 0,118,188, 0, + 117,190, 0,118,189, 0,120,184, 0,122,175, 0,123,169, 4, + 124,157, 15,124,145, 29,121,127, 58,114,104,197,115,107,129, + 122,130, 53,124,147, 26,124,160, 12,123,170, 3,122,178, 0, + 120,184, 0,118,188, 0,117,190, 0,117,190, 0,119,187, 0, + 120,183, 0,118,189, 0,115,195, 0,113,198, 0,112,199, 0, + 110,203, 0,108,205, 0,108,205, 0,109,204, 0,111,202, 0, + 113,199, 0,114,196, 0,114,197, 0,114,197, 0,115,194, 0, + 117,191, 0,118,189, 0,119,187, 0,120,183, 0,122,176, 0, + 123,169, 4,124,166, 7,123,168, 5,122,176, 0,120,183, 0, + 119,187, 0,118,189, 0,118,189, 0,119,187, 0,120,183, 0, + 120,183, 0,120,184, 0,120,182, 0,122,178, 0,123,171, 3, + 123,169, 4,123,172, 2,123,174, 1,122,175, 0,123,173, 1, + 123,174, 1,121,180, 0,120,183, 0,120,184, 0,121,182, 0, + 120,184, 0,118,188, 0,118,189, 0,118,189, 0,118,188, 0, + 118,188, 0,118,189, 0,117,192, 0,115,194, 0,113,198, 0, + 111,202, 0,108,205, 0,107,207, 0,106,208, 0,106,208, 0, + 103,211, 0,100,214, 0, 99,216, 0, 98,217, 0, 97,217, 0, + 97,218, 0, 98,217, 0,100,214, 0,104,210, 0,109,204, 0, + 110,202, 0,107,207, 0,106,207, 0,107,207, 0,109,204, 0, + 111,201, 0,112,200, 0,114,197, 0,117,191, 0,119,187, 0, + 120,184, 0,120,183, 0,121,181, 0,122,177, 0,123,170, 3, + 123,169, 4,124,165, 8,124,155, 16,124,157, 15,124,158, 13, + 124,158, 13,124,152, 20,123,141, 34,123,137, 41,123,141, 34, + 124,161, 11,123,173, 1,121,179, 0,121,182, 0,121,179, 0, + 123,173, 1,123,169, 4,123,169, 4,121,181, 0,118,188, 0, + 116,192, 0,115,194, 0,116,192, 0,118,189, 0,120,184, 0, + 121,182, 0,121,180, 0,122,178, 0,120,183, 0,120,184, 0, + 120,183, 0,122,177, 0,124,166, 7,124,158, 13,124,167, 6, + 123,171, 3,123,170, 3,124,165, 8,124,162, 10,124,165, 8, + 124,164, 9,124,155, 16,122,135, 44,121,125, 63,121,127, 58, + 122,130, 53,122,135, 44,122,130, 53,118,114, 96,114,104,197, + 118,117, 85,119,119, 77,119,119, 77,118,117, 85,122,135, 44, + 124,149, 24,124,155, 16,124,154, 18,123,141, 34,124,155, 16, + 124,160, 12,124,158, 13,124,147, 26,118,117, 85, 69, 60,230, + 30, 37,192,114,104,197,122,130, 53,123,139, 37,123,141, 34, + 122,135, 44,120,122, 70,114,104,197,109, 92,251,105, 84,252, + 114,104,197,119,119, 77,121,125, 63,120,122, 70,118,114, 96, + 109, 92,251, 30, 49,213, 30, 55,222,113,100,242,122,132, 48, + 124,149, 24,124,155, 16,124,157, 15,124,152, 20,123,139, 37, + 123,137, 41,123,139, 37,122,135, 44,120,122, 70,115,107,129, + 117,110,109,118,117, 85,120,122, 70,119,119, 77,115,107,129, + 114,104,197,118,114, 96,119,119, 77,121,127, 58,122,132, 48, + 121,127, 58,122,135, 44,124,154, 18,124,164, 9,124,167, 6, + 124,165, 8,124,157, 15,124,154, 18,124,155, 16,124,158, 13, + 123,168, 5,121,180, 0,119,187, 0,117,191, 0,117,191, 0, + 118,188, 0,118,188, 0,118,189, 0,119,186, 0,121,179, 0, + 124,166, 7,123,141, 34,119,119, 77,111, 96,248, 99, 75,247, + 99, 75,247,122,130, 53,124,160, 12,123,173, 1,121,181, 0, + 120,184, 0,117,192, 0,114,197, 0,111,201, 0,109,203, 0, + 109,204, 0,109,203, 0,112,200, 0,115,195, 0,118,189, 0, + 117,192, 0,117,191, 0,119,185, 0,121,182, 0,119,185, 0, + 118,189, 0,117,190, 0,118,189, 0,120,183, 0,121,179, 0, + 122,175, 0,124,164, 9,123,141, 34,118,114, 96,117,110,109, + 118,114, 96,118,117, 85,123,139, 37,124,157, 15,123,169, 4, + 122,178, 0,120,184, 0,118,188, 0,118,189, 0,118,188, 0, + 120,184, 0,118,188, 0,117,191, 0,114,196, 0,113,198, 0, + 114,197, 0,115,194, 0,115,194, 0,115,195, 0,115,194, 0, + 116,193, 0,117,191, 0,117,191, 0,116,192, 0,116,193, 0, + 117,191, 0,118,188, 0,118,189, 0,118,188, 0,119,185, 0, + 121,179, 0,123,170, 3,124,166, 7,124,155, 16,123,168, 5, + 122,178, 0,120,184, 0,118,188, 0,118,189, 0,118,188, 0, + 120,184, 0,122,178, 0,121,179, 0,122,178, 0,123,174, 1, + 123,168, 5,124,161, 11,124,157, 15,124,161, 11,124,158, 13, + 123,168, 5,122,177, 0,120,182, 0,119,186, 0,119,187, 0, + 119,186, 0,120,184, 0,121,182, 0,120,184, 0,119,185, 0, + 119,185, 0,118,188, 0,117,190, 0,117,191, 0,117,191, 0, + 118,189, 0,115,195, 0,112,200, 0,109,204, 0,107,207, 0, + 105,209, 0,104,210, 0,103,212, 0,101,213, 0,100,214, 0, + 98,216, 0, 97,218, 0, 97,218, 0, 98,217, 0,101,214, 0, + 105,209, 0,110,202, 0,107,206, 0,106,207, 0,107,207, 0, + 109,204, 0,111,202, 0,111,201, 0,113,198, 0,117,192, 0, + 117,191, 0,118,188, 0,119,185, 0,121,181, 0,122,177, 0, + 123,169, 4,124,158, 13,124,145, 29,124,149, 24,124,155, 16, + 124,161, 11,124,162, 10,124,162, 10,124,157, 15,124,145, 29, + 124,147, 26,124,165, 8,122,176, 0,120,183, 0,119,186, 0, + 119,185, 0,121,180, 0,123,172, 2,124,162, 10,122,177, 0, + 120,184, 0,118,188, 0,118,188, 0,119,186, 0,120,184, 0, + 121,182, 0,121,181, 0,120,184, 0,118,188, 0,117,191, 0, + 117,191, 0,118,189, 0,120,184, 0,123,173, 1,124,155, 16, + 124,167, 6,123,173, 1,122,175, 0,123,173, 1,123,169, 4, + 124,164, 9,124,160, 12,124,154, 18,124,155, 16,124,160, 12, + 124,161, 11,124,158, 13,124,149, 24,122,130, 53,114,104,197, + 69, 60,230,105, 84,252,109, 92,251,113,100,242,114,104,197, + 114,104,197,119,119, 77,122,130, 53,121,127, 58,124,145, 29, + 124,157, 15,124,164, 9,124,162, 10,124,157, 15,123,139, 37, + 113,100,242, 30, 31,180,113,100,242,122,130, 53,123,141, 34, + 123,141, 34,122,135, 44,120,122, 70,115,107,129,111, 96,248, + 107, 89,252,111, 96,248,117,110,109,118,117, 85,118,114, 96, + 114,104,197, 99, 75,247, 30, 26,168, 69, 60,230,111, 96,248, + 120,122, 70,123,141, 34,124,152, 20,124,155, 16,124,150, 22, + 123,139, 37,124,150, 22,124,154, 18,124,150, 22,123,139, 37, + 121,125, 63,114,104,197,109, 92,251,102, 80,250,105, 84,252, + 105, 84,252,113,100,242,118,114, 96,119,119, 77,118,117, 85, + 114,104,197,113,100,242,122,135, 44,124,155, 16,124,165, 8, + 124,167, 6,124,164, 9,124,152, 20,122,135, 44,118,114, 96, + 124,143, 31,124,166, 7,121,179, 0,119,186, 0,117,190, 0, + 117,190, 0,119,187, 0,120,182, 0,120,184, 0,120,183, 0, + 122,177, 0,124,166, 7,124,145, 29,115,107,129, 30, 55,222, + 30, 10,132,109, 92,251,124,143, 31,124,167, 6,122,178, 0, + 119,185, 0,118,188, 0,118,189, 0,118,189, 0,116,193, 0, + 114,197, 0,113,199, 0,113,198, 0,114,196, 0,117,191, 0, + 119,185, 0,122,178, 0,120,182, 0,120,183, 0,120,184, 0, + 118,188, 0,117,191, 0,117,191, 0,118,189, 0,120,182, 0, + 120,182, 0,122,178, 0,123,169, 4,124,150, 22,118,114, 96, + 117,110,109,117,110,109,111, 96,248,123,141, 34,124,160, 12, + 123,170, 3,122,175, 0,121,181, 0,119,185, 0,119,187, 0, + 119,186, 0,117,190, 0,115,194, 0,114,196, 0,114,197, 0, + 114,196, 0,115,194, 0,117,190, 0,120,183, 0,118,188, 0, + 118,188, 0,119,185, 0,120,182, 0,120,183, 0,119,186, 0, + 119,186, 0,120,184, 0,118,188, 0,117,190, 0,117,190, 0, + 118,188, 0,120,184, 0,122,177, 0,124,167, 6,123,171, 3, + 123,169, 4,123,170, 3,122,176, 0,120,182, 0,119,185, 0, + 119,185, 0,120,182, 0,122,176, 0,123,172, 2,123,171, 3, + 124,167, 6,124,165, 8,124,167, 6,123,169, 4,123,170, 3, + 123,172, 2,122,176, 0,121,179, 0,120,182, 0,119,186, 0, + 118,188, 0,118,188, 0,119,186, 0,119,185, 0,119,186, 0, + 118,188, 0,117,191, 0,116,193, 0,114,196, 0,113,198, 0, + 113,198, 0,114,197, 0,115,195, 0,114,197, 0,113,199, 0, + 109,204, 0,106,208, 0,103,211, 0,101,213, 0, 99,215, 0, + 98,217, 0, 98,216, 0, 96,219, 0, 95,220, 0, 95,219, 0, + 97,217, 0,101,213, 0,106,207, 0,108,205, 0,107,206, 0, + 107,206, 0,108,205, 0,110,202, 0,112,200, 0,114,197, 0, + 114,196, 0,114,196, 0,115,194, 0,117,191, 0,119,187, 0, + 121,181, 0,123,174, 1,124,167, 6,124,165, 8,124,164, 9, + 124,160, 12,124,160, 12,124,166, 7,123,168, 5,124,167, 6, + 123,168, 5,123,172, 2,123,171, 3,122,177, 0,120,184, 0, + 119,187, 0,119,186, 0,120,182, 0,122,175, 0,123,174, 1, + 121,180, 0,120,184, 0,119,187, 0,119,187, 0,119,186, 0, + 120,183, 0,119,186, 0,117,190, 0,116,192, 0,115,195, 0, + 114,197, 0,114,197, 0,115,194, 0,118,189, 0,121,182, 0, + 123,168, 5,124,161, 11,123,169, 4,123,173, 1,122,175, 0, + 123,174, 1,123,171, 3,123,169, 4,123,168, 5,123,170, 3, + 123,173, 1,123,174, 1,123,173, 1,124,167, 6,124,157, 15, + 123,137, 41,118,114, 96,117,110,109,117,110,109,109, 92,251, + 105, 84,252,109, 92,251,113,100,242,117,110,109,121,125, 63, + 124,143, 31,124,160, 12,123,168, 5,123,169, 4,124,164, 9, + 124,150, 22,120,122, 70, 99, 75,247,111, 96,248,121,125, 63, + 122,135, 44,122,135, 44,122,130, 53,123,139, 37,123,141, 34, + 123,137, 41,121,125, 63,115,107,129,115,107,129,115,107,129, + 115,107,129,114,104,197,109, 92,251,115,107,129,118,117, 85, + 118,117, 85,122,135, 44,124,147, 26,124,150, 22,124,152, 20, + 124,149, 24,124,150, 22,124,160, 12,124,161, 11,124,157, 15, + 124,147, 26,122,130, 53,122,132, 48,122,135, 44,122,132, 48, + 121,125, 63,119,119, 77,117,110,109,117,110,109,118,117, 85, + 118,117, 85,114,104,197,113,100,242,121,127, 58,124,150, 22, + 124,160, 12,124,162, 10,124,158, 13,124,147, 26,122,130, 53, + 123,137, 41,123,139, 37,124,157, 15,123,173, 1,120,182, 0, + 119,186, 0,118,188, 0,119,185, 0,121,181, 0,121,180, 0, + 121,179, 0,122,176, 0,124,167, 6,124,150, 22,120,122, 70, + 99, 75,247, 30, 37,192,114,104,197,124,147, 26,123,168, 5, + 121,179, 0,119,185, 0,118,188, 0,118,189, 0,118,188, 0, + 119,186, 0,118,188, 0,117,191, 0,117,191, 0,117,190, 0, + 118,188, 0,118,188, 0,119,186, 0,120,184, 0,119,186, 0, + 118,188, 0,117,191, 0,117,192, 0,117,191, 0,118,188, 0, + 120,184, 0,120,184, 0,121,181, 0,123,172, 2,124,155, 16, + 123,139, 37,122,132, 48,121,125, 63,121,125, 63,124,152, 20, + 124,167, 6,122,175, 0,121,179, 0,120,182, 0,120,184, 0, + 119,186, 0,118,189, 0,116,193, 0,114,197, 0,112,199, 0, + 112,200, 0,113,199, 0,114,196, 0,117,192, 0,118,188, 0, + 119,186, 0,119,185, 0,120,183, 0,121,179, 0,123,173, 1, + 123,174, 1,122,178, 0,120,182, 0,119,187, 0,117,191, 0, + 117,192, 0,117,191, 0,118,189, 0,120,184, 0,122,178, 0, + 120,182, 0,122,177, 0,122,175, 0,123,173, 1,123,170, 3, + 122,176, 0,122,178, 0,122,176, 0,123,171, 3,124,164, 9, + 124,161, 11,124,166, 7,123,170, 3,123,172, 2,122,175, 0, + 122,177, 0,122,178, 0,121,182, 0,120,184, 0,119,186, 0, + 119,186, 0,118,189, 0,118,189, 0,118,188, 0,118,188, 0, + 118,189, 0,117,191, 0,115,195, 0,113,199, 0,111,202, 0, + 109,204, 0,108,205, 0,109,204, 0,110,202, 0,112,200, 0, + 111,201, 0,111,202, 0,108,205, 0,105,209, 0,102,213, 0, + 99,216, 0, 97,218, 0, 97,218, 0, 95,220, 0, 93,222, 0, + 92,222, 0, 94,221, 0, 97,218, 0,101,213, 0,106,208, 0, + 108,205, 0,109,203, 0,110,203, 0,110,202, 0,111,201, 0, + 112,200, 0,112,200, 0,112,200, 0,113,198, 0,114,196, 0, + 116,192, 0,118,188, 0,120,182, 0,122,178, 0,122,177, 0, + 122,177, 0,123,172, 2,124,165, 8,124,167, 6,123,172, 2, + 122,175, 0,120,184, 0,119,187, 0,119,186, 0,120,183, 0, + 121,182, 0,120,184, 0,120,184, 0,121,181, 0,122,178, 0, + 120,184, 0,119,187, 0,118,189, 0,118,189, 0,118,188, 0, + 119,185, 0,118,188, 0,116,193, 0,114,196, 0,114,197, 0, + 113,199, 0,112,199, 0,113,199, 0,114,197, 0,116,192, 0, + 119,186, 0,122,177, 0,124,164, 9,124,164, 9,123,170, 3, + 123,174, 1,122,176, 0,122,176, 0,122,176, 0,122,175, 0, + 122,177, 0,121,180, 0,121,182, 0,121,182, 0,122,178, 0, + 123,171, 3,124,161, 11,124,147, 26,122,135, 44,123,137, 41, + 122,130, 53,117,110,109,102, 80,250,111, 96,248,115,107,129, + 121,125, 63,124,149, 24,124,162, 10,123,168, 5,123,170, 3, + 124,166, 7,124,155, 16,122,135, 44,111, 96,248,111, 96,248, + 119,119, 77,121,125, 63,122,130, 53,124,145, 29,124,157, 15, + 124,161, 11,124,161, 11,124,155, 16,124,149, 24,124,143, 31, + 123,141, 34,122,135, 44,122,130, 53,122,135, 44,124,143, 31, + 124,147, 26,124,145, 29,124,149, 24,124,158, 13,124,160, 12, + 124,155, 16,124,145, 29,124,158, 13,124,166, 7,124,166, 7, + 124,161, 11,124,150, 22,124,143, 31,124,152, 20,124,157, 15, + 124,155, 16,124,150, 22,123,141, 34,122,130, 53,117,110,109, + 117,110,109,117,110,109,117,110,109,120,122, 70,123,137, 41, + 124,143, 31,124,152, 20,124,157, 15,124,154, 18,124,147, 26, + 124,147, 26,124,152, 20,124,154, 18,124,152, 20,124,158, 13, + 123,172, 2,121,179, 0,121,181, 0,121,180, 0,122,177, 0, + 122,176, 0,122,177, 0,123,174, 1,124,167, 6,124,154, 18, + 122,132, 48,113,100,242, 87, 65,237,113,100,242,123,141, 34, + 124,164, 9,122,175, 0,121,182, 0,120,184, 0,119,186, 0, + 119,186, 0,120,183, 0,122,177, 0,121,179, 0,121,182, 0, + 119,187, 0,117,191, 0,116,192, 0,117,191, 0,118,189, 0, + 118,189, 0,117,191, 0,117,192, 0,117,192, 0,117,190, 0, + 119,186, 0,119,185, 0,120,184, 0,121,181, 0,123,173, 1, + 124,158, 13,124,152, 20,124,145, 29,123,137, 41,122,135, 44, + 124,160, 12,123,170, 3,122,178, 0,121,181, 0,120,184, 0, + 119,186, 0,118,188, 0,117,190, 0,116,193, 0,114,197, 0, + 112,200, 0,111,201, 0,111,201, 0,113,199, 0,114,196, 0, + 115,194, 0,117,192, 0,118,189, 0,119,185, 0,121,180, 0, + 123,173, 1,123,169, 4,123,172, 2,121,179, 0,119,186, 0, + 117,191, 0,116,193, 0,115,194, 0,116,193, 0,117,190, 0, + 119,186, 0,118,189, 0,119,186, 0,121,181, 0,122,175, 0, + 123,171, 3,124,167, 6,124,165, 8,124,165, 8,124,165, 8, + 124,166, 7,123,169, 4,123,173, 1,122,177, 0,122,178, 0, + 122,178, 0,121,179, 0,121,181, 0,120,183, 0,119,186, 0, + 119,187, 0,119,186, 0,118,188, 0,118,189, 0,118,189, 0, + 118,189, 0,117,190, 0,116,193, 0,113,198, 0,111,202, 0, + 108,205, 0,106,208, 0,104,210, 0,104,210, 0,106,208, 0, + 108,205, 0,109,204, 0,108,205, 0,109,204, 0,107,207, 0, + 103,211, 0,100,215, 0, 97,217, 0, 96,219, 0, 93,222, 0, + 91,224, 0, 90,225, 0, 90,224, 0, 92,222, 0, 96,219, 0, + 100,215, 0,103,211, 0,107,207, 0,110,202, 0,112,200, 0, + 112,200, 0,111,201, 0,111,202, 0,111,202, 0,111,201, 0, + 113,199, 0,115,195, 0,117,191, 0,119,187, 0,120,184, 0, + 120,184, 0,120,184, 0,121,181, 0,123,173, 1,124,166, 7, + 123,173, 1,119,185, 0,116,192, 0,114,196, 0,115,195, 0, + 116,192, 0,118,189, 0,119,186, 0,120,184, 0,120,184, 0, + 119,186, 0,118,189, 0,117,191, 0,117,192, 0,117,191, 0, + 118,188, 0,119,186, 0,116,193, 0,113,198, 0,112,200, 0, + 112,199, 0,112,200, 0,112,199, 0,113,198, 0,114,196, 0, + 116,193, 0,118,188, 0,121,182, 0,123,172, 2,123,171, 3, + 123,171, 3,123,173, 1,122,177, 0,122,178, 0,121,179, 0, + 121,179, 0,121,181, 0,120,184, 0,119,186, 0,119,186, 0, + 120,184, 0,121,181, 0,123,174, 1,124,165, 8,124,152, 20, + 124,157, 15,124,154, 18,124,143, 31,120,122, 70,107, 89,252, + 113,100,242,121,127, 58,124,147, 26,124,160, 12,124,166, 7, + 124,167, 6,124,164, 9,124,155, 16,123,141, 34,122,132, 48, + 121,125, 63,118,114, 96,118,117, 85,122,135, 44,124,152, 20, + 124,164, 9,123,170, 3,123,171, 3,123,170, 3,123,168, 5, + 124,165, 8,124,162, 10,124,158, 13,124,157, 15,124,157, 15, + 124,160, 12,124,161, 11,124,158, 13,124,162, 10,123,168, 5, + 123,169, 4,124,164, 9,124,158, 13,124,162, 10,123,168, 5, + 123,168, 5,124,162, 10,124,150, 22,124,152, 20,124,161, 11, + 124,166, 7,124,166, 7,124,161, 11,124,150, 22,123,137, 41, + 118,117, 85,113,100,242,118,114, 96,121,127, 58,123,139, 37, + 124,147, 26,124,150, 22,124,149, 24,124,147, 26,124,149, 24, + 124,152, 20,124,160, 12,124,161, 11,124,160, 12,124,158, 13, + 124,155, 16,124,152, 20,124,165, 8,123,170, 3,123,171, 3, + 123,169, 4,123,171, 3,123,174, 1,123,172, 2,124,167, 6, + 124,155, 16,123,139, 37,118,114, 96,105, 84,252,113,100,242, + 120,122, 70,124,150, 22,124,165, 8,123,172, 2,122,176, 0, + 122,178, 0,121,179, 0,122,177, 0,123,169, 4,124,164, 9, + 121,180, 0,118,189, 0,115,194, 0,114,196, 0,115,194, 0, + 117,192, 0,117,191, 0,117,191, 0,117,191, 0,117,191, 0, + 118,188, 0,120,184, 0,120,184, 0,120,183, 0,121,180, 0, + 123,172, 2,124,161, 11,124,157, 15,124,149, 24,123,139, 37, + 124,145, 29,124,162, 10,123,172, 2,122,178, 0,121,181, 0, + 120,183, 0,119,186, 0,119,187, 0,118,189, 0,117,191, 0, + 115,195, 0,113,198, 0,112,200, 0,111,201, 0,112,200, 0, + 112,199, 0,113,198, 0,114,196, 0,116,193, 0,118,189, 0, + 120,182, 0,123,173, 1,123,171, 3,123,169, 4,123,174, 1, + 120,182, 0,118,189, 0,116,193, 0,115,195, 0,115,195, 0, + 115,194, 0,117,192, 0,117,192, 0,117,190, 0,119,187, 0, + 120,184, 0,121,179, 0,122,176, 0,123,172, 2,123,171, 3, + 123,172, 2,122,175, 0,121,179, 0,120,182, 0,119,185, 0, + 119,185, 0,120,184, 0,121,180, 0,121,180, 0,120,182, 0, + 120,184, 0,119,186, 0,120,184, 0,118,188, 0,118,189, 0, + 118,188, 0,118,188, 0,118,189, 0,116,193, 0,113,198, 0, + 110,203, 0,107,207, 0,104,210, 0,102,213, 0,101,213, 0, + 103,212, 0,106,208, 0,107,207, 0,106,208, 0,107,207, 0, + 108,205, 0,106,208, 0,102,212, 0, 99,216, 0, 95,220, 0, + 91,223, 0, 89,226, 0, 87,227, 0, 87,227, 0, 89,225, 0, + 91,223, 0, 94,220, 0, 97,217, 0,101,214, 0,105,209, 0, + 110,202, 0,112,200, 0,112,200, 0,111,202, 0,110,202, 0, + 111,201, 0,113,199, 0,114,196, 0,117,192, 0,118,188, 0, + 119,186, 0,119,187, 0,118,188, 0,119,185, 0,121,179, 0, + 123,168, 5,122,178, 0,118,189, 0,114,197, 0,112,200, 0, + 112,200, 0,113,198, 0,115,194, 0,117,191, 0,118,188, 0, + 118,188, 0,118,189, 0,117,191, 0,116,193, 0,116,193, 0, + 117,191, 0,119,186, 0,118,189, 0,114,196, 0,112,199, 0, + 112,200, 0,113,199, 0,114,197, 0,114,196, 0,115,194, 0, + 116,192, 0,117,190, 0,119,187, 0,120,182, 0,122,177, 0, + 122,178, 0,122,178, 0,122,178, 0,122,178, 0,121,181, 0, + 121,182, 0,121,182, 0,120,182, 0,119,185, 0,119,187, 0, + 118,189, 0,118,188, 0,119,186, 0,121,182, 0,122,175, 0, + 124,166, 7,123,170, 3,123,169, 4,124,164, 9,124,149, 24, + 119,119, 77,111, 96,248,120,122, 70,123,139, 37,124,152, 20, + 124,158, 13,124,160, 12,124,160, 12,124,157, 15,124,158, 13, + 124,155, 16,124,149, 24,123,139, 37,120,122, 70,122,135, 44, + 124,150, 22,124,164, 9,123,171, 3,123,174, 1,122,175, 0, + 122,176, 0,122,176, 0,123,174, 1,123,172, 2,123,169, 4, + 124,167, 6,123,168, 5,124,167, 6,124,166, 7,122,175, 0, + 122,178, 0,122,177, 0,123,171, 3,124,166, 7,123,170, 3, + 123,168, 5,124,167, 6,124,160, 12,124,147, 26,124,154, 18, + 124,164, 9,123,168, 5,123,168, 5,124,164, 9,124,152, 20, + 124,149, 24,124,143, 31,121,127, 58,123,137, 41,124,143, 31, + 124,149, 24,124,152, 20,124,154, 18,124,155, 16,124,158, 13, + 124,160, 12,124,164, 9,124,166, 7,124,165, 8,124,164, 9, + 124,161, 11,124,158, 13,124,150, 22,123,139, 37,124,154, 18, + 124,158, 13,124,158, 13,123,168, 5,123,171, 3,123,171, 3, + 124,166, 7,124,157, 15,123,141, 34,119,119, 77,113,100,242, + 107, 89,252,113,100,242,120,122, 70,124,143, 31,124,154, 18, + 124,160, 12,124,164, 9,124,167, 6,124,166, 7,123,170, 3, + 123,172, 2,121,182, 0,117,191, 0,114,196, 0,113,198, 0, + 114,196, 0,116,193, 0,117,191, 0,117,191, 0,117,190, 0, + 118,188, 0,120,184, 0,121,182, 0,121,181, 0,121,179, 0, + 122,176, 0,123,168, 5,124,162, 10,124,158, 13,124,149, 24, + 123,137, 41,124,152, 20,124,165, 8,123,171, 3,122,177, 0, + 120,183, 0,119,186, 0,118,188, 0,118,189, 0,118,189, 0, + 118,188, 0,117,191, 0,115,194, 0,114,197, 0,113,199, 0, + 112,200, 0,112,200, 0,112,199, 0,113,198, 0,114,196, 0, + 117,191, 0,119,185, 0,122,175, 0,123,171, 3,123,170, 3, + 123,169, 4,123,174, 1,120,184, 0,118,189, 0,116,193, 0, + 115,194, 0,115,194, 0,116,193, 0,117,190, 0,117,190, 0, + 118,189, 0,118,188, 0,119,187, 0,119,185, 0,120,183, 0, + 121,182, 0,121,181, 0,121,181, 0,119,186, 0,118,189, 0, + 117,191, 0,117,191, 0,118,188, 0,120,183, 0,122,177, 0, + 121,179, 0,121,182, 0,120,182, 0,121,181, 0,119,185, 0, + 119,187, 0,119,186, 0,119,186, 0,118,188, 0,117,191, 0, + 114,197, 0,110,202, 0,107,207, 0,104,210, 0,101,213, 0, + 101,214, 0,102,212, 0,105,209, 0,105,209, 0,104,210, 0, + 105,209, 0,107,207, 0,109,204, 0,105,209, 0, 99,215, 0, + 94,220, 0, 91,224, 0, 88,226, 0, 86,228, 0, 86,228, 0, + 86,228, 0, 88,226, 0, 90,225, 0, 92,222, 0, 95,219, 0, + 100,215, 0,106,208, 0,111,202, 0,112,200, 0,112,199, 0, + 112,200, 0,113,198, 0,114,196, 0,116,193, 0,118,189, 0, + 119,186, 0,119,185, 0,119,186, 0,118,188, 0,119,187, 0, + 120,182, 0,122,178, 0,121,180, 0,117,191, 0,113,198, 0, + 111,201, 0,111,201, 0,112,199, 0,114,196, 0,116,192, 0, + 117,190, 0,118,188, 0,118,188, 0,117,191, 0,116,192, 0, + 117,192, 0,118,189, 0,120,184, 0,117,190, 0,114,196, 0, + 113,198, 0,113,198, 0,115,195, 0,116,192, 0,118,189, 0, + 119,187, 0,119,185, 0,120,184, 0,120,183, 0,121,180, 0, + 121,180, 0,121,182, 0,120,182, 0,120,182, 0,121,182, 0, + 121,182, 0,120,183, 0,120,184, 0,120,184, 0,119,185, 0, + 118,188, 0,118,189, 0,117,190, 0,118,189, 0,119,186, 0, + 121,182, 0,122,176, 0,121,179, 0,121,179, 0,122,175, 0, + 124,164, 9,123,139, 37,115,107,129,115,107,129,121,125, 63, + 123,137, 41,124,152, 20,124,165, 8,123,171, 3,123,173, 1, + 123,172, 2,123,169, 4,124,164, 9,124,155, 16,124,145, 29, + 124,149, 24,124,160, 12,123,168, 5,123,174, 1,122,178, 0, + 121,179, 0,121,181, 0,121,180, 0,121,180, 0,122,178, 0, + 122,176, 0,123,173, 1,123,172, 2,123,170, 3,122,178, 0, + 120,184, 0,119,186, 0,120,184, 0,122,178, 0,123,169, 4, + 123,172, 2,123,170, 3,124,162, 10,124,155, 16,124,143, 31, + 124,152, 20,124,161, 11,124,167, 6,124,167, 6,124,160, 12, + 124,161, 11,124,165, 8,124,162, 10,124,152, 20,124,152, 20, + 124,152, 20,124,154, 18,124,158, 13,123,169, 4,122,175, 0, + 122,178, 0,122,177, 0,123,173, 1,123,170, 3,124,167, 6, + 124,165, 8,124,162, 10,124,158, 13,124,154, 18,124,143, 31, + 121,127, 58,123,139, 37,124,147, 26,124,164, 9,123,168, 5, + 123,168, 5,124,164, 9,124,155, 16,124,147, 26,122,132, 48, + 117,110,109,111, 96,248, 99, 75,247, 87, 65,237,111, 96,248, + 118,114, 96,121,125, 63,124,145, 29,124,162, 10,123,173, 1, + 122,178, 0,121,180, 0,120,184, 0,117,192, 0,114,197, 0, + 113,198, 0,114,196, 0,116,192, 0,117,191, 0,118,189, 0, + 118,188, 0,119,185, 0,121,181, 0,122,176, 0,123,174, 1, + 123,173, 1,123,170, 3,124,161, 11,124,160, 12,124,155, 16, + 124,143, 31,124,147, 26,124,157, 15,124,165, 8,122,176, 0, + 120,183, 0,119,187, 0,118,189, 0,117,190, 0,117,190, 0, + 118,189, 0,118,188, 0,119,186, 0,118,189, 0,117,192, 0, + 115,194, 0,114,196, 0,114,197, 0,113,198, 0,114,197, 0, + 114,196, 0,116,192, 0,119,186, 0,122,178, 0,123,168, 5, + 123,168, 5,124,167, 6,124,166, 7,123,173, 1,121,182, 0, + 119,187, 0,118,189, 0,117,191, 0,117,191, 0,120,184, 0, + 119,186, 0,118,188, 0,117,190, 0,117,191, 0,117,191, 0, + 117,190, 0,118,188, 0,119,186, 0,119,186, 0,117,191, 0, + 115,194, 0,114,196, 0,115,194, 0,117,191, 0,120,184, 0, + 122,175, 0,123,174, 1,122,177, 0,122,177, 0,122,178, 0, + 120,183, 0,120,184, 0,120,183, 0,120,182, 0,120,184, 0, + 118,189, 0,116,193, 0,113,199, 0,109,204, 0,106,208, 0, + 103,211, 0,102,212, 0,103,211, 0,104,210, 0,102,212, 0, + 102,213, 0,103,211, 0,106,208, 0,110,203, 0,106,208, 0, + 99,215, 0, 94,221, 0, 91,224, 0, 88,226, 0, 86,228, 0, + 85,228, 0, 85,229, 0, 85,228, 0, 86,228, 0, 87,227, 0, + 90,224, 0, 95,220, 0,101,214, 0,108,205, 0,112,200, 0, + 114,197, 0,115,195, 0,116,193, 0,117,191, 0,119,187, 0, + 120,184, 0,121,181, 0,121,180, 0,120,183, 0,119,186, 0, + 119,186, 0,119,185, 0,119,187, 0,119,186, 0,118,189, 0, + 114,196, 0,112,200, 0,112,200, 0,113,198, 0,115,194, 0, + 117,191, 0,118,188, 0,119,186, 0,119,186, 0,118,188, 0, + 118,189, 0,118,188, 0,119,186, 0,120,184, 0,117,190, 0, + 115,194, 0,115,195, 0,116,193, 0,118,189, 0,119,186, 0, + 119,187, 0,119,185, 0,121,181, 0,123,174, 1,123,174, 1, + 123,174, 1,121,180, 0,120,183, 0,120,184, 0,120,184, 0, + 120,184, 0,120,184, 0,119,185, 0,119,187, 0,118,188, 0, + 119,186, 0,119,186, 0,118,189, 0,117,190, 0,117,190, 0, + 118,188, 0,120,184, 0,120,182, 0,119,186, 0,119,186, 0, + 120,182, 0,123,171, 3,124,152, 20,119,119, 77,102, 80,250, + 114,104,197,124,145, 29,124,167, 6,122,177, 0,121,182, 0, + 120,182, 0,121,180, 0,122,176, 0,123,170, 3,124,165, 8, + 124,160, 12,124,158, 13,124,164, 9,123,169, 4,123,174, 1, + 122,177, 0,121,180, 0,120,182, 0,120,182, 0,121,181, 0, + 121,181, 0,122,178, 0,122,176, 0,123,174, 1,121,179, 0, + 119,187, 0,117,192, 0,116,193, 0,117,191, 0,120,184, 0, + 123,172, 2,123,172, 2,123,169, 4,124,158, 13,124,145, 29, + 122,132, 48,124,145, 29,124,155, 16,124,160, 12,124,161, 11, + 124,160, 12,123,171, 3,122,176, 0,123,174, 1,124,166, 7, + 124,161, 11,124,160, 12,124,158, 13,123,169, 4,121,180, 0, + 119,186, 0,118,188, 0,119,187, 0,120,183, 0,122,175, 0, + 123,172, 2,123,173, 1,123,169, 4,124,161, 11,124,155, 16, + 124,149, 24,123,137, 41,118,117, 85,123,137, 41,124,157, 15, + 124,165, 8,124,165, 8,124,161, 11,124,158, 13,124,157, 15, + 124,149, 24,121,125, 63,117,110,109,109, 92,251, 87, 65,237, + 94, 70,242,113,100,242,121,125, 63,124,147, 26,124,166, 7, + 122,178, 0,120,183, 0,119,186, 0,119,186, 0,116,193, 0, + 114,197, 0,113,198, 0,115,194, 0,116,192, 0,117,192, 0, + 118,189, 0,119,186, 0,121,182, 0,122,177, 0,123,168, 5, + 124,164, 9,124,162, 10,124,160, 12,124,150, 22,124,152, 20, + 124,147, 26,124,152, 20,124,155, 16,124,160, 12,123,173, 1, + 120,182, 0,119,187, 0,118,189, 0,117,190, 0,118,189, 0, + 118,189, 0,118,188, 0,118,188, 0,119,185, 0,121,182, 0, + 120,184, 0,119,187, 0,118,189, 0,117,191, 0,116,193, 0, + 116,193, 0,116,193, 0,117,191, 0,119,187, 0,121,180, 0, + 124,167, 6,124,162, 10,124,161, 11,124,160, 12,124,157, 15, + 124,167, 6,122,176, 0,121,180, 0,120,183, 0,120,184, 0, + 123,174, 1,121,179, 0,120,184, 0,118,189, 0,117,192, 0, + 116,193, 0,116,193, 0,116,192, 0,118,189, 0,117,191, 0, + 115,195, 0,113,198, 0,113,198, 0,114,196, 0,117,191, 0, + 120,183, 0,123,172, 2,123,168, 5,123,170, 3,123,170, 3, + 122,176, 0,121,180, 0,121,181, 0,121,179, 0,122,178, 0, + 119,186, 0,117,190, 0,116,192, 0,115,194, 0,112,200, 0, + 109,204, 0,106,207, 0,105,209, 0,106,208, 0,102,212, 0, + 100,215, 0,100,215, 0,101,213, 0,105,209, 0,109,204, 0, + 107,207, 0,100,214, 0, 95,220, 0, 91,223, 0, 89,225, 0, + 85,229, 0, 83,230, 0, 84,229, 0, 85,229, 0, 84,229, 0, + 85,229, 0, 87,227, 0, 91,224, 0, 97,218, 0,104,210, 0, + 112,200, 0,115,195, 0,118,188, 0,119,187, 0,120,184, 0, + 121,179, 0,122,176, 0,122,175, 0,123,173, 1,122,176, 0, + 121,181, 0,120,183, 0,117,190, 0,116,193, 0,116,193, 0, + 117,191, 0,116,192, 0,115,195, 0,115,195, 0,116,193, 0, + 117,190, 0,119,186, 0,120,183, 0,121,180, 0,121,179, 0, + 121,182, 0,120,184, 0,120,184, 0,121,182, 0,120,184, 0, + 118,189, 0,117,191, 0,117,191, 0,118,188, 0,119,186, 0, + 118,189, 0,117,191, 0,118,189, 0,120,184, 0,122,176, 0, + 124,161, 11,123,170, 3,122,178, 0,121,182, 0,120,183, 0, + 120,184, 0,120,184, 0,120,184, 0,119,186, 0,118,188, 0, + 118,189, 0,118,188, 0,119,186, 0,118,188, 0,118,189, 0, + 117,190, 0,118,188, 0,120,184, 0,119,187, 0,117,190, 0, + 117,191, 0,119,186, 0,122,176, 0,124,158, 13,121,125, 63, + 94, 70,242,118,114, 96,124,155, 16,123,174, 1,120,183, 0, + 119,186, 0,119,186, 0,120,183, 0,122,178, 0,123,173, 1, + 123,170, 3,124,167, 6,124,166, 7,124,166, 7,123,168, 5, + 123,171, 3,123,174, 1,122,178, 0,121,182, 0,120,183, 0, + 121,181, 0,121,182, 0,121,180, 0,122,178, 0,122,175, 0, + 119,186, 0,116,193, 0,114,197, 0,113,198, 0,115,195, 0, + 118,188, 0,122,178, 0,123,169, 4,124,166, 7,124,154, 18, + 122,130, 53,118,117, 85,122,132, 48,124,143, 31,124,150, 22, + 124,150, 22,124,165, 8,122,177, 0,120,182, 0,121,182, 0, + 123,174, 1,124,167, 6,124,164, 9,124,160, 12,122,175, 0, + 119,186, 0,117,192, 0,115,194, 0,116,193, 0,118,189, 0, + 121,182, 0,122,176, 0,122,177, 0,123,174, 1,123,168, 5, + 124,158, 13,124,154, 18,124,147, 26,122,132, 48,119,119, 77, + 124,147, 26,124,158, 13,124,161, 11,124,158, 13,124,161, 11, + 124,162, 10,124,157, 15,123,141, 34,121,125, 63,117,110,109, + 107, 89,252,107, 89,252,120,122, 70,124,143, 31,124,155, 16, + 123,169, 4,121,181, 0,119,186, 0,118,189, 0,118,189, 0, + 115,194, 0,114,197, 0,114,197, 0,116,193, 0,116,193, 0, + 116,192, 0,118,188, 0,120,182, 0,122,178, 0,123,172, 2, + 124,161, 11,124,147, 26,124,145, 29,124,143, 31,122,135, 44, + 124,145, 29,124,157, 15,124,161, 11,124,161, 11,124,165, 8, + 122,178, 0,119,185, 0,118,189, 0,118,189, 0,118,189, 0, + 119,187, 0,119,185, 0,120,184, 0,120,184, 0,120,182, 0, + 121,180, 0,122,177, 0,122,176, 0,121,179, 0,120,182, 0, + 120,184, 0,119,186, 0,118,188, 0,118,188, 0,119,186, 0, + 121,181, 0,123,171, 3,124,155, 16,124,152, 20,124,149, 24, + 124,145, 29,123,137, 41,124,154, 18,124,164, 9,123,168, 5, + 123,171, 3,124,160, 12,123,169, 4,122,178, 0,119,186, 0, + 117,191, 0,116,193, 0,115,194, 0,116,193, 0,117,191, 0, + 115,194, 0,113,198, 0,112,200, 0,112,199, 0,115,195, 0, + 118,188, 0,122,178, 0,124,162, 10,124,164, 9,124,165, 8, + 124,167, 6,122,176, 0,121,179, 0,121,179, 0,122,177, 0, + 123,174, 1,120,184, 0,118,188, 0,117,191, 0,116,193, 0, + 115,194, 0,113,199, 0,110,202, 0,109,203, 0,105,209, 0, + 101,214, 0, 98,216, 0, 98,216, 0,100,215, 0,103,211, 0, + 108,206, 0,110,202, 0,103,211, 0, 98,217, 0, 93,221, 0, + 88,226, 0, 84,229, 0, 82,231, 0, 82,231, 0, 85,228, 0, + 84,229, 0, 83,230, 0, 84,229, 0, 88,226, 0, 93,221, 0, + 101,214, 0,109,204, 0,116,193, 0,119,186, 0,122,178, 0, + 122,175, 0,122,177, 0,122,178, 0,122,178, 0,122,175, 0, + 123,168, 5,122,175, 0,119,185, 0,116,193, 0,114,197, 0, + 113,198, 0,114,196, 0,116,193, 0,117,190, 0,117,190, 0, + 118,188, 0,119,186, 0,119,185, 0,120,183, 0,121,181, 0, + 122,178, 0,123,174, 1,122,177, 0,122,178, 0,121,180, 0, + 120,184, 0,119,187, 0,118,188, 0,119,186, 0,120,182, 0, + 119,187, 0,117,191, 0,116,192, 0,117,191, 0,119,186, 0, + 122,177, 0,124,166, 7,124,164, 9,123,173, 1,122,178, 0, + 121,180, 0,121,181, 0,120,184, 0,119,187, 0,119,187, 0, + 119,186, 0,119,187, 0,119,186, 0,119,186, 0,118,188, 0, + 118,189, 0,118,189, 0,119,187, 0,120,184, 0,118,189, 0, + 116,192, 0,116,193, 0,118,189, 0,122,178, 0,124,161, 11, + 122,130, 53, 94, 70,242,118,117, 85,124,157, 15,122,175, 0, + 120,183, 0,119,186, 0,119,185, 0,121,181, 0,122,176, 0, + 123,174, 1,123,171, 3,123,171, 3,123,172, 2,123,174, 1, + 122,175, 0,123,174, 1,123,171, 3,122,176, 0,121,181, 0, + 120,183, 0,120,182, 0,121,181, 0,121,182, 0,121,180, 0, + 122,178, 0,118,188, 0,114,196, 0,112,200, 0,111,201, 0, + 113,198, 0,117,191, 0,121,181, 0,124,165, 8,124,161, 11, + 124,147, 26,119,119, 77,113,100,242,118,117, 85,122,130, 53, + 123,137, 41,123,139, 37,124,165, 8,122,178, 0,120,184, 0, + 120,184, 0,122,178, 0,123,169, 4,124,165, 8,124,161, 11, + 122,177, 0,118,188, 0,115,194, 0,113,198, 0,114,197, 0, + 116,193, 0,119,186, 0,122,177, 0,123,174, 1,123,173, 1, + 123,168, 5,124,164, 9,124,162, 10,124,157, 15,124,147, 26, + 121,125, 63,122,132, 48,124,149, 24,124,155, 16,124,155, 16, + 124,160, 12,124,162, 10,124,158, 13,124,143, 31,123,141, 34, + 122,132, 48,118,114, 96,118,114, 96,124,143, 31,124,161, 11, + 123,169, 4,123,173, 1,120,184, 0,118,189, 0,117,191, 0, + 117,191, 0,115,195, 0,114,197, 0,114,196, 0,117,192, 0, + 116,193, 0,117,190, 0,119,185, 0,122,178, 0,123,172, 2, + 124,166, 7,124,152, 20,120,122, 70,119,119, 77,119,119, 77, + 122,130, 53,124,157, 15,124,166, 7,123,168, 5,124,167, 6, + 124,166, 7,122,178, 0,120,184, 0,119,187, 0,119,186, 0, + 120,184, 0,121,180, 0,122,176, 0,123,174, 1,123,174, 1, + 123,174, 1,123,173, 1,123,172, 2,123,171, 3,123,170, 3, + 123,170, 3,123,171, 3,122,175, 0,121,179, 0,120,182, 0, + 120,182, 0,121,180, 0,123,173, 1,124,162, 10,123,139, 37, + 123,137, 41,122,132, 48,119,119, 77,118,114, 96,122,132, 48, + 124,145, 29,124,152, 20,124,165, 8,123,171, 3,123,174, 1, + 120,182, 0,118,189, 0,116,193, 0,115,194, 0,115,194, 0, + 116,193, 0,114,197, 0,112,200, 0,112,200, 0,113,198, 0, + 117,192, 0,120,182, 0,124,167, 6,124,160, 12,124,160, 12, + 124,160, 12,123,168, 5,122,175, 0,122,178, 0,122,176, 0, + 123,171, 3,123,170, 3,121,179, 0,119,185, 0,118,188, 0, + 118,189, 0,117,191, 0,116,193, 0,114,197, 0,110,202, 0, + 106,208, 0,101,213, 0, 99,216, 0, 98,216, 0,100,215, 0, + 103,212, 0,107,207, 0,110,202, 0,106,208, 0,100,215, 0, + 93,221, 0, 88,226, 0, 83,230, 0, 82,231, 0, 82,231, 0, + 85,229, 0, 85,229, 0, 83,230, 0, 83,230, 0, 86,228, 0, + 91,224, 0, 97,217, 0,105,209, 0,112,199, 0,118,188, 0, + 122,177, 0,122,177, 0,122,178, 0,122,178, 0,122,178, 0, + 123,174, 1,123,168, 5,123,169, 4,120,184, 0,116,192, 0, + 114,197, 0,113,199, 0,113,198, 0,114,196, 0,116,193, 0, + 117,191, 0,117,190, 0,118,189, 0,118,188, 0,119,186, 0, + 120,184, 0,119,185, 0,120,184, 0,121,181, 0,122,178, 0, + 121,182, 0,120,184, 0,120,184, 0,120,184, 0,121,182, 0, + 121,182, 0,119,186, 0,118,189, 0,117,190, 0,118,188, 0, + 120,183, 0,123,173, 1,123,168, 5,124,167, 6,123,170, 3, + 123,174, 1,121,179, 0,120,184, 0,119,186, 0,118,188, 0, + 118,188, 0,119,186, 0,120,184, 0,120,184, 0,119,186, 0, + 119,187, 0,118,188, 0,118,188, 0,119,186, 0,120,183, 0, + 118,188, 0,117,191, 0,117,192, 0,118,188, 0,122,178, 0, + 124,160, 12,121,127, 58,111, 96,248,118,114, 96,124,155, 16, + 123,172, 2,121,179, 0,121,182, 0,121,179, 0,121,181, 0, + 120,184, 0,120,182, 0,121,179, 0,122,176, 0,122,175, 0, + 121,179, 0,120,182, 0,120,184, 0,120,182, 0,122,177, 0, + 121,180, 0,120,183, 0,120,184, 0,121,180, 0,121,181, 0, + 121,181, 0,121,180, 0,118,189, 0,114,196, 0,111,201, 0, + 111,202, 0,112,200, 0,116,193, 0,120,184, 0,123,170, 3, + 124,152, 20,122,135, 44,117,110,109,105, 84,252,113,100,242, + 118,117, 85,121,127, 58,122,132, 48,124,160, 12,122,175, 0, + 120,183, 0,120,184, 0,121,180, 0,123,170, 3,124,166, 7, + 124,162, 10,122,175, 0,119,187, 0,115,194, 0,113,199, 0, + 112,199, 0,114,197, 0,117,191, 0,120,184, 0,122,176, 0, + 123,170, 3,123,170, 3,123,170, 3,123,170, 3,124,167, 6, + 124,161, 11,124,147, 26,121,127, 58,121,127, 58,123,141, 34, + 124,152, 20,124,160, 12,124,161, 11,124,158, 13,124,152, 20, + 124,150, 22,124,143, 31,122,130, 53,122,132, 48,124,157, 15, + 123,171, 3,122,178, 0,121,181, 0,119,186, 0,117,191, 0, + 116,192, 0,116,193, 0,114,196, 0,114,196, 0,115,195, 0, + 117,191, 0,117,191, 0,119,187, 0,121,180, 0,123,172, 2, + 124,167, 6,124,158, 13,123,141, 34,113,100,242,117,110,109, + 122,132, 48,124,143, 31,124,162, 10,123,170, 3,123,171, 3, + 123,170, 3,123,169, 4,122,177, 0,121,182, 0,120,182, 0, + 121,180, 0,122,175, 0,123,174, 1,123,174, 1,123,171, 3, + 124,165, 8,124,160, 12,124,161, 11,124,162, 10,124,165, 8, + 124,166, 7,123,168, 5,123,171, 3,123,171, 3,124,167, 6, + 123,172, 2,122,176, 0,122,177, 0,122,176, 0,123,171, 3, + 124,158, 13,122,135, 44,119,119, 77,109, 92,251, 69, 60,230, + 87, 65,237,118,117, 85,124,149, 24,124,167, 6,123,173, 1, + 122,177, 0,121,179, 0,118,188, 0,116,193, 0,114,196, 0, + 114,196, 0,115,194, 0,114,197, 0,113,199, 0,113,198, 0, + 115,194, 0,119,187, 0,123,174, 1,124,157, 15,124,157, 15, + 124,155, 16,124,157, 15,124,167, 6,123,172, 2,123,172, 2, + 123,168, 5,124,161, 11,124,167, 6,122,175, 0,121,180, 0, + 120,182, 0,120,184, 0,119,186, 0,118,189, 0,116,193, 0, + 112,199, 0,108,206, 0,103,211, 0,101,214, 0,101,214, 0, + 102,213, 0,104,210, 0,108,206, 0,110,202, 0,107,207, 0, + 101,214, 0, 94,220, 0, 89,225, 0, 85,229, 0, 83,230, 0, + 83,230, 0, 86,228, 0, 85,228, 0, 83,230, 0, 83,230, 0, + 84,229, 0, 88,226, 0, 94,221, 0,101,213, 0,108,205, 0, + 114,196, 0,119,186, 0,121,179, 0,121,179, 0,121,179, 0, + 122,178, 0,123,174, 1,123,168, 5,123,169, 4,122,178, 0, + 118,188, 0,115,194, 0,114,197, 0,114,197, 0,114,196, 0, + 116,193, 0,116,192, 0,117,191, 0,117,191, 0,118,189, 0, + 118,188, 0,117,191, 0,116,192, 0,117,191, 0,118,188, 0, + 120,184, 0,120,182, 0,120,182, 0,121,182, 0,121,179, 0, + 122,178, 0,121,182, 0,120,184, 0,119,186, 0,119,185, 0, + 121,182, 0,122,175, 0,124,166, 7,124,165, 8,124,165, 8, + 123,169, 4,123,174, 1,121,179, 0,120,184, 0,119,186, 0, + 118,188, 0,119,187, 0,120,184, 0,120,182, 0,121,182, 0, + 120,183, 0,120,184, 0,119,186, 0,119,186, 0,119,185, 0, + 120,182, 0,120,183, 0,119,187, 0,118,188, 0,120,183, 0, + 123,172, 2,124,150, 22,118,117, 85,119,119, 77,120,122, 70, + 124,154, 18,123,169, 4,122,175, 0,122,175, 0,122,176, 0, + 119,185, 0,118,188, 0,119,187, 0,120,184, 0,121,180, 0, + 122,175, 0,120,182, 0,118,188, 0,118,189, 0,118,189, 0, + 119,185, 0,122,178, 0,120,182, 0,120,182, 0,121,179, 0, + 122,177, 0,122,178, 0,121,182, 0,117,190, 0,114,197, 0, + 111,201, 0,110,202, 0,111,201, 0,114,196, 0,119,187, 0, + 122,175, 0,124,160, 12,123,139, 37,118,114, 96,102, 80,250, + 94, 70,242,109, 92,251,118,114, 96,121,125, 63,124,147, 26, + 123,168, 5,122,178, 0,120,182, 0,121,181, 0,123,173, 1, + 124,167, 6,124,165, 8,123,169, 4,120,184, 0,116,193, 0, + 113,198, 0,112,200, 0,113,199, 0,115,194, 0,118,188, 0, + 121,182, 0,123,174, 1,123,174, 1,122,175, 0,122,175, 0, + 123,174, 1,123,170, 3,124,162, 10,124,150, 22,123,139, 37, + 124,147, 26,124,155, 16,124,161, 11,124,162, 10,124,160, 12, + 124,150, 22,124,150, 22,124,143, 31,124,143, 31,124,150, 22, + 123,169, 4,121,180, 0,119,186, 0,118,188, 0,118,188, 0, + 117,192, 0,115,194, 0,115,194, 0,115,194, 0,116,193, 0, + 117,192, 0,117,190, 0,118,188, 0,120,183, 0,123,173, 1, + 123,168, 5,124,161, 11,124,150, 22,124,143, 31,122,130, 53, + 122,132, 48,124,147, 26,124,152, 20,124,162, 10,123,168, 5, + 123,169, 4,123,168, 5,124,167, 6,123,174, 1,122,177, 0, + 122,175, 0,123,171, 3,123,169, 4,123,168, 5,124,167, 6, + 124,161, 11,124,150, 22,123,139, 37,124,143, 31,124,149, 24, + 124,154, 18,124,158, 13,124,164, 9,123,168, 5,123,169, 4, + 123,168, 5,123,168, 5,122,176, 0,120,183, 0,119,186, 0, + 120,184, 0,121,179, 0,123,168, 5,124,154, 18,122,130, 53, + 111, 96,248, 30, 55,222,119,119, 77,124,152, 20,124,165, 8, + 123,172, 2,122,176, 0,122,178, 0,118,188, 0,115,194, 0, + 114,197, 0,113,198, 0,114,196, 0,115,195, 0,114,196, 0, + 115,194, 0,117,190, 0,121,182, 0,124,165, 8,124,152, 20, + 124,149, 24,124,145, 29,124,157, 15,124,165, 8,124,166, 7, + 124,162, 10,124,155, 16,124,149, 24,124,164, 9,123,169, 4, + 123,171, 3,123,172, 2,123,174, 1,122,176, 0,121,180, 0, + 119,186, 0,115,194, 0,111,201, 0,107,206, 0,105,209, 0, + 104,210, 0,105,209, 0,107,206, 0,110,203, 0,111,201, 0, + 109,204, 0,103,212, 0, 96,219, 0, 91,223, 0, 87,227, 0, + 85,228, 0, 86,228, 0, 89,225, 0, 87,227, 0, 85,229, 0, + 83,230, 0, 84,229, 0, 87,227, 0, 92,222, 0, 98,216, 0, + 105,209, 0,111,201, 0,116,193, 0,120,183, 0,121,179, 0, + 122,178, 0,122,176, 0,123,172, 2,123,169, 4,123,174, 1, + 122,176, 0,121,181, 0,118,188, 0,117,192, 0,116,193, 0, + 116,193, 0,116,192, 0,117,192, 0,117,191, 0,117,191, 0, + 118,189, 0,116,193, 0,114,196, 0,114,197, 0,114,196, 0, + 117,192, 0,119,186, 0,121,182, 0,121,180, 0,122,177, 0, + 122,176, 0,121,179, 0,121,181, 0,121,181, 0,121,179, 0, + 122,176, 0,123,170, 3,124,161, 11,124,152, 20,124,154, 18, + 124,158, 13,124,165, 8,123,171, 3,122,178, 0,121,182, 0, + 120,184, 0,120,184, 0,120,183, 0,121,179, 0,122,176, 0, + 122,175, 0,122,177, 0,122,178, 0,121,181, 0,120,182, 0, + 120,182, 0,121,180, 0,122,175, 0,121,180, 0,121,181, 0, + 122,176, 0,124,164, 9,123,137, 41,121,127, 58,122,135, 44, + 122,135, 44,124,150, 22,124,165, 8,123,169, 4,124,167, 6, + 122,176, 0,119,186, 0,117,190, 0,118,189, 0,119,186, 0, + 121,181, 0,122,175, 0,120,184, 0,118,189, 0,116,193, 0, + 116,192, 0,118,189, 0,120,184, 0,121,182, 0,121,182, 0, + 122,178, 0,123,170, 3,123,174, 1,120,183, 0,117,191, 0, + 114,197, 0,111,201, 0,110,202, 0,111,201, 0,114,196, 0, + 118,189, 0,122,178, 0,124,164, 9,123,141, 34,117,110,109, + 94, 70,242, 30, 37,192, 99, 75,247,111, 96,248,118,117, 85, + 121,127, 58,124,157, 15,123,172, 2,122,178, 0,121,180, 0, + 122,176, 0,124,166, 7,124,164, 9,124,160, 12,122,178, 0, + 118,189, 0,114,196, 0,113,199, 0,113,198, 0,115,195, 0, + 118,189, 0,120,184, 0,122,178, 0,123,173, 1,122,175, 0, + 122,176, 0,122,177, 0,122,175, 0,123,170, 3,124,164, 9, + 124,155, 16,124,150, 22,124,158, 13,124,162, 10,124,164, 9, + 124,161, 11,124,154, 18,124,147, 26,124,150, 22,124,157, 15, + 124,165, 8,122,178, 0,119,187, 0,117,192, 0,116,193, 0, + 116,192, 0,116,193, 0,114,196, 0,114,197, 0,114,196, 0, + 115,194, 0,117,191, 0,118,189, 0,119,185, 0,122,177, 0, + 123,169, 4,124,164, 9,124,162, 10,124,165, 8,124,161, 11, + 124,150, 22,124,150, 22,124,157, 15,124,157, 15,124,157, 15, + 124,162, 10,124,162, 10,124,160, 12,124,164, 9,123,170, 3, + 123,169, 4,124,166, 7,124,164, 9,124,162, 10,124,158, 13, + 124,150, 22,123,139, 37,119,119, 77,115,107,129,118,117, 85, + 121,125, 63,122,132, 48,124,143, 31,124,152, 20,124,165, 8, + 123,174, 1,122,178, 0,121,179, 0,122,178, 0,119,186, 0, + 117,191, 0,116,192, 0,117,190, 0,119,185, 0,122,178, 0, + 123,168, 5,124,154, 18,121,127, 58,118,117, 85,124,149, 24, + 124,157, 15,124,166, 7,123,171, 3,122,177, 0,118,189, 0, + 114,196, 0,113,199, 0,112,199, 0,113,198, 0,116,193, 0, + 116,193, 0,117,191, 0,119,186, 0,122,175, 0,124,154, 18, + 123,141, 34,122,135, 44,123,141, 34,124,154, 18,124,158, 13, + 124,157, 15,124,149, 24,123,141, 34,124,157, 15,124,167, 6, + 123,169, 4,124,167, 6,124,162, 10,124,155, 16,124,157, 15, + 124,165, 8,122,175, 0,119,185, 0,115,194, 0,112,200, 0, + 110,202, 0,109,203, 0,110,203, 0,111,201, 0,112,199, 0, + 113,198, 0,111,201, 0,105,209, 0, 99,216, 0, 94,221, 0, + 90,224, 0, 89,225, 0, 90,225, 0, 91,223, 0, 89,226, 0, + 87,227, 0, 85,229, 0, 86,228, 0, 88,226, 0, 92,222, 0, + 97,217, 0,103,211, 0,108,205, 0,113,198, 0,118,189, 0, + 122,178, 0,123,174, 1,123,170, 3,124,166, 7,124,167, 6, + 123,174, 1,122,178, 0,121,180, 0,121,181, 0,120,184, 0, + 119,187, 0,118,188, 0,118,189, 0,118,189, 0,118,189, 0, + 118,189, 0,117,191, 0,115,195, 0,114,197, 0,113,198, 0, + 114,197, 0,116,193, 0,119,186, 0,121,180, 0,122,176, 0, + 123,171, 3,122,176, 0,122,178, 0,122,178, 0,122,175, 0, + 123,170, 3,124,162, 10,124,150, 22,122,132, 48,121,125, 63, + 123,139, 37,124,154, 18,124,160, 12,124,166, 7,123,173, 1, + 122,178, 0,121,180, 0,121,179, 0,122,176, 0,123,170, 3, + 124,165, 8,124,164, 9,124,165, 8,123,169, 4,123,173, 1, + 122,176, 0,122,178, 0,122,177, 0,123,171, 3,123,170, 3, + 123,172, 2,123,168, 5,124,154, 18,121,125, 63,122,132, 48, + 123,141, 34,123,141, 34,124,143, 31,124,157, 15,124,161, 11, + 124,158, 13,123,170, 3,120,182, 0,119,187, 0,119,186, 0, + 120,184, 0,122,178, 0,123,172, 2,121,182, 0,118,189, 0, + 116,193, 0,116,193, 0,117,191, 0,119,187, 0,120,183, 0, + 121,181, 0,122,178, 0,123,173, 1,122,176, 0,120,184, 0, + 117,190, 0,115,195, 0,113,199, 0,112,200, 0,112,199, 0, + 114,196, 0,118,189, 0,121,179, 0,124,165, 8,123,139, 37, + 114,104,197,102, 80,250, 87, 65,237, 87, 65,237,111, 96,248, + 115,107,129,121,125, 63,123,141, 34,124,162, 10,123,173, 1, + 122,178, 0,122,177, 0,123,170, 3,124,160, 12,124,152, 20, + 123,170, 3,120,184, 0,117,191, 0,115,195, 0,115,195, 0, + 116,193, 0,118,188, 0,121,182, 0,122,175, 0,123,169, 4, + 123,169, 4,123,172, 2,123,174, 1,122,175, 0,123,173, 1, + 123,168, 5,124,164, 9,124,157, 15,124,155, 16,124,160, 12, + 124,161, 11,124,160, 12,124,154, 18,124,152, 20,124,155, 16, + 124,162, 10,123,173, 1,120,184, 0,117,191, 0,115,195, 0, + 114,196, 0,115,195, 0,115,195, 0,113,199, 0,112,200, 0, + 113,199, 0,114,196, 0,117,192, 0,118,188, 0,121,182, 0, + 123,169, 4,124,166, 7,123,171, 3,122,177, 0,122,177, 0, + 123,173, 1,124,164, 9,124,164, 9,124,165, 8,124,161, 11, + 124,150, 22,124,150, 22,124,149, 24,124,149, 24,124,160, 12, + 124,162, 10,124,158, 13,124,158, 13,124,155, 16,124,150, 22, + 123,139, 37,122,135, 44,121,127, 58,118,114, 96,118,114, 96, + 115,107,129,109, 92,251,109, 92,251,118,114, 96,124,147, 26, + 124,166, 7,122,178, 0,120,184, 0,119,186, 0,119,186, 0, + 119,186, 0,116,193, 0,114,197, 0,114,197, 0,115,194, 0, + 117,191, 0,119,185, 0,122,176, 0,124,161, 11,122,132, 48, + 123,139, 37,123,139, 37,124,152, 20,124,161, 11,122,178, 0, + 118,189, 0,114,196, 0,112,199, 0,112,200, 0,113,198, 0, + 115,194, 0,117,190, 0,118,188, 0,120,182, 0,123,171, 3, + 124,147, 26,120,122, 70,117,110,109,122,135, 44,124,145, 29, + 124,147, 26,124,150, 22,124,149, 24,124,149, 24,124,165, 8, + 123,171, 3,123,170, 3,124,164, 9,124,155, 16,124,149, 24, + 123,141, 34,122,130, 53,124,150, 22,123,170, 3,120,183, 0, + 117,190, 0,115,194, 0,114,196, 0,114,196, 0,115,195, 0, + 114,196, 0,113,198, 0,112,199, 0,107,206, 0,101,213, 0, + 97,218, 0, 94,221, 0, 93,222, 0, 94,221, 0, 94,221, 0, + 92,223, 0, 89,225, 0, 88,226, 0, 89,226, 0, 91,224, 0, + 94,221, 0, 98,216, 0,103,211, 0,108,206, 0,112,200, 0, + 117,192, 0,121,182, 0,124,165, 8,124,160, 12,124,152, 20, + 124,157, 15,123,168, 5,122,176, 0,121,180, 0,121,182, 0, + 121,182, 0,121,180, 0,121,182, 0,120,184, 0,119,185, 0, + 119,186, 0,119,185, 0,117,191, 0,115,194, 0,114,196, 0, + 115,195, 0,115,194, 0,117,190, 0,120,182, 0,122,176, 0, + 123,170, 3,123,171, 3,122,176, 0,122,178, 0,123,174, 1, + 124,167, 6,124,155, 16,124,145, 29,123,139, 37,121,127, 58, + 120,122, 70,122,135, 44,124,147, 26,124,150, 22,124,157, 15, + 124,167, 6,123,171, 3,123,172, 2,123,170, 3,124,165, 8, + 124,154, 18,124,155, 16,124,154, 18,124,147, 26,124,150, 22, + 124,158, 13,124,165, 8,123,169, 4,123,169, 4,124,165, 8, + 124,160, 12,124,162, 10,124,160, 12,124,147, 26,122,130, 53, + 122,130, 53,123,137, 41,123,139, 37,122,135, 44,124,145, 29, + 124,149, 24,124,145, 29,124,155, 16,123,171, 3,122,178, 0, + 122,178, 0,122,176, 0,123,171, 3,124,165, 8,122,178, 0, + 119,186, 0,117,191, 0,117,191, 0,118,189, 0,119,186, 0, + 120,184, 0,120,183, 0,121,182, 0,121,180, 0,122,177, 0, + 120,184, 0,118,188, 0,116,192, 0,114,196, 0,114,197, 0, + 114,196, 0,116,193, 0,119,187, 0,122,178, 0,124,162, 10, + 122,132, 48,117,110,109,115,107,129,109, 92,251, 30, 55,222, + 105, 84,252,115,107,129,120,122, 70,122,132, 48,124,150, 22, + 124,167, 6,123,174, 1,122,177, 0,123,174, 1,124,165, 8, + 124,143, 31,124,160, 12,122,176, 0,120,184, 0,118,189, 0, + 118,189, 0,119,186, 0,121,181, 0,123,174, 1,124,166, 7, + 124,160, 12,124,154, 18,124,160, 12,124,166, 7,123,169, 4, + 123,169, 4,124,167, 6,124,165, 8,124,160, 12,124,152, 20, + 124,152, 20,124,154, 18,124,152, 20,124,149, 24,124,149, 24, + 124,155, 16,124,162, 10,122,178, 0,118,188, 0,116,193, 0, + 114,197, 0,113,198, 0,114,196, 0,114,197, 0,111,201, 0, + 110,202, 0,111,201, 0,114,197, 0,117,192, 0,119,186, 0, + 122,177, 0,123,168, 5,122,178, 0,120,184, 0,119,187, 0, + 119,186, 0,121,182, 0,123,173, 1,123,173, 1,123,171, 3, + 124,165, 8,124,150, 22,122,130, 53,121,127, 58,124,143, 31, + 124,150, 22,124,149, 24,124,147, 26,124,149, 24,124,143, 31, + 123,139, 37,123,141, 34,123,137, 41,121,127, 58,122,132, 48, + 121,127, 58,118,117, 85,111, 96,248,109, 92,251,111, 96,248, + 123,137, 41,124,162, 10,122,178, 0,119,186, 0,117,191, 0, + 116,192, 0,117,191, 0,115,194, 0,112,199, 0,111,201, 0, + 112,200, 0,113,198, 0,115,194, 0,119,187, 0,122,175, 0, + 124,152, 20,119,119, 77,114,104,197,121,127, 58,124,157, 15, + 121,179, 0,117,190, 0,114,196, 0,112,199, 0,112,199, 0, + 113,198, 0,115,194, 0,118,189, 0,119,186, 0,121,181, 0, + 123,169, 4,124,143, 31,109, 92,251,107, 89,252,119,119, 77, + 122,132, 48,124,145, 29,124,150, 22,124,149, 24,124,157, 15, + 123,168, 5,123,170, 3,124,166, 7,124,155, 16,124,157, 15, + 124,155, 16,124,147, 26,121,125, 63,111, 96,248,123,141, 34, + 124,165, 8,122,176, 0,120,182, 0,119,186, 0,119,187, 0, + 117,190, 0,114,196, 0,113,198, 0,113,199, 0,109,204, 0, + 103,211, 0, 99,215, 0, 97,218, 0, 96,219, 0, 98,217, 0, + 97,217, 0, 95,220, 0, 93,221, 0, 92,222, 0, 92,222, 0, + 94,220, 0, 97,217, 0,101,214, 0,105,209, 0,108,205, 0, + 112,199, 0,116,192, 0,120,183, 0,123,168, 5,123,139, 37, + 121,125, 63,122,135, 44,124,155, 16,123,168, 5,122,176, 0, + 121,180, 0,121,182, 0,121,181, 0,122,177, 0,122,177, 0, + 121,179, 0,121,181, 0,120,183, 0,119,187, 0,118,189, 0, + 117,190, 0,118,189, 0,119,187, 0,120,182, 0,123,173, 1, + 123,169, 4,124,165, 8,123,172, 2,122,176, 0,122,175, 0, + 123,169, 4,124,157, 15,124,157, 15,124,155, 16,124,150, 22, + 123,137, 41,118,117, 85,120,122, 70,122,130, 53,122,132, 48, + 124,143, 31,124,157, 15,124,162, 10,124,162, 10,124,157, 15, + 124,145, 29,122,135, 44,123,141, 34,123,139, 37,122,132, 48, + 119,119, 77,122,135, 44,124,145, 29,124,154, 18,124,157, 15, + 124,154, 18,124,149, 24,124,155, 16,124,154, 18,124,145, 29, + 122,130, 53,122,130, 53,121,125, 63,122,130, 53,121,127, 58, + 120,122, 70,122,132, 48,122,130, 53,119,119, 77,124,147, 26, + 124,158, 13,124,161, 11,124,158, 13,124,155, 16,124,154, 18, + 123,170, 3,121,181, 0,119,185, 0,119,186, 0,120,184, 0, + 120,183, 0,120,184, 0,120,183, 0,120,184, 0,120,183, 0, + 121,180, 0,121,181, 0,119,185, 0,118,188, 0,117,191, 0, + 117,192, 0,117,191, 0,118,189, 0,120,184, 0,123,174, 1, + 124,155, 16,122,132, 48,123,141, 34,123,141, 34,122,130, 53, + 113,100,242, 69, 60,230,109, 92,251,118,114, 96,122,135, 44, + 123,141, 34,124,160, 12,123,170, 3,122,175, 0,122,175, 0, + 123,169, 4,124,152, 20,124,160, 12,124,165, 8,122,175, 0, + 121,179, 0,121,179, 0,122,175, 0,123,168, 5,124,158, 13, + 124,147, 26,123,137, 41,121,127, 58,122,132, 48,124,145, 29, + 124,154, 18,124,158, 13,124,160, 12,124,160, 12,124,155, 16, + 124,149, 24,123,137, 41,122,135, 44,122,135, 44,122,132, 48, + 123,137, 41,124,145, 29,124,165, 8,121,180, 0,118,189, 0, + 115,194, 0,114,197, 0,113,198, 0,114,196, 0,112,200, 0, + 109,203, 0,109,204, 0,110,202, 0,113,198, 0,117,191, 0, + 120,183, 0,123,171, 3,121,181, 0,118,189, 0,115,194, 0, + 114,196, 0,116,193, 0,118,188, 0,121,180, 0,121,180, 0, + 122,176, 0,124,167, 6,124,150, 22,119,119, 77,118,114, 96, + 121,127, 58,122,132, 48,120,122, 70,122,132, 48,122,132, 48, + 123,141, 34,124,149, 24,124,147, 26,123,139, 37,123,141, 34, + 124,143, 31,122,135, 44,120,122, 70,120,122, 70,118,117, 85, + 118,114, 96,118,117, 85,124,152, 20,123,174, 1,119,186, 0, + 116,193, 0,114,197, 0,114,197, 0,115,195, 0,112,199, 0, + 111,202, 0,110,202, 0,111,202, 0,113,199, 0,116,193, 0, + 120,182, 0,124,161, 11,118,114, 96, 30, 49,213,117,110,109, + 124,160, 12,121,180, 0,117,190, 0,115,195, 0,114,197, 0, + 114,197, 0,114,196, 0,116,192, 0,118,189, 0,119,187, 0, + 121,182, 0,123,170, 3,124,147, 26,109, 92,251, 30, 55,222, + 111, 96,248,121,127, 58,124,145, 29,124,150, 22,124,150, 22, + 124,157, 15,124,165, 8,124,165, 8,124,155, 16,124,160, 12, + 124,162, 10,124,160, 12,124,150, 22,121,127, 58,102, 80,250, + 102, 80,250,122,132, 48,124,155, 16,124,167, 6,123,174, 1, + 122,178, 0,118,188, 0,115,194, 0,114,197, 0,112,200, 0, + 108,206, 0,104,210, 0,101,214, 0, 99,215, 0, 99,215, 0, + 101,214, 0,101,213, 0, 99,215, 0, 97,217, 0, 97,218, 0, + 97,217, 0, 99,215, 0,102,213, 0,105,209, 0,108,206, 0, + 110,202, 0,114,197, 0,117,191, 0,121,182, 0,124,167, 6, + 123,139, 37,105, 84,252,105, 84,252,121,127, 58,124,154, 18, + 123,168, 5,122,176, 0,121,179, 0,121,179, 0,122,176, 0, + 123,169, 4,123,174, 1,122,176, 0,122,178, 0,121,181, 0, + 121,181, 0,121,180, 0,122,178, 0,123,174, 1,123,168, 5, + 124,165, 8,124,161, 11,123,168, 5,123,173, 1,122,175, 0, + 123,172, 2,124,164, 9,124,160, 12,124,162, 10,124,162, 10, + 124,157, 15,124,143, 31,118,117, 85,105, 84,252,109, 92,251, + 109, 92,251,121,125, 63,124,143, 31,124,150, 22,124,149, 24, + 123,139, 37,119,119, 77,118,114, 96,122,132, 48,123,141, 34, + 123,141, 34,122,132, 48,120,122, 70,121,125, 63,121,127, 58, + 122,132, 48,122,132, 48,124,143, 31,124,152, 20,124,154, 18, + 124,149, 24,123,137, 41,121,125, 63,121,125, 63,120,122, 70, + 118,117, 85,118,114, 96,114,104,197,115,107,129,111, 96,248, + 105, 84,252,117,110,109,118,117, 85,119,119, 77,120,122, 70, + 123,137, 41,124,158, 13,123,170, 3,122,176, 0,122,177, 0, + 121,180, 0,120,184, 0,119,185, 0,120,184, 0,120,182, 0, + 120,182, 0,121,180, 0,122,176, 0,121,180, 0,120,183, 0, + 119,185, 0,119,186, 0,119,186, 0,120,184, 0,122,178, 0, + 123,168, 5,124,147, 26,124,154, 18,124,161, 11,124,161, 11, + 124,152, 20,122,130, 53, 99, 75,247, 69, 60,230,114,104,197, + 122,132, 48,124,145, 29,124,154, 18,124,166, 7,123,173, 1, + 122,175, 0,123,170, 3,124,166, 7,123,170, 3,123,172, 2, + 123,169, 4,124,165, 8,124,162, 10,124,155, 16,123,141, 34, + 121,125, 63,115,107,129,117,110,109,114,104,197,107, 89,252, + 113,100,242,120,122, 70,123,137, 41,124,143, 31,124,145, 29, + 124,143, 31,123,139, 37,121,127, 58,115,107,129,111, 96,248, + 109, 92,251,114,104,197,122,135, 44,124,164, 9,121,179, 0, + 118,188, 0,116,193, 0,114,196, 0,114,196, 0,113,198, 0, + 110,203, 0,107,206, 0,107,207, 0,109,204, 0,113,199, 0, + 117,191, 0,121,181, 0,121,180, 0,117,191, 0,113,198, 0, + 111,202, 0,110,202, 0,113,199, 0,116,193, 0,119,185, 0, + 120,184, 0,121,180, 0,123,169, 4,124,150, 22,121,125, 63, + 115,107,129,109, 92,251,107, 89,252,107, 89,252,115,107,129, + 123,137, 41,124,152, 20,124,157, 15,124,154, 18,124,143, 31, + 124,145, 29,124,145, 29,124,145, 29,124,145, 29,123,139, 37, + 123,137, 41,122,132, 48,119,119, 77,123,137, 41,123,168, 5, + 120,184, 0,115,194, 0,112,200, 0,110,202, 0,111,201, 0, + 113,198, 0,111,201, 0,110,202, 0,110,202, 0,111,201, 0, + 115,195, 0,119,185, 0,124,165, 8,118,114, 96, 30, 2,128, + 117,110,109,124,158, 13,122,178, 0,118,188, 0,116,192, 0, + 116,193, 0,116,193, 0,117,192, 0,116,192, 0,117,192, 0, + 117,190, 0,119,185, 0,123,174, 1,124,154, 18,115,107,129, + 30, 10,132, 99, 75,247,120,122, 70,124,143, 31,124,152, 20, + 124,154, 18,124,154, 18,124,155, 16,124,152, 20,124,158, 13, + 124,164, 9,124,165, 8,124,161, 11,124,150, 22,121,125, 63, + 105, 84,252, 30, 2,128,105, 84,252,121,127, 58,124,150, 22, + 124,164, 9,123,173, 1,120,183, 0,117,190, 0,115,194, 0, + 111,202, 0,105,209, 0,101,213, 0, 99,216, 0, 98,216, 0, + 100,215, 0,102,212, 0,105,209, 0,103,211, 0,102,213, 0, + 101,214, 0,102,213, 0,103,211, 0,106,208, 0,108,205, 0, + 110,202, 0,112,199, 0,115,195, 0,118,189, 0,121,180, 0, + 124,165, 8,123,137, 41,105, 84,252, 30, 2,128, 94, 70,242, + 121,125, 63,124,152, 20,124,167, 6,123,173, 1,123,174, 1, + 123,172, 2,124,165, 8,123,171, 3,123,173, 1,123,171, 3, + 123,170, 3,123,168, 5,124,164, 9,124,160, 12,124,150, 22, + 124,155, 16,124,158, 13,124,166, 7,123,172, 2,123,174, 1, + 123,174, 1,123,169, 4,124,158, 13,124,162, 10,124,166, 7, + 124,164, 9,124,155, 16,124,143, 31,118,117, 85, 87, 65,237, + 30, 2,128, 30, 55,222,114,104,197,121,125, 63,122,135, 44, + 122,130, 53,121,127, 58,119,119, 77,117,110,109,122,135, 44, + 124,145, 29,124,149, 24,124,147, 26,123,139, 37,121,127, 58, + 119,119, 77,111, 96,248,121,127, 58,124,147, 26,124,157, 15, + 124,161, 11,124,160, 12,124,154, 18,123,141, 34,120,122, 70, + 121,125, 63,122,130, 53,122,130, 53,120,122, 70,114,104,197, + 99, 75,247, 30, 37,192, 30, 2,128, 30, 15,143, 30, 31,180, + 69, 60,230,115,107,129,123,137, 41,124,155, 16,124,164, 9, + 124,165, 8,122,178, 0,120,183, 0,119,185, 0,120,184, 0, + 121,182, 0,122,178, 0,122,176, 0,123,168, 5,122,178, 0, + 120,183, 0,120,184, 0,120,182, 0,121,182, 0,121,179, 0, + 123,174, 1,124,165, 8,124,162, 10,124,166, 7,123,170, 3, + 123,170, 3,124,164, 9,124,143, 31,111, 96,248, 30, 2,128, + 107, 89,252,121,127, 58,124,147, 26,124,155, 16,124,165, 8, + 123,171, 3,123,173, 1,123,169, 4,122,175, 0,122,178, 0, + 122,178, 0,122,175, 0,124,167, 6,124,152, 20,121,127, 58, + 111, 96,248,115,107,129,118,117, 85,120,122, 70,120,122, 70, + 118,117, 85,115,107,129,109, 92,251,111, 96,248,117,110,109, + 119,119, 77,120,122, 70,118,117, 85,114,104,197,102, 80,250, + 30, 43,203, 30, 2,128, 69, 60,230,120,122, 70,124,157, 15, + 122,176, 0,119,186, 0,117,191, 0,115,194, 0,115,195, 0, + 111,201, 0,107,206, 0,105,209, 0,106,208, 0,108,205, 0, + 113,199, 0,117,190, 0,121,179, 0,117,190, 0,113,199, 0, + 109,204, 0,106,207, 0,107,207, 0,110,203, 0,114,197, 0, + 118,188, 0,119,187, 0,121,182, 0,123,171, 3,124,154, 18, + 120,122, 70,111, 96,248, 30, 55,222, 30, 2,128, 94, 70,242, + 120,122, 70,124,149, 24,124,161, 11,124,164, 9,124,161, 11, + 124,152, 20,124,143, 31,124,154, 18,124,157, 15,124,157, 15, + 124,161, 11,124,161, 11,124,158, 13,124,149, 24,121,127, 58, + 124,164, 9,120,184, 0,115,195, 0,111,202, 0,108,205, 0, + 108,205, 0,110,202, 0,112,199, 0,111,201, 0,110,202, 0, + 111,201, 0,114,196, 0,119,186, 0,124,166, 7,118,117, 85, + 30, 26,168,115,107,129,124,155, 16,122,175, 0,120,184, 0, + 118,188, 0,118,188, 0,116,193, 0,114,196, 0,114,196, 0, + 114,196, 0,116,193, 0,118,188, 0,122,178, 0,124,158, 13, + 118,117, 85, 30, 31,180, 30, 55,222,118,114, 96,123,139, 37, + 124,152, 20,124,158, 13,124,160, 12,124,161, 11,124,161, 11, + 124,165, 8,123,168, 5,123,168, 5,124,164, 9,124,161, 11, + 124,150, 22,120,122, 70, 69, 60,230, 94, 70,242,117,110,109, + 123,137, 41,124,157, 15,123,170, 3,121,180, 0,118,188, 0, + 114,196, 0,108,205, 0,103,212, 0, 98,217, 0, 95,220, 0, + 94,220, 0, 95,219, 0, 98,217, 0,101,214, 0,103,212, 0, + 103,211, 0,106,208, 0,106,208, 0,107,207, 0,106,208, 0, + 106,207, 0,108,205, 0,111,201, 0,115,194, 0,118,188, 0, + 121,180, 0,124,166, 7,124,145, 29,118,117, 85, 94, 70,242, + 30, 49,213,105, 84,252,121,127, 58,124,150, 22,124,161, 11, + 124,164, 9,124,161, 11,124,166, 7,123,172, 2,123,173, 1, + 123,170, 3,124,165, 8,124,154, 18,124,157, 15,124,162, 10, + 124,162, 10,124,164, 9,124,167, 6,123,171, 3,123,173, 1, + 123,173, 1,123,171, 3,124,164, 9,124,164, 9,124,167, 6, + 123,172, 2,123,174, 1,123,171, 3,124,162, 10,123,141, 34, + 115,107,129,111, 96,248,111, 96,248,114,104,197,118,114, 96, + 122,130, 53,123,137, 41,122,135, 44,122,130, 53,120,122, 70, + 122,135, 44,124,147, 26,124,154, 18,124,154, 18,124,152, 20, + 124,145, 29,123,137, 41,122,130, 53,121,125, 63,124,145, 29, + 124,158, 13,124,165, 8,124,166, 7,124,164, 9,124,157, 15, + 124,149, 24,122,132, 48,122,135, 44,124,143, 31,124,145, 29, + 123,141, 34,122,132, 48,118,117, 85,113,100,242,105, 84,252, + 87, 65,237, 30, 37,192, 94, 70,242,118,114, 96,123,139, 37, + 124,150, 22,124,155, 16,123,170, 3,122,178, 0,120,183, 0, + 120,184, 0,120,183, 0,121,181, 0,122,178, 0,122,176, 0, + 120,183, 0,118,188, 0,118,189, 0,118,188, 0,119,186, 0, + 120,183, 0,121,179, 0,122,177, 0,122,176, 0,122,175, 0, + 123,174, 1,123,172, 2,124,165, 8,124,145, 29,113,100,242, + 69, 60,230,105, 84,252,121,127, 58,124,147, 26,124,158, 13, + 124,164, 9,123,168, 5,123,172, 2,122,177, 0,121,182, 0, + 120,184, 0,120,182, 0,122,178, 0,123,168, 5,124,152, 20, + 123,139, 37,124,145, 29,124,147, 26,124,145, 29,123,137, 41, + 122,132, 48,122,132, 48,121,127, 58,120,122, 70,119,119, 77, + 118,117, 85,118,117, 85,118,117, 85,117,110,109,114,104,197, + 111, 96,248,111, 96,248,111, 96,248,107, 89,252,114,104,197, + 124,145, 29,123,169, 4,121,182, 0,118,188, 0,116,192, 0, + 114,197, 0,110,203, 0,107,207, 0,105,209, 0,106,208, 0, + 109,204, 0,114,197, 0,118,188, 0,117,191, 0,112,199, 0, + 107,206, 0,103,211, 0,102,212, 0,103,212, 0,106,207, 0, + 111,201, 0,116,193, 0,119,187, 0,121,182, 0,123,172, 2, + 124,155, 16,121,127, 58,107, 89,252,107, 89,252,109, 92,251, + 118,117, 85,123,139, 37,124,157, 15,124,167, 6,123,171, 3, + 123,172, 2,123,171, 3,123,169, 4,123,170, 3,123,172, 2, + 123,174, 1,122,175, 0,123,174, 1,123,170, 3,124,162, 10, + 124,149, 24,124,161, 11,121,182, 0,116,193, 0,111,201, 0, + 108,205, 0,108,205, 0,109,204, 0,111,201, 0,112,200, 0, + 111,201, 0,112,200, 0,115,194, 0,119,185, 0,124,165, 8, + 118,117, 85, 30, 49,213,115,107,129,124,154, 18,123,172, 2, + 121,180, 0,120,183, 0,118,188, 0,115,195, 0,113,198, 0, + 113,199, 0,113,198, 0,115,194, 0,118,189, 0,122,178, 0, + 124,160, 12,119,119, 77,105, 84,252, 30, 55,222,114,104,197, + 123,137, 41,124,154, 18,124,164, 9,123,168, 5,123,169, 4, + 123,170, 3,123,171, 3,123,172, 2,123,171, 3,123,174, 1, + 123,171, 3,124,164, 9,124,143, 31,113,100,242, 94, 70,242, + 115,107,129,122,130, 53,124,149, 24,124,167, 6,121,179, 0, + 118,189, 0,113,198, 0,107,207, 0,101,214, 0, 95,219, 0, + 92,222, 0, 91,223, 0, 92,223, 0, 93,221, 0, 95,220, 0, + 97,218, 0,100,215, 0,102,212, 0,106,208, 0,106,208, 0, + 105,209, 0,105,209, 0,107,207, 0,110,202, 0,114,197, 0, + 117,191, 0,120,183, 0,123,173, 1,124,160, 12,123,139, 37, + 114,104,197, 30, 37,192, 30, 55,222,109, 92,251,121,125, 63, + 123,139, 37,124,145, 29,124,158, 13,123,168, 5,123,173, 1, + 123,173, 1,123,169, 4,124,160, 12,124,150, 22,124,161, 11, + 124,167, 6,123,169, 4,123,170, 3,123,172, 2,123,173, 1, + 123,172, 2,123,170, 3,124,165, 8,124,167, 6,123,174, 1, + 121,182, 0,119,185, 0,119,186, 0,120,182, 0,122,176, 0, + 124,162, 10,124,147, 26,124,143, 31,123,139, 37,124,143, 31, + 124,147, 26,124,147, 26,124,143, 31,123,141, 34,123,137, 41, + 122,130, 53,124,143, 31,124,155, 16,124,161, 11,124,162, 10, + 124,161, 11,124,157, 15,124,152, 20,124,147, 26,124,145, 29, + 124,143, 31,124,147, 26,124,158, 13,124,162, 10,124,164, 9, + 124,161, 11,124,158, 13,124,150, 22,122,132, 48,124,145, 29, + 124,154, 18,124,158, 13,124,157, 15,124,152, 20,124,145, 29, + 123,137, 41,120,122, 70,114,104,197,107, 89,252,111, 96,248, + 121,127, 58,124,145, 29,124,154, 18,124,161, 11,123,173, 1, + 121,180, 0,120,184, 0,119,185, 0,119,185, 0,119,185, 0, + 120,184, 0,119,187, 0,117,192, 0,115,194, 0,115,194, 0, + 116,193, 0,117,191, 0,118,188, 0,119,186, 0,119,185, 0, + 120,184, 0,121,182, 0,122,176, 0,124,167, 6,124,152, 20, + 122,130, 53,118,114, 96,120,122, 70,121,127, 58,124,147, 26, + 124,158, 13,124,165, 8,123,170, 3,122,176, 0,121,181, 0, + 120,184, 0,119,186, 0,120,184, 0,122,178, 0,124,166, 7, + 124,157, 15,124,165, 8,123,169, 4,123,169, 4,124,167, 6, + 124,161, 11,124,150, 22,122,135, 44,123,137, 41,123,139, 37, + 124,143, 31,124,147, 26,124,147, 26,124,147, 26,124,145, 29, + 124,143, 31,123,141, 34,123,141, 34,123,141, 34,123,139, 37, + 121,127, 58,123,137, 41,124,160, 12,122,175, 0,120,184, 0, + 117,190, 0,114,197, 0,110,202, 0,108,205, 0,107,206, 0, + 109,204, 0,112,200, 0,116,193, 0,116,193, 0,111,201, 0, + 106,207, 0,101,213, 0, 98,217, 0, 97,218, 0, 98,217, 0, + 102,213, 0,107,207, 0,112,199, 0,117,191, 0,120,182, 0, + 123,171, 3,124,157, 15,122,132, 48,122,135, 44,123,137, 41, + 123,141, 34,124,152, 20,124,166, 7,122,175, 0,121,182, 0, + 119,185, 0,119,186, 0,119,186, 0,119,185, 0,119,185, 0, + 119,186, 0,119,186, 0,120,184, 0,120,182, 0,122,178, 0, + 123,172, 2,124,162, 10,124,155, 16,122,177, 0,118,189, 0, + 114,197, 0,111,201, 0,110,202, 0,111,202, 0,112,200, 0, + 113,199, 0,112,200, 0,113,198, 0,116,193, 0,120,183, 0, + 124,164, 9,118,114, 96, 87, 65,237,115,107,129,124,152, 20, + 123,170, 3,122,177, 0,122,178, 0,119,187, 0,115,195, 0, + 113,199, 0,113,199, 0,114,197, 0,116,193, 0,119,186, 0, + 122,175, 0,124,154, 18,122,135, 44,118,117, 85,111, 96,248, + 113,100,242,122,132, 48,124,155, 16,124,167, 6,123,173, 1, + 122,176, 0,122,177, 0,122,176, 0,122,176, 0,122,178, 0, + 121,180, 0,122,178, 0,123,170, 3,124,155, 16,121,125, 63, + 111, 96,248,111, 96,248,120,122, 70,123,137, 41,124,162, 10, + 122,178, 0,118,189, 0,112,199, 0,106,208, 0, 99,215, 0, + 94,221, 0, 91,224, 0, 89,225, 0, 89,225, 0, 90,225, 0, + 91,224, 0, 92,223, 0, 94,221, 0, 96,219, 0,100,215, 0, + 103,211, 0,105,209, 0,105,209, 0,107,207, 0,110,203, 0, + 113,198, 0,116,192, 0,119,186, 0,122,178, 0,124,166, 7, + 124,149, 24,120,122, 70, 94, 70,242, 30, 31,180, 69, 60,230, + 111, 96,248,120,122, 70,124,143, 31,124,157, 15,124,167, 6, + 123,171, 3,123,170, 3,124,165, 8,124,154, 18,123,141, 34, + 124,157, 15,124,166, 7,123,170, 3,123,172, 2,123,173, 1, + 123,172, 2,123,169, 4,124,165, 8,124,166, 7,121,180, 0, + 118,188, 0,117,192, 0,116,193, 0,116,193, 0,117,190, 0, + 119,185, 0,122,177, 0,123,168, 5,124,164, 9,124,160, 12, + 124,160, 12,124,161, 11,124,161, 11,124,158, 13,124,150, 22, + 123,139, 37,123,137, 41,124,152, 20,124,164, 9,123,168, 5, + 123,169, 4,123,168, 5,124,165, 8,124,160, 12,124,157, 15, + 124,155, 16,124,154, 18,124,152, 20,124,149, 24,124,149, 24, + 124,155, 16,124,158, 13,124,161, 11,124,157, 15,124,145, 29, + 124,143, 31,124,157, 15,124,165, 8,124,167, 6,124,167, 6, + 124,164, 9,124,157, 15,124,145, 29,121,127, 58,117,110,109, + 118,117, 85,120,122, 70,124,145, 29,124,155, 16,124,161, 11, + 124,166, 7,122,177, 0,120,183, 0,119,186, 0,118,189, 0, + 117,190, 0,117,190, 0,117,190, 0,115,195, 0,113,198, 0, + 112,200, 0,112,199, 0,114,197, 0,115,195, 0,116,193, 0, + 117,192, 0,118,189, 0,119,186, 0,121,181, 0,123,173, 1, + 124,162, 10,124,149, 24,124,152, 20,124,155, 16,124,154, 18, + 124,150, 22,124,154, 18,124,162, 10,123,169, 4,122,175, 0, + 121,181, 0,119,185, 0,119,186, 0,120,182, 0,122,175, 0, + 124,162, 10,123,170, 3,122,178, 0,121,181, 0,121,181, 0, + 122,177, 0,123,171, 3,124,161, 11,124,147, 26,123,137, 41, + 124,145, 29,124,155, 16,124,162, 10,124,165, 8,124,165, 8, + 124,165, 8,124,164, 9,124,162, 10,124,161, 11,124,162, 10, + 124,161, 11,124,157, 15,124,149, 24,124,160, 12,123,173, 1, + 120,182, 0,118,189, 0,115,194, 0,113,199, 0,111,201, 0, + 111,202, 0,112,199, 0,115,195, 0,114,196, 0,110,202, 0, + 105,209, 0,100,214, 0, 96,219, 0, 93,222, 0, 92,222, 0, + 94,221, 0, 98,217, 0,103,212, 0,108,205, 0,113,198, 0, + 117,190, 0,121,181, 0,123,168, 5,124,164, 9,124,161, 11, + 124,160, 12,124,164, 9,123,170, 3,122,178, 0,119,185, 0, + 117,190, 0,116,192, 0,115,194, 0,115,194, 0,115,194, 0, + 115,194, 0,116,193, 0,116,192, 0,117,191, 0,118,188, 0, + 120,184, 0,122,178, 0,123,173, 1,124,165, 8,123,169, 4, + 120,182, 0,117,190, 0,115,194, 0,114,197, 0,114,197, 0, + 114,196, 0,114,197, 0,113,198, 0,114,196, 0,117,191, 0, + 121,181, 0,124,160, 12,117,110,109,107, 89,252,118,117, 85, + 124,150, 22,123,168, 5,122,175, 0,122,175, 0,120,183, 0, + 117,192, 0,114,196, 0,114,196, 0,116,193, 0,118,189, 0, + 121,180, 0,123,168, 5,124,157, 15,124,145, 29,123,137, 41, + 122,130, 53,119,119, 77,122,130, 53,124,155, 16,123,169, 4, + 122,177, 0,121,180, 0,121,180, 0,122,178, 0,121,179, 0, + 121,182, 0,120,182, 0,121,180, 0,123,174, 1,124,162, 10, + 123,137, 41,120,122, 70,109, 92,251,114,104,197,120,122, 70, + 124,154, 18,123,173, 1,119,187, 0,113,198, 0,106,208, 0, + 100,215, 0, 94,220, 0, 91,224, 0, 89,226, 0, 88,226, 0, + 87,227, 0, 87,227, 0, 87,227, 0, 88,226, 0, 91,224, 0, + 94,220, 0, 99,215, 0,105,209, 0,106,208, 0,108,206, 0, + 110,202, 0,113,198, 0,114,196, 0,117,192, 0,120,184, 0, + 123,174, 1,124,157, 15,122,130, 53,107, 89,252, 87, 65,237, + 30, 49,213, 87, 65,237,115,107,129,122,135, 44,124,152, 20, + 124,161, 11,124,166, 7,124,165, 8,124,158, 13,124,143, 31, + 120,122, 70,124,143, 31,124,158, 13,124,166, 7,123,168, 5, + 123,169, 4,123,168, 5,124,164, 9,124,164, 9,121,180, 0, + 117,190, 0,114,196, 0,113,199, 0,112,199, 0,113,198, 0, + 114,196, 0,117,191, 0,119,186, 0,121,180, 0,122,175, 0, + 123,170, 3,123,169, 4,123,169, 4,123,169, 4,124,166, 7, + 124,161, 11,124,154, 18,124,147, 26,124,162, 10,123,169, 4, + 123,173, 1,123,174, 1,123,172, 2,123,169, 4,123,170, 3, + 124,167, 6,124,162, 10,124,160, 12,124,157, 15,124,154, 18, + 124,150, 22,124,147, 26,124,147, 26,124,155, 16,124,155, 16, + 124,149, 24,122,135, 44,124,154, 18,124,165, 8,123,170, 3, + 123,171, 3,123,169, 4,124,162, 10,124,152, 20,122,135, 44, + 120,122, 70,122,130, 53,122,135, 44,124,145, 29,124,158, 13, + 124,166, 7,123,170, 3,123,173, 1,121,181, 0,119,186, 0, + 117,190, 0,116,192, 0,116,193, 0,115,194, 0,114,196, 0, + 111,201, 0,110,202, 0,110,203, 0,110,202, 0,112,200, 0, + 113,198, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 122,178, 0,123,171, 3,123,172, 2,123,173, 1,123,172, 2, + 123,169, 4,124,165, 8,124,160, 12,124,155, 16,124,161, 11, + 123,169, 4,122,178, 0,120,182, 0,120,182, 0,121,179, 0, + 123,171, 3,124,165, 8,122,175, 0,120,182, 0,119,186, 0, + 120,184, 0,121,180, 0,123,173, 1,124,162, 10,124,149, 24, + 122,130, 53,124,143, 31,124,158, 13,123,168, 5,123,172, 2, + 123,174, 1,123,174, 1,123,173, 1,123,171, 3,123,171, 3, + 123,172, 2,123,172, 2,123,171, 3,123,168, 5,124,165, 8, + 123,173, 1,121,182, 0,119,187, 0,117,191, 0,115,194, 0, + 114,196, 0,114,197, 0,113,198, 0,113,198, 0,111,202, 0, + 105,209, 0,100,214, 0, 95,219, 0, 92,223, 0, 89,225, 0, + 89,225, 0, 91,223, 0, 95,220, 0,100,215, 0,105,209, 0, + 110,203, 0,114,196, 0,118,188, 0,121,180, 0,121,180, 0, + 122,178, 0,123,174, 1,122,176, 0,121,180, 0,119,185, 0, + 117,190, 0,115,194, 0,114,197, 0,113,198, 0,113,198, 0, + 113,199, 0,112,199, 0,113,198, 0,114,196, 0,116,193, 0, + 117,191, 0,119,187, 0,120,183, 0,121,180, 0,122,175, 0, + 124,167, 6,123,172, 2,121,181, 0,119,186, 0,117,191, 0, + 116,193, 0,115,194, 0,114,196, 0,115,195, 0,116,193, 0, + 118,188, 0,122,178, 0,124,157, 15,115,107,129,114,104,197, + 122,132, 48,124,149, 24,124,167, 6,123,173, 1,123,173, 1, + 123,174, 1,119,185, 0,118,189, 0,118,189, 0,119,186, 0, + 121,179, 0,123,173, 1,124,165, 8,124,155, 16,124,147, 26, + 124,147, 26,124,147, 26,123,141, 34,122,135, 44,124,155, 16, + 123,169, 4,122,178, 0,121,181, 0,121,180, 0,122,178, 0, + 121,180, 0,121,182, 0,120,182, 0,121,181, 0,122,176, 0, + 124,167, 6,124,147, 26,123,139, 37,118,114, 96, 99, 75,247, + 111, 96,248,123,139, 37,124,166, 7,120,183, 0,114,196, 0, + 107,206, 0,101,214, 0, 96,219, 0, 92,222, 0, 90,225, 0, + 88,226, 0, 87,227, 0, 86,228, 0, 85,229, 0, 85,229, 0, + 87,227, 0, 90,224, 0, 96,219, 0,102,212, 0,107,207, 0, + 109,204, 0,111,202, 0,111,202, 0,112,200, 0,114,197, 0, + 117,191, 0,121,182, 0,123,168, 5,124,150, 22,121,125, 63, + 117,110,109,111, 96,248,105, 84,252,109, 92,251,118,114, 96, + 123,137, 41,124,147, 26,124,152, 20,124,150, 22,124,145, 29, + 122,130, 53,114,104,197,118,114, 96,123,137, 41,124,150, 22, + 124,158, 13,124,160, 12,124,166, 7,123,169, 4,123,174, 1, + 118,188, 0,114,196, 0,111,201, 0,110,202, 0,110,202, 0, + 111,201, 0,113,198, 0,115,195, 0,117,192, 0,118,188, 0, + 119,185, 0,121,181, 0,122,175, 0,123,174, 1,123,173, 1, + 123,171, 3,123,168, 5,124,161, 11,124,162, 10,124,167, 6, + 123,174, 1,122,178, 0,122,178, 0,122,176, 0,122,175, 0, + 122,178, 0,122,177, 0,123,173, 1,123,168, 5,124,164, 9, + 124,155, 16,124,152, 20,124,150, 22,124,150, 22,124,152, 20, + 124,149, 24,124,145, 29,122,132, 48,124,145, 29,124,160, 12, + 124,167, 6,123,170, 3,123,168, 5,124,160, 12,124,147, 26, + 121,127, 58,121,125, 63,122,135, 44,124,143, 31,124,147, 26, + 124,160, 12,123,168, 5,123,174, 1,122,176, 0,122,178, 0, + 119,185, 0,118,189, 0,116,192, 0,115,194, 0,115,195, 0, + 114,196, 0,111,201, 0,109,204, 0,108,205, 0,109,204, 0, + 110,203, 0,111,201, 0,113,199, 0,115,195, 0,117,191, 0, + 118,189, 0,118,189, 0,118,188, 0,119,186, 0,119,185, 0, + 120,183, 0,122,178, 0,123,172, 2,124,167, 6,124,164, 9, + 124,158, 13,124,160, 12,123,168, 5,123,174, 1,122,175, 0, + 123,172, 2,124,165, 8,124,164, 9,122,175, 0,120,182, 0, + 119,185, 0,120,184, 0,122,178, 0,123,169, 4,124,157, 15, + 123,139, 37,119,119, 77,122,130, 53,124,155, 16,123,169, 4, + 122,176, 0,122,178, 0,122,178, 0,122,178, 0,122,177, 0, + 122,178, 0,122,177, 0,121,179, 0,121,180, 0,121,179, 0, + 122,177, 0,123,173, 1,121,180, 0,119,185, 0,118,189, 0, + 115,194, 0,114,197, 0,112,199, 0,112,200, 0,111,201, 0, + 107,207, 0,102,213, 0, 97,217, 0, 93,221, 0, 90,225, 0, + 88,226, 0, 87,227, 0, 89,225, 0, 93,221, 0, 98,217, 0, + 103,212, 0,107,206, 0,111,201, 0,115,194, 0,117,191, 0, + 117,191, 0,118,189, 0,119,185, 0,120,183, 0,119,186, 0, + 118,189, 0,116,193, 0,114,196, 0,114,197, 0,113,198, 0, + 113,199, 0,112,200, 0,111,201, 0,112,200, 0,113,198, 0, + 115,194, 0,117,192, 0,118,189, 0,118,188, 0,119,186, 0, + 120,184, 0,121,182, 0,122,177, 0,120,183, 0,118,188, 0, + 117,191, 0,116,192, 0,116,192, 0,116,192, 0,116,192, 0, + 117,190, 0,119,185, 0,122,175, 0,124,155, 16,115,107,129, + 119,119, 77,123,141, 34,124,158, 13,124,167, 6,123,173, 1, + 123,172, 2,124,166, 7,123,172, 2,122,178, 0,122,178, 0, + 122,177, 0,123,172, 2,124,166, 7,124,158, 13,124,150, 22, + 124,147, 26,124,150, 22,124,157, 15,124,155, 16,124,150, 22, + 124,155, 16,123,168, 5,122,176, 0,121,179, 0,122,178, 0, + 122,177, 0,122,178, 0,121,180, 0,121,181, 0,121,180, 0, + 122,177, 0,123,169, 4,124,155, 16,124,150, 22,122,130, 53, + 102, 80,250, 69, 60,230,118,117, 85,124,154, 18,122,176, 0, + 117,191, 0,110,202, 0,104,210, 0, 99,216, 0, 95,220, 0, + 92,222, 0, 91,224, 0, 89,226, 0, 86,228, 0, 84,229, 0, + 83,230, 0, 84,229, 0, 88,226, 0, 94,221, 0,101,214, 0, + 108,206, 0,110,203, 0,109,204, 0,108,205, 0,109,204, 0, + 111,201, 0,115,195, 0,119,187, 0,122,177, 0,124,162, 10, + 124,145, 29,123,139, 37,122,130, 53,118,114, 96,118,114, 96, + 119,119, 77,120,122, 70,120,122, 70,120,122, 70,121,125, 63, + 119,119, 77,115,107,129,102, 80,250, 30, 43,203,107, 89,252, + 121,127, 58,124,149, 24,124,162, 10,123,170, 3,122,175, 0, + 121,181, 0,117,192, 0,113,199, 0,110,202, 0,109,203, 0, + 110,202, 0,111,201, 0,112,200, 0,112,199, 0,114,197, 0, + 115,194, 0,117,192, 0,118,189, 0,120,184, 0,122,178, 0, + 122,176, 0,123,174, 1,123,171, 3,124,167, 6,123,169, 4, + 123,172, 2,122,177, 0,121,179, 0,121,180, 0,122,178, 0, + 121,179, 0,120,183, 0,120,182, 0,121,181, 0,121,179, 0, + 122,177, 0,123,174, 1,123,168, 5,124,160, 12,124,152, 20, + 124,155, 16,124,155, 16,124,147, 26,122,132, 48,122,132, 48, + 124,149, 24,124,160, 12,124,162, 10,124,160, 12,124,150, 22, + 122,132, 48,118,114, 96,120,122, 70,122,135, 44,124,143, 31, + 124,149, 24,124,158, 13,123,169, 4,122,176, 0,121,180, 0, + 121,182, 0,120,182, 0,119,187, 0,117,190, 0,117,192, 0, + 116,193, 0,115,194, 0,113,199, 0,109,203, 0,108,206, 0, + 108,206, 0,109,204, 0,110,202, 0,112,200, 0,113,199, 0, + 111,201, 0,111,201, 0,112,200, 0,114,197, 0,115,194, 0, + 116,192, 0,118,189, 0,120,183, 0,121,179, 0,122,178, 0, + 122,175, 0,123,168, 5,124,160, 12,124,164, 9,124,165, 8, + 124,162, 10,124,161, 11,124,155, 16,124,155, 16,123,168, 5, + 122,177, 0,121,180, 0,122,178, 0,123,170, 3,124,158, 13, + 124,149, 24,122,135, 44,114,104,197,115,107,129,124,147, 26, + 124,166, 7,122,175, 0,121,179, 0,121,181, 0,121,180, 0, + 120,183, 0,119,185, 0,120,184, 0,120,183, 0,120,184, 0, + 119,185, 0,120,184, 0,121,181, 0,121,179, 0,120,183, 0, + 118,188, 0,115,194, 0,113,198, 0,112,200, 0,112,200, 0, + 109,204, 0,105,209, 0,101,214, 0, 97,218, 0, 93,222, 0, + 90,225, 0, 88,226, 0, 88,226, 0, 90,225, 0, 93,221, 0, + 97,217, 0,102,212, 0,106,208, 0,110,203, 0,113,198, 0, + 113,198, 0,113,198, 0,114,197, 0,116,193, 0,118,188, 0, + 117,190, 0,117,192, 0,116,193, 0,115,195, 0,114,196, 0, + 114,196, 0,114,197, 0,113,198, 0,112,199, 0,113,198, 0, + 114,196, 0,115,194, 0,115,194, 0,115,195, 0,115,194, 0, + 117,191, 0,117,191, 0,117,190, 0,118,188, 0,120,184, 0, + 118,189, 0,117,190, 0,117,190, 0,118,189, 0,118,188, 0, + 118,188, 0,119,186, 0,121,181, 0,123,171, 3,124,152, 20, + 118,117, 85,121,127, 58,124,150, 22,124,166, 7,123,174, 1, + 122,177, 0,122,175, 0,123,169, 4,124,158, 13,124,166, 7, + 123,169, 4,123,168, 5,124,164, 9,124,157, 15,124,147, 26, + 124,143, 31,124,143, 31,124,154, 18,124,162, 10,124,165, 8, + 124,164, 9,124,162, 10,123,170, 3,122,176, 0,122,178, 0, + 122,177, 0,122,175, 0,122,175, 0,122,178, 0,122,178, 0, + 121,179, 0,122,178, 0,123,172, 2,124,161, 11,124,158, 13, + 123,141, 34,113,100,242, 30, 37,192,109, 92,251,124,147, 26, + 123,170, 3,119,185, 0,113,198, 0,107,207, 0,102,212, 0, + 99,216, 0, 96,219, 0, 93,222, 0, 91,224, 0, 88,226, 0, + 85,229, 0, 83,230, 0, 84,229, 0, 87,227, 0, 93,222, 0, + 100,214, 0,108,206, 0,109,203, 0,107,207, 0,106,208, 0, + 107,207, 0,109,203, 0,113,198, 0,117,190, 0,121,181, 0, + 123,169, 4,124,158, 13,124,157, 15,124,152, 20,124,143, 31, + 122,130, 53,122,130, 53,121,127, 58,120,122, 70,118,117, 85, + 118,117, 85,118,117, 85,117,110,109,114,104,197,107, 89,252, + 99, 75,247,119,119, 77,124,147, 26,124,164, 9,123,173, 1, + 121,179, 0,120,184, 0,116,193, 0,112,199, 0,110,202, 0, + 110,202, 0,109,204, 0,107,207, 0,107,207, 0,108,205, 0, + 111,202, 0,114,197, 0,115,195, 0,116,192, 0,118,188, 0, + 120,184, 0,122,178, 0,122,177, 0,122,175, 0,123,170, 3, + 123,171, 3,123,174, 1,122,176, 0,121,179, 0,121,181, 0, + 121,180, 0,121,180, 0,120,184, 0,120,184, 0,120,184, 0, + 120,184, 0,120,184, 0,120,183, 0,121,180, 0,123,174, 1, + 124,165, 8,124,158, 13,124,160, 12,124,155, 16,124,145, 29, + 122,130, 53,123,137, 41,124,143, 31,124,149, 24,124,145, 29, + 122,130, 53,122,130, 53,123,139, 37,123,141, 34,123,139, 37, + 123,139, 37,124,149, 24,124,154, 18,124,167, 6,122,176, 0, + 121,182, 0,120,184, 0,119,186, 0,120,184, 0,119,186, 0, + 118,188, 0,117,190, 0,117,192, 0,114,196, 0,110,202, 0, + 108,205, 0,107,206, 0,108,205, 0,110,203, 0,109,204, 0, + 107,207, 0,105,209, 0,106,208, 0,107,207, 0,109,203, 0, + 112,199, 0,114,196, 0,117,192, 0,118,188, 0,118,188, 0, + 119,187, 0,120,183, 0,122,177, 0,124,166, 7,124,160, 12, + 124,161, 11,124,158, 13,124,152, 20,123,141, 34,123,137, 41, + 124,154, 18,124,165, 8,123,169, 4,124,167, 6,124,167, 6, + 123,170, 3,123,168, 5,124,158, 13,122,135, 44,111, 96,248, + 122,132, 48,124,160, 12,123,172, 2,122,178, 0,121,181, 0, + 121,182, 0,119,187, 0,118,189, 0,119,187, 0,119,186, 0, + 118,188, 0,118,189, 0,118,188, 0,119,186, 0,121,182, 0, + 121,182, 0,119,186, 0,116,193, 0,114,197, 0,113,199, 0, + 111,201, 0,108,205, 0,105,209, 0,101,213, 0, 98,217, 0, + 94,221, 0, 91,223, 0, 90,225, 0, 90,224, 0, 92,223, 0, + 94,220, 0, 98,216, 0,102,212, 0,106,208, 0,109,204, 0, + 112,200, 0,110,202, 0,109,203, 0,110,202, 0,112,199, 0, + 116,193, 0,116,193, 0,116,193, 0,115,194, 0,116,193, 0, + 116,192, 0,117,192, 0,117,192, 0,115,194, 0,115,195, 0, + 115,195, 0,116,193, 0,114,196, 0,113,198, 0,112,199, 0, + 113,198, 0,115,194, 0,114,196, 0,114,197, 0,114,196, 0, + 116,192, 0,119,187, 0,119,187, 0,119,186, 0,120,183, 0, + 120,182, 0,121,182, 0,121,180, 0,122,176, 0,123,168, 5, + 124,150, 22,122,135, 44,123,137, 41,124,157, 15,123,171, 3, + 121,179, 0,120,182, 0,121,181, 0,122,176, 0,124,167, 6, + 124,157, 15,124,161, 11,124,160, 12,124,154, 18,124,152, 20, + 124,155, 16,124,157, 15,124,157, 15,124,154, 18,124,166, 7, + 123,170, 3,123,172, 2,123,171, 3,122,175, 0,122,178, 0, + 121,179, 0,122,177, 0,121,181, 0,121,181, 0,122,178, 0, + 122,178, 0,121,179, 0,122,178, 0,122,175, 0,124,165, 8, + 124,162, 10,124,149, 24,118,114, 96,102, 80,250, 99, 75,247, + 123,139, 37,124,166, 7,122,178, 0,116,193, 0,110,202, 0, + 106,208, 0,102,212, 0, 97,218, 0, 93,222, 0, 90,224, 0, + 91,223, 0, 87,227, 0, 85,229, 0, 85,229, 0, 87,227, 0, + 93,222, 0,100,215, 0,107,207, 0,109,204, 0,106,208, 0, + 105,209, 0,106,207, 0,109,204, 0,113,199, 0,117,191, 0, + 121,181, 0,123,169, 4,124,165, 8,124,166, 7,124,165, 8, + 124,161, 11,124,154, 18,124,145, 29,122,132, 48,120,122, 70, + 118,117, 85,119,119, 77,120,122, 70,121,125, 63,121,125, 63, + 120,122, 70,119,119, 77,118,117, 85,124,147, 26,124,165, 8, + 122,175, 0,120,183, 0,118,188, 0,116,193, 0,113,198, 0, + 111,201, 0,108,206, 0,105,209, 0,103,211, 0,103,212, 0, + 105,209, 0,108,206, 0,112,200, 0,115,194, 0,117,192, 0, + 118,188, 0,120,184, 0,121,182, 0,121,180, 0,122,178, 0, + 123,173, 1,124,166, 7,123,170, 3,123,172, 2,122,178, 0, + 121,180, 0,121,182, 0,121,182, 0,121,181, 0,121,182, 0, + 121,182, 0,120,183, 0,119,185, 0,119,185, 0,120,184, 0, + 121,180, 0,123,172, 2,124,161, 11,124,164, 9,124,161, 11, + 124,155, 16,124,145, 29,123,137, 41,123,139, 37,123,137, 41, + 121,125, 63,123,141, 34,124,152, 20,124,158, 13,124,161, 11, + 124,160, 12,124,155, 16,124,149, 24,124,143, 31,124,160, 12, + 123,172, 2,121,181, 0,119,186, 0,118,188, 0,118,188, 0, + 119,187, 0,120,183, 0,119,186, 0,118,188, 0,116,193, 0, + 112,200, 0,108,205, 0,107,206, 0,107,206, 0,109,204, 0, + 105,209, 0,102,212, 0,101,214, 0,102,213, 0,104,210, 0, + 107,206, 0,111,201, 0,114,197, 0,114,196, 0,114,196, 0, + 115,194, 0,116,192, 0,118,188, 0,121,181, 0,123,170, 3, + 124,154, 18,124,155, 16,124,155, 16,124,152, 20,124,147, 26, + 123,139, 37,122,132, 48,124,143, 31,124,152, 20,124,166, 7, + 123,174, 1,122,178, 0,122,176, 0,123,168, 5,124,150, 22, + 117,110,109,118,117, 85,124,154, 18,123,169, 4,122,178, 0, + 121,182, 0,120,183, 0,119,187, 0,118,188, 0,119,186, 0, + 118,188, 0,117,191, 0,117,192, 0,117,191, 0,118,189, 0, + 120,184, 0,120,183, 0,120,183, 0,117,191, 0,115,194, 0, + 114,196, 0,112,200, 0,109,204, 0,106,207, 0,103,211, 0, + 100,215, 0, 96,219, 0, 94,221, 0, 92,222, 0, 92,222, 0, + 94,221, 0, 97,218, 0,100,215, 0,104,210, 0,107,207, 0, + 109,203, 0,110,202, 0,108,205, 0,107,207, 0,108,206, 0, + 110,202, 0,114,197, 0,114,197, 0,114,196, 0,115,194, 0, + 117,192, 0,118,189, 0,119,187, 0,118,188, 0,118,188, 0, + 117,190, 0,117,192, 0,115,195, 0,113,198, 0,112,200, 0, + 111,201, 0,112,200, 0,114,196, 0,112,199, 0,111,201, 0, + 112,200, 0,114,197, 0,117,191, 0,120,182, 0,122,177, 0, + 123,173, 1,123,174, 1,123,173, 1,123,172, 2,123,170, 3, + 124,164, 9,124,152, 20,124,149, 24,124,150, 22,124,160, 12, + 123,173, 1,121,181, 0,120,184, 0,120,184, 0,121,181, 0, + 123,174, 1,124,166, 7,124,160, 12,124,157, 15,124,158, 13, + 124,161, 11,124,164, 9,124,166, 7,124,166, 7,124,166, 7, + 123,169, 4,122,175, 0,122,178, 0,122,178, 0,121,181, 0, + 120,183, 0,120,182, 0,120,184, 0,119,186, 0,119,186, 0, + 120,183, 0,120,182, 0,120,184, 0,120,183, 0,121,179, 0, + 123,171, 3,124,162, 10,124,150, 22,122,130, 53,118,117, 85, + 117,110,109,119,119, 77,124,155, 16,123,172, 2,118,189, 0, + 113,199, 0,108,205, 0,105,209, 0,100,215, 0, 95,220, 0, + 92,222, 0, 93,222, 0, 91,224, 0, 88,226, 0, 88,226, 0, + 90,224, 0, 95,220, 0,101,214, 0,106,208, 0,107,206, 0, + 105,209, 0,105,209, 0,106,208, 0,109,204, 0,113,198, 0, + 116,193, 0,118,188, 0,121,180, 0,122,176, 0,123,172, 2, + 123,171, 3,123,170, 3,123,168, 5,124,164, 9,124,157, 15, + 124,149, 24,123,137, 41,120,122, 70,122,130, 53,123,141, 34, + 124,147, 26,124,145, 29,123,139, 37,123,139, 37,124,145, 29, + 124,165, 8,122,178, 0,119,186, 0,116,192, 0,113,198, 0, + 111,202, 0,108,206, 0,104,210, 0,102,213, 0,100,214, 0, + 100,214, 0,102,212, 0,105,209, 0,109,203, 0,113,198, 0, + 116,193, 0,117,190, 0,118,188, 0,119,186, 0,120,184, 0, + 121,181, 0,122,177, 0,123,170, 3,124,162, 10,124,161, 11, + 123,170, 3,122,176, 0,121,179, 0,121,182, 0,120,183, 0, + 120,184, 0,120,183, 0,121,180, 0,120,183, 0,119,185, 0, + 120,184, 0,121,182, 0,122,176, 0,124,166, 7,124,166, 7, + 124,165, 8,124,161, 11,124,155, 16,124,147, 26,123,141, 34, + 123,139, 37,124,147, 26,124,157, 15,124,166, 7,123,170, 3, + 123,172, 2,123,171, 3,123,168, 5,124,164, 9,124,160, 12, + 124,167, 6,123,171, 3,122,177, 0,120,184, 0,118,188, 0, + 118,189, 0,118,189, 0,119,186, 0,120,183, 0,119,185, 0, + 117,191, 0,112,199, 0,109,204, 0,107,207, 0,106,207, 0, + 106,208, 0,102,212, 0,100,215, 0, 99,216, 0,100,215, 0, + 103,212, 0,107,207, 0,109,203, 0,110,202, 0,111,201, 0, + 112,200, 0,113,198, 0,115,194, 0,118,189, 0,121,181, 0, + 123,168, 5,124,150, 22,124,152, 20,124,154, 18,124,152, 20, + 124,150, 22,124,145, 29,124,143, 31,124,143, 31,124,150, 22, + 124,166, 7,123,174, 1,122,178, 0,122,177, 0,123,170, 3, + 124,155, 16,120,122, 70,119,119, 77,124,147, 26,124,166, 7, + 122,176, 0,121,181, 0,120,184, 0,119,185, 0,119,186, 0, + 119,187, 0,118,189, 0,117,191, 0,117,192, 0,117,191, 0, + 118,188, 0,120,184, 0,120,184, 0,120,184, 0,118,188, 0, + 117,192, 0,116,193, 0,114,197, 0,111,201, 0,106,208, 0, + 101,214, 0, 97,217, 0, 95,220, 0, 94,220, 0, 94,221, 0, + 94,221, 0, 95,220, 0, 96,219, 0, 97,217, 0,100,215, 0, + 103,211, 0,108,205, 0,109,203, 0,107,207, 0,106,208, 0, + 106,208, 0,108,205, 0,112,199, 0,113,199, 0,114,197, 0, + 115,194, 0,118,189, 0,119,186, 0,118,189, 0,117,191, 0, + 117,192, 0,117,192, 0,115,194, 0,114,197, 0,112,199, 0, + 111,201, 0,111,202, 0,112,200, 0,113,198, 0,112,200, 0, + 111,202, 0,112,200, 0,114,197, 0,117,191, 0,121,182, 0, + 123,174, 1,122,175, 0,122,176, 0,123,171, 3,124,162, 10, + 124,162, 10,124,158, 13,124,160, 12,124,157, 15,124,161, 11, + 124,158, 13,123,171, 3,121,180, 0,120,184, 0,119,186, 0, + 120,184, 0,121,180, 0,123,174, 1,123,169, 4,124,167, 6, + 123,168, 5,123,169, 4,123,170, 3,123,172, 2,123,173, 1, + 123,173, 1,123,174, 1,121,179, 0,120,182, 0,120,184, 0, + 119,186, 0,119,187, 0,119,186, 0,118,188, 0,118,189, 0, + 118,189, 0,118,188, 0,118,188, 0,117,191, 0,117,191, 0, + 118,189, 0,120,182, 0,123,171, 3,124,162, 10,124,152, 20, + 124,143, 31,122,130, 53,118,114, 96,122,132, 48,124,166, 7, + 119,186, 0,114,196, 0,110,202, 0,107,207, 0,104,210, 0, + 100,215, 0, 97,218, 0, 96,219, 0, 95,220, 0, 93,221, 0, + 93,221, 0, 95,219, 0,100,215, 0,105,209, 0,106,208, 0, + 106,208, 0,103,211, 0,103,211, 0,105,209, 0,109,204, 0, + 111,201, 0,113,198, 0,115,194, 0,119,187, 0,119,185, 0, + 121,182, 0,122,178, 0,122,178, 0,122,176, 0,123,172, 2, + 123,168, 5,124,162, 10,124,152, 20,123,137, 41,124,145, 29, + 124,157, 15,124,162, 10,124,164, 9,124,160, 12,124,155, 16, + 124,160, 12,124,166, 7,122,178, 0,118,188, 0,114,196, 0, + 111,202, 0,107,206, 0,104,210, 0,102,213, 0,100,215, 0, + 99,215, 0,100,215, 0,102,213, 0,105,209, 0,108,205, 0, + 112,200, 0,114,196, 0,116,193, 0,117,191, 0,118,188, 0, + 119,186, 0,120,183, 0,122,178, 0,123,173, 1,124,166, 7, + 124,157, 15,124,158, 13,124,165, 8,123,172, 2,122,177, 0, + 121,180, 0,120,182, 0,121,182, 0,122,178, 0,120,182, 0, + 120,184, 0,119,185, 0,120,183, 0,122,178, 0,123,169, 4, + 124,166, 7,124,167, 6,124,165, 8,124,162, 10,124,158, 13, + 124,157, 15,124,157, 15,124,162, 10,123,169, 4,123,174, 1, + 122,177, 0,122,178, 0,122,176, 0,123,173, 1,123,169, 4, + 124,167, 6,123,172, 2,122,176, 0,122,178, 0,121,179, 0, + 120,184, 0,119,186, 0,119,187, 0,119,186, 0,121,181, 0, + 120,182, 0,118,189, 0,113,198, 0,109,204, 0,106,207, 0, + 105,209, 0,105,209, 0,101,214, 0, 99,216, 0, 98,217, 0, + 99,215, 0,102,213, 0,106,208, 0,107,206, 0,108,205, 0, + 110,203, 0,111,201, 0,113,198, 0,116,193, 0,119,187, 0, + 122,177, 0,124,161, 11,124,147, 26,124,150, 22,124,152, 20, + 124,152, 20,124,150, 22,124,149, 24,124,145, 29,124,145, 29, + 124,150, 22,124,158, 13,123,169, 4,122,175, 0,122,175, 0, + 123,170, 3,124,157, 15,122,130, 53,119,119, 77,123,137, 41, + 124,160, 12,123,171, 3,122,178, 0,121,181, 0,120,183, 0, + 120,184, 0,120,184, 0,119,186, 0,119,187, 0,119,187, 0, + 119,186, 0,120,182, 0,121,181, 0,120,182, 0,120,183, 0, + 118,188, 0,117,191, 0,117,191, 0,115,194, 0,110,202, 0, + 105,209, 0,100,214, 0, 97,218, 0, 95,220, 0, 93,221, 0, + 92,222, 0, 92,222, 0, 92,223, 0, 92,222, 0, 94,221, 0, + 95,219, 0,100,215, 0,105,209, 0,108,205, 0,105,209, 0, + 103,211, 0,103,211, 0,106,208, 0,110,202, 0,113,199, 0, + 114,196, 0,116,193, 0,116,193, 0,114,196, 0,113,199, 0, + 112,200, 0,113,198, 0,115,195, 0,114,197, 0,113,199, 0, + 111,201, 0,111,202, 0,110,202, 0,111,201, 0,113,199, 0, + 113,199, 0,112,199, 0,113,198, 0,116,193, 0,119,187, 0, + 119,185, 0,119,186, 0,120,184, 0,121,180, 0,123,172, 2, + 124,162, 10,124,155, 16,124,161, 11,124,164, 9,124,165, 8, + 123,168, 5,124,164, 9,124,167, 6,122,177, 0,120,182, 0, + 119,185, 0,119,185, 0,120,183, 0,122,178, 0,122,175, 0, + 123,174, 1,123,174, 1,122,175, 0,122,178, 0,121,180, 0, + 121,179, 0,122,178, 0,121,179, 0,120,183, 0,119,186, 0, + 118,188, 0,117,191, 0,117,192, 0,117,191, 0,117,190, 0, + 117,191, 0,116,192, 0,117,191, 0,116,193, 0,114,196, 0, + 113,198, 0,114,196, 0,117,191, 0,120,184, 0,122,176, 0, + 123,168, 5,124,158, 13,124,145, 29,121,125, 63,117,110,109, + 124,158, 13,121,180, 0,117,192, 0,112,199, 0,109,204, 0, + 106,207, 0,105,209, 0,102,212, 0,101,214, 0,100,215, 0, + 99,215, 0,100,215, 0,102,212, 0,106,208, 0,107,207, 0, + 106,208, 0,102,212, 0,101,214, 0,102,213, 0,105,209, 0, + 107,206, 0,108,205, 0,110,202, 0,114,197, 0,116,193, 0, + 117,191, 0,118,188, 0,120,184, 0,120,183, 0,121,181, 0, + 122,178, 0,123,173, 1,123,168, 5,124,161, 11,124,155, 16, + 124,158, 13,124,166, 7,123,170, 3,123,173, 1,123,172, 2, + 124,167, 6,123,169, 4,123,173, 1,122,178, 0,118,189, 0, + 113,198, 0,109,204, 0,105,209, 0,102,212, 0,101,214, 0, + 100,214, 0,101,214, 0,102,213, 0,104,210, 0,106,207, 0, + 109,203, 0,112,200, 0,114,196, 0,116,193, 0,117,190, 0, + 118,188, 0,120,184, 0,121,181, 0,122,177, 0,123,171, 3, + 124,165, 8,124,158, 13,124,158, 13,124,161, 11,124,162, 10, + 124,166, 7,123,171, 3,122,175, 0,122,175, 0,122,176, 0, + 121,181, 0,120,184, 0,120,184, 0,120,183, 0,122,178, 0, + 123,170, 3,124,164, 9,124,166, 7,124,167, 6,124,166, 7, + 124,165, 8,124,165, 8,124,166, 7,123,170, 3,122,175, 0, + 122,178, 0,121,179, 0,121,179, 0,122,177, 0,123,174, 1, + 123,170, 3,123,169, 4,123,174, 1,121,179, 0,121,181, 0, + 121,182, 0,121,181, 0,121,181, 0,120,183, 0,120,182, 0, + 122,178, 0,122,178, 0,119,187, 0,114,197, 0,110,203, 0, + 106,207, 0,104,210, 0,102,212, 0,101,214, 0, 99,216, 0, + 98,216, 0, 99,215, 0,101,213, 0,105,209, 0,107,207, 0, + 108,205, 0,110,203, 0,112,200, 0,114,196, 0,117,191, 0, + 121,182, 0,122,176, 0,123,168, 5,123,171, 3,123,172, 2, + 123,170, 3,124,164, 9,124,150, 22,124,147, 26,124,143, 31, + 124,143, 31,124,145, 29,124,152, 20,124,158, 13,124,166, 7, + 123,169, 4,124,166, 7,124,155, 16,122,132, 48,118,117, 85, + 122,130, 53,124,152, 20,124,165, 8,123,172, 2,122,176, 0, + 121,179, 0,121,180, 0,122,178, 0,122,178, 0,121,179, 0, + 122,178, 0,122,175, 0,123,172, 2,122,178, 0,121,180, 0, + 120,184, 0,118,189, 0,117,190, 0,117,191, 0,116,193, 0, + 111,201, 0,106,208, 0,101,213, 0, 98,217, 0, 96,219, 0, + 94,220, 0, 93,222, 0, 92,223, 0, 91,224, 0, 91,224, 0, + 91,223, 0, 93,221, 0, 97,217, 0,103,212, 0,105,209, 0, + 103,211, 0,101,214, 0,101,214, 0,103,211, 0,108,206, 0, + 112,199, 0,113,198, 0,112,200, 0,110,203, 0,107,206, 0, + 105,209, 0,105,209, 0,105,209, 0,108,206, 0,110,203, 0, + 112,199, 0,112,200, 0,111,202, 0,110,202, 0,111,202, 0, + 112,200, 0,113,198, 0,115,195, 0,116,193, 0,116,192, 0, + 116,192, 0,116,192, 0,117,192, 0,117,190, 0,119,186, 0, + 122,178, 0,123,168, 5,124,155, 16,124,162, 10,124,166, 7, + 123,168, 5,123,171, 3,123,170, 3,124,162, 10,123,171, 3, + 122,178, 0,120,182, 0,120,184, 0,120,183, 0,121,180, 0, + 122,178, 0,122,177, 0,121,181, 0,119,185, 0,119,187, 0, + 118,188, 0,119,185, 0,121,181, 0,120,182, 0,119,187, 0, + 118,189, 0,116,192, 0,115,194, 0,114,196, 0,115,195, 0, + 117,192, 0,116,193, 0,115,194, 0,115,194, 0,114,196, 0, + 112,199, 0,111,201, 0,111,201, 0,114,197, 0,117,191, 0, + 120,184, 0,122,177, 0,123,168, 5,124,154, 18,122,132, 48, + 122,130, 53,124,143, 31,123,172, 2,119,187, 0,115,195, 0, + 111,201, 0,109,204, 0,108,206, 0,107,207, 0,106,207, 0, + 106,208, 0,106,208, 0,107,206, 0,109,203, 0,110,202, 0, + 106,208, 0,102,212, 0, 99,215, 0, 98,216, 0,100,214, 0, + 103,211, 0,105,209, 0,106,207, 0,109,204, 0,112,199, 0, + 114,196, 0,116,193, 0,117,191, 0,118,189, 0,118,188, 0, + 119,185, 0,121,181, 0,122,176, 0,123,170, 3,124,166, 7, + 123,168, 5,123,169, 4,123,171, 3,122,175, 0,122,178, 0, + 122,178, 0,122,176, 0,122,175, 0,122,178, 0,121,181, 0, + 117,190, 0,113,199, 0,108,205, 0,104,210, 0,102,212, 0, + 102,212, 0,103,212, 0,104,210, 0,106,208, 0,108,205, 0, + 110,202, 0,112,199, 0,114,196, 0,116,192, 0,118,189, 0, + 119,186, 0,120,183, 0,122,178, 0,123,173, 1,123,168, 5, + 124,162, 10,124,155, 16,124,152, 20,124,155, 16,124,157, 15, + 124,160, 12,124,161, 11,124,162, 10,124,164, 9,124,167, 6, + 123,172, 2,122,178, 0,121,182, 0,120,182, 0,121,181, 0, + 122,176, 0,124,167, 6,124,160, 12,124,164, 9,124,166, 7, + 124,167, 6,124,167, 6,124,167, 6,123,168, 5,123,172, 2, + 122,176, 0,122,178, 0,122,178, 0,122,176, 0,123,173, 1, + 123,170, 3,124,167, 6,124,167, 6,123,173, 1,121,179, 0, + 120,182, 0,120,184, 0,120,183, 0,121,181, 0,122,178, 0, + 122,178, 0,122,176, 0,123,174, 1,119,186, 0,115,194, 0, + 111,202, 0,107,207, 0,103,211, 0,101,214, 0,100,215, 0, + 100,215, 0, 99,215, 0,100,215, 0,101,213, 0,104,210, 0, + 107,206, 0,109,203, 0,111,201, 0,113,198, 0,116,193, 0, + 118,189, 0,118,189, 0,119,186, 0,119,185, 0,118,188, 0, + 118,188, 0,119,186, 0,121,182, 0,123,171, 3,124,152, 20, + 123,137, 41,122,135, 44,122,135, 44,123,141, 34,124,147, 26, + 124,152, 20,124,158, 13,124,158, 13,124,150, 22,122,132, 48, + 117,110,109,119,119, 77,123,141, 34,124,157, 15,124,164, 9, + 123,170, 3,122,175, 0,122,175, 0,123,169, 4,124,167, 6, + 124,165, 8,124,161, 11,124,157, 15,123,168, 5,123,174, 1, + 121,181, 0,118,188, 0,117,190, 0,118,189, 0,116,193, 0, + 115,194, 0,112,199, 0,108,206, 0,104,210, 0,101,213, 0, + 99,215, 0, 97,218, 0, 95,220, 0, 93,221, 0, 92,223, 0, + 91,223, 0, 91,223, 0, 93,221, 0, 97,217, 0,102,213, 0, + 104,210, 0,101,214, 0, 99,216, 0, 99,216, 0,101,213, 0, + 106,208, 0,109,204, 0,108,205, 0,107,207, 0,104,210, 0, + 102,213, 0,100,215, 0, 98,216, 0, 99,215, 0,101,213, 0, + 104,210, 0,107,206, 0,111,201, 0,112,199, 0,112,200, 0, + 112,200, 0,112,199, 0,113,198, 0,113,198, 0,114,197, 0, + 114,197, 0,114,196, 0,114,196, 0,115,194, 0,117,192, 0, + 119,186, 0,121,179, 0,123,170, 3,124,158, 13,124,161, 11, + 124,166, 7,123,169, 4,123,171, 3,123,172, 2,123,169, 4, + 124,164, 9,123,170, 3,122,176, 0,121,179, 0,121,180, 0, + 122,178, 0,122,178, 0,120,184, 0,118,188, 0,117,191, 0, + 116,193, 0,117,192, 0,118,189, 0,120,183, 0,119,185, 0, + 117,191, 0,116,193, 0,115,195, 0,113,198, 0,112,199, 0, + 113,198, 0,115,195, 0,115,194, 0,114,196, 0,114,196, 0, + 114,197, 0,112,200, 0,110,202, 0,110,202, 0,112,200, 0, + 115,194, 0,118,189, 0,120,182, 0,123,173, 1,124,161, 11, + 123,141, 34,123,139, 37,123,137, 41,124,158, 13,122,178, 0, + 118,189, 0,114,196, 0,112,199, 0,111,201, 0,111,201, 0, + 111,201, 0,111,202, 0,110,202, 0,111,202, 0,110,203, 0, + 105,209, 0,102,213, 0, 99,216, 0, 97,218, 0, 97,218, 0, + 99,215, 0,102,212, 0,103,212, 0,104,210, 0,107,206, 0, + 112,200, 0,114,197, 0,115,194, 0,115,194, 0,116,193, 0, + 117,191, 0,118,188, 0,120,184, 0,121,182, 0,122,177, 0, + 123,171, 3,122,176, 0,121,179, 0,121,180, 0,122,178, 0, + 121,179, 0,121,181, 0,121,180, 0,122,178, 0,120,182, 0, + 120,184, 0,117,190, 0,113,199, 0,108,205, 0,106,208, 0, + 105,209, 0,105,209, 0,107,206, 0,110,203, 0,112,200, 0, + 114,196, 0,116,193, 0,117,191, 0,118,188, 0,119,185, 0, + 121,182, 0,122,178, 0,123,172, 2,124,165, 8,124,157, 15, + 124,147, 26,123,139, 37,122,132, 48,123,137, 41,124,143, 31, + 124,147, 26,124,150, 22,124,154, 18,124,155, 16,124,157, 15, + 124,161, 11,124,167, 6,123,173, 1,122,177, 0,122,178, 0, + 122,176, 0,123,170, 3,124,161, 11,124,152, 20,124,158, 13, + 124,161, 11,124,162, 10,124,162, 10,124,162, 10,124,164, 9, + 123,168, 5,123,171, 3,123,172, 2,123,170, 3,123,168, 5, + 124,165, 8,124,161, 11,124,160, 12,124,160, 12,123,169, 4, + 122,178, 0,120,182, 0,120,184, 0,120,184, 0,121,182, 0, + 122,178, 0,122,176, 0,123,172, 2,123,172, 2,119,185, 0, + 115,194, 0,111,201, 0,107,206, 0,103,211, 0,100,215, 0, + 98,217, 0, 97,217, 0, 99,216, 0,101,214, 0,102,212, 0, + 104,210, 0,107,207, 0,110,202, 0,113,198, 0,115,194, 0, + 116,193, 0,114,196, 0,114,196, 0,115,194, 0,114,196, 0, + 113,198, 0,113,198, 0,114,196, 0,117,192, 0,120,184, 0, + 123,169, 4,123,141, 34,120,122, 70,120,122, 70,121,125, 63, + 122,130, 53,122,135, 44,123,141, 34,124,147, 26,124,143, 31, + 122,130, 53,111, 96,248,114,104,197,122,130, 53,124,145, 29, + 124,154, 18,124,164, 9,123,168, 5,123,169, 4,124,161, 11, + 124,147, 26,123,141, 34,122,135, 44,124,154, 18,124,165, 8, + 122,177, 0,119,186, 0,117,190, 0,117,191, 0,117,191, 0, + 115,195, 0,114,197, 0,114,197, 0,111,202, 0,108,206, 0, + 106,208, 0,103,211, 0,101,213, 0, 99,216, 0, 97,218, 0, + 95,220, 0, 93,221, 0, 94,221, 0, 95,220, 0, 99,215, 0, + 102,213, 0,101,213, 0, 98,217, 0, 97,218, 0, 97,217, 0, + 100,214, 0,105,209, 0,105,209, 0,105,209, 0,103,212, 0, + 101,214, 0, 98,216, 0, 96,219, 0, 95,220, 0, 95,219, 0, + 98,217, 0,101,214, 0,104,210, 0,108,205, 0,113,198, 0, + 114,196, 0,114,197, 0,114,197, 0,114,197, 0,114,197, 0, + 113,198, 0,113,198, 0,114,197, 0,114,196, 0,116,193, 0, + 118,189, 0,120,183, 0,122,176, 0,124,167, 6,124,157, 15, + 124,157, 15,124,162, 10,124,166, 7,124,167, 6,123,172, 2, + 123,171, 3,124,167, 6,124,161, 11,124,167, 6,123,171, 3, + 123,173, 1,122,177, 0,121,182, 0,118,188, 0,117,192, 0, + 115,195, 0,114,196, 0,115,194, 0,117,191, 0,119,185, 0, + 118,189, 0,115,194, 0,114,197, 0,114,197, 0,112,200, 0, + 111,202, 0,111,201, 0,113,198, 0,114,196, 0,114,197, 0, + 113,198, 0,113,198, 0,112,199, 0,111,201, 0,111,202, 0, + 112,199, 0,115,194, 0,118,189, 0,120,184, 0,122,176, 0, + 124,164, 9,124,145, 29,123,139, 37,122,135, 44,122,130, 53, + 124,165, 8,121,180, 0,118,189, 0,116,193, 0,115,194, 0, + 115,194, 0,116,193, 0,115,194, 0,114,197, 0,111,202, 0, + 106,208, 0,101,213, 0, 98,216, 0, 97,218, 0, 95,219, 0, + 96,219, 0, 99,216, 0,100,215, 0,100,214, 0,102,212, 0, + 106,207, 0,111,201, 0,114,196, 0,114,197, 0,113,198, 0, + 114,197, 0,115,194, 0,117,191, 0,118,189, 0,119,187, 0, + 120,182, 0,122,177, 0,120,183, 0,119,187, 0,118,189, 0, + 118,188, 0,119,185, 0,121,181, 0,121,182, 0,121,181, 0, + 119,185, 0,118,188, 0,118,189, 0,114,197, 0,110,202, 0, + 108,206, 0,108,206, 0,110,202, 0,113,198, 0,115,194, 0, + 117,192, 0,119,186, 0,121,180, 0,122,178, 0,122,175, 0, + 123,172, 2,124,167, 6,124,160, 12,124,149, 24,123,141, 34, + 122,132, 48,120,122, 70,117,110,109,115,107,129,117,110,109, + 118,117, 85,121,127, 58,122,135, 44,123,139, 37,123,141, 34, + 124,143, 31,124,149, 24,124,157, 15,124,165, 8,123,168, 5, + 123,169, 4,124,167, 6,124,160, 12,124,147, 26,123,141, 34, + 124,149, 24,124,152, 20,124,152, 20,124,150, 22,124,149, 24, + 124,149, 24,124,154, 18,124,158, 13,124,158, 13,124,155, 16, + 124,152, 20,124,147, 26,124,150, 22,124,149, 24,124,149, 24, + 124,161, 11,123,173, 1,121,180, 0,120,183, 0,120,182, 0, + 121,180, 0,122,175, 0,123,171, 3,124,165, 8,122,176, 0, + 118,189, 0,113,199, 0,109,204, 0,106,208, 0,104,210, 0, + 100,215, 0, 97,217, 0, 97,218, 0, 98,217, 0,101,214, 0, + 103,211, 0,105,209, 0,107,206, 0,110,202, 0,115,195, 0, + 115,194, 0,112,199, 0,110,202, 0,110,203, 0,111,202, 0, + 110,203, 0,109,204, 0,109,204, 0,110,202, 0,113,198, 0, + 117,191, 0,121,180, 0,124,158, 13,118,114, 96,119,119, 77, + 121,127, 58,119,119, 77,119,119, 77,119,119, 77,122,130, 53, + 122,132, 48,120,122, 70,109, 92,251,105, 84,252,119,119, 77, + 122,135, 44,123,139, 37,124,150, 22,124,158, 13,124,158, 13, + 124,147, 26,121,125, 63,115,107,129,122,135, 44,124,158, 13, + 123,172, 2,120,182, 0,118,188, 0,117,191, 0,117,191, 0, + 115,194, 0,113,198, 0,112,199, 0,113,199, 0,114,197, 0, + 112,200, 0,110,202, 0,107,207, 0,105,209, 0,104,210, 0, + 101,213, 0, 99,215, 0, 98,217, 0, 98,217, 0, 99,215, 0, + 101,214, 0,102,213, 0, 98,217, 0, 95,220, 0, 95,220, 0, + 96,219, 0,100,215, 0,103,211, 0,103,211, 0,103,212, 0, + 101,213, 0,100,215, 0, 97,217, 0, 95,219, 0, 94,220, 0, + 95,220, 0, 97,218, 0,100,215, 0,103,211, 0,107,206, 0, + 112,200, 0,117,192, 0,117,191, 0,117,192, 0,116,193, 0, + 115,194, 0,115,195, 0,115,195, 0,115,194, 0,117,192, 0, + 118,188, 0,120,182, 0,122,175, 0,124,167, 6,124,160, 12, + 124,152, 20,124,150, 22,124,154, 18,124,160, 12,124,157, 15, + 124,167, 6,123,170, 3,123,169, 4,124,165, 8,124,155, 16, + 124,160, 12,123,169, 4,122,176, 0,121,182, 0,118,188, 0, + 116,193, 0,114,196, 0,114,197, 0,114,196, 0,116,192, 0, + 119,186, 0,116,193, 0,113,198, 0,112,200, 0,112,200, 0, + 110,202, 0,109,203, 0,110,203, 0,112,200, 0,113,199, 0, + 113,198, 0,112,200, 0,112,200, 0,113,199, 0,113,198, 0, + 113,198, 0,114,196, 0,117,192, 0,118,189, 0,119,186, 0, + 121,180, 0,123,169, 4,124,150, 22,122,132, 48,121,125, 63, + 119,119, 77,123,137, 41,124,166, 7,122,178, 0,120,184, 0, + 119,186, 0,119,185, 0,120,183, 0,119,187, 0,114,196, 0, + 108,205, 0,103,212, 0, 99,215, 0, 97,218, 0, 96,219, 0, + 95,219, 0, 96,219, 0, 97,218, 0, 97,218, 0, 98,217, 0, + 101,214, 0,105,209, 0,111,202, 0,115,195, 0,112,199, 0, + 111,201, 0,112,200, 0,114,197, 0,116,193, 0,117,191, 0, + 118,189, 0,119,186, 0,121,182, 0,118,188, 0,116,192, 0, + 115,195, 0,115,194, 0,117,191, 0,119,185, 0,121,180, 0, + 120,183, 0,118,188, 0,117,191, 0,117,192, 0,114,196, 0, + 112,200, 0,111,202, 0,112,200, 0,114,196, 0,114,197, 0, + 114,197, 0,115,194, 0,118,188, 0,122,177, 0,124,158, 13, + 124,152, 20,124,147, 26,123,141, 34,124,145, 29,124,143, 31, + 123,139, 37,121,127, 58,118,117, 85,118,114, 96,120,122, 70, + 121,127, 58,121,125, 63,119,119, 77,115,107,129,117,110,109, + 118,117, 85,118,117, 85,121,125, 63,123,137, 41,124,149, 24, + 124,154, 18,124,155, 16,124,152, 20,123,141, 34,120,122, 70, + 121,125, 63,122,132, 48,122,135, 44,122,130, 53,121,125, 63, + 118,117, 85,118,114, 96,120,122, 70,122,130, 53,122,130, 53, + 121,125, 63,123,137, 41,124,147, 26,124,152, 20,124,149, 24, + 122,135, 44,124,152, 20,124,167, 6,122,176, 0,121,179, 0, + 122,178, 0,123,174, 1,123,168, 5,124,161, 11,124,160, 12, + 121,181, 0,116,193, 0,110,202, 0,106,207, 0,104,210, 0, + 103,212, 0,101,214, 0, 98,217, 0, 97,217, 0, 98,216, 0, + 101,213, 0,105,209, 0,107,207, 0,109,204, 0,112,200, 0, + 115,194, 0,111,201, 0,108,205, 0,106,208, 0,105,209, 0, + 106,208, 0,107,207, 0,106,208, 0,106,207, 0,107,206, 0, + 110,202, 0,114,196, 0,119,186, 0,123,169, 4,122,135, 44, + 118,117, 85,121,127, 58,120,122, 70,115,107,129,117,110,109, + 118,114, 96,119,119, 77,118,114, 96,105, 84,252,102, 80,250, + 117,110,109,121,125, 63,121,127, 58,122,130, 53,123,139, 37, + 123,139, 37,120,122, 70,109, 92,251,119,119, 77,124,145, 29, + 124,164, 9,122,176, 0,120,184, 0,118,189, 0,117,191, 0, + 117,191, 0,114,197, 0,112,200, 0,111,202, 0,111,201, 0, + 114,197, 0,116,193, 0,114,197, 0,109,203, 0,107,207, 0, + 107,207, 0,106,207, 0,104,210, 0,103,212, 0,103,212, 0, + 102,213, 0,102,212, 0, 98,217, 0, 94,220, 0, 93,222, 0, + 93,221, 0, 96,219, 0,100,214, 0,104,210, 0,104,210, 0, + 102,213, 0, 99,215, 0, 98,217, 0, 98,217, 0, 97,218, 0, + 96,219, 0, 96,219, 0, 98,217, 0,101,214, 0,104,210, 0, + 108,205, 0,113,199, 0,117,191, 0,120,183, 0,120,184, 0, + 119,186, 0,118,188, 0,118,189, 0,118,189, 0,118,188, 0, + 120,184, 0,122,178, 0,123,169, 4,124,160, 12,124,150, 22, + 124,152, 20,124,150, 22,124,150, 22,124,147, 26,124,145, 29, + 123,141, 34,124,157, 15,124,165, 8,124,167, 6,124,166, 7, + 124,162, 10,124,164, 9,123,168, 5,123,174, 1,121,180, 0, + 119,187, 0,116,192, 0,114,196, 0,113,198, 0,114,197, 0, + 116,193, 0,119,187, 0,114,196, 0,111,201, 0,110,202, 0, + 110,203, 0,110,203, 0,109,204, 0,109,204, 0,111,201, 0, + 110,202, 0,110,202, 0,111,202, 0,111,202, 0,112,200, 0, + 113,198, 0,115,194, 0,118,189, 0,117,191, 0,117,191, 0, + 118,189, 0,119,186, 0,121,180, 0,123,169, 4,124,149, 24, + 117,110,109,111, 96,248,111, 96,248,123,137, 41,124,161, 11, + 123,171, 3,123,174, 1,123,173, 1,123,169, 4,119,186, 0, + 113,198, 0,107,207, 0,102,212, 0, 99,215, 0, 98,217, 0, + 97,218, 0, 97,218, 0, 95,220, 0, 94,221, 0, 93,221, 0, + 95,220, 0, 98,217, 0,103,211, 0,109,203, 0,115,194, 0, + 112,199, 0,111,201, 0,111,201, 0,112,199, 0,114,196, 0, + 117,192, 0,118,189, 0,119,186, 0,119,187, 0,116,192, 0, + 114,197, 0,113,199, 0,113,198, 0,115,194, 0,118,188, 0, + 122,178, 0,119,186, 0,117,191, 0,115,194, 0,114,196, 0, + 114,196, 0,113,198, 0,113,198, 0,114,196, 0,113,198, 0, + 113,199, 0,113,198, 0,115,194, 0,118,188, 0,122,175, 0, + 124,152, 20,118,114, 96,122,130, 53,123,137, 41,123,141, 34, + 123,139, 37,122,132, 48,119,119, 77,117,110,109,119,119, 77, + 121,127, 58,122,132, 48,122,135, 44,122,130, 53,119,119, 77, + 114,104,197,105, 84,252, 69, 60,230, 99, 75,247,111, 96,248, + 118,117, 85,121,127, 58,122,130, 53,121,125, 63,115,107,129, + 102, 80,250,111, 96,248,114,104,197,113,100,242,107, 89,252, + 94, 70,242, 30, 43,203, 30, 43,203, 30, 43,203, 87, 65,237, + 102, 80,250,115,107,129,121,127, 58,123,139, 37,123,141, 34, + 123,137, 41,119,119, 77,123,141, 34,124,160, 12,123,169, 4, + 123,172, 2,123,170, 3,124,164, 9,124,152, 20,123,139, 37, + 124,167, 6,119,185, 0,114,197, 0,108,205, 0,105,209, 0, + 102,212, 0,102,212, 0,103,212, 0,100,215, 0, 99,216, 0, + 100,215, 0,102,212, 0,106,208, 0,109,203, 0,112,200, 0, + 115,195, 0,111,201, 0,107,206, 0,103,211, 0,101,214, 0, + 100,215, 0,101,214, 0,102,212, 0,105,209, 0,106,208, 0, + 106,207, 0,109,204, 0,112,199, 0,117,191, 0,122,177, 0, + 124,152, 20,114,104,197,119,119, 77,118,117, 85,117,110,109, + 117,110,109,115,107,129,117,110,109,114,104,197,102, 80,250, + 111, 96,248,114,104,197,118,117, 85,119,119, 77,118,114, 96, + 115,107,129,114,104,197, 94, 70,242,117,110,109,122,135, 44, + 124,150, 22,124,162, 10,123,173, 1,121,182, 0,119,186, 0, + 118,188, 0,115,194, 0,112,200, 0,110,202, 0,109,204, 0, + 110,202, 0,113,199, 0,115,194, 0,118,189, 0,114,197, 0, + 111,201, 0,110,203, 0,110,203, 0,109,204, 0,107,207, 0, + 104,210, 0,103,211, 0, 99,216, 0, 95,220, 0, 92,222, 0, + 91,223, 0, 93,222, 0, 96,219, 0,101,214, 0,106,207, 0, + 103,211, 0,100,215, 0, 97,218, 0, 95,220, 0, 94,221, 0, + 95,220, 0, 97,218, 0, 99,215, 0,101,214, 0,103,212, 0, + 106,207, 0,110,202, 0,114,196, 0,118,188, 0,121,179, 0, + 123,169, 4,123,173, 1,122,177, 0,121,179, 0,121,179, 0, + 122,178, 0,123,172, 2,124,164, 9,124,149, 24,124,157, 15, + 124,160, 12,124,160, 12,124,157, 15,124,160, 12,124,160, 12, + 124,155, 16,124,160, 12,124,145, 29,124,154, 18,124,162, 10, + 124,166, 7,123,171, 3,122,176, 0,121,180, 0,120,184, 0, + 118,188, 0,118,188, 0,117,191, 0,114,196, 0,113,198, 0, + 114,197, 0,115,194, 0,117,190, 0,113,198, 0,110,202, 0, + 108,205, 0,108,206, 0,108,205, 0,108,205, 0,108,205, 0, + 108,206, 0,107,207, 0,106,207, 0,107,207, 0,108,205, 0, + 110,203, 0,112,200, 0,113,198, 0,113,198, 0,113,198, 0, + 115,194, 0,116,192, 0,117,191, 0,118,188, 0,121,182, 0, + 123,171, 3,124,154, 18,118,114, 96,111, 96,248,107, 89,252, + 121,125, 63,124,145, 29,124,154, 18,124,160, 12,123,174, 1, + 118,189, 0,112,200, 0,107,207, 0,103,211, 0,101,214, 0, + 100,215, 0, 95,219, 0, 92,222, 0, 91,224, 0, 89,225, 0, + 89,225, 0, 91,223, 0, 95,220, 0,101,214, 0,107,207, 0, + 113,198, 0,114,197, 0,112,200, 0,111,201, 0,112,200, 0, + 113,198, 0,114,196, 0,116,193, 0,117,191, 0,116,192, 0, + 114,196, 0,113,199, 0,112,200, 0,113,199, 0,115,194, 0, + 118,189, 0,119,186, 0,117,190, 0,115,195, 0,113,198, 0, + 112,200, 0,112,200, 0,112,199, 0,113,199, 0,113,198, 0, + 113,199, 0,113,199, 0,114,197, 0,116,192, 0,120,184, 0, + 123,170, 3,124,150, 22,123,137, 41,121,125, 63,122,130, 53, + 122,132, 48,122,130, 53,121,127, 58,123,141, 34,124,152, 20, + 124,157, 15,124,154, 18,124,149, 24,123,141, 34,122,135, 44, + 121,125, 63,119,119, 77,118,117, 85,118,114, 96,114,104,197, + 99, 75,247,102, 80,250,105, 84,252,105, 84,252,102, 80,250, + 30, 55,222, 30, 37,192, 69, 60,230, 87, 65,237, 87, 65,237, + 30, 49,213, 30, 31,180, 69, 60,230,105, 84,252,107, 89,252, + 107, 89,252,105, 84,252,105, 84,252,115,107,129,118,117, 85, + 119,119, 77,117,110,109,117,110,109,122,132, 48,124,150, 22, + 124,160, 12,124,160, 12,124,154, 18,123,139, 37,120,122, 70, + 124,157, 15,122,177, 0,117,191, 0,111,201, 0,106,207, 0, + 102,212, 0,101,214, 0,101,214, 0,102,213, 0,103,212, 0, + 102,212, 0,102,212, 0,103,211, 0,106,208, 0,109,204, 0, + 111,201, 0,110,202, 0,107,207, 0,102,212, 0, 99,216, 0, + 96,219, 0, 95,219, 0, 96,219, 0, 98,216, 0,101,213, 0, + 106,208, 0,106,207, 0,108,205, 0,111,201, 0,116,193, 0, + 121,182, 0,124,162, 10,121,127, 58,117,110,109,117,110,109, + 118,114, 96,117,110,109,114,104,197,111, 96,248,107, 89,252, + 111, 96,248,118,114, 96,118,117, 85,118,114, 96,118,117, 85, + 118,114, 96,113,100,242,105, 84,252,115,107,129,122,132, 48, + 124,150, 22,124,161, 11,123,169, 4,122,175, 0,121,180, 0, + 119,185, 0,117,190, 0,114,196, 0,111,201, 0,109,203, 0, + 109,204, 0,109,203, 0,112,200, 0,114,196, 0,117,192, 0, + 117,191, 0,115,194, 0,113,198, 0,111,201, 0,110,203, 0, + 107,206, 0,105,209, 0,101,214, 0, 97,218, 0, 93,222, 0, + 91,223, 0, 91,223, 0, 93,222, 0, 97,218, 0,102,213, 0, + 103,211, 0,103,212, 0, 99,215, 0, 96,219, 0, 94,221, 0, + 92,222, 0, 93,222, 0, 94,221, 0, 96,219, 0, 98,216, 0, + 102,213, 0,105,209, 0,109,204, 0,113,198, 0,116,192, 0, + 119,186, 0,121,179, 0,123,170, 3,124,160, 12,124,162, 10, + 124,164, 9,124,162, 10,124,157, 15,124,150, 22,124,152, 20, + 124,161, 11,124,164, 9,124,162, 10,124,161, 11,124,167, 6, + 123,170, 3,123,168, 5,123,171, 3,124,162, 10,124,152, 20, + 124,157, 15,123,168, 5,122,177, 0,120,183, 0,119,187, 0, + 117,191, 0,116,193, 0,116,192, 0,117,191, 0,115,194, 0, + 114,197, 0,114,196, 0,116,193, 0,116,193, 0,112,200, 0, + 109,204, 0,107,207, 0,106,208, 0,106,208, 0,106,208, 0, + 105,209, 0,104,210, 0,103,212, 0,102,212, 0,103,211, 0, + 104,210, 0,106,207, 0,108,205, 0,109,204, 0,109,204, 0, + 109,204, 0,110,202, 0,113,198, 0,116,193, 0,117,192, 0, + 118,189, 0,120,183, 0,123,173, 1,124,155, 16,119,119, 77, + 109, 92,251,113,100,242,121,127, 58,123,141, 34,124,165, 8, + 121,182, 0,116,193, 0,111,202, 0,107,207, 0,104,210, 0, + 102,212, 0, 97,217, 0, 91,223, 0, 88,226, 0, 86,228, 0, + 85,229, 0, 85,228, 0, 88,226, 0, 92,222, 0, 98,216, 0, + 105,209, 0,111,201, 0,113,199, 0,113,198, 0,112,200, 0, + 112,200, 0,112,199, 0,113,198, 0,114,196, 0,115,194, 0, + 114,196, 0,113,198, 0,112,200, 0,111,201, 0,112,200, 0, + 113,198, 0,114,196, 0,116,193, 0,115,195, 0,112,200, 0, + 110,203, 0,108,205, 0,108,205, 0,109,203, 0,111,202, 0, + 112,200, 0,113,199, 0,114,197, 0,115,194, 0,118,188, 0, + 121,179, 0,124,165, 8,124,162, 10,124,155, 16,124,149, 24, + 124,143, 31,124,143, 31,124,145, 29,124,155, 16,124,166, 7, + 123,173, 1,122,175, 0,123,174, 1,123,170, 3,124,165, 8, + 124,157, 15,124,149, 24,123,141, 34,123,137, 41,122,135, 44, + 121,127, 58,117,110,109,102, 80,250,105, 84,252,105, 84,252, + 94, 70,242, 30, 37,192, 30, 55,222,105, 84,252,109, 92,251, + 107, 89,252, 99, 75,247, 94, 70,242,111, 96,248,115,107,129, + 115,107,129,114,104,197,113,100,242,109, 92,251, 99, 75,247, + 107, 89,252,107, 89,252, 99, 75,247,113,100,242,120,122, 70, + 123,141, 34,124,147, 26,124,143, 31,121,127, 58,114,104,197, + 124,145, 29,123,170, 3,120,184, 0,114,196, 0,109,204, 0, + 104,210, 0,100,214, 0, 99,216, 0, 99,216, 0,100,215, 0, + 101,214, 0,103,211, 0,105,209, 0,105,209, 0,107,207, 0, + 108,205, 0,110,203, 0,106,208, 0,102,212, 0, 98,217, 0, + 95,220, 0, 92,222, 0, 92,222, 0, 93,221, 0, 96,219, 0, + 99,216, 0,103,211, 0,105,209, 0,107,207, 0,110,202, 0, + 115,195, 0,120,184, 0,123,168, 5,123,141, 34,114,104,197, + 113,100,242,114,104,197,113,100,242,107, 89,252, 87, 65,237, + 105, 84,252,117,110,109,121,127, 58,122,132, 48,122,132, 48, + 121,127, 58,119,119, 77,118,114, 96,118,117, 85,122,132, 48, + 124,149, 24,124,160, 12,124,167, 6,123,171, 3,122,176, 0, + 121,179, 0,120,184, 0,118,189, 0,115,194, 0,113,199, 0, + 110,202, 0,109,203, 0,110,202, 0,112,200, 0,114,197, 0, + 115,194, 0,116,193, 0,115,194, 0,113,198, 0,111,202, 0, + 108,205, 0,105,209, 0,102,213, 0, 98,216, 0, 95,220, 0, + 92,222, 0, 91,224, 0, 91,223, 0, 93,222, 0, 97,218, 0, + 101,214, 0,103,212, 0,105,209, 0,101,213, 0, 98,217, 0, + 95,220, 0, 93,222, 0, 92,222, 0, 92,222, 0, 94,221, 0, + 95,219, 0, 98,217, 0,101,213, 0,105,209, 0,109,204, 0, + 113,199, 0,116,192, 0,119,186, 0,122,178, 0,124,166, 7, + 124,150, 22,124,154, 18,124,157, 15,124,155, 16,124,152, 20, + 124,149, 24,124,157, 15,124,158, 13,124,157, 15,124,165, 8, + 123,171, 3,122,175, 0,122,176, 0,122,176, 0,123,170, 3, + 124,162, 10,124,154, 18,123,168, 5,121,179, 0,119,186, 0, + 117,191, 0,115,194, 0,115,195, 0,115,194, 0,116,193, 0, + 115,194, 0,114,196, 0,114,196, 0,115,194, 0,114,196, 0, + 111,201, 0,108,205, 0,106,208, 0,105,209, 0,104,210, 0, + 104,210, 0,103,211, 0,102,213, 0,100,214, 0,100,215, 0, + 101,214, 0,102,212, 0,104,210, 0,105,209, 0,106,207, 0, + 105,209, 0,105,209, 0,106,208, 0,108,205, 0,112,200, 0, + 115,194, 0,116,192, 0,118,189, 0,120,184, 0,123,174, 1, + 124,160, 12,123,137, 41,117,110,109,123,137, 41,124,158, 13, + 122,175, 0,118,188, 0,114,197, 0,109,203, 0,106,207, 0, + 105,209, 0,102,212, 0, 95,220, 0, 89,226, 0, 85,229, 0, + 83,230, 0, 82,231, 0, 83,230, 0, 86,228, 0, 91,223, 0, + 97,218, 0,104,210, 0,110,202, 0,111,202, 0,110,202, 0, + 111,202, 0,111,201, 0,112,199, 0,113,198, 0,114,197, 0, + 114,196, 0,114,197, 0,112,199, 0,111,202, 0,110,203, 0, + 109,203, 0,110,202, 0,111,201, 0,113,199, 0,112,200, 0, + 108,205, 0,106,208, 0,105,209, 0,105,209, 0,107,207, 0, + 109,204, 0,111,201, 0,113,198, 0,116,193, 0,118,188, 0, + 121,181, 0,123,169, 4,124,166, 7,124,167, 6,124,165, 8, + 124,161, 11,124,160, 12,124,158, 13,124,165, 8,123,173, 1, + 122,178, 0,120,183, 0,119,185, 0,120,184, 0,121,181, 0, + 122,175, 0,123,168, 5,124,160, 12,124,150, 22,124,143, 31, + 123,141, 34,123,137, 41,121,125, 63,114,104,197,107, 89,252, + 105, 84,252, 94, 70,242, 30, 55,222,109, 92,251,118,117, 85, + 119,119, 77,118,114, 96,111, 96,248,114,104,197,120,122, 70, + 122,130, 53,120,122, 70,114,104,197,111, 96,248,105, 84,252, + 94, 70,242, 30, 55,222, 30, 55,222, 69, 60,230,109, 92,251, + 118,114, 96,121,127, 58,122,130, 53,119,119, 77,118,114, 96, + 117,110,109,124,157, 15,122,178, 0,118,189, 0,112,199, 0, + 107,207, 0,102,212, 0, 99,215, 0, 98,217, 0, 98,217, 0, + 98,217, 0,100,215, 0,103,212, 0,107,207, 0,107,206, 0, + 108,206, 0,108,205, 0,107,206, 0,103,211, 0,100,215, 0, + 95,219, 0, 92,222, 0, 91,224, 0, 90,224, 0, 92,223, 0, + 94,220, 0, 98,217, 0,102,213, 0,104,210, 0,106,207, 0, + 110,203, 0,114,196, 0,119,186, 0,123,171, 3,124,149, 24, + 119,119, 77,107, 89,252,105, 84,252,102, 80,250, 69, 60,230, + 87, 65,237,111, 96,248,120,122, 70,123,137, 41,124,143, 31, + 123,141, 34,123,139, 37,122,132, 48,122,130, 53,122,132, 48, + 124,143, 31,124,155, 16,124,162, 10,123,168, 5,123,171, 3, + 123,173, 1,122,176, 0,121,179, 0,120,184, 0,117,190, 0, + 115,195, 0,113,198, 0,112,200, 0,112,200, 0,112,199, 0, + 114,197, 0,114,196, 0,114,196, 0,114,196, 0,114,197, 0, + 110,202, 0,107,207, 0,103,211, 0,100,214, 0, 97,217, 0, + 95,220, 0, 92,222, 0, 92,223, 0, 92,222, 0, 94,221, 0, + 97,218, 0,101,214, 0,103,211, 0,106,208, 0,105,209, 0, + 102,213, 0, 98,217, 0, 95,220, 0, 94,221, 0, 92,222, 0, + 93,222, 0, 94,221, 0, 96,219, 0, 98,216, 0,102,213, 0, + 106,208, 0,110,202, 0,114,196, 0,118,189, 0,121,181, 0, + 123,169, 4,124,154, 18,124,155, 16,124,157, 15,124,157, 15, + 124,154, 18,124,149, 24,124,147, 26,124,149, 24,124,157, 15, + 124,165, 8,123,171, 3,122,177, 0,122,178, 0,122,176, 0, + 123,172, 2,124,167, 6,124,161, 11,123,168, 5,122,178, 0, + 119,187, 0,117,192, 0,115,194, 0,115,194, 0,116,193, 0, + 117,192, 0,115,195, 0,114,197, 0,114,196, 0,114,196, 0, + 113,198, 0,111,201, 0,108,205, 0,107,207, 0,105,209, 0, + 105,209, 0,104,210, 0,103,212, 0,101,214, 0,100,215, 0, + 99,215, 0,100,215, 0,101,213, 0,103,211, 0,104,210, 0, + 105,209, 0,103,211, 0,102,213, 0,102,212, 0,103,211, 0, + 107,207, 0,111,201, 0,115,194, 0,116,192, 0,118,189, 0, + 120,184, 0,122,177, 0,123,168, 5,124,158, 13,124,157, 15, + 123,171, 3,121,182, 0,117,191, 0,113,199, 0,109,204, 0, + 107,207, 0,105,209, 0,101,213, 0, 94,220, 0, 88,226, 0, + 85,229, 0, 83,230, 0, 82,231, 0, 83,230, 0, 85,228, 0, + 91,224, 0, 97,218, 0,103,211, 0,109,204, 0,109,204, 0, + 108,205, 0,108,206, 0,109,204, 0,110,202, 0,113,198, 0, + 114,196, 0,115,195, 0,114,196, 0,112,199, 0,110,202, 0, + 109,204, 0,108,206, 0,107,206, 0,108,205, 0,109,203, 0, + 108,205, 0,105,209, 0,102,212, 0,101,214, 0,102,213, 0, + 104,210, 0,108,206, 0,112,200, 0,115,194, 0,118,188, 0, + 121,179, 0,124,167, 6,124,152, 20,124,162, 10,124,167, 6, + 123,169, 4,124,167, 6,124,166, 7,123,170, 3,122,176, 0, + 121,181, 0,120,184, 0,119,187, 0,118,189, 0,118,188, 0, + 119,185, 0,121,180, 0,123,171, 3,124,162, 10,124,150, 22, + 124,143, 31,123,141, 34,123,139, 37,122,130, 53,117,110,109, + 109, 92,251,107, 89,252,105, 84,252,107, 89,252,118,117, 85, + 122,132, 48,122,135, 44,121,127, 58,117,110,109,119,119, 77, + 122,135, 44,123,139, 37,121,127, 58,115,107,129,105, 84,252, + 87, 65,237, 30, 55,222, 30, 37,192, 30, 26,168, 30, 49,213, + 102, 80,250,114,104,197,118,114, 96,120,122, 70,122,130, 53, + 121,127, 58,119,119, 77,124,162, 10,121,180, 0,117,191, 0, + 111,201, 0,106,207, 0,102,212, 0, 99,215, 0, 98,217, 0, + 98,217, 0, 98,217, 0,100,215, 0,103,212, 0,107,206, 0, + 110,203, 0,109,203, 0,109,203, 0,107,207, 0,103,212, 0, + 99,215, 0, 95,220, 0, 92,222, 0, 91,224, 0, 90,224, 0, + 92,223, 0, 95,220, 0, 98,217, 0,102,213, 0,103,212, 0, + 105,209, 0,109,203, 0,114,196, 0,119,186, 0,123,172, 2, + 124,160, 12,123,141, 34,117,110,109,102, 80,250, 30, 55,222, + 30, 49,213, 87, 65,237,111, 96,248,121,125, 63,123,141, 34, + 124,145, 29,124,145, 29,123,141, 34,123,137, 41,122,135, 44, + 122,135, 44,124,145, 29,124,158, 13,124,167, 6,123,170, 3, + 123,171, 3,123,169, 4,123,168, 5,122,177, 0,121,182, 0, + 120,184, 0,118,189, 0,116,192, 0,115,194, 0,114,196, 0, + 114,196, 0,114,196, 0,114,197, 0,114,197, 0,114,197, 0, + 114,197, 0,111,202, 0,107,206, 0,103,211, 0,100,214, 0, + 98,217, 0, 96,219, 0, 95,220, 0, 95,220, 0, 95,220, 0, + 96,219, 0, 98,217, 0,100,214, 0,103,211, 0,107,206, 0, + 110,203, 0,107,207, 0,103,211, 0,100,215, 0, 97,217, 0, + 95,219, 0, 95,220, 0, 95,220, 0, 96,219, 0, 97,217, 0, + 100,214, 0,104,210, 0,109,204, 0,113,198, 0,117,190, 0, + 121,181, 0,124,167, 6,124,152, 20,124,157, 15,124,158, 13, + 124,157, 15,124,152, 20,124,147, 26,124,143, 31,124,147, 26, + 124,152, 20,124,160, 12,124,167, 6,123,173, 1,122,176, 0, + 123,170, 3,123,171, 3,123,170, 3,124,165, 8,123,170, 3, + 121,179, 0,120,184, 0,118,189, 0,117,192, 0,117,191, 0, + 117,190, 0,116,192, 0,114,197, 0,113,198, 0,114,196, 0, + 114,197, 0,112,199, 0,112,200, 0,110,202, 0,109,204, 0, + 107,206, 0,107,207, 0,106,208, 0,105,209, 0,102,212, 0, + 101,214, 0,101,214, 0,101,213, 0,103,212, 0,104,210, 0, + 105,209, 0,105,209, 0,103,211, 0,101,214, 0,100,215, 0, + 100,214, 0,103,212, 0,107,207, 0,112,200, 0,116,193, 0, + 117,192, 0,118,189, 0,119,186, 0,120,182, 0,122,177, 0, + 123,171, 3,122,176, 0,120,184, 0,117,191, 0,113,198, 0, + 110,202, 0,108,206, 0,106,207, 0,102,212, 0, 96,219, 0, + 91,224, 0, 87,227, 0, 85,229, 0, 84,229, 0, 85,229, 0, + 87,227, 0, 92,223, 0, 97,217, 0,103,211, 0,108,205, 0, + 107,207, 0,106,208, 0,106,208, 0,106,207, 0,109,204, 0, + 112,200, 0,115,194, 0,116,192, 0,115,194, 0,114,197, 0, + 111,201, 0,109,204, 0,107,207, 0,106,208, 0,106,208, 0, + 107,207, 0,105,209, 0,101,213, 0, 98,216, 0, 98,217, 0, + 99,215, 0,103,212, 0,107,206, 0,112,199, 0,117,191, 0, + 121,180, 0,124,166, 7,124,143, 31,122,130, 53,124,150, 22, + 124,162, 10,124,167, 6,123,168, 5,123,172, 2,122,178, 0, + 121,181, 0,120,184, 0,119,186, 0,118,188, 0,118,189, 0, + 118,189, 0,119,186, 0,121,180, 0,123,171, 3,124,160, 12, + 124,145, 29,122,135, 44,122,135, 44,123,137, 41,121,127, 58, + 117,110,109,111, 96,248,113,100,242,114,104,197,114,104,197, + 121,125, 63,123,139, 37,123,141, 34,122,135, 44,119,119, 77, + 121,127, 58,123,137, 41,123,137, 41,121,125, 63,113,100,242, + 99, 75,247, 30, 49,213, 30, 31,180, 30, 26,168, 30, 15,143, + 30, 31,180, 94, 70,242,115,107,129,122,130, 53,123,141, 34, + 123,141, 34,123,141, 34,122,135, 44,124,160, 12,121,179, 0, + 117,190, 0,112,199, 0,108,206, 0,104,210, 0,102,213, 0, + 100,214, 0,100,214, 0,100,214, 0,101,213, 0,104,210, 0, + 108,205, 0,112,199, 0,112,200, 0,112,200, 0,109,203, 0, + 105,209, 0,101,213, 0, 97,217, 0, 94,221, 0, 92,222, 0, + 92,222, 0, 93,221, 0, 96,219, 0, 99,215, 0,100,214, 0, + 102,213, 0,105,209, 0,109,204, 0,114,196, 0,119,187, 0, + 121,180, 0,123,170, 3,124,154, 18,121,127, 58,114,104,197, + 94, 70,242, 30, 26,168, 30, 49,213,107, 89,252,120,122, 70, + 123,137, 41,123,141, 34,123,141, 34,123,137, 41,122,130, 53, + 121,125, 63,121,127, 58,124,143, 31,124,160, 12,123,168, 5, + 123,172, 2,123,173, 1,123,171, 3,123,170, 3,122,177, 0, + 122,178, 0,121,182, 0,120,184, 0,119,186, 0,118,188, 0, + 118,189, 0,117,191, 0,116,193, 0,115,195, 0,114,197, 0, + 114,197, 0,114,197, 0,112,199, 0,109,204, 0,105,209, 0, + 102,212, 0,101,214, 0,100,214, 0,100,215, 0,100,215, 0, + 99,215, 0, 99,215, 0,100,215, 0,101,214, 0,103,211, 0, + 107,207, 0,112,200, 0,113,199, 0,109,203, 0,106,207, 0, + 103,211, 0,101,214, 0,100,215, 0, 99,216, 0, 98,216, 0, + 99,215, 0,101,214, 0,105,209, 0,109,203, 0,114,196, 0, + 119,187, 0,122,176, 0,124,160, 12,124,155, 16,124,157, 15, + 124,157, 15,124,154, 18,124,147, 26,123,141, 34,123,137, 41, + 123,137, 41,123,139, 37,124,147, 26,124,155, 16,124,162, 10, + 123,168, 5,124,161, 11,123,169, 4,123,170, 3,123,168, 5, + 123,172, 2,121,181, 0,119,185, 0,119,186, 0,119,186, 0, + 119,186, 0,118,189, 0,115,195, 0,113,199, 0,112,199, 0, + 113,198, 0,114,196, 0,112,199, 0,112,200, 0,113,199, 0, + 112,200, 0,111,201, 0,110,202, 0,109,203, 0,108,205, 0, + 106,208, 0,104,210, 0,103,211, 0,104,210, 0,105,209, 0, + 106,207, 0,107,207, 0,106,208, 0,104,210, 0,101,214, 0, + 99,216, 0, 98,216, 0,100,215, 0,104,210, 0,109,204, 0, + 114,196, 0,117,192, 0,117,191, 0,117,190, 0,118,189, 0, + 119,186, 0,121,181, 0,122,177, 0,120,183, 0,118,189, 0, + 115,195, 0,112,200, 0,110,203, 0,108,206, 0,105,209, 0, + 100,215, 0, 95,219, 0, 93,222, 0, 91,224, 0, 89,225, 0, + 89,226, 0, 91,224, 0, 94,221, 0, 99,215, 0,105,209, 0, + 108,205, 0,107,207, 0,105,209, 0,104,210, 0,105,209, 0, + 108,206, 0,112,200, 0,116,193, 0,118,189, 0,116,193, 0, + 114,196, 0,112,199, 0,109,203, 0,107,207, 0,105,209, 0, + 104,210, 0,105,209, 0,103,212, 0, 99,216, 0, 96,219, 0, + 95,219, 0, 98,217, 0,102,212, 0,108,206, 0,114,197, 0, + 118,189, 0,120,184, 0,123,174, 1,124,155, 16,122,135, 44, + 122,130, 53,124,149, 24,124,160, 12,124,167, 6,122,175, 0, + 121,179, 0,121,182, 0,120,183, 0,120,184, 0,119,185, 0, + 119,187, 0,119,187, 0,120,184, 0,122,177, 0,124,167, 6, + 124,152, 20,124,147, 26,123,141, 34,121,127, 58,122,130, 53, + 120,122, 70,115,107,129,113,100,242,118,114, 96,119,119, 77, + 118,117, 85,122,130, 53,124,143, 31,124,143, 31,123,137, 41, + 121,125, 63,122,130, 53,122,135, 44,122,132, 48,118,114, 96, + 105, 84,252, 99, 75,247, 30, 49,213, 30, 20,155, 30, 15,143, + 30, 20,155, 30, 31,180, 94, 70,242,118,117, 85,123,141, 34, + 124,155, 16,124,160, 12,124,157, 15,124,147, 26,124,150, 22, + 123,173, 1,119,186, 0,115,195, 0,111,201, 0,108,206, 0, + 106,208, 0,105,209, 0,104,210, 0,104,210, 0,105,209, 0, + 106,207, 0,110,203, 0,114,197, 0,115,194, 0,115,194, 0, + 113,198, 0,109,203, 0,106,208, 0,102,213, 0, 98,217, 0, + 95,219, 0, 95,220, 0, 95,219, 0, 98,217, 0,100,215, 0, + 100,215, 0,101,213, 0,105,209, 0,109,203, 0,114,196, 0, + 117,191, 0,120,184, 0,122,175, 0,124,160, 12,123,141, 34, + 121,125, 63,113,100,242, 30, 55,222, 30, 20,155, 94, 70,242, + 117,110,109,121,127, 58,122,132, 48,122,130, 53,120,122, 70, + 118,114, 96,115,107,129,118,114, 96,123,137, 41,124,155, 16, + 124,165, 8,123,170, 3,123,172, 2,123,171, 3,123,173, 1, + 121,180, 0,120,182, 0,121,182, 0,120,182, 0,119,186, 0, + 118,189, 0,117,190, 0,117,190, 0,118,189, 0,117,192, 0, + 115,194, 0,114,196, 0,115,195, 0,114,196, 0,112,200, 0, + 109,204, 0,107,207, 0,106,208, 0,106,208, 0,106,208, 0, + 106,208, 0,105,209, 0,104,210, 0,103,211, 0,103,211, 0, + 104,210, 0,107,207, 0,111,201, 0,116,193, 0,116,193, 0, + 113,198, 0,110,202, 0,108,205, 0,106,208, 0,105,209, 0, + 103,211, 0,103,211, 0,104,210, 0,107,206, 0,112,200, 0, + 117,191, 0,121,181, 0,124,166, 7,124,157, 15,124,157, 15, + 124,155, 16,124,152, 20,124,143, 31,122,132, 48,121,125, 63, + 118,117, 85,118,114, 96,118,114, 96,119,119, 77,121,127, 58, + 123,141, 34,124,150, 22,124,152, 20,124,164, 9,123,168, 5, + 123,168, 5,122,175, 0,121,182, 0,119,185, 0,119,186, 0, + 120,183, 0,120,182, 0,117,191, 0,113,198, 0,111,201, 0, + 111,201, 0,112,199, 0,114,196, 0,114,197, 0,113,198, 0, + 114,197, 0,115,194, 0,115,194, 0,115,195, 0,114,197, 0, + 113,199, 0,110,202, 0,108,205, 0,108,206, 0,108,205, 0, + 108,205, 0,106,207, 0,105,209, 0,105,209, 0,106,208, 0, + 102,212, 0, 99,215, 0, 98,217, 0, 99,216, 0,102,212, 0, + 107,206, 0,113,198, 0,117,191, 0,117,191, 0,117,192, 0, + 117,191, 0,118,189, 0,119,185, 0,122,178, 0,122,177, 0, + 120,183, 0,118,189, 0,115,194, 0,112,199, 0,110,203, 0, + 108,205, 0,105,209, 0,102,212, 0,100,215, 0, 97,217, 0, + 95,219, 0, 94,220, 0, 95,220, 0, 97,217, 0,101,213, 0, + 106,208, 0,109,204, 0,107,207, 0,105,209, 0,104,210, 0, + 105,209, 0,107,206, 0,111,201, 0,116,192, 0,119,187, 0, + 116,193, 0,114,197, 0,112,199, 0,110,202, 0,107,207, 0, + 105,209, 0,103,211, 0,105,209, 0,101,213, 0, 97,218, 0, + 94,220, 0, 94,220, 0, 97,218, 0,102,212, 0,109,204, 0, + 115,194, 0,117,191, 0,118,188, 0,121,181, 0,124,166, 7, + 124,152, 20,124,147, 26,122,132, 48,124,145, 29,124,165, 8, + 123,174, 1,122,178, 0,121,179, 0,121,179, 0,121,179, 0, + 121,180, 0,120,182, 0,120,182, 0,121,179, 0,123,172, 2, + 124,161, 11,124,152, 20,124,149, 24,123,141, 34,121,127, 58, + 121,125, 63,119,119, 77,114,104,197,115,107,129,120,122, 70, + 122,130, 53,122,130, 53,122,130, 53,124,143, 31,124,143, 31, + 123,137, 41,121,125, 63,121,127, 58,122,130, 53,120,122, 70, + 111, 96,248,111, 96,248,105, 84,252, 30, 55,222, 30, 20,155, + 30, 10,132, 30, 31,180, 30, 43,203, 87, 65,237,118,117, 85, + 124,149, 24,124,165, 8,123,171, 3,123,172, 2,123,168, 5, + 124,160, 12,124,165, 8,122,178, 0,118,188, 0,115,195, 0, + 112,199, 0,110,202, 0,110,203, 0,109,203, 0,109,204, 0, + 109,204, 0,110,202, 0,112,199, 0,115,194, 0,118,188, 0, + 119,186, 0,118,188, 0,115,195, 0,111,201, 0,107,207, 0, + 103,212, 0,100,215, 0, 98,217, 0, 98,217, 0,100,215, 0, + 101,214, 0,100,214, 0,102,212, 0,105,209, 0,110,203, 0, + 114,196, 0,117,191, 0,119,185, 0,122,177, 0,124,161, 11, + 124,154, 18,123,141, 34,120,122, 70,107, 89,252, 30, 26,168, + 30, 31,180,107, 89,252,117,110,109,118,114, 96,117,110,109, + 113,100,242,105, 84,252, 99, 75,247,109, 92,251,119,119, 77, + 124,143, 31,124,158, 13,124,166, 7,123,169, 4,123,171, 3, + 123,173, 1,121,180, 0,120,183, 0,120,184, 0,119,185, 0, + 119,186, 0,119,187, 0,118,189, 0,118,189, 0,118,188, 0, + 119,187, 0,117,191, 0,116,192, 0,117,192, 0,117,191, 0, + 115,194, 0,113,198, 0,112,200, 0,112,200, 0,112,199, 0, + 110,202, 0,108,205, 0,107,206, 0,107,206, 0,108,205, 0, + 107,207, 0,107,207, 0,109,204, 0,112,200, 0,115,194, 0, + 118,189, 0,117,190, 0,117,190, 0,115,194, 0,114,197, 0, + 112,200, 0,110,203, 0,109,204, 0,109,203, 0,112,200, 0, + 116,193, 0,120,184, 0,123,170, 3,124,157, 15,124,157, 15, + 124,158, 13,124,157, 15,124,149, 24,122,132, 48,117,110,109, + 105, 84,252, 99, 75,247, 69, 60,230,102, 80,250,111, 96,248, + 113,100,242,113,100,242,121,127, 58,123,139, 37,124,157, 15, + 124,164, 9,123,171, 3,122,178, 0,120,183, 0,119,186, 0, + 119,185, 0,120,182, 0,118,188, 0,116,193, 0,113,198, 0, + 111,202, 0,110,202, 0,111,201, 0,113,198, 0,115,194, 0, + 115,195, 0,115,194, 0,117,191, 0,119,186, 0,118,189, 0, + 116,193, 0,115,194, 0,115,195, 0,113,199, 0,112,200, 0, + 111,202, 0,109,204, 0,107,207, 0,106,208, 0,106,208, 0, + 107,206, 0,104,210, 0,101,214, 0, 99,215, 0,100,215, 0, + 103,211, 0,108,205, 0,113,198, 0,118,189, 0,117,190, 0, + 117,191, 0,117,191, 0,118,189, 0,119,185, 0,122,178, 0, + 123,170, 3,123,173, 1,120,182, 0,118,189, 0,115,194, 0, + 113,199, 0,110,202, 0,108,205, 0,107,207, 0,107,207, 0, + 105,209, 0,102,212, 0,101,214, 0,100,214, 0,101,213, 0, + 104,210, 0,108,205, 0,111,201, 0,109,204, 0,106,208, 0, + 105,209, 0,105,209, 0,107,206, 0,111,201, 0,116,192, 0, + 120,183, 0,117,191, 0,114,196, 0,113,199, 0,110,202, 0, + 107,207, 0,105,209, 0,103,211, 0,105,209, 0,101,214, 0, + 97,218, 0, 94,220, 0, 94,220, 0, 97,218, 0,102,212, 0, + 109,204, 0,116,193, 0,117,192, 0,118,189, 0,120,183, 0, + 123,171, 3,124,157, 15,124,152, 20,123,139, 37,122,135, 44, + 124,161, 11,123,171, 3,122,175, 0,122,178, 0,120,183, 0, + 120,184, 0,120,182, 0,122,177, 0,122,177, 0,123,173, 1, + 124,166, 7,124,154, 18,124,147, 26,123,141, 34,122,132, 48, + 118,117, 85,121,125, 63,119,119, 77,114,104,197,115,107,129, + 121,127, 58,123,137, 41,123,137, 41,121,127, 58,123,141, 34, + 123,141, 34,122,135, 44,120,122, 70,121,127, 58,121,125, 63, + 118,114, 96,105, 84,252,109, 92,251,105, 84,252, 69, 60,230, + 30, 26,168, 30, 26,168, 30, 55,222, 69, 60,230, 69, 60,230, + 118,117, 85,124,152, 20,123,170, 3,122,178, 0,121,180, 0, + 122,178, 0,123,174, 1,123,168, 5,122,178, 0,120,183, 0, + 118,188, 0,117,192, 0,115,194, 0,115,195, 0,114,196, 0, + 114,196, 0,114,197, 0,114,197, 0,115,195, 0,117,192, 0, + 119,187, 0,121,181, 0,122,177, 0,120,184, 0,117,192, 0, + 112,200, 0,107,206, 0,103,211, 0,101,214, 0,100,214, 0, + 101,213, 0,103,211, 0,103,212, 0,104,210, 0,107,207, 0, + 110,202, 0,114,196, 0,118,188, 0,120,182, 0,123,172, 2, + 123,168, 5,124,164, 9,124,155, 16,123,139, 37,118,114, 96, + 69, 60,230, 69, 60,230, 69, 60,230,107, 89,252,109, 92,251, + 111, 96,248,114,104,197,113,100,242,114,104,197,114,104,197, + 113,100,242,122,130, 53,124,149, 24,124,160, 12,124,167, 6, + 123,171, 3,123,173, 1,122,176, 0,122,178, 0,121,182, 0, + 119,185, 0,118,188, 0,118,188, 0,119,186, 0,119,186, 0, + 120,184, 0,121,181, 0,119,186, 0,119,187, 0,119,187, 0, + 119,186, 0,119,185, 0,118,188, 0,117,190, 0,118,189, 0, + 115,195, 0,112,199, 0,110,202, 0,109,203, 0,109,204, 0, + 110,203, 0,111,201, 0,112,200, 0,113,199, 0,114,196, 0, + 117,191, 0,120,184, 0,120,184, 0,119,185, 0,119,185, 0, + 120,184, 0,118,188, 0,116,193, 0,114,196, 0,114,196, 0, + 116,192, 0,119,186, 0,123,173, 1,124,162, 10,123,168, 5, + 123,171, 3,123,170, 3,124,166, 7,124,155, 16,123,137, 41, + 115,107,129, 94, 70,242, 69, 60,230, 30, 49,213, 94, 70,242, + 111, 96,248,118,117, 85,121,127, 58,122,135, 44,124,150, 22, + 124,158, 13,123,168, 5,122,175, 0,121,181, 0,120,184, 0, + 119,185, 0,120,184, 0,119,187, 0,116,192, 0,114,197, 0, + 112,199, 0,111,202, 0,110,203, 0,110,202, 0,112,200, 0, + 114,197, 0,115,195, 0,116,193, 0,117,192, 0,118,189, 0, + 118,188, 0,117,191, 0,116,193, 0,116,193, 0,115,194, 0, + 114,197, 0,112,199, 0,111,202, 0,109,203, 0,109,204, 0, + 109,203, 0,109,204, 0,106,208, 0,103,211, 0,102,212, 0, + 103,211, 0,107,207, 0,111,201, 0,116,192, 0,119,187, 0, + 118,189, 0,117,190, 0,118,189, 0,118,188, 0,120,183, 0, + 122,176, 0,123,168, 5,123,172, 2,121,181, 0,119,185, 0, + 118,188, 0,115,194, 0,112,200, 0,110,202, 0,109,204, 0, + 109,204, 0,110,202, 0,110,202, 0,108,206, 0,107,207, 0, + 107,207, 0,108,205, 0,111,201, 0,114,197, 0,111,201, 0, + 108,205, 0,107,207, 0,107,207, 0,109,204, 0,112,199, 0, + 117,192, 0,120,182, 0,119,186, 0,116,192, 0,114,196, 0, + 111,202, 0,107,206, 0,105,209, 0,104,210, 0,105,209, 0, + 102,213, 0, 98,217, 0, 96,219, 0, 96,219, 0, 99,216, 0, + 104,210, 0,110,202, 0,116,193, 0,117,191, 0,118,189, 0, + 120,184, 0,123,173, 1,124,155, 16,124,152, 20,124,143, 31, + 124,149, 24,124,161, 11,123,170, 3,122,178, 0,120,183, 0, + 119,186, 0,119,186, 0,120,184, 0,122,178, 0,123,170, 3, + 124,166, 7,124,158, 13,124,147, 26,123,137, 41,121,125, 63, + 118,117, 85,121,127, 58,122,132, 48,121,127, 58,117,110,109, + 111, 96,248,120,122, 70,123,137, 41,123,139, 37,122,132, 48, + 123,141, 34,123,141, 34,122,132, 48,120,122, 70,121,127, 58, + 122,135, 44,122,135, 44,118,117, 85,111, 96,248,107, 89,252, + 69, 60,230, 30, 55,222, 87, 65,237,107, 89,252,109, 92,251, + 105, 84,252,117,110,109,124,150, 22,123,170, 3,121,180, 0, + 120,184, 0,120,184, 0,121,182, 0,122,178, 0,121,179, 0, + 120,184, 0,119,186, 0,119,186, 0,120,184, 0,117,191, 0, + 114,196, 0,114,197, 0,115,195, 0,116,192, 0,118,188, 0, + 119,186, 0,120,183, 0,121,179, 0,123,173, 1,120,182, 0, + 118,188, 0,116,193, 0,111,201, 0,107,207, 0,104,210, 0, + 103,212, 0,103,211, 0,105,209, 0,107,207, 0,107,206, 0, + 109,203, 0,112,199, 0,115,194, 0,118,188, 0,121,182, 0, + 122,176, 0,123,173, 1,123,169, 4,124,164, 9,124,152, 20, + 122,132, 48,113,100,242,114,104,197,115,107,129,117,110,109, + 118,117, 85,120,122, 70,120,122, 70,118,117, 85,119,119, 77, + 118,117, 85,115,107,129,119,119, 77,124,143, 31,124,157, 15, + 124,166, 7,123,172, 2,122,176, 0,121,179, 0,120,182, 0, + 120,184, 0,120,182, 0,119,186, 0,118,188, 0,118,188, 0, + 120,184, 0,122,177, 0,123,172, 2,122,178, 0,121,180, 0, + 121,179, 0,122,178, 0,122,176, 0,122,175, 0,122,177, 0, + 121,182, 0,118,188, 0,116,193, 0,114,197, 0,112,200, 0, + 111,201, 0,112,200, 0,112,199, 0,113,199, 0,114,196, 0, + 117,190, 0,119,186, 0,121,180, 0,123,174, 1,122,177, 0, + 122,177, 0,122,175, 0,123,173, 1,121,180, 0,120,184, 0, + 119,185, 0,120,184, 0,120,183, 0,122,178, 0,123,173, 1, + 122,176, 0,122,178, 0,122,175, 0,123,169, 4,124,157, 15, + 122,135, 44,118,117, 85,115,107,129,111, 96,248,114,104,197, + 115,107,129,118,114, 96,122,130, 53,123,139, 37,124,145, 29, + 124,155, 16,124,164, 9,123,171, 3,122,177, 0,121,181, 0, + 120,182, 0,120,184, 0,118,188, 0,116,192, 0,114,196, 0, + 112,200, 0,110,202, 0,110,202, 0,110,203, 0,109,203, 0, + 110,202, 0,112,200, 0,113,198, 0,114,196, 0,115,194, 0, + 117,192, 0,118,189, 0,118,188, 0,117,190, 0,117,192, 0, + 116,192, 0,115,194, 0,114,196, 0,114,197, 0,113,198, 0, + 113,198, 0,113,199, 0,111,202, 0,109,204, 0,108,206, 0, + 108,205, 0,110,203, 0,113,198, 0,117,191, 0,120,182, 0, + 119,185, 0,119,186, 0,119,187, 0,119,186, 0,120,184, 0, + 121,180, 0,123,174, 1,123,170, 3,123,173, 1,123,172, 2, + 122,177, 0,120,183, 0,117,191, 0,114,196, 0,112,200, 0, + 111,202, 0,110,202, 0,112,200, 0,114,196, 0,116,193, 0, + 114,196, 0,114,197, 0,114,196, 0,115,194, 0,117,192, 0, + 114,196, 0,112,200, 0,110,202, 0,110,202, 0,112,200, 0, + 115,195, 0,118,188, 0,121,180, 0,121,182, 0,118,188, 0, + 115,194, 0,112,200, 0,108,205, 0,106,207, 0,106,208, 0, + 107,207, 0,103,211, 0,100,214, 0, 99,216, 0, 99,215, 0, + 102,212, 0,107,207, 0,113,199, 0,116,193, 0,117,191, 0, + 118,188, 0,120,183, 0,123,173, 1,124,154, 18,124,149, 24, + 124,145, 29,124,158, 13,123,169, 4,122,177, 0,120,182, 0, + 119,186, 0,118,188, 0,118,188, 0,119,185, 0,121,180, 0, + 123,173, 1,124,165, 8,124,155, 16,123,141, 34,121,125, 63, + 120,122, 70,121,125, 63,123,137, 41,124,143, 31,123,137, 41, + 120,122, 70,115,107,129,118,114, 96,122,132, 48,123,139, 37, + 122,130, 53,123,141, 34,123,141, 34,122,132, 48,119,119, 77, + 122,135, 44,124,147, 26,124,150, 22,123,139, 37,119,119, 77, + 115,107,129,113,100,242,109, 92,251,114,104,197,119,119, 77, + 121,125, 63,118,117, 85,114,104,197,123,141, 34,124,167, 6, + 121,180, 0,119,186, 0,118,188, 0,119,187, 0,120,184, 0, + 120,183, 0,119,185, 0,119,187, 0,118,188, 0,117,190, 0, + 117,192, 0,115,194, 0,114,197, 0,114,196, 0,116,192, 0, + 119,187, 0,121,180, 0,122,177, 0,123,174, 1,123,170, 3, + 122,176, 0,120,182, 0,119,186, 0,115,195, 0,111,201, 0, + 108,206, 0,106,208, 0,106,208, 0,107,206, 0,109,204, 0, + 111,201, 0,113,198, 0,115,194, 0,118,189, 0,120,184, 0, + 121,180, 0,122,176, 0,123,174, 1,123,172, 2,123,168, 5, + 124,158, 13,124,145, 29,122,132, 48,123,139, 37,123,139, 37, + 123,141, 34,124,143, 31,123,141, 34,123,137, 41,121,125, 63, + 119,119, 77,118,114, 96,113,100,242,118,117, 85,123,139, 37, + 124,155, 16,124,166, 7,123,172, 2,122,177, 0,121,179, 0, + 120,183, 0,120,184, 0,120,182, 0,119,185, 0,118,188, 0, + 118,189, 0,119,186, 0,122,178, 0,123,168, 5,123,168, 5, + 123,170, 3,123,168, 5,124,165, 8,124,161, 11,124,160, 12, + 124,162, 10,123,169, 4,122,178, 0,119,186, 0,117,191, 0, + 115,194, 0,114,196, 0,114,197, 0,114,196, 0,114,196, 0, + 116,193, 0,119,186, 0,121,180, 0,122,175, 0,124,166, 7, + 124,162, 10,124,162, 10,124,160, 12,124,152, 20,124,162, 10, + 123,169, 4,121,180, 0,119,185, 0,119,186, 0,120,183, 0, + 122,178, 0,121,179, 0,121,179, 0,122,176, 0,123,169, 4, + 124,157, 15,122,135, 44,122,130, 53,122,130, 53,123,137, 41, + 123,139, 37,123,139, 37,122,132, 48,122,132, 48,123,141, 34, + 124,149, 24,124,155, 16,124,165, 8,123,171, 3,122,176, 0, + 121,179, 0,121,182, 0,118,188, 0,116,193, 0,114,196, 0, + 113,199, 0,111,202, 0,110,203, 0,110,203, 0,111,202, 0, + 110,202, 0,110,202, 0,111,201, 0,112,199, 0,114,197, 0, + 115,194, 0,117,192, 0,118,189, 0,119,185, 0,119,185, 0, + 119,187, 0,118,189, 0,117,190, 0,117,191, 0,117,191, 0, + 118,189, 0,117,191, 0,115,194, 0,114,197, 0,113,198, 0, + 113,198, 0,114,196, 0,117,192, 0,119,186, 0,122,177, 0, + 122,178, 0,121,181, 0,121,182, 0,121,182, 0,121,181, 0, + 122,178, 0,122,175, 0,123,170, 3,124,166, 7,123,169, 4, + 124,167, 6,124,165, 8,122,177, 0,119,185, 0,117,191, 0, + 115,195, 0,114,197, 0,114,197, 0,115,195, 0,117,192, 0, + 118,188, 0,120,184, 0,121,182, 0,120,183, 0,120,183, 0, + 119,186, 0,117,190, 0,116,193, 0,115,194, 0,115,194, 0, + 116,192, 0,118,188, 0,120,183, 0,122,178, 0,122,175, 0, + 120,183, 0,117,192, 0,113,198, 0,110,202, 0,108,205, 0, + 108,205, 0,109,204, 0,106,208, 0,103,211, 0,103,212, 0, + 103,211, 0,106,207, 0,111,202, 0,115,194, 0,116,193, 0, + 117,191, 0,119,187, 0,121,181, 0,123,171, 3,124,154, 18, + 123,139, 37,124,145, 29,124,162, 10,123,173, 1,121,180, 0, + 120,184, 0,119,186, 0,119,187, 0,119,186, 0,120,184, 0, + 121,180, 0,123,174, 1,124,167, 6,124,157, 15,123,141, 34, + 118,117, 85,121,125, 63,122,130, 53,123,141, 34,124,149, 24, + 124,143, 31,122,130, 53,117,110,109,115,107,129,121,127, 58, + 122,135, 44,122,132, 48,124,143, 31,124,143, 31,122,132, 48, + 117,110,109,123,139, 37,124,154, 18,124,160, 12,124,154, 18, + 123,141, 34,122,130, 53,120,122, 70,118,114, 96,122,130, 53, + 123,141, 34,124,145, 29,124,143, 31,121,127, 58,121,127, 58, + 124,161, 11,122,178, 0,119,186, 0,118,189, 0,118,189, 0, + 118,188, 0,119,186, 0,119,187, 0,118,189, 0,117,192, 0, + 115,194, 0,114,197, 0,113,198, 0,114,196, 0,115,194, 0, + 117,191, 0,119,186, 0,122,178, 0,124,167, 6,124,166, 7, + 124,162, 10,124,164, 9,123,173, 1,122,178, 0,118,188, 0, + 115,195, 0,112,200, 0,110,202, 0,110,202, 0,110,202, 0, + 112,200, 0,113,198, 0,115,194, 0,117,190, 0,120,183, 0, + 122,177, 0,123,174, 1,123,172, 2,123,171, 3,123,170, 3, + 124,167, 6,124,162, 10,124,155, 16,124,155, 16,124,158, 13, + 124,160, 12,124,160, 12,124,158, 13,124,154, 18,124,145, 29, + 121,127, 58,118,114, 96,113,100,242,107, 89,252,118,114, 96, + 123,139, 37,124,154, 18,124,165, 8,123,170, 3,123,174, 1, + 122,177, 0,121,180, 0,121,181, 0,122,178, 0,120,184, 0, + 118,188, 0,118,189, 0,119,186, 0,122,178, 0,123,172, 2, + 123,168, 5,124,157, 15,124,149, 24,123,141, 34,122,132, 48, + 121,127, 58,122,132, 48,124,154, 18,123,171, 3,121,182, 0, + 119,186, 0,118,188, 0,117,190, 0,117,191, 0,117,190, 0, + 118,189, 0,119,187, 0,121,179, 0,123,173, 1,123,170, 3, + 124,160, 12,123,139, 37,122,135, 44,121,125, 63,118,114, 96, + 124,149, 24,123,170, 3,121,182, 0,119,186, 0,119,187, 0, + 119,186, 0,120,182, 0,121,180, 0,122,178, 0,122,175, 0, + 123,169, 4,124,158, 13,123,139, 37,124,147, 26,124,154, 18, + 124,157, 15,124,157, 15,124,154, 18,124,147, 26,122,132, 48, + 122,135, 44,124,145, 29,124,149, 24,124,161, 11,123,169, 4, + 123,174, 1,122,176, 0,120,183, 0,117,190, 0,115,195, 0, + 113,198, 0,112,200, 0,111,201, 0,110,202, 0,110,202, 0, + 111,201, 0,112,199, 0,112,200, 0,112,199, 0,113,198, 0, + 115,195, 0,117,192, 0,118,188, 0,119,185, 0,121,182, 0, + 122,178, 0,121,180, 0,120,182, 0,120,183, 0,120,182, 0, + 121,181, 0,122,178, 0,120,184, 0,118,188, 0,118,189, 0, + 118,188, 0,119,186, 0,121,181, 0,123,173, 1,124,166, 7, + 123,168, 5,123,170, 3,123,171, 3,123,171, 3,123,171, 3, + 123,170, 3,123,168, 5,124,166, 7,124,164, 9,124,162, 10, + 124,161, 11,124,164, 9,124,166, 7,123,168, 5,122,176, 0, + 120,182, 0,119,186, 0,118,189, 0,118,189, 0,119,187, 0, + 120,184, 0,121,181, 0,122,178, 0,122,175, 0,123,174, 1, + 122,175, 0,122,178, 0,121,181, 0,120,183, 0,120,184, 0, + 120,183, 0,121,182, 0,121,180, 0,121,179, 0,122,177, 0, + 123,171, 3,122,178, 0,118,188, 0,114,196, 0,112,200, 0, + 111,202, 0,111,201, 0,112,200, 0,109,203, 0,108,206, 0, + 107,206, 0,108,205, 0,111,202, 0,114,196, 0,115,195, 0, + 116,193, 0,118,189, 0,120,184, 0,122,178, 0,124,167, 6, + 124,150, 22,120,122, 70,122,135, 44,124,160, 12,123,172, 2, + 121,179, 0,120,182, 0,120,184, 0,120,184, 0,120,183, 0, + 121,181, 0,122,177, 0,123,173, 1,124,167, 6,124,158, 13, + 124,149, 24,123,137, 41,122,135, 44,124,143, 31,124,143, 31, + 124,149, 24,124,145, 29,122,132, 48,117,110,109,115,107,129, + 120,122, 70,122,130, 53,122,132, 48,124,145, 29,124,145, 29, + 123,137, 41,119,119, 77,122,132, 48,124,152, 20,124,161, 11, + 124,160, 12,124,150, 22,123,141, 34,122,130, 53,122,130, 53, + 124,143, 31,124,152, 20,124,158, 13,124,160, 12,124,154, 18, + 122,135, 44,124,154, 18,123,173, 1,120,184, 0,118,189, 0, + 117,191, 0,118,189, 0,118,188, 0,118,188, 0,117,191, 0, + 116,193, 0,114,196, 0,112,200, 0,111,202, 0,112,200, 0, + 114,196, 0,119,187, 0,121,182, 0,123,173, 1,124,161, 11, + 124,154, 18,124,149, 24,124,143, 31,124,157, 15,123,170, 3, + 121,181, 0,118,188, 0,117,192, 0,115,194, 0,114,196, 0, + 114,196, 0,114,196, 0,115,194, 0,117,192, 0,119,187, 0, + 121,179, 0,124,166, 7,124,161, 11,124,161, 11,124,162, 10, + 124,162, 10,124,165, 8,124,164, 9,124,160, 12,124,167, 6, + 123,169, 4,123,169, 4,123,169, 4,124,167, 6,124,161, 11, + 124,149, 24,121,127, 58,113,100,242, 99, 75,247,107, 89,252, + 121,127, 58,124,154, 18,124,166, 7,123,171, 3,123,170, 3, + 123,169, 4,123,171, 3,123,173, 1,123,173, 1,123,174, 1, + 120,183, 0,118,188, 0,118,188, 0,119,185, 0,122,177, 0, + 123,173, 1,123,170, 3,124,158, 13,122,132, 48,107, 89,252, + 94, 70,242,107, 89,252,121,125, 63,124,152, 20,123,170, 3, + 121,182, 0,119,186, 0,118,188, 0,119,185, 0,121,182, 0, + 121,181, 0,121,180, 0,122,176, 0,124,166, 7,124,167, 6, + 124,164, 9,124,152, 20,120,122, 70,102, 80,250, 99, 75,247, + 119,119, 77,124,155, 16,123,172, 2,121,182, 0,119,186, 0, + 118,188, 0,119,187, 0,119,185, 0,121,182, 0,122,177, 0, + 123,173, 1,123,168, 5,124,158, 13,124,158, 13,124,162, 10, + 124,166, 7,124,167, 6,124,166, 7,124,161, 11,124,152, 20, + 123,137, 41,118,117, 85,122,132, 48,122,132, 48,124,152, 20, + 124,164, 9,123,170, 3,123,173, 1,120,183, 0,117,190, 0, + 115,195, 0,114,197, 0,113,198, 0,112,199, 0,112,200, 0, + 112,199, 0,113,198, 0,115,194, 0,115,194, 0,115,194, 0, + 116,193, 0,118,189, 0,119,185, 0,121,181, 0,122,177, 0, + 123,174, 1,123,170, 3,124,166, 7,123,170, 3,123,170, 3, + 123,169, 4,124,165, 8,124,165, 8,123,173, 1,122,178, 0, + 122,178, 0,123,173, 1,124,165, 8,124,152, 20,124,154, 18, + 124,155, 16,124,157, 15,124,162, 10,124,164, 9,124,160, 12, + 124,154, 18,124,149, 24,124,149, 24,124,149, 24,124,152, 20, + 124,155, 16,124,158, 13,124,161, 11,124,164, 9,124,166, 7, + 124,167, 6,124,167, 6,123,171, 3,123,173, 1,123,173, 1, + 123,171, 3,123,169, 4,124,167, 6,124,165, 8,124,164, 9, + 124,162, 10,124,162, 10,124,165, 8,124,166, 7,123,168, 5, + 122,175, 0,121,179, 0,121,181, 0,121,181, 0,122,178, 0, + 122,175, 0,123,168, 5,123,172, 2,120,184, 0,117,192, 0, + 114,196, 0,114,197, 0,114,196, 0,115,194, 0,113,198, 0, + 112,200, 0,112,200, 0,113,198, 0,115,195, 0,116,193, 0, + 115,194, 0,116,192, 0,119,187, 0,121,180, 0,123,171, 3, + 124,160, 12,123,141, 34,118,117, 85,115,107,129,124,147, 26, + 124,166, 7,123,174, 1,122,178, 0,122,178, 0,122,178, 0, + 122,176, 0,123,173, 1,123,171, 3,123,170, 3,124,166, 7, + 124,166, 7,124,164, 9,124,154, 18,123,137, 41,124,147, 26, + 124,147, 26,124,145, 29,123,141, 34,122,130, 53,117,110,109, + 114,104,197,118,117, 85,121,125, 63,122,132, 48,124,145, 29, + 124,149, 24,123,141, 34,120,122, 70,118,117, 85,124,143, 31, + 124,155, 16,124,155, 16,124,147, 26,123,137, 41,121,125, 63, + 123,137, 41,124,149, 24,124,157, 15,124,164, 9,123,168, 5, + 124,167, 6,124,158, 13,124,149, 24,123,168, 5,121,180, 0, + 119,186, 0,118,189, 0,118,189, 0,119,187, 0,118,189, 0, + 116,193, 0,115,194, 0,115,195, 0,112,199, 0,111,202, 0, + 111,202, 0,113,198, 0,117,191, 0,121,179, 0,124,166, 7, + 124,152, 20,122,135, 44,121,127, 58,118,114, 96,124,143, 31, + 124,161, 11,123,172, 2,122,178, 0,120,183, 0,119,185, 0, + 119,187, 0,118,188, 0,118,189, 0,118,189, 0,118,188, 0, + 120,184, 0,122,175, 0,124,158, 13,122,132, 48,123,137, 41, + 124,145, 29,124,157, 15,124,161, 11,124,160, 12,124,167, 6, + 123,172, 2,123,174, 1,123,174, 1,123,173, 1,123,170, 3, + 124,164, 9,124,150, 22,121,125, 63,102, 80,250, 87, 65,237, + 105, 84,252,123,137, 41,124,161, 11,123,173, 1,122,178, 0, + 122,178, 0,123,174, 1,124,166, 7,124,161, 11,124,160, 12, + 123,173, 1,120,182, 0,119,187, 0,119,187, 0,120,183, 0, + 123,174, 1,123,172, 2,123,168, 5,124,155, 16,121,127, 58, + 94, 70,242, 94, 70,242,105, 84,252,119,119, 77,124,149, 24, + 123,169, 4,121,180, 0,119,186, 0,119,186, 0,120,183, 0, + 123,173, 1,124,165, 8,124,162, 10,124,157, 15,124,157, 15, + 124,161, 11,124,157, 15,123,141, 34,114,104,197, 99, 75,247, + 102, 80,250,122,135, 44,124,160, 12,123,173, 1,121,181, 0, + 119,185, 0,119,187, 0,119,187, 0,119,187, 0,120,184, 0, + 121,180, 0,123,172, 2,123,168, 5,123,173, 1,122,175, 0, + 123,173, 1,123,173, 1,123,174, 1,123,170, 3,124,164, 9, + 124,152, 20,122,135, 44,117,110,109,115,107,129,114,104,197, + 123,137, 41,124,155, 16,124,164, 9,123,168, 5,121,179, 0, + 118,188, 0,116,192, 0,115,194, 0,115,194, 0,115,194, 0, + 115,194, 0,115,194, 0,116,192, 0,118,189, 0,119,186, 0, + 119,186, 0,120,184, 0,121,179, 0,122,178, 0,122,177, 0, + 123,171, 3,124,160, 12,124,157, 15,124,155, 16,124,147, 26, + 124,149, 24,124,145, 29,123,137, 41,123,141, 34,124,152, 20, + 124,158, 13,124,154, 18,123,141, 34,121,127, 58,122,135, 44, + 124,149, 24,124,162, 10,124,167, 6,123,168, 5,124,167, 6, + 124,164, 9,124,157, 15,124,145, 29,122,130, 53,118,117, 85, + 122,130, 53,123,141, 34,124,150, 22,124,155, 16,124,158, 13, + 124,158, 13,124,157, 15,124,154, 18,124,145, 29,123,137, 41, + 122,135, 44,122,135, 44,123,137, 41,123,139, 37,123,139, 37, + 123,139, 37,123,139, 37,123,139, 37,123,139, 37,123,139, 37, + 124,160, 12,123,171, 3,122,178, 0,121,181, 0,121,180, 0, + 122,177, 0,123,172, 2,124,167, 6,123,173, 1,121,180, 0, + 118,188, 0,117,192, 0,116,192, 0,118,189, 0,118,188, 0, + 117,191, 0,116,193, 0,116,193, 0,117,192, 0,118,189, 0, + 117,192, 0,116,192, 0,117,190, 0,120,184, 0,122,176, 0, + 123,170, 3,124,157, 15,121,127, 58,113,100,242, 87, 65,237, + 120,122, 70,124,152, 20,124,164, 9,124,167, 6,123,168, 5, + 124,167, 6,124,165, 8,124,162, 10,124,162, 10,124,164, 9, + 123,168, 5,123,172, 2,123,172, 2,124,165, 8,124,149, 24, + 123,141, 34,123,141, 34,122,135, 44,122,135, 44,121,127, 58, + 117,110,109,109, 92,251,119,119, 77,121,125, 63,122,132, 48, + 124,145, 29,124,150, 22,124,147, 26,122,132, 48,118,117, 85, + 118,117, 85,123,137, 41,123,141, 34,122,132, 48,119,119, 77, + 120,122, 70,123,137, 41,124,147, 26,124,158, 13,124,166, 7, + 123,172, 2,122,175, 0,123,171, 3,124,165, 8,123,170, 3, + 122,177, 0,120,184, 0,119,186, 0,119,186, 0,120,184, 0, + 116,192, 0,114,197, 0,113,199, 0,113,198, 0,114,196, 0, + 112,200, 0,111,201, 0,113,198, 0,116,192, 0,120,183, 0, + 124,167, 6,123,137, 41,118,117, 85,113,100,242,111, 96,248, + 121,127, 58,124,150, 22,124,161, 11,124,167, 6,122,175, 0, + 121,179, 0,121,179, 0,122,178, 0,121,181, 0,120,184, 0, + 120,183, 0,122,178, 0,123,168, 5,124,147, 26,115,107,129, + 117,110,109,122,135, 44,124,147, 26,124,152, 20,124,162, 10, + 123,171, 3,122,175, 0,122,175, 0,122,175, 0,123,174, 1, + 123,170, 3,124,164, 9,124,150, 22,121,127, 58,102, 80,250, + 69, 60,230,105, 84,252,123,137, 41,124,164, 9,122,176, 0, + 121,182, 0,120,182, 0,122,178, 0,123,171, 3,124,161, 11, + 124,157, 15,123,172, 2,121,182, 0,119,186, 0,119,186, 0, + 121,181, 0,123,171, 3,124,167, 6,124,162, 10,124,147, 26, + 121,125, 63,121,125, 63,120,122, 70,122,132, 48,122,130, 53, + 124,145, 29,124,166, 7,122,178, 0,120,184, 0,120,184, 0, + 121,179, 0,124,166, 7,123,139, 37,122,130, 53,121,127, 58, + 124,147, 26,124,154, 18,124,149, 24,124,143, 31,123,137, 41, + 119,119, 77,118,117, 85,124,145, 29,124,164, 9,123,174, 1, + 121,180, 0,120,183, 0,119,185, 0,119,186, 0,119,187, 0, + 119,186, 0,121,182, 0,121,179, 0,121,180, 0,120,184, 0, + 119,185, 0,120,184, 0,121,179, 0,122,178, 0,123,173, 1, + 124,165, 8,124,149, 24,122,130, 53,115,107,129, 94, 70,242, + 30, 55,222,118,114, 96,123,139, 37,124,155, 16,124,162, 10, + 123,171, 3,121,182, 0,119,187, 0,118,189, 0,118,188, 0, + 118,188, 0,118,188, 0,118,188, 0,119,186, 0,120,182, 0, + 122,175, 0,123,173, 1,123,169, 4,122,175, 0,121,179, 0, + 121,179, 0,122,175, 0,124,165, 8,124,143, 31,122,135, 44, + 121,127, 58,115,107,129,113,100,242,105, 84,252,111, 96,248, + 117,110,109,118,117, 85,113,100,242,102, 80,250,117,110,109, + 124,145, 29,124,162, 10,123,169, 4,123,171, 3,123,170, 3, + 124,167, 6,124,162, 10,124,157, 15,124,155, 16,124,149, 24, + 123,137, 41,118,114, 96,118,117, 85,122,132, 48,123,141, 34, + 124,143, 31,124,143, 31,123,139, 37,122,130, 53,118,114, 96, + 111, 96,248, 94, 70,242, 99, 75,247,109, 92,251,118,114, 96, + 120,122, 70,120,122, 70,118,114, 96,109, 92,251,107, 89,252, + 117,110,109,124,147, 26,124,166, 7,123,174, 1,122,177, 0, + 122,176, 0,123,172, 2,124,166, 7,124,167, 6,122,176, 0, + 121,182, 0,120,184, 0,119,187, 0,119,187, 0,120,183, 0, + 121,179, 0,120,184, 0,119,186, 0,119,186, 0,119,185, 0, + 119,186, 0,118,189, 0,118,189, 0,119,186, 0,120,182, 0, + 120,183, 0,121,181, 0,123,174, 1,124,160, 12,120,122, 70, + 30, 49,213, 94, 70,242,121,125, 63,124,143, 31,124,150, 22, + 124,152, 20,124,152, 20,124,154, 18,124,149, 24,124,150, 22, + 124,157, 15,123,168, 5,122,175, 0,122,176, 0,123,171, 3, + 124,158, 13,122,132, 48,121,125, 63,121,125, 63,121,127, 58, + 120,122, 70,117,110,109,118,117, 85,120,122, 70,121,127, 58, + 122,130, 53,124,145, 29,124,152, 20,124,152, 20,123,141, 34, + 119,119, 77,111, 96,248,107, 89,252,113,100,242,107, 89,252, + 109, 92,251,118,114, 96,121,127, 58,123,141, 34,124,154, 18, + 124,165, 8,123,174, 1,122,178, 0,121,179, 0,122,176, 0, + 121,180, 0,120,182, 0,120,182, 0,120,183, 0,120,182, 0, + 119,187, 0,115,195, 0,111,201, 0,110,203, 0,110,203, 0, + 112,200, 0,114,196, 0,113,198, 0,114,196, 0,117,192, 0, + 120,184, 0,123,170, 3,124,147, 26,114,104,197, 87, 65,237, + 69, 60,230,115,107,129,122,135, 44,124,145, 29,124,160, 12, + 123,169, 4,123,173, 1,123,172, 2,124,166, 7,123,171, 3, + 122,175, 0,122,176, 0,123,171, 3,124,160, 12,124,143, 31, + 118,114, 96,107, 89,252,119,119, 77,122,135, 44,124,157, 15, + 123,169, 4,122,175, 0,122,176, 0,122,175, 0,123,174, 1, + 123,173, 1,123,170, 3,124,165, 8,124,154, 18,122,135, 44, + 113,100,242, 30, 49,213, 69, 60,230,121,127, 58,124,160, 12, + 123,174, 1,121,181, 0,120,183, 0,121,180, 0,123,174, 1, + 124,164, 9,123,171, 3,122,177, 0,121,179, 0,120,184, 0, + 120,184, 0,122,178, 0,124,167, 6,124,157, 15,124,152, 20, + 124,147, 26,124,150, 22,124,150, 22,124,158, 13,124,165, 8, + 124,164, 9,124,158, 13,124,167, 6,122,175, 0,121,181, 0, + 121,181, 0,123,174, 1,124,157, 15,118,117, 85, 87, 65,237, + 115,107,129,122,135, 44,124,150, 22,124,161, 11,124,164, 9, + 124,158, 13,124,149, 24,124,147, 26,124,157, 15,124,166, 7, + 123,174, 1,122,178, 0,121,182, 0,119,186, 0,119,186, 0, + 119,186, 0,119,186, 0,118,188, 0,118,189, 0,118,188, 0, + 117,191, 0,116,192, 0,117,191, 0,119,186, 0,121,182, 0, + 122,176, 0,124,165, 8,124,155, 16,124,143, 31,120,122, 70, + 105, 84,252, 30, 2,128, 99, 75,247,119,119, 77,124,143, 31, + 124,155, 16,124,164, 9,123,171, 3,122,178, 0,121,181, 0, + 119,185, 0,119,186, 0,119,185, 0,121,182, 0,122,178, 0, + 123,174, 1,124,166, 7,124,150, 22,124,164, 9,123,173, 1, + 122,178, 0,121,179, 0,122,175, 0,124,164, 9,124,143, 31, + 115,107,129,113,100,242,102, 80,250, 30, 37,192, 30, 10,132, + 30, 31,180, 30, 49,213, 30, 43,203, 30, 2,128, 99, 75,247, + 121,127, 58,124,152, 20,124,165, 8,123,170, 3,123,170, 3, + 124,167, 6,124,162, 10,123,168, 5,123,171, 3,123,171, 3, + 124,167, 6,124,160, 12,124,145, 29,120,122, 70,111, 96,248, + 117,110,109,118,117, 85,118,117, 85,115,107,129,109, 92,251, + 105, 84,252,105, 84,252,105, 84,252,111, 96,248,117,110,109, + 119,119, 77,121,125, 63,119,119, 77,113,100,242, 69, 60,230, + 30, 2,128, 99, 75,247,121,127, 58,124,154, 18,124,166, 7, + 123,169, 4,124,167, 6,124,162, 10,124,155, 16,123,168, 5, + 121,179, 0,119,185, 0,119,187, 0,119,186, 0,121,182, 0, + 122,176, 0,123,168, 5,122,175, 0,122,178, 0,121,180, 0, + 121,181, 0,121,181, 0,120,184, 0,119,185, 0,120,182, 0, + 119,186, 0,118,188, 0,118,188, 0,120,184, 0,123,174, 1, + 124,152, 20,113,100,242, 30, 2,128,102, 80,250,118,114, 96, + 122,130, 53,124,147, 26,124,155, 16,124,158, 13,124,154, 18, + 123,139, 37,124,149, 24,124,162, 10,123,172, 2,122,175, 0, + 123,172, 2,124,162, 10,123,141, 34,111, 96,248,119,119, 77, + 121,127, 58,121,127, 58,123,137, 41,124,143, 31,124,143, 31, + 122,135, 44,121,125, 63,124,143, 31,124,152, 20,124,155, 16, + 124,150, 22,122,135, 44,114,104,197, 30, 49,213, 30, 2,128, + 30, 31,180, 69, 60,230,114,104,197,122,132, 48,124,152, 20, + 124,165, 8,123,171, 3,123,174, 1,121,181, 0,120,183, 0, + 119,186, 0,118,189, 0,117,190, 0,118,189, 0,119,186, 0, + 121,179, 0,118,189, 0,114,197, 0,110,203, 0,107,206, 0, + 107,207, 0,108,205, 0,112,200, 0,115,195, 0,115,194, 0, + 118,189, 0,120,182, 0,123,169, 4,124,155, 16,122,130, 53, + 105, 84,252, 30, 2,128, 99, 75,247,117,110,109,122,132, 48, + 124,152, 20,124,162, 10,124,166, 7,124,162, 10,124,150, 22, + 124,160, 12,124,165, 8,123,168, 5,123,168, 5,124,161, 11, + 124,143, 31,118,114, 96, 99, 75,247,120,122, 70,124,152, 20, + 123,168, 5,122,176, 0,121,179, 0,121,179, 0,122,177, 0, + 122,175, 0,122,176, 0,123,173, 1,123,168, 5,124,162, 10, + 124,150, 22,121,127, 58,105, 84,252, 30, 2,128,109, 92,251, + 124,143, 31,124,167, 6,122,177, 0,121,180, 0,121,179, 0, + 123,174, 1,124,166, 7,123,174, 1,121,181, 0,120,184, 0, + 120,184, 0,121,180, 0,122,175, 0,124,162, 10,123,141, 34, + 124,155, 16,124,161, 11,124,164, 9,123,168, 5,122,175, 0, + 122,178, 0,122,178, 0,123,171, 3,122,178, 0,120,182, 0, + 120,183, 0,121,179, 0,123,169, 4,124,147, 26,111, 96,248, + 30, 2,128,109, 92,251,123,141, 34,124,164, 9,123,173, 1, + 122,175, 0,123,172, 2,124,166, 7,124,160, 12,124,167, 6, + 123,169, 4,123,172, 2,121,179, 0,119,185, 0,118,188, 0, + 118,188, 0,120,183, 0,117,191, 0,115,194, 0,115,195, 0, + 115,194, 0,115,195, 0,115,195, 0,116,193, 0,119,187, 0, + 119,185, 0,122,178, 0,124,166, 7,124,154, 18,123,139, 37, + 118,117, 85, 94, 70,242, 94, 70,242,109, 92,251,118,114, 96, + 119,119, 77,124,143, 31,124,157, 15,124,167, 6,123,174, 1, + 121,181, 0,119,186, 0,118,188, 0,119,186, 0,120,184, 0, + 122,178, 0,123,172, 2,124,165, 8,124,158, 13,124,160, 12, + 123,169, 4,123,174, 1,123,174, 1,123,168, 5,124,155, 16, + 122,130, 53,107, 89,252, 87, 65,237, 87, 65,237, 94, 70,242, + 87, 65,237, 69, 60,230, 69, 60,230, 94, 70,242,111, 96,248, + 121,125, 63,124,145, 29,124,155, 16,124,165, 8,123,168, 5, + 124,167, 6,124,164, 9,123,169, 4,122,175, 0,122,178, 0, + 122,178, 0,122,176, 0,123,170, 3,124,161, 11,124,147, 26, + 122,132, 48,119,119, 77,118,114, 96,117,110,109,118,114, 96, + 119,119, 77,121,127, 58,123,137, 41,124,143, 31,123,141, 34, + 122,135, 44,122,135, 44,123,139, 37,122,135, 44,120,122, 70, + 114,104,197,114,104,197,114,104,197,122,132, 48,124,145, 29, + 124,147, 26,124,150, 22,124,149, 24,123,141, 34,124,154, 18, + 123,169, 4,121,179, 0,119,185, 0,118,188, 0,119,187, 0, + 120,183, 0,122,178, 0,123,173, 1,121,180, 0,121,181, 0, + 122,178, 0,121,179, 0,121,180, 0,121,182, 0,120,184, 0, + 119,186, 0,117,191, 0,116,193, 0,116,192, 0,118,189, 0, + 121,181, 0,124,165, 8,122,130, 53, 30, 55,222, 30, 37,192, + 109, 92,251,121,125, 63,124,143, 31,124,152, 20,124,154, 18, + 124,150, 22,123,137, 41,124,147, 26,124,157, 15,124,167, 6, + 123,171, 3,123,169, 4,124,158, 13,123,137, 41,121,127, 58, + 122,130, 53,123,141, 34,124,150, 22,124,160, 12,124,164, 9, + 124,164, 9,124,160, 12,124,145, 29,124,143, 31,124,150, 22, + 124,155, 16,124,152, 20,123,141, 34,121,125, 63,115,107,129, + 113,100,242,117,110,109,120,122, 70,122,132, 48,124,145, 29, + 124,162, 10,123,172, 2,121,179, 0,120,182, 0,119,186, 0, + 117,190, 0,116,193, 0,114,196, 0,114,197, 0,114,196, 0, + 116,193, 0,117,192, 0,116,193, 0,113,198, 0,109,204, 0, + 107,207, 0,106,207, 0,108,206, 0,110,202, 0,113,198, 0, + 116,193, 0,118,189, 0,120,184, 0,122,178, 0,124,167, 6, + 124,150, 22,119,119, 77,107, 89,252,109, 92,251,115,107,129, + 122,135, 44,124,150, 22,124,158, 13,124,158, 13,124,152, 20, + 122,135, 44,124,149, 24,124,160, 12,124,165, 8,124,162, 10, + 124,152, 20,122,135, 44,121,127, 58,122,132, 48,124,157, 15, + 123,171, 3,121,180, 0,120,184, 0,119,185, 0,120,184, 0, + 121,182, 0,119,186, 0,119,187, 0,120,184, 0,122,178, 0, + 123,169, 4,124,162, 10,124,149, 24,121,125, 63, 94, 70,242, + 30, 26,168,117,110,109,124,150, 22,124,167, 6,123,174, 1, + 122,176, 0,123,173, 1,124,167, 6,123,171, 3,121,180, 0, + 119,185, 0,119,186, 0,120,184, 0,121,179, 0,123,170, 3, + 124,162, 10,124,160, 12,124,167, 6,123,171, 3,122,178, 0, + 120,184, 0,119,186, 0,119,186, 0,121,182, 0,120,184, 0, + 118,188, 0,118,188, 0,119,185, 0,122,177, 0,124,160, 12, + 120,122, 70, 69, 60,230,118,114, 96,124,154, 18,123,172, 2, + 121,181, 0,120,183, 0,121,182, 0,122,178, 0,123,174, 1, + 123,174, 1,122,177, 0,121,179, 0,120,184, 0,118,188, 0, + 117,190, 0,117,192, 0,116,192, 0,115,195, 0,113,198, 0, + 112,199, 0,113,199, 0,113,198, 0,114,197, 0,116,193, 0, + 118,189, 0,119,186, 0,122,178, 0,124,165, 8,124,143, 31, + 122,132, 48,121,127, 58,117,110,109,120,122, 70,118,114, 96, + 122,130, 53,123,137, 41,123,139, 37,124,143, 31,124,161, 11, + 123,171, 3,121,179, 0,120,184, 0,119,186, 0,119,185, 0, + 120,182, 0,122,178, 0,123,172, 2,124,166, 7,124,160, 12, + 124,157, 15,124,164, 9,124,167, 6,124,165, 8,124,155, 16, + 122,135, 44,113,100,242, 87, 65,237,105, 84,252,111, 96,248, + 114,104,197,115,107,129,117,110,109,118,117, 85,121,127, 58, + 124,143, 31,124,158, 13,123,168, 5,123,173, 1,123,173, 1, + 123,170, 3,124,167, 6,124,164, 9,123,171, 3,122,176, 0, + 121,179, 0,121,180, 0,122,178, 0,123,174, 1,124,167, 6, + 124,158, 13,124,149, 24,123,141, 34,123,137, 41,123,137, 41, + 123,141, 34,124,149, 24,124,157, 15,124,164, 9,123,168, 5, + 123,170, 3,123,169, 4,124,166, 7,124,164, 9,124,161, 11, + 124,160, 12,124,160, 12,124,160, 12,124,158, 13,124,152, 20, + 124,154, 18,124,149, 24,122,135, 44,114,104,197,120,122, 70, + 124,150, 22,124,166, 7,122,175, 0,121,182, 0,120,184, 0, + 120,182, 0,121,179, 0,123,174, 1,122,178, 0,120,184, 0, + 120,184, 0,120,182, 0,122,178, 0,121,182, 0,120,184, 0, + 118,188, 0,117,191, 0,115,195, 0,114,197, 0,114,197, 0, + 116,193, 0,119,186, 0,123,172, 2,124,145, 29,109, 92,251, + 30, 55,222, 30, 55,222,111, 96,248,120,122, 70,122,135, 44, + 123,139, 37,122,135, 44,121,127, 58,124,143, 31,124,157, 15, + 124,165, 8,124,167, 6,124,165, 8,124,158, 13,124,150, 22, + 124,143, 31,124,147, 26,124,157, 15,124,165, 8,123,172, 2, + 122,176, 0,122,177, 0,122,175, 0,124,167, 6,124,160, 12, + 124,165, 8,124,167, 6,124,166, 7,124,162, 10,124,155, 16, + 124,149, 24,124,147, 26,124,152, 20,124,158, 13,124,161, 11, + 124,165, 8,124,166, 7,122,175, 0,120,182, 0,119,187, 0, + 117,191, 0,114,196, 0,112,200, 0,110,202, 0,110,202, 0, + 111,202, 0,112,200, 0,110,202, 0,110,203, 0,110,202, 0, + 110,202, 0,108,205, 0,108,206, 0,109,204, 0,112,200, 0, + 114,196, 0,116,193, 0,117,190, 0,119,186, 0,121,182, 0, + 123,174, 1,124,162, 10,123,141, 34,124,143, 31,124,143, 31, + 124,147, 26,124,154, 18,124,160, 12,124,162, 10,124,162, 10, + 124,158, 13,124,147, 26,123,139, 37,124,149, 24,124,150, 22, + 124,145, 29,124,143, 31,124,147, 26,124,145, 29,124,165, 8, + 122,176, 0,120,184, 0,118,189, 0,117,191, 0,117,191, 0, + 118,189, 0,118,189, 0,116,192, 0,116,193, 0,117,191, 0, + 119,186, 0,122,178, 0,123,168, 5,124,160, 12,124,143, 31, + 117,110,109, 30, 31,180, 30, 55,222,120,122, 70,124,150, 22, + 124,164, 9,123,169, 4,123,169, 4,124,167, 6,124,166, 7, + 122,177, 0,120,184, 0,119,187, 0,119,187, 0,120,184, 0, + 121,180, 0,122,175, 0,123,173, 1,122,176, 0,121,181, 0, + 119,186, 0,118,189, 0,116,192, 0,116,192, 0,117,190, 0, + 119,186, 0,118,189, 0,117,191, 0,118,188, 0,121,182, 0, + 123,169, 4,124,143, 31,118,114, 96,122,135, 44,124,165, 8, + 121,179, 0,119,186, 0,118,189, 0,118,188, 0,119,186, 0, + 120,183, 0,120,183, 0,119,185, 0,118,188, 0,117,192, 0, + 115,195, 0,114,197, 0,113,199, 0,113,199, 0,112,200, 0, + 110,202, 0,110,203, 0,110,202, 0,112,200, 0,113,198, 0, + 115,194, 0,118,188, 0,120,184, 0,122,175, 0,124,161, 11, + 124,155, 16,124,155, 16,124,152, 20,124,143, 31,124,154, 18, + 124,145, 29,123,139, 37,124,147, 26,124,150, 22,124,152, 20, + 124,152, 20,124,164, 9,123,174, 1,121,179, 0,121,180, 0, + 122,178, 0,122,175, 0,123,171, 3,124,166, 7,124,161, 11, + 124,157, 15,124,154, 18,124,154, 18,124,154, 18,124,147, 26, + 121,127, 58,113,100,242,105, 84,252,107, 89,252,115,107,129, + 118,114, 96,119,119, 77,121,125, 63,122,132, 48,123,141, 34, + 124,154, 18,124,164, 9,123,173, 1,121,179, 0,120,182, 0, + 120,182, 0,121,179, 0,123,173, 1,124,167, 6,123,170, 3, + 122,175, 0,122,178, 0,122,178, 0,122,178, 0,123,174, 1, + 123,169, 4,124,164, 9,124,157, 15,124,152, 20,124,150, 22, + 124,152, 20,124,155, 16,124,162, 10,123,170, 3,122,176, 0, + 121,182, 0,120,184, 0,119,185, 0,119,185, 0,120,184, 0, + 120,184, 0,120,184, 0,120,183, 0,120,183, 0,121,181, 0, + 122,175, 0,124,162, 10,124,150, 22,122,135, 44,114,104,197, + 120,122, 70,123,139, 37,124,155, 16,124,166, 7,123,171, 3, + 123,174, 1,123,173, 1,123,171, 3,123,174, 1,120,182, 0, + 119,186, 0,119,186, 0,120,184, 0,121,181, 0,120,182, 0, + 119,186, 0,117,191, 0,115,195, 0,113,199, 0,112,200, 0, + 112,199, 0,114,196, 0,118,188, 0,122,176, 0,124,155, 16, + 119,119, 77, 99, 75,247, 94, 70,242, 94, 70,242,102, 80,250, + 113,100,242,115,107,129,114,104,197,119,119, 77,123,139, 37, + 124,155, 16,124,164, 9,124,167, 6,124,167, 6,124,162, 10, + 124,155, 16,124,152, 20,124,158, 13,124,165, 8,123,173, 1, + 121,180, 0,120,184, 0,119,186, 0,120,184, 0,121,179, 0, + 123,170, 3,122,175, 0,122,177, 0,122,176, 0,123,173, 1, + 123,170, 3,124,167, 6,123,168, 5,123,172, 2,122,175, 0, + 122,177, 0,122,178, 0,122,177, 0,122,175, 0,120,183, 0, + 118,189, 0,115,195, 0,112,200, 0,109,204, 0,106,207, 0, + 106,208, 0,106,207, 0,106,208, 0,104,210, 0,103,211, 0, + 105,209, 0,107,206, 0,111,201, 0,111,201, 0,112,199, 0, + 114,196, 0,116,192, 0,117,191, 0,118,189, 0,119,187, 0, + 120,183, 0,122,178, 0,124,167, 6,124,167, 6,124,167, 6, + 124,166, 7,124,167, 6,123,169, 4,123,170, 3,123,170, 3, + 123,169, 4,124,165, 8,124,155, 16,123,137, 41,121,127, 58, + 120,122, 70,122,130, 53,124,147, 26,124,154, 18,123,171, 3, + 121,182, 0,119,187, 0,116,192, 0,115,195, 0,114,197, 0, + 114,196, 0,116,193, 0,117,192, 0,115,195, 0,114,196, 0, + 115,194, 0,117,191, 0,119,185, 0,122,175, 0,124,164, 9, + 124,152, 20,122,130, 53,105, 84,252, 30, 20,155, 87, 65,237, + 118,117, 85,124,143, 31,124,157, 15,124,164, 9,124,166, 7, + 124,162, 10,123,171, 3,121,180, 0,119,186, 0,118,188, 0, + 118,188, 0,119,186, 0,120,182, 0,121,181, 0,120,182, 0, + 119,186, 0,118,189, 0,116,193, 0,114,196, 0,113,198, 0, + 114,197, 0,116,192, 0,117,191, 0,117,192, 0,117,190, 0, + 119,185, 0,122,176, 0,124,158, 13,124,147, 26,124,154, 18, + 123,174, 1,119,186, 0,117,192, 0,115,194, 0,116,193, 0, + 117,192, 0,118,189, 0,116,192, 0,115,194, 0,114,196, 0, + 113,199, 0,111,201, 0,109,203, 0,109,204, 0,109,204, 0, + 108,205, 0,107,207, 0,107,207, 0,108,205, 0,111,202, 0, + 113,199, 0,115,195, 0,118,188, 0,122,178, 0,123,173, 1, + 123,173, 1,123,171, 3,123,171, 3,123,169, 4,124,165, 8, + 123,173, 1,124,164, 9,124,154, 18,124,150, 22,124,155, 16, + 124,160, 12,124,161, 11,124,161, 11,124,160, 12,124,166, 7, + 124,167, 6,124,164, 9,124,160, 12,124,155, 16,124,150, 22, + 124,147, 26,124,145, 29,123,141, 34,122,135, 44,122,130, 53, + 118,114, 96,115,107,129,111, 96,248,105, 84,252,117,110,109, + 119,119, 77,120,122, 70,121,125, 63,122,132, 48,123,139, 37, + 124,152, 20,124,164, 9,123,172, 2,121,180, 0,120,184, 0, + 119,187, 0,119,187, 0,119,185, 0,121,180, 0,123,174, 1, + 123,172, 2,123,174, 1,122,176, 0,122,176, 0,122,175, 0, + 123,173, 1,123,169, 4,124,165, 8,124,161, 11,124,161, 11, + 124,164, 9,124,166, 7,123,169, 4,123,171, 3,122,177, 0, + 120,183, 0,118,189, 0,116,192, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,114,196, 0,114,196, 0,115,195, 0, + 116,193, 0,119,187, 0,122,176, 0,124,155, 16,122,135, 44, + 123,137, 41,124,145, 29,124,143, 31,122,135, 44,124,147, 26, + 124,154, 18,124,157, 15,124,157, 15,123,168, 5,122,177, 0, + 120,183, 0,119,186, 0,119,186, 0,120,183, 0,121,180, 0, + 121,182, 0,119,187, 0,116,192, 0,114,197, 0,111,201, 0, + 110,202, 0,111,201, 0,114,197, 0,117,190, 0,121,179, 0, + 124,162, 10,124,145, 29,119,119, 77,105, 84,252,102, 80,250, + 99, 75,247, 30, 55,222, 30, 43,203, 87, 65,237,114,104,197, + 122,130, 53,124,150, 22,124,161, 11,124,166, 7,124,166, 7, + 124,164, 9,124,158, 13,124,158, 13,123,169, 4,123,172, 2, + 122,178, 0,119,185, 0,118,189, 0,117,191, 0,117,191, 0, + 119,187, 0,121,181, 0,120,182, 0,120,183, 0,120,182, 0, + 121,181, 0,122,178, 0,122,177, 0,121,180, 0,120,184, 0, + 119,186, 0,119,186, 0,119,186, 0,120,184, 0,121,182, 0, + 120,182, 0,117,191, 0,114,197, 0,109,204, 0,105,209, 0, + 102,212, 0,101,213, 0,102,212, 0,100,214, 0, 98,216, 0, + 98,217, 0,100,215, 0,103,211, 0,108,205, 0,114,197, 0, + 116,192, 0,118,189, 0,119,187, 0,119,186, 0,119,186, 0, + 119,185, 0,120,182, 0,122,177, 0,122,177, 0,122,178, 0, + 122,178, 0,122,177, 0,122,176, 0,122,176, 0,122,176, 0, + 123,174, 1,123,172, 2,123,168, 5,124,160, 12,124,145, 29, + 120,122, 70,117,110,109,123,139, 37,124,160, 12,122,175, 0, + 120,184, 0,117,190, 0,116,193, 0,114,197, 0,112,199, 0, + 112,200, 0,112,199, 0,114,196, 0,116,193, 0,114,196, 0, + 114,197, 0,114,197, 0,115,194, 0,117,190, 0,120,183, 0, + 123,170, 3,124,155, 16,123,141, 34,118,114, 96, 94, 70,242, + 87, 65,237, 94, 70,242,115,107,129,122,135, 44,124,152, 20, + 124,161, 11,124,164, 9,124,165, 8,122,176, 0,120,183, 0, + 119,187, 0,118,189, 0,118,188, 0,119,186, 0,119,185, 0, + 119,185, 0,119,187, 0,117,191, 0,115,194, 0,113,198, 0, + 111,201, 0,111,201, 0,113,199, 0,116,193, 0,116,193, 0, + 117,192, 0,118,188, 0,121,182, 0,123,170, 3,124,167, 6, + 123,170, 3,121,181, 0,117,191, 0,114,196, 0,113,199, 0, + 113,198, 0,114,197, 0,114,196, 0,112,200, 0,111,202, 0, + 111,202, 0,109,204, 0,107,206, 0,106,208, 0,106,208, 0, + 106,207, 0,105,209, 0,103,211, 0,104,210, 0,106,207, 0, + 110,203, 0,113,198, 0,115,194, 0,118,188, 0,120,184, 0, + 120,184, 0,120,183, 0,120,182, 0,121,182, 0,121,180, 0, + 122,178, 0,120,184, 0,121,179, 0,123,170, 3,124,160, 12, + 124,155, 16,124,160, 12,124,162, 10,124,162, 10,124,157, 15, + 124,143, 31,122,132, 48,121,127, 58,119,119, 77,118,114, 96, + 118,114, 96,118,114, 96,118,117, 85,118,114, 96,115,107,129, + 118,114, 96,117,110,109,113,100,242,107, 89,252,117,110,109, + 121,125, 63,121,125, 63,121,127, 58,123,141, 34,124,149, 24, + 124,149, 24,124,152, 20,124,166, 7,122,175, 0,121,182, 0, + 119,186, 0,118,189, 0,118,189, 0,118,188, 0,120,184, 0, + 121,180, 0,122,176, 0,121,180, 0,121,182, 0,120,182, 0, + 120,182, 0,121,180, 0,122,178, 0,122,175, 0,123,172, 2, + 123,172, 2,123,174, 1,122,178, 0,121,181, 0,120,183, 0, + 120,184, 0,118,188, 0,116,193, 0,113,198, 0,111,202, 0, + 109,203, 0,109,204, 0,108,205, 0,109,204, 0,109,203, 0, + 110,202, 0,112,200, 0,115,194, 0,120,184, 0,124,166, 7, + 124,145, 29,124,155, 16,124,160, 12,124,154, 18,123,137, 41, + 115,107,129,120,122, 70,124,149, 24,124,162, 10,123,171, 3, + 122,177, 0,121,181, 0,120,183, 0,121,182, 0,121,179, 0, + 122,177, 0,121,179, 0,119,186, 0,116,192, 0,113,198, 0, + 111,202, 0,110,202, 0,111,201, 0,114,197, 0,117,191, 0, + 121,182, 0,123,174, 1,124,166, 7,124,149, 24,121,127, 58, + 118,114, 96,111, 96,248, 94, 70,242, 30, 31,180, 30, 37,192, + 94, 70,242,117,110,109,123,139, 37,124,154, 18,124,161, 11, + 124,164, 9,124,162, 10,124,160, 12,124,167, 6,122,176, 0, + 121,179, 0,121,182, 0,118,188, 0,116,192, 0,115,194, 0, + 115,194, 0,117,191, 0,119,186, 0,118,188, 0,118,188, 0, + 119,187, 0,119,186, 0,120,184, 0,119,186, 0,118,188, 0, + 117,191, 0,116,193, 0,116,193, 0,116,193, 0,116,192, 0, + 117,191, 0,118,189, 0,115,194, 0,112,199, 0,107,207, 0, + 102,213, 0, 98,216, 0, 97,218, 0, 97,217, 0, 96,219, 0, + 94,220, 0, 94,221, 0, 97,218, 0,101,214, 0,106,208, 0, + 112,200, 0,117,191, 0,120,183, 0,121,180, 0,121,180, 0, + 121,181, 0,121,181, 0,121,179, 0,121,182, 0,120,182, 0, + 120,182, 0,121,182, 0,121,181, 0,121,179, 0,122,178, 0, + 122,176, 0,123,174, 1,123,172, 2,123,168, 5,124,161, 11, + 124,149, 24,122,130, 53,124,147, 26,124,165, 8,122,177, 0, + 119,185, 0,117,191, 0,115,194, 0,114,197, 0,112,199, 0, + 111,201, 0,111,202, 0,111,201, 0,113,198, 0,115,194, 0, + 116,193, 0,114,196, 0,114,197, 0,114,197, 0,116,193, 0, + 118,188, 0,121,179, 0,124,164, 9,124,145, 29,122,130, 53, + 117,110,109,118,117, 85,118,117, 85,118,114, 96,118,117, 85, + 122,135, 44,124,155, 16,124,162, 10,124,162, 10,123,169, 4, + 122,178, 0,120,184, 0,118,188, 0,118,189, 0,118,188, 0, + 119,185, 0,119,185, 0,119,186, 0,117,191, 0,116,193, 0, + 113,198, 0,111,202, 0,109,203, 0,110,203, 0,112,200, 0, + 115,195, 0,115,194, 0,117,192, 0,119,187, 0,121,179, 0, + 122,178, 0,121,181, 0,119,186, 0,115,195, 0,111,201, 0, + 110,203, 0,110,203, 0,111,202, 0,109,204, 0,106,207, 0, + 106,208, 0,106,208, 0,105,209, 0,103,211, 0,103,212, 0, + 103,212, 0,104,210, 0,102,213, 0,101,214, 0,102,213, 0, + 105,209, 0,109,204, 0,114,197, 0,116,193, 0,116,192, 0, + 116,192, 0,117,192, 0,117,191, 0,117,191, 0,117,191, 0, + 117,190, 0,119,187, 0,117,191, 0,118,189, 0,120,184, 0, + 123,174, 1,124,164, 9,124,158, 13,124,158, 13,124,157, 15, + 124,149, 24,121,127, 58,107, 89,252, 30, 31,180, 87, 65,237, + 99, 75,247, 30, 55,222, 30, 43,203,105, 84,252,115,107,129, + 119,119, 77,120,122, 70,119,119, 77,118,114, 96,121,125, 63, + 124,143, 31,124,149, 24,124,143, 31,124,155, 16,124,162, 10, + 124,164, 9,124,161, 11,124,158, 13,124,162, 10,123,173, 1, + 121,181, 0,119,186, 0,118,188, 0,118,189, 0,118,188, 0, + 119,186, 0,120,182, 0,121,179, 0,120,183, 0,119,186, 0, + 118,188, 0,118,188, 0,119,186, 0,120,184, 0,121,181, 0, + 122,178, 0,122,178, 0,121,182, 0,119,185, 0,118,189, 0, + 117,191, 0,116,192, 0,116,193, 0,114,197, 0,110,202, 0, + 108,206, 0,106,208, 0,105,209, 0,105,209, 0,105,209, 0, + 106,208, 0,107,207, 0,109,204, 0,113,198, 0,118,188, 0, + 123,174, 1,124,166, 7,123,168, 5,123,168, 5,124,160, 12, + 123,141, 34,122,130, 53,122,130, 53,124,152, 20,124,164, 9, + 123,169, 4,123,173, 1,122,176, 0,122,177, 0,122,175, 0, + 122,177, 0,122,178, 0,120,182, 0,119,185, 0,117,191, 0, + 114,197, 0,111,201, 0,110,202, 0,111,201, 0,114,197, 0, + 117,192, 0,119,186, 0,120,184, 0,122,178, 0,124,167, 6, + 124,155, 16,124,149, 24,123,139, 37,121,125, 63,111, 96,248, + 105, 84,252,102, 80,250,107, 89,252,118,114, 96,123,139, 37, + 124,152, 20,124,158, 13,124,160, 12,124,160, 12,123,171, 3, + 121,180, 0,120,183, 0,120,184, 0,118,189, 0,116,193, 0, + 114,196, 0,114,196, 0,116,193, 0,117,191, 0,117,192, 0, + 117,192, 0,117,191, 0,118,189, 0,117,191, 0,115,194, 0, + 114,197, 0,113,198, 0,112,199, 0,112,200, 0,111,201, 0, + 111,201, 0,112,200, 0,114,197, 0,114,197, 0,111,201, 0, + 105,209, 0, 99,216, 0, 95,220, 0, 92,222, 0, 92,222, 0, + 94,221, 0, 92,223, 0, 92,223, 0, 94,220, 0, 99,216, 0, + 105,209, 0,110,202, 0,114,196, 0,117,192, 0,120,184, 0, + 121,179, 0,121,181, 0,120,182, 0,120,184, 0,120,184, 0, + 120,184, 0,120,182, 0,121,181, 0,121,179, 0,122,178, 0, + 122,175, 0,123,172, 2,123,170, 3,124,167, 6,124,164, 9, + 124,157, 15,124,143, 31,124,145, 29,124,164, 9,122,175, 0, + 120,184, 0,118,189, 0,116,193, 0,114,196, 0,114,197, 0, + 113,199, 0,111,201, 0,111,201, 0,111,201, 0,113,198, 0, + 115,195, 0,117,191, 0,116,193, 0,114,196, 0,114,197, 0, + 114,196, 0,117,191, 0,120,184, 0,123,171, 3,124,152, 20, + 123,141, 34,122,132, 48,124,145, 29,124,149, 24,124,147, 26, + 123,139, 37,121,125, 63,124,145, 29,124,160, 12,124,165, 8, + 124,165, 8,123,173, 1,121,181, 0,119,185, 0,119,187, 0, + 119,186, 0,120,183, 0,119,185, 0,117,191, 0,115,195, 0, + 114,197, 0,114,197, 0,111,201, 0,109,204, 0,108,205, 0, + 109,203, 0,112,200, 0,114,197, 0,114,196, 0,116,192, 0, + 119,187, 0,119,186, 0,118,188, 0,117,191, 0,112,199, 0, + 109,204, 0,107,207, 0,106,207, 0,107,207, 0,105,209, 0, + 102,213, 0,101,214, 0,101,214, 0,102,213, 0,100,214, 0, + 100,215, 0,100,214, 0,102,213, 0, 99,215, 0, 98,216, 0, + 100,215, 0,103,211, 0,108,205, 0,114,197, 0,115,194, 0, + 113,198, 0,113,198, 0,113,198, 0,114,196, 0,113,198, 0, + 113,198, 0,114,197, 0,115,194, 0,115,195, 0,115,194, 0, + 117,192, 0,119,186, 0,122,178, 0,124,166, 7,124,162, 10, + 124,155, 16,123,141, 34,117,110,109,111, 96,248,114,104,197, + 115,107,129,113,100,242,109, 92,251, 99, 75,247,105, 84,252, + 118,114, 96,121,127, 58,122,132, 48,122,135, 44,122,135, 44, + 124,152, 20,124,161, 11,124,162, 10,124,164, 9,123,173, 1, + 122,178, 0,121,179, 0,122,175, 0,124,167, 6,124,161, 11, + 123,168, 5,122,177, 0,120,182, 0,119,186, 0,119,187, 0, + 119,187, 0,119,185, 0,120,182, 0,121,179, 0,120,184, 0, + 118,188, 0,118,189, 0,117,190, 0,118,189, 0,119,186, 0, + 120,184, 0,121,181, 0,121,182, 0,119,185, 0,118,189, 0, + 116,193, 0,114,196, 0,113,198, 0,112,199, 0,112,199, 0, + 108,205, 0,105,209, 0,103,212, 0,102,213, 0,102,213, 0, + 102,212, 0,104,210, 0,105,209, 0,107,207, 0,111,201, 0, + 117,192, 0,119,186, 0,121,179, 0,122,177, 0,123,174, 1, + 124,165, 8,124,157, 15,124,158, 13,124,155, 16,124,150, 22, + 124,160, 12,124,164, 9,124,164, 9,124,166, 7,124,166, 7, + 123,173, 1,121,180, 0,119,185, 0,118,189, 0,117,191, 0, + 117,191, 0,115,195, 0,112,199, 0,111,201, 0,112,200, 0, + 113,198, 0,116,193, 0,117,191, 0,118,189, 0,120,184, 0, + 122,176, 0,123,173, 1,123,171, 3,124,167, 6,124,158, 13, + 123,141, 34,119,119, 77,118,114, 96,115,107,129,117,110,109, + 121,125, 63,123,137, 41,124,150, 22,124,157, 15,124,160, 12, + 123,171, 3,121,180, 0,120,184, 0,119,185, 0,117,190, 0, + 115,194, 0,114,196, 0,114,196, 0,116,193, 0,115,194, 0, + 114,196, 0,115,195, 0,115,194, 0,116,193, 0,114,196, 0, + 112,199, 0,111,201, 0,110,203, 0,109,204, 0,108,206, 0, + 107,206, 0,107,206, 0,108,205, 0,110,202, 0,113,198, 0, + 110,203, 0,102,212, 0, 96,219, 0, 91,223, 0, 89,226, 0, + 89,226, 0, 91,224, 0, 91,223, 0, 91,223, 0, 94,221, 0, + 98,217, 0,103,211, 0,108,205, 0,111,202, 0,114,197, 0, + 118,189, 0,120,184, 0,119,185, 0,119,186, 0,119,186, 0, + 120,184, 0,121,182, 0,122,178, 0,122,177, 0,122,175, 0, + 123,172, 2,123,173, 1,123,171, 3,124,166, 7,124,158, 13, + 124,154, 18,124,145, 29,122,132, 48,124,150, 22,123,168, 5, + 122,178, 0,119,186, 0,117,190, 0,116,193, 0,116,193, 0, + 115,194, 0,114,196, 0,113,198, 0,113,198, 0,113,198, 0, + 114,196, 0,116,193, 0,118,189, 0,117,190, 0,116,193, 0, + 114,196, 0,115,195, 0,116,192, 0,119,186, 0,123,174, 1, + 124,157, 15,124,149, 24,124,149, 24,124,162, 10,124,167, 6, + 124,167, 6,124,160, 12,124,149, 24,123,141, 34,124,155, 16, + 124,165, 8,123,168, 5,123,168, 5,122,176, 0,121,182, 0, + 120,184, 0,120,182, 0,121,182, 0,118,189, 0,115,195, 0, + 112,199, 0,111,201, 0,111,201, 0,112,200, 0,109,203, 0, + 108,205, 0,108,205, 0,110,202, 0,112,200, 0,112,200, 0, + 113,198, 0,116,193, 0,117,191, 0,116,192, 0,115,194, 0, + 109,203, 0,105,209, 0,103,212, 0,102,212, 0,103,212, 0, + 100,214, 0, 97,217, 0, 97,218, 0, 97,217, 0, 98,217, 0, + 97,218, 0, 97,218, 0, 98,216, 0,100,215, 0, 98,217, 0, + 97,217, 0, 99,216, 0,102,212, 0,108,206, 0,113,198, 0, + 114,197, 0,111,201, 0,111,202, 0,111,201, 0,111,201, 0, + 110,203, 0,109,203, 0,110,202, 0,112,199, 0,114,197, 0, + 113,198, 0,114,196, 0,116,192, 0,119,187, 0,121,180, 0, + 123,170, 3,124,162, 10,124,152, 20,124,150, 22,124,154, 18, + 124,157, 15,124,157, 15,124,150, 22,123,139, 37,120,122, 70, + 113,100,242,120,122, 70,123,139, 37,124,149, 24,124,152, 20, + 124,157, 15,124,165, 8,123,170, 3,123,171, 3,121,179, 0, + 119,186, 0,118,189, 0,118,189, 0,119,185, 0,122,177, 0, + 123,168, 5,124,161, 11,123,171, 3,122,178, 0,121,182, 0, + 120,183, 0,120,183, 0,121,181, 0,122,178, 0,122,175, 0, + 121,181, 0,119,186, 0,118,188, 0,118,189, 0,118,188, 0, + 119,185, 0,120,182, 0,121,179, 0,121,180, 0,120,184, 0, + 118,189, 0,115,194, 0,113,198, 0,111,201, 0,110,202, 0, + 110,202, 0,106,208, 0,102,212, 0,100,214, 0,100,215, 0, + 100,215, 0,101,214, 0,102,213, 0,104,210, 0,106,208, 0, + 110,203, 0,112,199, 0,115,194, 0,119,187, 0,120,184, 0, + 121,179, 0,123,170, 3,123,172, 2,123,172, 2,124,167, 6, + 124,155, 16,124,147, 26,124,147, 26,124,145, 29,124,147, 26, + 124,164, 9,122,175, 0,120,184, 0,117,191, 0,115,194, 0, + 114,196, 0,115,195, 0,116,193, 0,113,198, 0,112,200, 0, + 111,201, 0,112,199, 0,114,197, 0,116,193, 0,117,192, 0, + 118,188, 0,119,187, 0,119,187, 0,119,186, 0,120,184, 0, + 121,179, 0,123,170, 3,124,157, 15,122,132, 48,121,125, 63, + 118,114, 96,121,127, 58,123,139, 37,124,143, 31,124,154, 18, + 124,160, 12,124,165, 8,122,177, 0,121,182, 0,119,185, 0, + 117,190, 0,115,194, 0,114,196, 0,115,195, 0,116,192, 0, + 114,196, 0,113,198, 0,113,198, 0,114,197, 0,114,197, 0, + 113,198, 0,111,201, 0,110,203, 0,106,207, 0,105,209, 0, + 104,210, 0,105,209, 0,105,209, 0,106,208, 0,108,206, 0, + 111,201, 0,108,206, 0,100,214, 0, 94,221, 0, 89,226, 0, + 86,228, 0, 86,228, 0, 88,226, 0, 91,223, 0, 91,223, 0, + 94,221, 0, 98,217, 0,103,212, 0,107,207, 0,110,203, 0, + 113,198, 0,117,190, 0,118,189, 0,118,189, 0,118,188, 0, + 119,186, 0,120,183, 0,122,178, 0,123,173, 1,123,170, 3, + 122,176, 0,121,180, 0,121,181, 0,122,178, 0,123,173, 1, + 124,164, 9,124,147, 26,121,125, 63,118,117, 85,124,149, 24, + 124,166, 7,122,177, 0,120,184, 0,118,188, 0,118,189, 0, + 118,189, 0,118,189, 0,117,191, 0,116,192, 0,116,193, 0, + 116,192, 0,117,191, 0,119,187, 0,120,182, 0,119,186, 0, + 117,191, 0,116,193, 0,116,193, 0,118,189, 0,120,183, 0, + 123,171, 3,124,154, 18,124,155, 16,124,162, 10,123,173, 1, + 122,177, 0,122,177, 0,123,171, 3,124,161, 11,124,149, 24, + 124,160, 12,124,164, 9,123,169, 4,123,171, 3,123,172, 2, + 122,178, 0,121,180, 0,122,178, 0,119,185, 0,116,192, 0, + 113,198, 0,110,202, 0,109,204, 0,109,204, 0,110,203, 0, + 111,202, 0,109,204, 0,109,204, 0,109,203, 0,109,204, 0, + 109,204, 0,110,202, 0,112,199, 0,115,194, 0,115,195, 0, + 113,198, 0,107,207, 0,102,213, 0, 98,216, 0, 97,217, 0, + 97,217, 0, 98,217, 0, 95,220, 0, 94,221, 0, 94,220, 0, + 94,221, 0, 93,221, 0, 94,220, 0, 96,219, 0, 99,215, 0, + 98,217, 0, 97,217, 0, 99,216, 0,102,212, 0,107,206, 0, + 109,204, 0,110,202, 0,111,202, 0,110,202, 0,109,204, 0, + 107,206, 0,106,207, 0,106,207, 0,108,206, 0,110,202, 0, + 113,198, 0,113,199, 0,113,198, 0,114,196, 0,116,193, 0, + 118,188, 0,120,183, 0,122,177, 0,123,173, 1,123,173, 1, + 122,175, 0,122,176, 0,123,174, 1,123,170, 3,124,162, 10, + 124,149, 24,122,135, 44,123,141, 34,124,152, 20,124,161, 11, + 124,167, 6,123,170, 3,123,174, 1,122,178, 0,120,183, 0, + 118,189, 0,116,193, 0,114,196, 0,114,196, 0,117,192, 0, + 120,184, 0,123,171, 3,124,164, 9,124,166, 7,123,173, 1, + 122,177, 0,122,178, 0,122,178, 0,120,182, 0,120,184, 0, + 121,182, 0,120,184, 0,119,186, 0,119,186, 0,119,186, 0, + 119,186, 0,119,186, 0,119,186, 0,119,186, 0,119,186, 0, + 120,184, 0,118,188, 0,116,193, 0,113,198, 0,111,202, 0, + 109,203, 0,109,204, 0,104,210, 0,101,214, 0, 98,216, 0, + 98,217, 0, 99,216, 0, 97,218, 0, 96,219, 0, 98,217, 0, + 101,214, 0,104,210, 0,108,206, 0,112,200, 0,116,193, 0, + 117,191, 0,119,186, 0,122,178, 0,121,179, 0,122,177, 0, + 123,170, 3,124,154, 18,118,117, 85,117,110,109,121,125, 63, + 124,150, 22,124,166, 7,121,180, 0,118,189, 0,114,196, 0, + 113,199, 0,112,200, 0,113,199, 0,114,197, 0,114,197, 0, + 112,200, 0,111,202, 0,110,202, 0,111,201, 0,112,200, 0, + 113,198, 0,114,197, 0,114,197, 0,113,198, 0,113,198, 0, + 114,196, 0,116,192, 0,119,186, 0,122,178, 0,124,166, 7, + 124,143, 31,122,130, 53,120,122, 70,123,139, 37,124,147, 26, + 124,150, 22,124,157, 15,124,165, 8,123,171, 3,122,178, 0, + 120,184, 0,118,189, 0,116,192, 0,116,193, 0,116,192, 0, + 116,193, 0,114,197, 0,113,199, 0,112,200, 0,112,200, 0, + 112,199, 0,112,200, 0,110,202, 0,107,207, 0,103,211, 0, + 101,214, 0,100,215, 0,100,214, 0,101,213, 0,103,211, 0, + 105,209, 0,106,207, 0,107,207, 0,100,215, 0, 93,222, 0, + 88,226, 0, 85,229, 0, 84,229, 0, 86,228, 0, 89,225, 0, + 93,222, 0, 95,220, 0, 98,217, 0,102,212, 0,107,207, 0, + 110,202, 0,114,197, 0,115,194, 0,116,192, 0,117,191, 0, + 118,189, 0,119,186, 0,121,181, 0,122,175, 0,123,168, 5, + 123,173, 1,121,179, 0,120,182, 0,120,183, 0,121,181, 0, + 122,175, 0,124,166, 7,124,152, 20,122,132, 48,122,132, 48, + 124,154, 18,123,168, 5,122,178, 0,120,184, 0,118,189, 0, + 117,192, 0,117,192, 0,118,189, 0,119,185, 0,120,184, 0, + 120,184, 0,120,183, 0,121,180, 0,122,175, 0,122,178, 0, + 120,183, 0,119,186, 0,118,188, 0,119,187, 0,120,183, 0, + 123,174, 1,124,160, 12,124,161, 11,124,162, 10,123,172, 2, + 121,180, 0,120,184, 0,120,183, 0,122,178, 0,123,171, 3, + 124,162, 10,124,161, 11,124,165, 8,124,167, 6,123,170, 3, + 123,172, 2,122,177, 0,122,178, 0,121,180, 0,119,186, 0, + 116,193, 0,113,198, 0,110,202, 0,109,204, 0,108,205, 0, + 109,204, 0,109,203, 0,109,204, 0,108,206, 0,106,207, 0, + 105,209, 0,105,209, 0,106,207, 0,108,205, 0,112,200, 0, + 114,197, 0,111,201, 0,104,210, 0, 98,217, 0, 94,221, 0, + 92,223, 0, 92,223, 0, 92,223, 0, 91,223, 0, 91,224, 0, + 90,225, 0, 89,225, 0, 90,225, 0, 91,223, 0, 94,221, 0, + 98,217, 0,100,215, 0, 99,215, 0,101,214, 0,103,211, 0, + 105,209, 0,105,209, 0,106,207, 0,107,207, 0,107,207, 0, + 106,208, 0,105,209, 0,105,209, 0,105,209, 0,107,207, 0, + 110,203, 0,112,199, 0,112,200, 0,112,200, 0,113,199, 0, + 114,196, 0,116,193, 0,118,189, 0,119,185, 0,120,183, 0, + 120,182, 0,120,183, 0,120,183, 0,121,182, 0,122,178, 0, + 123,172, 2,124,164, 9,124,154, 18,124,154, 18,124,160, 12, + 123,168, 5,123,174, 1,121,179, 0,120,184, 0,119,187, 0, + 118,189, 0,115,194, 0,113,198, 0,112,200, 0,112,199, 0, + 114,196, 0,118,188, 0,122,177, 0,124,164, 9,124,161, 11, + 123,168, 5,123,170, 3,122,177, 0,120,183, 0,119,187, 0, + 118,189, 0,118,189, 0,117,191, 0,117,192, 0,117,191, 0, + 117,191, 0,117,191, 0,117,191, 0,117,191, 0,117,192, 0, + 117,191, 0,117,190, 0,118,189, 0,117,192, 0,113,198, 0, + 110,202, 0,108,205, 0,107,206, 0,103,211, 0,100,215, 0, + 98,217, 0, 98,217, 0, 96,219, 0, 93,222, 0, 92,223, 0, + 93,222, 0, 96,219, 0,100,215, 0,103,211, 0,108,205, 0, + 112,200, 0,113,198, 0,115,194, 0,118,188, 0,120,182, 0, + 122,178, 0,123,168, 5,124,147, 26,109, 92,251,109, 92,251, + 122,135, 44,124,157, 15,122,175, 0,119,187, 0,115,195, 0, + 112,200, 0,110,202, 0,110,203, 0,111,202, 0,112,200, 0, + 113,198, 0,112,200, 0,110,202, 0,109,204, 0,108,205, 0, + 108,205, 0,108,205, 0,108,205, 0,108,206, 0,107,206, 0, + 108,205, 0,109,204, 0,112,200, 0,114,196, 0,118,189, 0, + 121,182, 0,123,169, 4,124,145, 29,121,127, 58,121,127, 58, + 123,141, 34,124,147, 26,124,149, 24,124,155, 16,124,166, 7, + 122,175, 0,121,182, 0,119,186, 0,118,188, 0,118,189, 0, + 118,188, 0,116,193, 0,114,197, 0,112,199, 0,112,200, 0, + 112,200, 0,112,200, 0,112,200, 0,110,202, 0,106,207, 0, + 102,212, 0, 99,215, 0, 97,217, 0, 97,217, 0, 98,217, 0, + 100,215, 0,101,213, 0,103,211, 0,104,210, 0,101,214, 0, + 95,220, 0, 90,225, 0, 86,228, 0, 85,229, 0, 86,228, 0, + 88,226, 0, 92,223, 0, 95,219, 0,100,215, 0,103,211, 0, + 106,207, 0,110,203, 0,112,199, 0,114,196, 0,116,193, 0, + 117,191, 0,118,188, 0,120,184, 0,122,178, 0,123,170, 3, + 124,162, 10,123,171, 3,122,177, 0,121,180, 0,121,180, 0, + 122,178, 0,123,172, 2,124,162, 10,124,147, 26,121,127, 58, + 124,143, 31,124,161, 11,123,174, 1,120,182, 0,118,188, 0, + 117,191, 0,116,192, 0,117,191, 0,118,188, 0,120,183, 0, + 122,178, 0,123,173, 1,123,170, 3,123,170, 3,123,171, 3, + 123,174, 1,122,177, 0,121,179, 0,122,178, 0,122,176, 0, + 123,169, 4,124,157, 15,124,162, 10,124,166, 7,123,168, 5, + 122,178, 0,119,185, 0,118,188, 0,119,187, 0,120,184, 0, + 121,179, 0,123,173, 1,123,169, 4,124,167, 6,123,168, 5, + 123,170, 3,122,175, 0,122,178, 0,122,178, 0,121,181, 0, + 119,187, 0,117,191, 0,115,195, 0,112,199, 0,110,202, 0, + 110,203, 0,109,203, 0,109,204, 0,108,205, 0,106,208, 0, + 104,210, 0,102,212, 0,102,213, 0,102,212, 0,105,209, 0, + 108,205, 0,113,198, 0,110,203, 0,102,213, 0, 95,220, 0, + 90,225, 0, 87,227, 0, 87,227, 0, 87,227, 0, 87,227, 0, + 87,227, 0, 86,228, 0, 86,228, 0, 87,227, 0, 89,226, 0, + 92,223, 0, 96,219, 0,102,213, 0,102,212, 0,103,211, 0, + 104,210, 0,103,212, 0,103,212, 0,104,210, 0,105,209, 0, + 105,209, 0,105,209, 0,105,209, 0,106,208, 0,107,207, 0, + 109,204, 0,112,200, 0,112,199, 0,112,200, 0,112,200, 0, + 112,199, 0,113,198, 0,115,195, 0,116,192, 0,118,189, 0, + 118,188, 0,119,187, 0,119,186, 0,119,186, 0,120,184, 0, + 121,181, 0,122,177, 0,123,171, 3,124,166, 7,124,164, 9, + 124,166, 7,122,176, 0,120,184, 0,118,189, 0,116,193, 0, + 115,194, 0,115,194, 0,114,197, 0,111,201, 0,110,202, 0, + 111,201, 0,113,198, 0,117,191, 0,121,180, 0,124,164, 9, + 124,157, 15,124,164, 9,123,169, 4,122,178, 0,120,184, 0, + 118,189, 0,117,191, 0,116,193, 0,115,195, 0,115,195, 0, + 115,194, 0,116,193, 0,116,193, 0,116,192, 0,116,193, 0, + 115,194, 0,115,194, 0,116,193, 0,117,192, 0,117,191, 0, + 114,197, 0,110,202, 0,107,206, 0,106,208, 0,104,210, 0, + 100,214, 0, 98,217, 0, 98,217, 0, 94,220, 0, 91,223, 0, + 89,225, 0, 90,225, 0, 92,222, 0, 96,219, 0,100,214, 0, + 105,209, 0,107,207, 0,108,205, 0,111,201, 0,114,196, 0, + 118,188, 0,121,179, 0,124,167, 6,124,152, 20,122,132, 48, + 115,107,129,123,139, 37,124,166, 7,121,181, 0,117,191, 0, + 113,198, 0,110,202, 0,109,204, 0,108,205, 0,109,203, 0, + 111,202, 0,112,200, 0,112,199, 0,110,203, 0,108,206, 0, + 106,207, 0,106,208, 0,105,209, 0,104,210, 0,103,211, 0, + 103,211, 0,104,210, 0,105,209, 0,108,206, 0,111,201, 0, + 114,196, 0,117,191, 0,120,183, 0,123,171, 3,124,149, 24, + 117,110,109,120,122, 70,122,135, 44,123,139, 37,123,139, 37, + 124,154, 18,124,167, 6,122,175, 0,121,181, 0,120,183, 0, + 120,183, 0,119,186, 0,117,192, 0,114,196, 0,113,198, 0, + 113,198, 0,113,198, 0,113,198, 0,113,199, 0,111,201, 0, + 107,206, 0,102,212, 0, 99,216, 0, 97,218, 0, 95,219, 0, + 96,219, 0, 97,218, 0, 99,216, 0,101,214, 0,102,212, 0, + 104,210, 0, 99,215, 0, 94,221, 0, 90,224, 0, 88,226, 0, + 87,227, 0, 89,226, 0, 92,223, 0, 96,219, 0,101,214, 0, + 105,209, 0,107,206, 0,110,202, 0,112,199, 0,115,195, 0, + 117,191, 0,118,188, 0,120,184, 0,122,178, 0,123,172, 2, + 124,165, 8,124,157, 15,124,164, 9,123,170, 3,123,173, 1, + 123,173, 1,123,170, 3,124,164, 9,124,152, 20,122,135, 44, + 119,119, 77,124,145, 29,124,165, 8,122,176, 0,120,184, 0, + 118,188, 0,117,191, 0,117,191, 0,118,188, 0,120,184, 0, + 122,178, 0,123,172, 2,124,167, 6,124,164, 9,124,164, 9, + 124,167, 6,123,168, 5,124,166, 7,124,165, 8,124,161, 11, + 124,154, 18,123,141, 34,124,152, 20,124,164, 9,124,167, 6, + 123,173, 1,121,182, 0,119,186, 0,118,189, 0,118,189, 0, + 119,187, 0,120,184, 0,121,181, 0,122,178, 0,122,177, 0, + 122,178, 0,122,178, 0,121,180, 0,120,182, 0,120,184, 0, + 119,185, 0,118,188, 0,117,192, 0,116,193, 0,115,194, 0, + 114,197, 0,112,200, 0,111,201, 0,110,203, 0,108,206, 0, + 105,209, 0,102,212, 0,100,215, 0, 99,216, 0,100,215, 0, + 102,213, 0,105,209, 0,111,202, 0,108,205, 0,100,215, 0, + 92,223, 0, 86,228, 0, 83,230, 0, 83,230, 0, 83,230, 0, + 84,229, 0, 84,229, 0, 83,230, 0, 84,229, 0, 85,229, 0, + 87,227, 0, 91,223, 0, 95,219, 0,102,213, 0,105,209, 0, + 106,208, 0,103,212, 0,101,213, 0,102,213, 0,103,212, 0, + 105,209, 0,106,208, 0,106,207, 0,107,206, 0,109,204, 0, + 110,202, 0,112,199, 0,113,199, 0,114,197, 0,113,198, 0, + 113,198, 0,113,198, 0,114,197, 0,114,196, 0,116,193, 0, + 117,191, 0,118,189, 0,118,189, 0,118,188, 0,119,186, 0, + 120,184, 0,120,182, 0,121,180, 0,122,178, 0,122,175, 0, + 123,171, 3,122,178, 0,119,186, 0,116,193, 0,114,197, 0, + 112,200, 0,111,201, 0,111,201, 0,112,200, 0,110,202, 0, + 109,203, 0,110,202, 0,113,199, 0,117,192, 0,121,181, 0, + 124,165, 8,124,154, 18,124,161, 11,124,166, 7,122,176, 0, + 120,184, 0,118,189, 0,117,191, 0,115,195, 0,114,197, 0, + 114,196, 0,115,194, 0,116,193, 0,116,192, 0,116,192, 0, + 116,193, 0,115,194, 0,114,196, 0,114,196, 0,114,196, 0, + 115,194, 0,114,197, 0,110,202, 0,107,207, 0,105,209, 0, + 105,209, 0,101,214, 0, 98,216, 0, 97,217, 0, 95,220, 0, + 91,223, 0, 89,225, 0, 89,225, 0, 91,223, 0, 94,220, 0, + 99,216, 0,101,214, 0,102,213, 0,104,210, 0,107,207, 0, + 110,202, 0,114,196, 0,118,189, 0,121,181, 0,123,170, 3, + 124,155, 16,122,132, 48,123,141, 34,123,168, 5,120,182, 0, + 116,192, 0,113,199, 0,110,203, 0,108,205, 0,108,205, 0, + 109,203, 0,111,202, 0,112,199, 0,113,199, 0,110,203, 0, + 107,206, 0,106,208, 0,104,210, 0,103,211, 0,102,213, 0, + 101,214, 0,101,214, 0,101,213, 0,103,211, 0,106,208, 0, + 109,204, 0,111,201, 0,114,197, 0,117,192, 0,119,185, 0, + 123,173, 1,124,154, 18,121,127, 58,122,132, 48,123,137, 41, + 123,139, 37,123,141, 34,124,154, 18,124,167, 6,123,173, 1, + 122,176, 0,122,176, 0,121,182, 0,118,188, 0,116,192, 0, + 115,194, 0,115,195, 0,115,195, 0,115,194, 0,114,196, 0, + 111,201, 0,108,205, 0,104,210, 0,100,215, 0, 97,218, 0, + 95,220, 0, 95,220, 0, 95,219, 0, 97,218, 0, 98,216, 0, + 101,214, 0,103,211, 0,105,209, 0,100,215, 0, 96,219, 0, + 93,222, 0, 91,223, 0, 91,223, 0, 94,221, 0, 98,217, 0, + 103,212, 0,108,205, 0,110,203, 0,112,200, 0,114,197, 0, + 117,191, 0,119,186, 0,121,180, 0,122,175, 0,123,169, 4, + 124,162, 10,124,155, 16,124,149, 24,124,149, 24,124,158, 13, + 124,162, 10,124,161, 11,124,155, 16,124,147, 26,122,132, 48, + 118,114, 96,118,114, 96,124,145, 29,124,165, 8,122,177, 0, + 120,184, 0,118,188, 0,118,188, 0,119,186, 0,120,182, 0, + 122,175, 0,123,168, 5,124,160, 12,124,161, 11,123,170, 3, + 122,176, 0,121,179, 0,121,179, 0,123,174, 1,124,162, 10, + 123,139, 37,114,104,197,122,130, 53,124,149, 24,124,158, 13, + 124,162, 10,123,172, 2,121,180, 0,120,184, 0,119,187, 0, + 118,188, 0,119,187, 0,119,186, 0,120,184, 0,120,183, 0, + 120,183, 0,120,183, 0,120,184, 0,119,185, 0,119,186, 0, + 118,188, 0,118,189, 0,117,191, 0,117,191, 0,117,191, 0, + 117,191, 0,117,191, 0,115,194, 0,113,198, 0,111,201, 0, + 108,205, 0,105,209, 0,101,214, 0, 98,216, 0, 97,217, 0, + 98,217, 0,100,214, 0,104,210, 0,109,203, 0,106,208, 0, + 97,218, 0, 89,225, 0, 83,230, 0, 81,232, 0, 80,232, 0, + 82,231, 0, 82,231, 0, 83,230, 0, 83,230, 0, 84,229, 0, + 85,229, 0, 88,226, 0, 92,223, 0, 97,218, 0,103,211, 0, + 108,205, 0,105,209, 0,102,212, 0,101,214, 0,101,213, 0, + 103,211, 0,105,209, 0,107,207, 0,107,206, 0,108,205, 0, + 110,203, 0,111,201, 0,112,199, 0,113,198, 0,116,193, 0, + 115,194, 0,115,194, 0,115,195, 0,115,195, 0,115,194, 0, + 116,193, 0,117,191, 0,116,193, 0,115,194, 0,114,196, 0, + 114,197, 0,114,196, 0,115,194, 0,117,191, 0,119,186, 0, + 121,182, 0,122,178, 0,119,186, 0,115,194, 0,112,200, 0, + 109,204, 0,108,206, 0,107,207, 0,107,206, 0,109,203, 0, + 109,203, 0,109,204, 0,110,202, 0,113,199, 0,117,191, 0, + 121,180, 0,124,162, 10,124,154, 18,124,160, 12,124,161, 11, + 123,171, 3,121,180, 0,119,186, 0,118,188, 0,115,194, 0, + 114,196, 0,115,194, 0,116,192, 0,117,191, 0,118,189, 0, + 118,189, 0,117,191, 0,116,193, 0,114,196, 0,112,200, 0, + 112,200, 0,112,199, 0,111,201, 0,109,204, 0,107,206, 0, + 106,208, 0,105,209, 0,102,213, 0, 98,217, 0, 97,218, 0, + 97,218, 0, 94,220, 0, 92,223, 0, 91,223, 0, 92,222, 0, + 95,219, 0, 97,217, 0, 97,218, 0, 98,217, 0,100,215, 0, + 103,211, 0,107,207, 0,110,202, 0,114,197, 0,118,189, 0, + 121,180, 0,124,167, 6,124,143, 31,122,132, 48,124,162, 10, + 122,178, 0,118,189, 0,114,196, 0,111,201, 0,110,202, 0, + 110,203, 0,111,202, 0,112,200, 0,114,197, 0,112,199, 0, + 109,204, 0,107,207, 0,106,208, 0,105,209, 0,103,211, 0, + 102,212, 0,101,214, 0,101,214, 0,101,213, 0,103,211, 0, + 105,209, 0,107,206, 0,109,203, 0,111,201, 0,113,198, 0, + 115,194, 0,119,187, 0,122,177, 0,124,157, 15,124,143, 31, + 123,141, 34,123,139, 37,122,130, 53,122,135, 44,124,155, 16, + 124,165, 8,123,168, 5,123,168, 5,123,173, 1,121,182, 0, + 119,187, 0,118,189, 0,117,190, 0,117,190, 0,117,190, 0, + 116,193, 0,112,200, 0,108,205, 0,106,208, 0,101,213, 0, + 97,217, 0, 95,220, 0, 94,221, 0, 94,220, 0, 94,220, 0, + 95,220, 0, 97,218, 0,100,215, 0,104,210, 0,107,207, 0, + 103,211, 0,100,215, 0, 97,218, 0, 96,219, 0, 98,217, 0, + 102,213, 0,107,207, 0,112,200, 0,113,199, 0,114,196, 0, + 117,191, 0,120,184, 0,122,176, 0,124,167, 6,124,160, 12, + 124,152, 20,124,145, 29,123,139, 37,122,135, 44,122,132, 48, + 123,137, 41,123,141, 34,123,139, 37,122,132, 48,119,119, 77, + 115,107,129,111, 96,248,118,117, 85,123,141, 34,124,164, 9, + 122,176, 0,120,182, 0,119,185, 0,120,184, 0,121,180, 0, + 123,172, 2,124,162, 10,124,150, 22,124,160, 12,123,171, 3, + 121,179, 0,120,184, 0,119,186, 0,120,184, 0,122,178, 0, + 124,165, 8,123,141, 34,122,135, 44,119,119, 77,123,137, 41, + 124,145, 29,124,157, 15,124,167, 6,123,173, 1,122,178, 0, + 121,181, 0,120,183, 0,120,184, 0,120,184, 0,120,184, 0, + 120,184, 0,119,185, 0,119,185, 0,119,186, 0,119,187, 0, + 118,188, 0,117,190, 0,116,192, 0,115,194, 0,115,195, 0, + 115,195, 0,115,194, 0,116,192, 0,115,194, 0,114,197, 0, + 113,199, 0,109,203, 0,105,209, 0,101,214, 0, 98,217, 0, + 97,218, 0, 98,217, 0,100,214, 0,104,210, 0,108,205, 0, + 103,211, 0, 94,220, 0, 87,227, 0, 82,231, 0, 79,234, 0, + 79,234, 0, 80,232, 0, 82,231, 0, 83,230, 0, 84,229, 0, + 85,229, 0, 87,227, 0, 90,225, 0, 94,221, 0, 99,215, 0, + 106,208, 0,107,206, 0,103,211, 0,101,214, 0,101,214, 0, + 102,213, 0,104,210, 0,106,207, 0,106,207, 0,107,207, 0, + 108,205, 0,110,202, 0,112,200, 0,114,197, 0,115,194, 0, + 119,186, 0,119,187, 0,118,189, 0,117,190, 0,117,191, 0, + 117,192, 0,117,192, 0,117,192, 0,114,196, 0,113,199, 0, + 111,202, 0,109,203, 0,109,203, 0,111,202, 0,113,198, 0, + 116,192, 0,119,186, 0,120,184, 0,116,193, 0,111,201, 0, + 108,206, 0,105,209, 0,104,210, 0,104,210, 0,105,209, 0, + 107,207, 0,109,204, 0,109,204, 0,110,202, 0,113,198, 0, + 117,190, 0,122,178, 0,124,158, 13,124,154, 18,124,161, 11, + 124,164, 9,124,165, 8,123,173, 1,121,179, 0,119,186, 0, + 117,192, 0,116,192, 0,117,191, 0,118,189, 0,117,192, 0, + 117,191, 0,118,188, 0,119,187, 0,117,190, 0,114,197, 0, + 111,202, 0,110,203, 0,110,202, 0,109,204, 0,107,207, 0, + 106,208, 0,107,207, 0,106,207, 0,102,213, 0, 98,217, 0, + 96,219, 0, 96,219, 0, 98,217, 0, 96,219, 0, 95,220, 0, + 95,219, 0, 97,218, 0, 95,220, 0, 94,221, 0, 95,220, 0, + 97,217, 0,100,214, 0,104,210, 0,107,206, 0,111,201, 0, + 115,194, 0,119,185, 0,123,171, 3,124,147, 26,115,107,129, + 124,145, 29,123,168, 5,120,182, 0,117,191, 0,114,196, 0, + 113,199, 0,112,199, 0,113,198, 0,114,196, 0,114,197, 0, + 110,202, 0,107,207, 0,105,209, 0,105,209, 0,105,209, 0, + 106,208, 0,104,210, 0,103,212, 0,102,212, 0,103,211, 0, + 104,210, 0,106,208, 0,106,208, 0,107,206, 0,109,204, 0, + 110,203, 0,111,201, 0,114,196, 0,118,189, 0,122,177, 0, + 124,154, 18,123,141, 34,122,135, 44,119,119, 77,117,110,109, + 123,141, 34,124,155, 16,124,160, 12,124,160, 12,124,158, 13, + 123,171, 3,122,178, 0,120,183, 0,119,187, 0,118,188, 0, + 118,188, 0,117,192, 0,113,198, 0,110,203, 0,107,207, 0, + 103,212, 0, 98,217, 0, 95,220, 0, 93,221, 0, 93,221, 0, + 93,222, 0, 92,222, 0, 93,221, 0, 95,220, 0, 98,216, 0, + 103,212, 0,108,206, 0,107,207, 0,104,210, 0,102,212, 0, + 103,211, 0,107,207, 0,111,201, 0,115,194, 0,116,193, 0, + 118,189, 0,120,183, 0,123,172, 2,124,157, 15,123,141, 34, + 121,127, 58,118,117, 85,118,114, 96,117,110,109,117,110,109, + 117,110,109,117,110,109,117,110,109,115,107,129,115,107,129, + 115,107,129,114,104,197,115,107,129,121,125, 63,123,139, 37, + 124,162, 10,123,174, 1,121,181, 0,120,182, 0,121,179, 0, + 123,172, 2,124,160, 12,124,150, 22,124,158, 13,124,166, 7, + 122,176, 0,120,184, 0,118,188, 0,118,189, 0,119,187, 0, + 121,179, 0,124,167, 6,124,165, 8,124,158, 13,124,143, 31, + 118,114, 96,119,119, 77,123,141, 34,124,154, 18,124,161, 11, + 124,166, 7,123,170, 3,123,174, 1,122,177, 0,121,179, 0, + 121,182, 0,120,182, 0,120,183, 0,120,184, 0,119,185, 0, + 119,186, 0,118,188, 0,118,189, 0,116,192, 0,114,196, 0, + 113,198, 0,113,199, 0,113,199, 0,113,198, 0,114,196, 0, + 113,198, 0,113,199, 0,110,202, 0,106,208, 0,101,213, 0, + 98,216, 0, 98,217, 0, 99,216, 0,102,213, 0,106,208, 0, + 108,206, 0,100,214, 0, 92,223, 0, 84,229, 0, 80,233, 0, + 78,234, 0, 78,234, 0, 81,232, 0, 81,232, 0, 82,231, 0, + 85,228, 0, 87,227, 0, 90,225, 0, 93,222, 0, 97,218, 0, + 102,212, 0,108,206, 0,106,208, 0,102,212, 0,100,214, 0, + 101,214, 0,102,212, 0,106,208, 0,107,207, 0,107,207, 0, + 108,206, 0,109,203, 0,112,200, 0,114,196, 0,117,192, 0, + 118,188, 0,122,175, 0,122,177, 0,121,180, 0,120,184, 0, + 119,187, 0,118,189, 0,117,191, 0,117,191, 0,114,196, 0, + 112,200, 0,109,204, 0,107,207, 0,106,207, 0,108,206, 0, + 110,202, 0,114,196, 0,118,189, 0,117,191, 0,112,200, 0, + 108,206, 0,104,210, 0,102,212, 0,102,212, 0,103,212, 0, + 104,210, 0,106,207, 0,107,206, 0,108,205, 0,110,202, 0, + 114,197, 0,118,188, 0,123,174, 1,124,154, 18,124,155, 16, + 124,164, 9,124,167, 6,124,166, 7,124,165, 8,123,169, 4, + 121,182, 0,118,188, 0,118,189, 0,117,192, 0,114,197, 0, + 113,198, 0,114,197, 0,116,193, 0,118,188, 0,119,187, 0, + 114,197, 0,110,202, 0,108,205, 0,108,205, 0,107,206, 0, + 105,209, 0,105,209, 0,106,208, 0,107,207, 0,102,213, 0, + 97,217, 0, 95,220, 0, 94,220, 0, 96,219, 0,100,215, 0, + 99,215, 0, 99,215, 0, 95,219, 0, 93,221, 0, 92,222, 0, + 94,221, 0, 96,219, 0, 99,216, 0,102,212, 0,105,209, 0, + 109,203, 0,114,197, 0,119,187, 0,123,172, 2,124,145, 29, + 105, 84,252,115,107,129,124,147, 26,123,169, 4,121,182, 0, + 118,188, 0,116,192, 0,115,194, 0,115,194, 0,114,196, 0, + 112,200, 0,108,206, 0,105,209, 0,104,210, 0,104,210, 0, + 106,208, 0,108,205, 0,108,205, 0,106,207, 0,106,208, 0, + 106,208, 0,104,210, 0,102,212, 0,102,213, 0,103,211, 0, + 107,207, 0,107,207, 0,108,205, 0,110,202, 0,114,196, 0, + 119,186, 0,123,170, 3,123,139, 37,121,125, 63,113,100,242, + 114,104,197,121,125, 63,124,145, 29,124,150, 22,124,150, 22, + 124,145, 29,124,161, 11,122,177, 0,119,185, 0,118,189, 0, + 117,192, 0,116,192, 0,115,195, 0,114,197, 0,112,200, 0, + 109,204, 0,103,211, 0, 98,217, 0, 94,220, 0, 92,222, 0, + 92,222, 0, 92,222, 0, 91,223, 0, 91,223, 0, 92,223, 0, + 94,221, 0, 98,217, 0,103,212, 0,109,204, 0,111,201, 0, + 109,203, 0,110,202, 0,113,199, 0,116,193, 0,118,188, 0, + 119,185, 0,121,180, 0,123,170, 3,124,154, 18,124,143, 31, + 123,137, 41,120,122, 70,111, 96,248, 99, 75,247, 69, 60,230, + 87, 65,237, 94, 70,242, 94, 70,242, 99, 75,247,102, 80,250, + 105, 84,252,107, 89,252,109, 92,251,118,117, 85,122,130, 53, + 123,141, 34,124,162, 10,123,174, 1,121,179, 0,121,179, 0, + 122,175, 0,124,165, 8,124,149, 24,124,157, 15,123,169, 4, + 122,175, 0,122,178, 0,119,186, 0,118,189, 0,117,191, 0, + 119,187, 0,121,180, 0,121,180, 0,122,178, 0,123,170, 3, + 124,154, 18,118,117, 85,105, 84,252,117,110,109,121,127, 58, + 123,137, 41,124,149, 24,124,157, 15,124,160, 12,124,165, 8, + 123,170, 3,123,174, 1,122,176, 0,122,178, 0,120,182, 0, + 119,185, 0,119,186, 0,119,186, 0,119,186, 0,118,189, 0, + 116,193, 0,114,197, 0,112,199, 0,112,200, 0,113,199, 0, + 114,197, 0,113,198, 0,113,198, 0,110,202, 0,106,208, 0, + 102,213, 0,100,215, 0, 99,215, 0,101,214, 0,104,210, 0, + 108,205, 0,106,208, 0, 97,217, 0, 89,225, 0, 82,231, 0, + 78,234, 0, 77,235, 0, 78,234, 0, 81,232, 0, 80,233, 0, + 81,232, 0, 85,229, 0, 90,224, 0, 93,221, 0, 97,218, 0, + 101,213, 0,107,207, 0,108,205, 0,104,210, 0,101,214, 0, + 100,215, 0,101,214, 0,103,211, 0,107,207, 0,108,205, 0, + 109,204, 0,110,202, 0,112,200, 0,114,196, 0,117,190, 0, + 120,184, 0,122,178, 0,124,157, 15,124,161, 11,123,168, 5, + 122,176, 0,120,183, 0,118,188, 0,117,191, 0,116,192, 0, + 116,193, 0,113,199, 0,109,203, 0,107,207, 0,106,208, 0, + 107,207, 0,110,203, 0,114,197, 0,118,189, 0,113,198, 0, + 109,204, 0,105,209, 0,102,213, 0,101,214, 0,101,213, 0, + 103,211, 0,105,209, 0,105,209, 0,106,208, 0,107,206, 0, + 110,202, 0,114,196, 0,119,186, 0,123,170, 3,123,141, 34, + 124,157, 15,124,166, 7,123,169, 4,123,170, 3,123,168, 5, + 123,169, 4,121,180, 0,119,186, 0,116,193, 0,112,199, 0, + 110,202, 0,109,204, 0,110,202, 0,113,198, 0,116,192, 0, + 119,186, 0,114,196, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,106,208, 0,105,209, 0,106,208, 0,103,211, 0, + 101,214, 0, 97,218, 0, 94,221, 0, 94,221, 0, 95,219, 0, + 99,215, 0,103,211, 0, 99,216, 0, 95,220, 0, 93,222, 0, + 92,222, 0, 93,221, 0, 95,219, 0, 98,216, 0,102,213, 0, + 105,209, 0,109,204, 0,113,198, 0,118,188, 0,123,173, 1, + 124,143, 31, 99, 75,247, 30, 20,155,114,104,197,124,145, 29, + 124,167, 6,122,178, 0,120,184, 0,118,189, 0,116,193, 0, + 114,196, 0,110,203, 0,106,208, 0,104,210, 0,103,211, 0, + 105,209, 0,107,207, 0,108,205, 0,112,200, 0,110,203, 0, + 108,206, 0,105,209, 0,102,213, 0,100,215, 0,100,215, 0, + 101,213, 0,105,209, 0,105,209, 0,105,209, 0,107,206, 0, + 111,201, 0,117,191, 0,122,177, 0,124,150, 22,114,104,197, + 117,110,109,118,117, 85,118,114, 96,123,137, 41,124,145, 29, + 124,147, 26,124,154, 18,124,158, 13,122,176, 0,119,185, 0, + 117,191, 0,115,194, 0,115,195, 0,114,196, 0,114,197, 0, + 114,196, 0,109,204, 0,103,212, 0, 97,218, 0, 93,222, 0, + 91,224, 0, 90,225, 0, 91,223, 0, 92,222, 0, 92,223, 0, + 91,223, 0, 92,222, 0, 95,220, 0, 99,215, 0,105,209, 0, + 111,202, 0,115,194, 0,116,193, 0,118,189, 0,120,184, 0, + 121,181, 0,122,178, 0,123,174, 1,124,167, 6,124,152, 20, + 124,154, 18,124,149, 24,123,139, 37,119,119, 77,107, 89,252, + 30, 55,222, 30, 31,180, 30, 2,128, 30, 37,192, 30, 43,203, + 30, 43,203, 30, 31,180, 30, 49,213,107, 89,252,117,110,109, + 119,119, 77,124,149, 24,124,166, 7,122,176, 0,121,180, 0, + 121,179, 0,123,173, 1,124,161, 11,123,137, 41,124,161, 11, + 123,172, 2,122,178, 0,120,182, 0,119,186, 0,118,189, 0, + 118,189, 0,119,186, 0,119,187, 0,119,186, 0,120,183, 0, + 123,174, 1,124,157, 15,121,127, 58,111, 96,248, 87, 65,237, + 119,119, 77,124,145, 29,124,160, 12,124,165, 8,124,167, 6, + 124,166, 7,124,164, 9,124,161, 11,123,170, 3,122,177, 0, + 121,181, 0,120,184, 0,119,185, 0,119,185, 0,120,184, 0, + 121,182, 0,119,187, 0,117,191, 0,115,194, 0,114,196, 0, + 114,196, 0,114,197, 0,114,197, 0,114,197, 0,110,202, 0, + 106,208, 0,102,212, 0,100,214, 0,101,214, 0,103,211, 0, + 107,207, 0,111,202, 0,103,211, 0, 95,220, 0, 87,227, 0, + 80,232, 0, 77,235, 0, 77,235, 0, 78,234, 0, 82,231, 0, + 80,233, 0, 81,232, 0, 85,229, 0, 90,224, 0, 96,219, 0, + 100,214, 0,105,209, 0,111,202, 0,107,207, 0,103,212, 0, + 100,214, 0, 99,215, 0,101,214, 0,103,211, 0,108,206, 0, + 112,200, 0,113,199, 0,114,197, 0,116,193, 0,118,188, 0, + 121,181, 0,123,171, 3,124,162, 10,122,175, 0,123,174, 1, + 123,169, 4,123,168, 5,122,178, 0,119,186, 0,117,192, 0, + 115,195, 0,114,197, 0,114,196, 0,111,201, 0,109,204, 0, + 107,206, 0,108,205, 0,111,202, 0,114,196, 0,114,196, 0, + 109,203, 0,105,209, 0,102,213, 0,100,214, 0,101,214, 0, + 102,212, 0,102,212, 0,102,212, 0,103,211, 0,105,209, 0, + 107,206, 0,111,202, 0,115,194, 0,120,183, 0,124,165, 8, + 124,152, 20,124,154, 18,124,164, 9,123,170, 3,123,172, 2, + 123,171, 3,121,180, 0,119,187, 0,116,193, 0,113,199, 0, + 109,203, 0,107,206, 0,107,207, 0,108,205, 0,111,201, 0, + 114,196, 0,117,190, 0,115,194, 0,111,201, 0,108,205, 0, + 107,207, 0,106,208, 0,106,208, 0,105,209, 0,104,210, 0, + 102,213, 0, 99,215, 0, 97,218, 0, 95,220, 0, 94,220, 0, + 96,219, 0, 99,215, 0,100,215, 0, 99,216, 0, 95,220, 0, + 93,221, 0, 93,222, 0, 93,221, 0, 92,222, 0, 93,221, 0, + 96,219, 0,101,214, 0,106,207, 0,112,199, 0,118,189, 0, + 122,177, 0,124,157, 15,120,122, 70,105, 84,252,115,107,129, + 121,125, 63,124,145, 29,123,168, 5,121,182, 0,118,189, 0, + 116,193, 0,114,197, 0,108,205, 0,105,209, 0,103,211, 0, + 103,211, 0,105,209, 0,105,209, 0,106,208, 0,109,204, 0, + 109,204, 0,106,208, 0,103,211, 0,100,214, 0, 98,216, 0, + 98,216, 0,100,214, 0,103,211, 0,102,212, 0,103,212, 0, + 105,209, 0,110,203, 0,115,194, 0,121,182, 0,124,160, 12, + 124,143, 31,122,130, 53,120,122, 70,118,114, 96,121,127, 58, + 124,147, 26,124,157, 15,124,164, 9,123,168, 5,123,174, 1, + 120,184, 0,117,191, 0,115,195, 0,113,198, 0,112,200, 0, + 112,200, 0,111,201, 0,108,206, 0,102,213, 0, 96,219, 0, + 92,222, 0, 89,225, 0, 89,226, 0, 89,225, 0, 91,224, 0, + 93,221, 0, 94,221, 0, 94,221, 0, 95,219, 0, 99,216, 0, + 103,211, 0,108,205, 0,113,198, 0,116,193, 0,118,189, 0, + 119,186, 0,120,184, 0,121,180, 0,122,175, 0,124,167, 6, + 124,161, 11,124,162, 10,124,158, 13,124,150, 22,123,137, 41, + 118,117, 85,115,107,129,113,100,242,111, 96,248,109, 92,251, + 102, 80,250, 87, 65,237, 94, 70,242,102, 80,250,109, 92,251, + 118,114, 96,122,135, 44,124,155, 16,123,170, 3,122,178, 0, + 121,181, 0,121,179, 0,123,173, 1,124,160, 12,124,149, 24, + 124,158, 13,123,171, 3,122,178, 0,120,183, 0,119,186, 0, + 118,188, 0,118,188, 0,118,189, 0,118,189, 0,118,188, 0, + 120,183, 0,123,173, 1,124,152, 20,122,132, 48,115,107,129, + 120,122, 70,122,135, 44,124,155, 16,124,166, 7,123,170, 3, + 123,171, 3,123,170, 3,123,172, 2,122,175, 0,122,178, 0, + 121,181, 0,120,183, 0,119,186, 0,119,187, 0,118,189, 0, + 118,189, 0,118,188, 0,119,185, 0,120,184, 0,119,187, 0, + 118,189, 0,117,192, 0,116,193, 0,115,194, 0,112,200, 0, + 108,205, 0,104,210, 0,102,213, 0,101,214, 0,102,212, 0, + 105,209, 0,109,203, 0,109,204, 0,102,213, 0, 93,221, 0, + 86,228, 0, 80,232, 0, 78,235, 0, 78,235, 0, 80,233, 0, + 83,230, 0, 81,232, 0, 83,230, 0, 86,228, 0, 91,223, 0, + 97,218, 0,103,212, 0,108,206, 0,109,204, 0,106,208, 0, + 102,212, 0,100,215, 0, 99,216, 0,100,214, 0,103,211, 0, + 108,206, 0,112,200, 0,116,193, 0,119,187, 0,121,182, 0, + 123,174, 1,124,164, 9,124,165, 8,123,172, 2,119,186, 0, + 119,186, 0,121,182, 0,123,174, 1,121,179, 0,119,186, 0, + 116,193, 0,113,199, 0,111,201, 0,111,201, 0,113,198, 0, + 110,202, 0,109,204, 0,109,204, 0,111,201, 0,114,197, 0, + 110,202, 0,106,208, 0,102,212, 0,100,214, 0,100,215, 0, + 101,214, 0,103,212, 0,101,214, 0,102,213, 0,104,210, 0, + 106,208, 0,109,204, 0,113,198, 0,117,191, 0,122,178, 0, + 124,167, 6,124,164, 9,124,158, 13,124,160, 12,124,167, 6, + 123,171, 3,122,177, 0,119,186, 0,116,192, 0,114,197, 0, + 111,201, 0,108,205, 0,107,207, 0,106,207, 0,108,206, 0, + 110,202, 0,113,198, 0,115,195, 0,113,198, 0,111,201, 0, + 109,203, 0,107,206, 0,106,208, 0,105,209, 0,105,209, 0, + 104,210, 0,102,213, 0, 99,216, 0, 97,218, 0, 95,220, 0, + 95,220, 0, 95,219, 0, 97,218, 0, 97,218, 0, 96,219, 0, + 95,220, 0, 94,221, 0, 92,223, 0, 90,225, 0, 89,226, 0, + 89,226, 0, 91,223, 0, 96,219, 0,101,213, 0,108,206, 0, + 114,197, 0,119,187, 0,123,174, 1,124,154, 18,120,122, 70, + 123,137, 41,124,145, 29,124,152, 20,124,162, 10,121,179, 0, + 118,188, 0,116,193, 0,113,198, 0,108,205, 0,105,209, 0, + 103,212, 0,103,212, 0,105,209, 0,105,209, 0,105,209, 0, + 107,207, 0,108,206, 0,105,209, 0,101,214, 0, 98,216, 0, + 97,218, 0, 96,219, 0, 98,217, 0,101,214, 0,102,213, 0, + 102,212, 0,104,210, 0,108,205, 0,113,198, 0,119,187, 0, + 123,173, 1,124,164, 9,124,154, 18,124,147, 26,123,141, 34, + 122,132, 48,124,152, 20,124,164, 9,123,170, 3,122,176, 0, + 121,181, 0,119,186, 0,117,190, 0,114,196, 0,112,200, 0, + 110,202, 0,108,205, 0,107,206, 0,107,207, 0,102,212, 0, + 97,218, 0, 93,221, 0, 91,224, 0, 90,225, 0, 90,225, 0, + 91,224, 0, 92,222, 0, 96,219, 0, 97,217, 0, 98,217, 0, + 100,214, 0,103,211, 0,107,207, 0,111,201, 0,114,197, 0, + 116,193, 0,117,190, 0,119,186, 0,120,182, 0,122,175, 0, + 124,167, 6,123,169, 4,123,170, 3,123,168, 5,124,162, 10, + 124,154, 18,124,154, 18,124,152, 20,124,149, 24,124,147, 26, + 124,145, 29,123,137, 41,121,125, 63,115,107,129,115,107,129, + 118,114, 96,121,125, 63,123,141, 34,124,158, 13,123,169, 4, + 122,176, 0,122,178, 0,122,176, 0,123,169, 4,124,158, 13, + 124,160, 12,124,165, 8,123,171, 3,122,177, 0,120,183, 0, + 119,186, 0,118,188, 0,118,189, 0,118,189, 0,118,189, 0, + 119,186, 0,121,180, 0,124,167, 6,124,150, 22,121,127, 58, + 123,137, 41,124,149, 24,124,158, 13,124,167, 6,123,173, 1, + 122,178, 0,121,179, 0,121,180, 0,121,181, 0,121,182, 0, + 120,184, 0,119,185, 0,119,187, 0,118,188, 0,117,190, 0, + 117,191, 0,117,191, 0,118,189, 0,119,187, 0,119,185, 0, + 120,184, 0,119,186, 0,118,189, 0,116,193, 0,113,198, 0, + 110,203, 0,106,207, 0,103,211, 0,102,213, 0,102,213, 0, + 104,210, 0,108,206, 0,109,203, 0,107,206, 0,101,214, 0, + 94,221, 0, 87,227, 0, 83,230, 0, 81,232, 0, 82,231, 0, + 84,229, 0, 84,229, 0, 83,230, 0, 85,229, 0, 89,226, 0, + 93,221, 0, 99,216, 0,104,210, 0,108,205, 0,107,207, 0, + 103,211, 0,101,214, 0, 99,216, 0, 99,216, 0,101,214, 0, + 104,210, 0,108,205, 0,112,199, 0,116,192, 0,119,185, 0, + 122,176, 0,124,166, 7,123,170, 3,122,178, 0,120,183, 0, + 116,192, 0,116,192, 0,118,189, 0,120,182, 0,120,184, 0, + 118,188, 0,115,194, 0,111,201, 0,109,204, 0,109,204, 0, + 110,202, 0,111,202, 0,109,204, 0,109,203, 0,111,201, 0, + 110,202, 0,107,206, 0,103,211, 0,101,214, 0, 99,215, 0, + 100,215, 0,102,213, 0,102,212, 0,101,214, 0,102,212, 0, + 106,208, 0,109,204, 0,112,199, 0,116,192, 0,120,184, 0, + 123,170, 3,123,171, 3,123,170, 3,124,167, 6,124,164, 9, + 124,162, 10,124,167, 6,121,182, 0,117,190, 0,115,195, 0, + 113,199, 0,111,201, 0,109,203, 0,108,205, 0,108,205, 0, + 109,204, 0,111,202, 0,113,199, 0,112,200, 0,110,203, 0, + 108,205, 0,107,207, 0,106,208, 0,106,208, 0,105,209, 0, + 105,209, 0,105,209, 0,103,211, 0,101,214, 0, 98,217, 0, + 96,219, 0, 95,220, 0, 94,220, 0, 95,220, 0, 95,220, 0, + 94,220, 0, 94,221, 0, 92,222, 0, 90,224, 0, 88,226, 0, + 86,228, 0, 86,228, 0, 88,226, 0, 93,222, 0, 98,217, 0, + 104,210, 0,110,202, 0,116,192, 0,121,182, 0,124,167, 6, + 124,147, 26,124,149, 24,124,155, 16,124,160, 12,124,166, 7, + 122,176, 0,119,187, 0,116,193, 0,114,197, 0,108,205, 0, + 105,209, 0,103,212, 0,103,212, 0,105,209, 0,106,208, 0, + 105,209, 0,107,207, 0,107,207, 0,103,211, 0,100,215, 0, + 97,218, 0, 95,220, 0, 94,220, 0, 95,219, 0, 98,217, 0, + 101,214, 0,102,213, 0,103,211, 0,106,207, 0,111,201, 0, + 116,193, 0,120,183, 0,122,175, 0,124,167, 6,124,161, 11, + 124,154, 18,124,147, 26,124,152, 20,124,166, 7,122,175, 0, + 121,182, 0,119,187, 0,117,191, 0,115,194, 0,114,196, 0, + 112,200, 0,109,204, 0,106,207, 0,105,209, 0,104,210, 0, + 104,210, 0,100,215, 0, 97,218, 0, 94,220, 0, 93,221, 0, + 93,222, 0, 92,222, 0, 93,221, 0, 95,219, 0, 99,215, 0, + 102,212, 0,103,211, 0,105,209, 0,107,206, 0,110,202, 0, + 113,199, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 122,175, 0,123,172, 2,122,175, 0,122,176, 0,123,174, 1, + 123,170, 3,123,172, 2,123,173, 1,123,172, 2,123,171, 3, + 123,171, 3,123,169, 4,124,164, 9,124,154, 18,123,137, 41, + 121,125, 63,121,125, 63,122,130, 53,123,141, 34,124,155, 16, + 124,166, 7,123,170, 3,123,171, 3,123,169, 4,124,162, 10, + 124,164, 9,124,167, 6,123,171, 3,122,176, 0,121,179, 0, + 121,182, 0,119,186, 0,118,188, 0,118,188, 0,118,188, 0, + 119,185, 0,121,181, 0,123,172, 2,124,155, 16,124,143, 31, + 123,137, 41,124,150, 22,124,160, 12,123,168, 5,122,175, 0, + 121,181, 0,120,184, 0,119,186, 0,119,185, 0,120,184, 0, + 120,184, 0,119,186, 0,119,186, 0,118,188, 0,118,188, 0, + 118,189, 0,117,190, 0,118,189, 0,118,188, 0,119,185, 0, + 120,184, 0,120,184, 0,119,187, 0,117,191, 0,114,196, 0, + 111,201, 0,108,205, 0,105,209, 0,103,212, 0,102,212, 0, + 103,212, 0,106,208, 0,109,203, 0,108,205, 0,106,208, 0, + 102,212, 0, 95,219, 0, 90,224, 0, 87,227, 0, 86,228, 0, + 87,227, 0, 89,225, 0, 87,227, 0, 87,227, 0, 89,225, 0, + 92,222, 0, 97,218, 0,102,213, 0,106,207, 0,108,205, 0, + 105,209, 0,102,212, 0,100,215, 0, 99,215, 0,100,215, 0, + 102,212, 0,106,208, 0,110,203, 0,114,197, 0,117,191, 0, + 120,182, 0,123,171, 3,123,174, 1,121,179, 0,120,184, 0, + 118,189, 0,115,195, 0,114,196, 0,116,193, 0,119,187, 0, + 119,186, 0,117,191, 0,115,195, 0,111,202, 0,108,205, 0, + 108,206, 0,108,205, 0,109,203, 0,108,205, 0,108,205, 0, + 109,204, 0,107,206, 0,106,208, 0,102,212, 0,100,214, 0, + 100,215, 0,101,214, 0,103,212, 0,103,212, 0,102,212, 0, + 105,209, 0,108,205, 0,112,199, 0,116,192, 0,120,184, 0, + 123,172, 2,124,167, 6,123,171, 3,123,173, 1,123,172, 2, + 123,168, 5,124,162, 10,123,170, 3,120,184, 0,117,192, 0, + 114,196, 0,113,199, 0,112,200, 0,112,200, 0,111,201, 0, + 111,201, 0,112,200, 0,113,199, 0,113,198, 0,109,203, 0, + 107,206, 0,106,208, 0,105,209, 0,105,209, 0,105,209, 0, + 105,209, 0,105,209, 0,106,208, 0,106,208, 0,103,211, 0, + 101,214, 0, 98,217, 0, 96,219, 0, 95,219, 0, 95,220, 0, + 95,220, 0, 94,220, 0, 94,221, 0, 92,222, 0, 91,223, 0, + 89,225, 0, 87,227, 0, 87,227, 0, 88,226, 0, 92,222, 0, + 97,217, 0,103,211, 0,110,203, 0,115,194, 0,120,183, 0, + 123,170, 3,124,155, 16,124,149, 24,124,157, 15,124,161, 11, + 124,166, 7,123,174, 1,119,186, 0,116,192, 0,114,196, 0, + 109,203, 0,105,209, 0,103,212, 0,102,212, 0,105,209, 0, + 108,205, 0,108,206, 0,109,204, 0,107,206, 0,103,211, 0, + 99,216, 0, 95,219, 0, 93,221, 0, 93,222, 0, 94,221, 0, + 95,219, 0, 98,217, 0,101,213, 0,102,212, 0,105,209, 0, + 109,204, 0,113,198, 0,117,191, 0,120,183, 0,122,175, 0, + 123,168, 5,124,162, 10,124,155, 16,124,150, 22,124,165, 8, + 122,177, 0,119,185, 0,117,191, 0,114,196, 0,112,199, 0, + 111,201, 0,112,200, 0,109,204, 0,106,208, 0,103,211, 0, + 102,212, 0,103,212, 0,105,209, 0,102,212, 0,100,215, 0, + 99,216, 0, 98,217, 0, 97,218, 0, 96,219, 0, 97,218, 0, + 99,215, 0,103,212, 0,107,207, 0,109,204, 0,109,203, 0, + 111,201, 0,113,199, 0,114,197, 0,115,194, 0,117,191, 0, + 119,185, 0,122,176, 0,123,174, 1,122,178, 0,121,179, 0, + 122,178, 0,121,180, 0,120,182, 0,120,183, 0,120,182, 0, + 120,182, 0,120,182, 0,121,181, 0,122,176, 0,124,167, 6, + 124,152, 20,122,132, 48,121,127, 58,121,127, 58,123,137, 41, + 124,150, 22,124,158, 13,124,161, 11,124,161, 11,124,157, 15, + 124,161, 11,124,166, 7,123,171, 3,122,176, 0,122,178, 0, + 121,180, 0,121,180, 0,120,184, 0,119,186, 0,119,186, 0, + 120,183, 0,122,178, 0,123,171, 3,124,160, 12,124,145, 29, + 122,132, 48,123,141, 34,124,152, 20,124,161, 11,123,171, 3, + 121,179, 0,120,184, 0,118,188, 0,118,189, 0,118,188, 0, + 119,186, 0,119,185, 0,119,185, 0,119,185, 0,119,187, 0, + 118,189, 0,118,189, 0,118,188, 0,119,186, 0,120,184, 0, + 121,181, 0,121,180, 0,121,181, 0,119,185, 0,118,189, 0, + 115,195, 0,112,200, 0,109,204, 0,106,208, 0,104,210, 0, + 103,211, 0,105,209, 0,107,207, 0,111,202, 0,108,205, 0, + 106,208, 0,105,209, 0, 99,215, 0, 95,220, 0, 93,222, 0, + 93,222, 0, 94,221, 0, 92,223, 0, 90,224, 0, 91,223, 0, + 94,221, 0, 98,217, 0,102,212, 0,106,207, 0,110,202, 0, + 108,205, 0,105,209, 0,102,212, 0,101,213, 0,101,213, 0, + 103,212, 0,106,208, 0,109,204, 0,113,199, 0,116,193, 0, + 119,186, 0,122,176, 0,123,174, 1,122,178, 0,120,182, 0, + 119,187, 0,117,191, 0,115,194, 0,114,196, 0,115,194, 0, + 118,189, 0,119,187, 0,117,192, 0,115,195, 0,111,201, 0, + 108,205, 0,108,206, 0,108,205, 0,108,206, 0,106,208, 0, + 106,208, 0,107,207, 0,105,209, 0,105,209, 0,103,212, 0, + 101,213, 0,101,214, 0,102,212, 0,104,210, 0,104,210, 0, + 104,210, 0,107,207, 0,112,200, 0,115,194, 0,119,186, 0, + 123,173, 1,124,154, 18,124,160, 12,123,168, 5,123,173, 1, + 123,173, 1,123,170, 3,124,166, 7,123,174, 1,119,186, 0, + 116,192, 0,114,196, 0,114,197, 0,114,196, 0,115,195, 0, + 115,194, 0,115,194, 0,116,193, 0,116,193, 0,112,199, 0, + 108,205, 0,106,208, 0,105,209, 0,105,209, 0,105,209, 0, + 106,208, 0,106,207, 0,106,207, 0,106,207, 0,107,207, 0, + 107,207, 0,105,209, 0,102,213, 0,100,215, 0, 98,217, 0, + 97,218, 0, 96,219, 0, 96,219, 0, 95,219, 0, 93,222, 0, + 93,222, 0, 92,222, 0, 91,224, 0, 90,225, 0, 91,223, 0, + 94,220, 0,100,215, 0,105,209, 0,111,201, 0,117,191, 0, + 121,180, 0,124,167, 6,124,152, 20,123,139, 37,124,150, 22, + 124,157, 15,124,162, 10,123,172, 2,120,184, 0,117,191, 0, + 116,193, 0,110,202, 0,106,208, 0,103,211, 0,102,212, 0, + 104,210, 0,108,205, 0,111,201, 0,111,201, 0,109,204, 0, + 104,210, 0, 99,215, 0, 95,219, 0, 93,221, 0, 92,222, 0, + 93,222, 0, 94,220, 0, 96,219, 0, 99,216, 0,102,213, 0, + 104,210, 0,107,207, 0,110,202, 0,114,197, 0,117,192, 0, + 118,188, 0,120,183, 0,122,178, 0,123,171, 3,124,161, 11, + 124,158, 13,122,175, 0,119,185, 0,116,193, 0,113,199, 0, + 110,202, 0,109,204, 0,109,204, 0,111,202, 0,107,207, 0, + 103,211, 0,102,212, 0,103,211, 0,105,209, 0,108,205, 0, + 107,207, 0,106,208, 0,104,210, 0,102,212, 0,100,214, 0, + 100,215, 0,100,214, 0,102,212, 0,106,207, 0,111,201, 0, + 113,199, 0,113,198, 0,114,197, 0,114,197, 0,115,195, 0, + 116,192, 0,119,186, 0,122,178, 0,123,174, 1,122,178, 0, + 121,179, 0,120,182, 0,119,186, 0,118,188, 0,118,188, 0, + 118,188, 0,119,187, 0,118,188, 0,119,186, 0,120,182, 0, + 123,173, 1,124,160, 12,122,135, 44,121,127, 58,122,132, 48, + 122,130, 53,123,141, 34,124,147, 26,124,147, 26,124,143, 31, + 124,152, 20,124,160, 12,124,166, 7,123,172, 2,122,178, 0, + 121,180, 0,121,180, 0,122,178, 0,121,180, 0,121,182, 0, + 121,180, 0,122,175, 0,124,167, 6,124,155, 16,123,137, 41, + 121,127, 58,121,127, 58,122,135, 44,124,145, 29,124,157, 15, + 123,169, 4,121,179, 0,119,186, 0,118,189, 0,118,189, 0, + 118,188, 0,119,186, 0,120,184, 0,120,183, 0,119,187, 0, + 118,189, 0,117,191, 0,117,190, 0,118,188, 0,119,185, 0, + 120,182, 0,121,180, 0,122,178, 0,122,176, 0,121,179, 0, + 119,186, 0,117,191, 0,114,197, 0,111,201, 0,109,204, 0, + 107,207, 0,106,207, 0,107,207, 0,109,204, 0,112,200, 0, + 109,203, 0,107,207, 0,106,207, 0,104,210, 0,101,214, 0, + 99,215, 0, 99,215, 0, 99,215, 0, 95,219, 0, 95,220, 0, + 97,218, 0,100,214, 0,104,210, 0,108,205, 0,112,200, 0, + 113,199, 0,109,203, 0,106,207, 0,105,209, 0,104,210, 0, + 105,209, 0,107,206, 0,110,202, 0,114,197, 0,117,191, 0, + 119,185, 0,122,178, 0,123,170, 3,123,174, 1,122,178, 0, + 120,182, 0,119,186, 0,117,191, 0,117,192, 0,115,194, 0, + 116,193, 0,117,190, 0,119,186, 0,117,190, 0,116,193, 0, + 112,199, 0,109,203, 0,108,205, 0,108,205, 0,104,210, 0, + 102,212, 0,102,212, 0,105,209, 0,104,210, 0,103,211, 0, + 105,209, 0,103,211, 0,103,211, 0,105,209, 0,106,208, 0, + 105,209, 0,106,207, 0,110,202, 0,112,199, 0,114,196, 0, + 118,189, 0,122,178, 0,124,158, 13,124,150, 22,124,160, 12, + 123,168, 5,123,170, 3,124,167, 6,123,172, 2,122,178, 0, + 119,187, 0,116,193, 0,115,195, 0,115,194, 0,115,195, 0, + 114,196, 0,115,195, 0,117,191, 0,119,185, 0,118,188, 0, + 113,198, 0,109,204, 0,107,207, 0,106,208, 0,106,208, 0, + 107,207, 0,108,206, 0,108,205, 0,108,205, 0,108,205, 0, + 108,205, 0,110,203, 0,109,204, 0,106,208, 0,103,211, 0, + 101,213, 0,100,215, 0,100,215, 0,100,215, 0, 95,220, 0, + 92,222, 0, 92,223, 0, 94,221, 0, 96,219, 0, 95,220, 0, + 96,219, 0, 99,215, 0,103,211, 0,109,204, 0,115,195, 0, + 119,185, 0,123,171, 3,124,155, 16,123,139, 37,118,117, 85, + 122,132, 48,124,145, 29,124,154, 18,123,170, 3,120,183, 0, + 118,189, 0,117,192, 0,112,200, 0,107,207, 0,104,210, 0, + 103,212, 0,104,210, 0,108,205, 0,113,198, 0,115,194, 0, + 111,202, 0,106,208, 0,101,214, 0, 97,218, 0, 94,220, 0, + 94,221, 0, 94,221, 0, 95,220, 0, 96,219, 0, 98,217, 0, + 100,215, 0,103,212, 0,106,208, 0,108,205, 0,111,202, 0, + 113,198, 0,114,196, 0,115,194, 0,117,192, 0,118,188, 0, + 120,182, 0,123,172, 2,123,170, 3,120,183, 0,116,192, 0, + 112,200, 0,108,205, 0,107,207, 0,107,207, 0,109,204, 0, + 109,204, 0,106,208, 0,104,210, 0,105,209, 0,107,207, 0, + 110,202, 0,113,198, 0,113,199, 0,111,201, 0,109,204, 0, + 106,208, 0,103,211, 0,102,212, 0,103,211, 0,107,207, 0, + 111,201, 0,116,193, 0,116,193, 0,115,194, 0,115,195, 0, + 115,195, 0,116,192, 0,118,188, 0,121,179, 0,123,170, 3, + 122,175, 0,121,180, 0,119,185, 0,118,188, 0,118,189, 0, + 118,189, 0,118,188, 0,118,188, 0,118,189, 0,118,189, 0, + 120,184, 0,122,176, 0,124,161, 11,123,137, 41,123,139, 37, + 123,141, 34,122,132, 48,122,132, 48,122,132, 48,121,127, 58, + 123,137, 41,124,147, 26,124,157, 15,124,165, 8,123,172, 2, + 122,178, 0,121,179, 0,122,178, 0,122,175, 0,122,175, 0, + 122,176, 0,123,172, 2,124,164, 9,124,152, 20,123,139, 37, + 118,117, 85,118,114, 96,118,114, 96,119,119, 77,122,130, 53, + 124,145, 29,124,164, 9,122,176, 0,120,184, 0,118,188, 0, + 118,189, 0,119,187, 0,120,184, 0,121,181, 0,119,185, 0, + 118,189, 0,117,191, 0,117,191, 0,117,190, 0,119,187, 0, + 120,183, 0,121,179, 0,122,177, 0,123,174, 1,123,172, 2, + 123,170, 3,122,178, 0,120,184, 0,117,190, 0,115,194, 0, + 113,198, 0,111,201, 0,110,203, 0,109,203, 0,110,202, 0, + 112,200, 0,112,200, 0,109,203, 0,109,204, 0,110,202, 0, + 107,207, 0,106,208, 0,105,209, 0,103,211, 0,101,214, 0, + 101,214, 0,103,211, 0,107,207, 0,111,201, 0,115,194, 0, + 118,188, 0,115,194, 0,112,199, 0,109,203, 0,108,205, 0, + 109,204, 0,110,202, 0,113,198, 0,116,193, 0,119,187, 0, + 121,179, 0,123,172, 2,124,162, 10,124,165, 8,123,170, 3, + 123,174, 1,122,178, 0,120,182, 0,119,187, 0,118,188, 0, + 117,191, 0,117,191, 0,118,189, 0,119,185, 0,119,186, 0, + 118,189, 0,114,197, 0,111,202, 0,109,203, 0,104,210, 0, + 100,215, 0, 98,217, 0, 98,217, 0,100,214, 0,104,210, 0, + 103,211, 0,105,209, 0,106,207, 0,106,207, 0,107,207, 0, + 106,208, 0,106,208, 0,108,205, 0,112,200, 0,112,199, 0, + 114,197, 0,117,191, 0,121,182, 0,123,168, 5,124,161, 11, + 124,149, 24,124,158, 13,124,162, 10,124,166, 7,123,174, 1, + 121,180, 0,118,188, 0,116,193, 0,115,194, 0,116,192, 0, + 114,196, 0,113,198, 0,114,197, 0,116,193, 0,118,188, 0, + 117,191, 0,114,196, 0,110,202, 0,108,205, 0,108,205, 0, + 109,204, 0,110,203, 0,111,202, 0,112,200, 0,112,200, 0, + 111,201, 0,111,201, 0,112,200, 0,113,199, 0,110,202, 0, + 108,205, 0,106,208, 0,105,209, 0,103,211, 0,101,214, 0, + 95,219, 0, 92,222, 0, 91,223, 0, 93,221, 0, 97,217, 0, + 102,212, 0,102,212, 0,105,209, 0,109,204, 0,114,196, 0, + 119,187, 0,123,174, 1,124,155, 16,122,132, 48,118,114, 96, + 107, 89,252,113,100,242,120,122, 70,124,147, 26,123,169, 4, + 121,181, 0,119,187, 0,118,189, 0,114,197, 0,109,204, 0, + 105,209, 0,104,210, 0,105,209, 0,109,204, 0,114,196, 0, + 117,190, 0,113,198, 0,108,206, 0,103,212, 0, 99,215, 0, + 97,218, 0, 96,219, 0, 97,218, 0, 97,218, 0, 97,217, 0, + 98,217, 0,100,215, 0,102,213, 0,104,210, 0,107,206, 0, + 109,203, 0,111,201, 0,112,200, 0,112,200, 0,112,200, 0, + 114,197, 0,116,193, 0,119,186, 0,122,176, 0,122,178, 0, + 117,190, 0,113,199, 0,108,205, 0,106,208, 0,106,208, 0, + 107,206, 0,110,203, 0,109,204, 0,107,206, 0,108,206, 0, + 110,203, 0,112,200, 0,114,196, 0,115,195, 0,116,193, 0, + 115,195, 0,111,201, 0,108,206, 0,106,208, 0,106,208, 0, + 108,206, 0,112,200, 0,116,192, 0,119,187, 0,118,189, 0, + 116,192, 0,116,193, 0,116,192, 0,118,188, 0,121,181, 0, + 123,169, 4,123,170, 3,121,181, 0,119,186, 0,118,188, 0, + 118,188, 0,118,188, 0,118,188, 0,119,187, 0,118,188, 0, + 118,188, 0,120,184, 0,122,175, 0,124,161, 11,124,145, 29, + 124,145, 29,124,143, 31,122,132, 48,121,125, 63,119,119, 77, + 119,119, 77,123,141, 34,124,158, 13,124,166, 7,123,169, 4, + 123,171, 3,122,177, 0,122,178, 0,122,175, 0,123,170, 3, + 123,169, 4,123,168, 5,124,161, 11,124,145, 29,123,139, 37, + 120,122, 70,109, 92,251,107, 89,252,109, 92,251,114,104,197, + 115,107,129,122,132, 48,124,158, 13,123,172, 2,121,181, 0, + 119,186, 0,118,188, 0,119,186, 0,120,182, 0,121,179, 0, + 119,186, 0,117,190, 0,117,191, 0,117,190, 0,118,188, 0, + 120,183, 0,122,178, 0,123,173, 1,123,169, 4,124,167, 6, + 124,165, 8,124,164, 9,124,164, 9,123,171, 3,122,178, 0, + 120,184, 0,118,188, 0,116,193, 0,114,197, 0,112,199, 0, + 112,200, 0,113,199, 0,114,196, 0,113,198, 0,112,200, 0, + 113,198, 0,113,199, 0,111,201, 0,110,202, 0,108,205, 0, + 106,207, 0,107,207, 0,110,202, 0,112,200, 0,114,197, 0, + 117,192, 0,118,189, 0,117,190, 0,116,192, 0,114,197, 0, + 113,198, 0,114,197, 0,116,193, 0,118,188, 0,121,181, 0, + 123,171, 3,124,160, 12,124,149, 24,123,141, 34,124,154, 18, + 124,161, 11,124,166, 7,122,175, 0,121,182, 0,120,184, 0, + 120,182, 0,119,187, 0,118,188, 0,119,187, 0,120,184, 0, + 121,182, 0,119,187, 0,115,195, 0,112,200, 0,106,208, 0, + 100,215, 0, 95,219, 0, 94,221, 0, 94,221, 0, 96,219, 0, + 100,215, 0,103,211, 0,105,209, 0,108,205, 0,108,205, 0, + 107,207, 0,106,208, 0,106,207, 0,109,204, 0,113,198, 0, + 113,198, 0,114,196, 0,117,191, 0,120,182, 0,123,172, 2, + 124,165, 8,124,154, 18,123,141, 34,124,147, 26,124,164, 9, + 123,173, 1,120,184, 0,117,190, 0,116,193, 0,116,193, 0, + 117,191, 0,115,194, 0,114,197, 0,114,197, 0,115,194, 0, + 116,193, 0,114,197, 0,113,198, 0,113,199, 0,111,201, 0, + 111,201, 0,111,201, 0,111,201, 0,112,200, 0,113,199, 0, + 114,196, 0,116,193, 0,116,193, 0,116,192, 0,116,193, 0, + 114,196, 0,112,199, 0,110,202, 0,109,204, 0,108,205, 0, + 102,212, 0, 97,217, 0, 94,221, 0, 92,222, 0, 94,221, 0, + 98,217, 0,103,211, 0,109,204, 0,111,201, 0,114,196, 0, + 119,187, 0,122,176, 0,124,165, 8,124,149, 24,120,122, 70, + 99, 75,247, 30, 55,222, 30, 55,222,114,104,197,124,145, 29, + 124,167, 6,122,178, 0,120,184, 0,119,185, 0,115,194, 0, + 110,202, 0,107,207, 0,106,208, 0,108,206, 0,112,200, 0, + 117,192, 0,119,186, 0,115,195, 0,110,202, 0,105,209, 0, + 102,212, 0,100,214, 0,100,215, 0,101,214, 0,101,214, 0, + 100,215, 0,100,214, 0,101,214, 0,102,212, 0,103,211, 0, + 106,208, 0,108,205, 0,111,201, 0,111,201, 0,110,202, 0, + 110,202, 0,110,202, 0,112,199, 0,116,193, 0,119,186, 0, + 123,174, 1,119,186, 0,114,196, 0,110,202, 0,107,207, 0, + 106,207, 0,108,206, 0,110,202, 0,112,199, 0,110,202, 0, + 110,202, 0,112,200, 0,113,198, 0,113,198, 0,114,197, 0, + 115,194, 0,118,188, 0,116,192, 0,113,199, 0,110,203, 0, + 109,204, 0,111,202, 0,114,197, 0,118,189, 0,121,180, 0, + 120,184, 0,118,189, 0,117,191, 0,117,191, 0,118,188, 0, + 121,182, 0,123,171, 3,123,169, 4,121,179, 0,120,184, 0, + 119,186, 0,118,188, 0,117,191, 0,116,192, 0,117,191, 0, + 119,186, 0,119,186, 0,120,182, 0,123,174, 1,124,160, 12, + 124,145, 29,124,143, 31,123,139, 37,120,122, 70,121,125, 63, + 118,117, 85,122,132, 48,124,152, 20,124,166, 7,123,173, 1, + 123,174, 1,123,170, 3,122,175, 0,123,174, 1,123,170, 3, + 124,162, 10,124,162, 10,124,158, 13,124,147, 26,120,122, 70, + 118,117, 85,109, 92,251, 30, 43,203, 30, 43,203, 99, 75,247, + 117,110,109,121,127, 58,122,132, 48,124,157, 15,123,170, 3, + 122,178, 0,119,185, 0,119,187, 0,119,186, 0,120,182, 0, + 122,178, 0,120,184, 0,118,188, 0,118,188, 0,119,186, 0, + 121,182, 0,123,174, 1,124,166, 7,124,158, 13,124,152, 20, + 124,154, 18,124,155, 16,124,154, 18,124,154, 18,124,157, 15, + 124,161, 11,124,166, 7,123,174, 1,120,182, 0,118,189, 0, + 115,194, 0,114,196, 0,114,197, 0,114,196, 0,116,192, 0, + 115,194, 0,116,193, 0,117,191, 0,115,194, 0,114,196, 0, + 114,197, 0,113,199, 0,114,197, 0,114,197, 0,114,197, 0, + 115,194, 0,118,189, 0,117,191, 0,117,191, 0,117,191, 0, + 118,189, 0,117,190, 0,118,188, 0,120,184, 0,121,181, 0, + 122,175, 0,124,165, 8,124,147, 26,115,107,129,118,117, 85, + 123,137, 41,124,149, 24,124,166, 7,122,176, 0,120,182, 0, + 120,184, 0,121,181, 0,121,180, 0,120,182, 0,120,182, 0, + 121,182, 0,121,180, 0,118,188, 0,114,197, 0,108,205, 0, + 102,213, 0, 96,219, 0, 92,223, 0, 89,225, 0, 90,225, 0, + 92,222, 0, 96,219, 0,100,215, 0,103,211, 0,106,208, 0, + 108,206, 0,106,207, 0,106,208, 0,106,207, 0,108,205, 0, + 112,199, 0,115,194, 0,116,192, 0,118,189, 0,121,181, 0, + 123,170, 3,124,165, 8,124,155, 16,124,143, 31,124,147, 26, + 124,165, 8,122,178, 0,119,186, 0,117,191, 0,116,193, 0, + 116,193, 0,117,191, 0,117,191, 0,116,193, 0,116,193, 0, + 117,192, 0,114,197, 0,111,201, 0,110,202, 0,110,202, 0, + 110,202, 0,110,202, 0,111,202, 0,111,201, 0,113,199, 0, + 114,197, 0,116,193, 0,117,190, 0,119,187, 0,119,187, 0, + 118,188, 0,117,190, 0,116,192, 0,115,194, 0,114,196, 0, + 111,202, 0,105,209, 0,100,214, 0, 97,218, 0, 95,219, 0, + 97,218, 0,101,214, 0,106,208, 0,111,201, 0,115,194, 0, + 118,188, 0,121,182, 0,123,173, 1,124,162, 10,124,149, 24, + 122,132, 48,114,104,197, 30, 55,222, 30, 37,192,113,100,242, + 123,139, 37,124,161, 11,123,172, 2,122,178, 0,120,183, 0, + 116,192, 0,112,200, 0,109,204, 0,109,204, 0,111,202, 0, + 115,195, 0,120,184, 0,120,183, 0,116,192, 0,112,200, 0, + 108,206, 0,105,209, 0,104,210, 0,104,210, 0,105,209, 0, + 101,213, 0,101,214, 0,101,214, 0,102,212, 0,103,211, 0, + 104,210, 0,105,209, 0,107,207, 0,109,204, 0,112,200, 0, + 110,202, 0,109,203, 0,109,203, 0,111,202, 0,114,197, 0, + 117,190, 0,121,181, 0,121,181, 0,117,191, 0,113,199, 0, + 110,202, 0,109,203, 0,110,202, 0,112,199, 0,114,196, 0, + 113,198, 0,113,199, 0,113,198, 0,114,197, 0,114,197, 0, + 114,196, 0,116,193, 0,117,190, 0,116,192, 0,115,195, 0, + 114,196, 0,113,198, 0,114,196, 0,117,191, 0,120,184, 0, + 123,174, 1,122,177, 0,120,183, 0,118,188, 0,118,189, 0, + 119,187, 0,121,182, 0,122,177, 0,122,178, 0,121,180, 0, + 120,184, 0,118,188, 0,117,191, 0,116,193, 0,115,194, 0, + 116,192, 0,118,188, 0,120,184, 0,121,180, 0,123,172, 2, + 124,158, 13,123,139, 37,123,137, 41,122,130, 53,121,125, 63, + 121,127, 58,122,132, 48,124,145, 29,124,160, 12,123,171, 3, + 122,178, 0,121,179, 0,122,175, 0,123,171, 3,123,169, 4, + 124,162, 10,124,157, 15,124,152, 20,124,143, 31,121,125, 63, + 109, 92,251,109, 92,251,105, 84,252, 30, 55,222, 69, 60,230, + 111, 96,248,121,125, 63,123,139, 37,123,141, 34,124,155, 16, + 123,168, 5,123,174, 1,120,182, 0,119,185, 0,120,184, 0, + 121,181, 0,122,177, 0,121,182, 0,120,184, 0,120,182, 0, + 122,178, 0,123,171, 3,124,167, 6,124,164, 9,124,154, 18, + 123,141, 34,123,139, 37,124,143, 31,124,145, 29,124,143, 31, + 124,143, 31,124,152, 20,124,160, 12,124,165, 8,123,169, 4, + 121,180, 0,119,187, 0,117,192, 0,115,194, 0,116,193, 0, + 117,191, 0,118,188, 0,118,188, 0,119,186, 0,118,188, 0, + 117,192, 0,116,193, 0,115,194, 0,115,194, 0,115,194, 0, + 115,194, 0,117,191, 0,118,189, 0,117,191, 0,117,191, 0, + 117,190, 0,118,189, 0,119,187, 0,119,186, 0,120,183, 0, + 121,179, 0,123,172, 2,124,161, 11,123,139, 37,111, 96,248, + 115,107,129,122,130, 53,124,152, 20,124,167, 6,122,176, 0, + 121,182, 0,120,183, 0,121,179, 0,123,174, 1,123,172, 2, + 122,175, 0,122,176, 0,120,184, 0,115,194, 0,110,203, 0, + 104,210, 0, 98,217, 0, 92,223, 0, 88,226, 0, 86,228, 0, + 87,227, 0, 90,225, 0, 94,221, 0, 98,217, 0,101,214, 0, + 104,210, 0,107,206, 0,107,207, 0,106,207, 0,106,207, 0, + 108,205, 0,111,201, 0,117,192, 0,118,189, 0,119,185, 0, + 122,178, 0,124,165, 8,124,161, 11,124,152, 20,123,141, 34, + 124,154, 18,124,167, 6,122,177, 0,120,184, 0,118,189, 0, + 117,192, 0,116,192, 0,117,190, 0,119,186, 0,118,188, 0, + 118,188, 0,117,192, 0,113,198, 0,110,202, 0,109,204, 0, + 108,205, 0,109,204, 0,109,203, 0,111,202, 0,112,199, 0, + 114,197, 0,116,193, 0,117,191, 0,118,189, 0,118,188, 0, + 119,186, 0,120,184, 0,121,181, 0,121,182, 0,120,183, 0, + 117,190, 0,114,197, 0,109,204, 0,104,210, 0,101,214, 0, + 100,215, 0,101,214, 0,105,209, 0,109,204, 0,114,197, 0, + 117,190, 0,120,183, 0,122,176, 0,123,168, 5,124,158, 13, + 124,155, 16,124,149, 24,121,125, 63,102, 80,250, 69, 60,230, + 107, 89,252,122,130, 53,124,154, 18,124,166, 7,123,171, 3, + 121,181, 0,117,191, 0,114,197, 0,111,201, 0,112,200, 0, + 114,196, 0,118,188, 0,122,175, 0,121,180, 0,118,189, 0, + 114,197, 0,110,202, 0,108,206, 0,107,207, 0,107,206, 0, + 107,207, 0,104,210, 0,103,211, 0,104,210, 0,105,209, 0, + 106,208, 0,106,208, 0,106,207, 0,108,206, 0,110,203, 0, + 112,200, 0,110,202, 0,109,204, 0,109,204, 0,110,202, 0, + 113,198, 0,117,192, 0,120,184, 0,119,185, 0,119,186, 0, + 116,192, 0,114,196, 0,114,197, 0,115,195, 0,116,192, 0, + 117,191, 0,115,194, 0,115,195, 0,115,194, 0,116,193, 0, + 115,194, 0,116,193, 0,117,191, 0,116,192, 0,115,194, 0, + 114,196, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 122,178, 0,123,169, 4,124,164, 9,123,174, 1,121,181, 0, + 120,184, 0,120,184, 0,121,182, 0,120,183, 0,120,184, 0, + 119,186, 0,118,188, 0,117,191, 0,116,193, 0,115,194, 0, + 115,194, 0,117,192, 0,118,188, 0,121,181, 0,122,176, 0, + 124,167, 6,124,150, 22,123,137, 41,122,132, 48,121,125, 63, + 120,122, 70,121,125, 63,124,145, 29,124,154, 18,124,166, 7, + 122,178, 0,120,183, 0,120,184, 0,121,181, 0,123,174, 1, + 124,167, 6,124,161, 11,124,152, 20,123,139, 37,123,139, 37, + 122,130, 53,122,132, 48,123,137, 41,122,132, 48,118,114, 96, + 115,107,129,117,110,109,121,127, 58,123,141, 34,124,143, 31, + 124,152, 20,124,162, 10,124,167, 6,122,176, 0,121,179, 0, + 121,179, 0,122,177, 0,122,176, 0,121,180, 0,121,181, 0, + 122,177, 0,123,169, 4,124,166, 7,124,165, 8,124,158, 13, + 124,145, 29,123,141, 34,123,137, 41,122,130, 53,122,132, 48, + 122,132, 48,121,125, 63,122,130, 53,123,141, 34,124,150, 22, + 124,154, 18,124,167, 6,122,178, 0,119,185, 0,118,189, 0, + 117,190, 0,118,189, 0,119,186, 0,121,181, 0,121,180, 0, + 121,182, 0,119,187, 0,117,191, 0,116,192, 0,116,193, 0, + 116,192, 0,117,191, 0,119,187, 0,119,187, 0,118,188, 0, + 118,189, 0,118,188, 0,118,188, 0,119,186, 0,120,184, 0, + 121,182, 0,122,178, 0,123,170, 3,124,157, 15,122,135, 44, + 114,104,197,119,119, 77,123,141, 34,124,157, 15,123,169, 4, + 122,177, 0,121,181, 0,121,182, 0,122,176, 0,123,171, 3, + 124,165, 8,124,162, 10,123,171, 3,118,188, 0,113,199, 0, + 106,207, 0,100,214, 0, 94,221, 0, 89,226, 0, 85,229, 0, + 84,229, 0, 85,229, 0, 88,226, 0, 93,222, 0, 97,218, 0, + 100,214, 0,104,210, 0,107,206, 0,108,205, 0,107,206, 0, + 107,207, 0,108,206, 0,110,202, 0,115,194, 0,119,185, 0, + 121,182, 0,122,175, 0,124,161, 11,124,152, 20,124,145, 29, + 123,139, 37,124,154, 18,124,165, 8,123,173, 1,121,180, 0, + 119,185, 0,118,188, 0,117,190, 0,118,189, 0,120,184, 0, + 121,181, 0,121,182, 0,118,189, 0,114,197, 0,110,202, 0, + 108,205, 0,108,206, 0,109,204, 0,110,202, 0,112,200, 0, + 114,196, 0,117,192, 0,117,191, 0,117,192, 0,117,191, 0, + 117,190, 0,119,187, 0,120,182, 0,123,174, 1,124,165, 8, + 123,173, 1,120,184, 0,117,192, 0,113,199, 0,109,204, 0, + 106,207, 0,105,209, 0,106,207, 0,109,204, 0,113,198, 0, + 117,191, 0,120,183, 0,122,175, 0,123,168, 5,124,160, 12, + 124,161, 11,124,162, 10,124,157, 15,123,137, 41,111, 96,248, + 99, 75,247,102, 80,250,118,117, 85,124,143, 31,124,157, 15, + 124,166, 7,122,178, 0,118,188, 0,115,194, 0,114,197, 0, + 114,196, 0,117,191, 0,121,180, 0,124,164, 9,122,175, 0, + 119,186, 0,116,193, 0,113,199, 0,111,202, 0,110,202, 0, + 110,202, 0,111,202, 0,108,205, 0,108,206, 0,108,206, 0, + 108,205, 0,108,205, 0,109,204, 0,109,203, 0,110,202, 0, + 112,200, 0,112,199, 0,111,202, 0,109,203, 0,109,203, 0, + 110,202, 0,113,198, 0,117,192, 0,119,186, 0,119,187, 0, + 119,187, 0,120,184, 0,119,186, 0,119,187, 0,119,185, 0, + 121,182, 0,119,186, 0,118,189, 0,117,191, 0,117,191, 0, + 117,191, 0,117,191, 0,117,191, 0,116,192, 0,115,194, 0, + 114,196, 0,114,197, 0,114,196, 0,116,193, 0,119,186, 0, + 122,176, 0,123,169, 4,124,164, 9,124,155, 16,124,160, 12, + 123,170, 3,122,176, 0,121,179, 0,121,182, 0,119,185, 0, + 119,187, 0,118,188, 0,117,190, 0,117,192, 0,116,193, 0, + 116,193, 0,117,192, 0,118,188, 0,120,184, 0,122,177, 0, + 123,169, 4,124,158, 13,123,139, 37,122,132, 48,122,130, 53, + 120,122, 70,118,117, 85,123,137, 41,124,154, 18,124,161, 11, + 123,172, 2,121,182, 0,119,187, 0,118,188, 0,119,185, 0, + 122,178, 0,123,171, 3,124,161, 11,124,160, 12,124,160, 12, + 124,157, 15,124,157, 15,124,162, 10,124,164, 9,124,160, 12, + 124,150, 22,123,137, 41,122,135, 44,122,130, 53,123,137, 41, + 123,141, 34,124,147, 26,124,152, 20,124,152, 20,124,165, 8, + 123,169, 4,123,171, 3,123,170, 3,122,177, 0,121,179, 0, + 122,178, 0,123,170, 3,124,164, 9,124,162, 10,124,158, 13, + 124,149, 24,122,135, 44,122,132, 48,121,127, 58,118,117, 85, + 119,119, 77,118,114, 96,115,107,129,109, 92,251,114,104,197, + 120,122, 70,122,132, 48,124,147, 26,124,164, 9,123,174, 1, + 121,181, 0,120,184, 0,119,185, 0,120,183, 0,122,178, 0, + 123,171, 3,123,173, 1,120,182, 0,118,188, 0,117,191, 0, + 117,191, 0,118,189, 0,119,186, 0,121,181, 0,121,180, 0, + 121,182, 0,120,183, 0,120,183, 0,120,184, 0,120,183, 0, + 121,182, 0,122,178, 0,123,173, 1,124,164, 9,124,149, 24, + 122,132, 48,122,130, 53,124,143, 31,124,157, 15,124,166, 7, + 123,170, 3,122,175, 0,122,178, 0,122,178, 0,123,169, 4, + 124,164, 9,124,157, 15,124,150, 22,123,174, 1,117,191, 0, + 111,202, 0,104,210, 0, 98,217, 0, 92,222, 0, 87,227, 0, + 84,229, 0, 83,230, 0, 85,229, 0, 89,226, 0, 93,222, 0, + 97,218, 0,101,214, 0,103,211, 0,106,207, 0,109,204, 0, + 109,204, 0,108,205, 0,108,205, 0,110,203, 0,114,197, 0, + 119,187, 0,121,179, 0,123,172, 2,124,160, 12,123,137, 41, + 122,130, 53,122,132, 48,124,147, 26,124,160, 12,123,173, 1, + 121,180, 0,120,182, 0,120,183, 0,119,186, 0,119,186, 0, + 120,184, 0,121,179, 0,123,172, 2,120,184, 0,116,192, 0, + 113,198, 0,110,202, 0,109,203, 0,110,202, 0,112,199, 0, + 115,194, 0,118,189, 0,118,189, 0,117,190, 0,117,191, 0, + 117,191, 0,117,190, 0,119,186, 0,121,180, 0,123,169, 4, + 124,154, 18,124,158, 13,123,174, 1,120,183, 0,117,190, 0, + 115,195, 0,113,199, 0,112,200, 0,112,199, 0,114,196, 0, + 117,190, 0,120,182, 0,123,172, 2,124,164, 9,124,155, 16, + 124,157, 15,124,162, 10,124,164, 9,124,158, 13,123,141, 34, + 114,104,197,102, 80,250,109, 92,251,113,100,242,122,132, 48, + 124,149, 24,124,162, 10,122,175, 0,120,184, 0,117,191, 0, + 116,193, 0,117,191, 0,120,184, 0,123,171, 3,124,147, 26, + 124,167, 6,121,180, 0,118,189, 0,115,194, 0,114,197, 0, + 113,198, 0,113,198, 0,114,197, 0,113,198, 0,112,199, 0, + 112,200, 0,112,200, 0,112,200, 0,112,199, 0,113,198, 0, + 114,196, 0,114,197, 0,113,198, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,114,197, 0,117,190, 0,118,188, 0, + 118,188, 0,119,186, 0,120,182, 0,123,173, 1,123,171, 3, + 123,168, 5,123,171, 3,122,178, 0,121,182, 0,120,183, 0, + 120,184, 0,119,186, 0,119,187, 0,118,189, 0,117,192, 0, + 115,194, 0,114,196, 0,114,197, 0,115,195, 0,117,191, 0, + 120,184, 0,123,173, 1,124,158, 13,124,157, 15,124,152, 20, + 123,139, 37,124,154, 18,124,165, 8,123,170, 3,122,178, 0, + 120,184, 0,119,186, 0,119,187, 0,118,188, 0,118,189, 0, + 117,190, 0,118,189, 0,119,186, 0,120,182, 0,122,175, 0, + 124,167, 6,124,158, 13,124,143, 31,121,125, 63,122,130, 53, + 122,130, 53,120,122, 70,117,110,109,124,147, 26,124,162, 10, + 123,168, 5,122,177, 0,120,184, 0,118,189, 0,117,191, 0, + 118,188, 0,121,182, 0,123,173, 1,123,172, 2,123,173, 1, + 123,173, 1,123,169, 4,123,174, 1,122,178, 0,122,178, 0, + 122,176, 0,123,169, 4,124,157, 15,124,149, 24,124,143, 31, + 122,135, 44,122,135, 44,123,137, 41,122,135, 44,122,132, 48, + 124,147, 26,124,155, 16,124,166, 7,123,173, 1,122,178, 0, + 122,178, 0,123,173, 1,124,164, 9,124,160, 12,124,157, 15, + 124,149, 24,122,132, 48,119,119, 77,118,114, 96,115,107,129, + 113,100,242,113,100,242,109, 92,251, 99, 75,247, 30, 49,213, + 30, 31,180, 94, 70,242,111, 96,248,118,117, 85,122,135, 44, + 124,154, 18,124,166, 7,123,174, 1,122,178, 0,122,177, 0, + 123,171, 3,124,160, 12,124,164, 9,122,177, 0,120,184, 0, + 119,187, 0,119,187, 0,120,184, 0,122,178, 0,123,170, 3, + 124,165, 8,124,167, 6,123,170, 3,123,173, 1,122,175, 0, + 122,176, 0,123,174, 1,123,170, 3,124,167, 6,124,164, 9, + 124,157, 15,124,143, 31,124,152, 20,124,162, 10,123,170, 3, + 122,175, 0,122,177, 0,122,176, 0,123,172, 2,123,172, 2, + 124,155, 16,124,149, 24,123,141, 34,124,160, 12,122,177, 0, + 117,192, 0,110,202, 0,104,210, 0, 98,217, 0, 93,222, 0, + 88,226, 0, 85,229, 0, 85,229, 0, 86,228, 0, 90,225, 0, + 94,220, 0, 98,216, 0,101,214, 0,101,213, 0,104,210, 0, + 105,209, 0,107,207, 0,109,203, 0,109,204, 0,109,204, 0, + 112,200, 0,117,191, 0,122,178, 0,123,171, 3,124,158, 13, + 122,132, 48,115,107,129,118,114, 96,122,130, 53,124,161, 11, + 122,177, 0,120,184, 0,119,185, 0,120,184, 0,121,180, 0, + 120,183, 0,120,183, 0,121,182, 0,122,178, 0,121,182, 0, + 120,184, 0,117,191, 0,114,196, 0,113,198, 0,114,196, 0, + 116,192, 0,119,186, 0,119,185, 0,119,186, 0,119,187, 0, + 118,188, 0,118,189, 0,118,188, 0,120,184, 0,122,177, 0, + 124,162, 10,124,149, 24,124,147, 26,124,157, 15,123,169, 4, + 122,177, 0,120,183, 0,119,187, 0,118,189, 0,118,189, 0, + 119,186, 0,121,180, 0,123,169, 4,124,157, 15,124,143, 31, + 124,143, 31,124,154, 18,124,160, 12,124,161, 11,124,157, 15, + 123,137, 41,111, 96,248,105, 84,252,113,100,242,118,117, 85, + 121,127, 58,123,141, 34,124,155, 16,123,169, 4,121,180, 0, + 119,186, 0,118,189, 0,119,186, 0,122,178, 0,124,161, 11, + 121,125, 63,124,154, 18,123,171, 3,121,182, 0,118,188, 0, + 117,191, 0,116,192, 0,116,192, 0,117,192, 0,116,193, 0, + 115,194, 0,115,195, 0,115,195, 0,115,194, 0,116,193, 0, + 117,192, 0,116,193, 0,115,194, 0,114,196, 0,113,198, 0, + 112,200, 0,112,200, 0,113,198, 0,115,194, 0,118,188, 0, + 118,188, 0,118,188, 0,119,185, 0,121,179, 0,123,168, 5, + 124,149, 24,123,141, 34,124,158, 13,124,166, 7,123,169, 4, + 123,171, 3,123,174, 1,122,178, 0,121,181, 0,119,185, 0, + 118,189, 0,116,192, 0,115,194, 0,115,194, 0,117,192, 0, + 118,188, 0,121,180, 0,123,169, 4,124,154, 18,124,145, 29, + 124,147, 26,123,141, 34,122,130, 53,124,145, 29,124,155, 16, + 123,170, 3,122,178, 0,121,180, 0,121,181, 0,120,183, 0, + 120,184, 0,120,184, 0,121,182, 0,122,178, 0,123,170, 3, + 124,161, 11,124,150, 22,123,141, 34,119,119, 77,118,114, 96, + 120,122, 70,121,127, 58,121,125, 63,123,137, 41,124,157, 15, + 123,169, 4,123,174, 1,121,179, 0,119,185, 0,118,189, 0, + 117,191, 0,118,188, 0,121,182, 0,121,180, 0,120,182, 0, + 120,184, 0,120,182, 0,121,181, 0,120,184, 0,119,186, 0, + 119,186, 0,119,185, 0,121,181, 0,123,171, 3,124,157, 15, + 124,149, 24,123,137, 41,121,127, 58,122,130, 53,121,127, 58, + 123,137, 41,124,154, 18,124,165, 8,123,171, 3,122,175, 0, + 122,177, 0,122,175, 0,123,168, 5,124,155, 16,124,155, 16, + 124,149, 24,122,135, 44,114,104,197,107, 89,252,102, 80,250, + 102, 80,250,111, 96,248,113,100,242,109, 92,251,107, 89,252, + 102, 80,250, 30, 49,213, 30, 26,168, 69, 60,230,102, 80,250, + 115,107,129,119,119, 77,123,137, 41,124,154, 18,124,164, 9, + 124,166, 7,124,160, 12,124,145, 29,124,152, 20,123,169, 4, + 122,178, 0,120,182, 0,121,181, 0,122,176, 0,124,166, 7, + 124,150, 22,122,130, 53,122,130, 53,123,141, 34,124,150, 22, + 124,157, 15,124,160, 12,124,166, 7,123,169, 4,123,168, 5, + 124,164, 9,124,157, 15,124,154, 18,124,162, 10,123,169, 4, + 122,175, 0,122,178, 0,122,178, 0,122,176, 0,123,168, 5, + 124,160, 12,121,127, 58,119,119, 77,124,143, 31,124,165, 8, + 122,178, 0,117,191, 0,111,201, 0,105,209, 0,100,215, 0, + 95,220, 0, 91,224, 0, 88,226, 0, 87,227, 0, 89,225, 0, + 92,222, 0, 97,218, 0,101,214, 0,100,214, 0,100,214, 0, + 101,213, 0,101,214, 0,102,212, 0,106,208, 0,109,204, 0, + 109,204, 0,111,201, 0,115,194, 0,120,183, 0,123,171, 3, + 124,160, 12,122,135, 44,102, 80,250,105, 84,252,121,125, 63, + 124,164, 9,121,179, 0,119,186, 0,118,188, 0,119,186, 0, + 121,180, 0,121,179, 0,120,182, 0,120,183, 0,120,182, 0, + 119,185, 0,120,184, 0,121,180, 0,119,186, 0,118,189, 0, + 119,186, 0,121,181, 0,121,179, 0,121,180, 0,121,181, 0, + 121,182, 0,120,183, 0,120,184, 0,120,184, 0,121,181, 0, + 123,171, 3,124,155, 16,124,143, 31,123,137, 41,121,125, 63, + 123,141, 34,124,152, 20,124,162, 10,123,169, 4,123,173, 1, + 123,174, 1,123,172, 2,124,166, 7,124,150, 22,123,139, 37, + 122,130, 53,122,130, 53,124,143, 31,124,152, 20,124,155, 16, + 124,150, 22,121,127, 58,111, 96,248,117,110,109,121,125, 63, + 122,135, 44,124,143, 31,124,149, 24,124,149, 24,124,164, 9, + 122,175, 0,120,182, 0,120,184, 0,121,182, 0,123,172, 2, + 124,150, 22,114,104,197,121,127, 58,124,155, 16,123,170, 3, + 122,178, 0,120,182, 0,120,184, 0,120,184, 0,119,185, 0, + 119,186, 0,118,188, 0,118,189, 0,118,189, 0,119,187, 0, + 119,185, 0,119,186, 0,118,188, 0,118,189, 0,117,192, 0, + 115,194, 0,114,196, 0,114,197, 0,115,195, 0,117,191, 0, + 119,186, 0,119,185, 0,119,185, 0,121,182, 0,123,174, 1, + 124,161, 11,123,137, 41,119,119, 77,122,132, 48,124,143, 31, + 124,147, 26,124,150, 22,124,162, 10,123,171, 3,122,175, 0, + 122,178, 0,120,184, 0,118,188, 0,117,190, 0,118,189, 0, + 119,187, 0,121,182, 0,123,173, 1,124,162, 10,124,147, 26, + 122,130, 53,123,139, 37,123,141, 34,122,132, 48,120,122, 70, + 122,135, 44,124,154, 18,124,165, 8,123,168, 5,123,169, 4, + 123,171, 3,123,172, 2,123,171, 3,123,168, 5,124,161, 11, + 124,150, 22,122,132, 48,121,127, 58,118,114, 96,114,104,197, + 113,100,242,118,117, 85,121,125, 63,124,145, 29,124,155, 16, + 124,166, 7,122,177, 0,121,182, 0,120,182, 0,119,185, 0, + 118,188, 0,118,189, 0,119,186, 0,121,182, 0,119,186, 0, + 118,189, 0,117,191, 0,117,190, 0,118,188, 0,117,190, 0, + 117,191, 0,117,191, 0,117,191, 0,119,187, 0,121,180, 0, + 124,166, 7,124,145, 29,121,127, 58,117,110,109,118,114, 96, + 119,119, 77,124,147, 26,124,162, 10,123,170, 3,123,173, 1, + 123,174, 1,123,174, 1,123,170, 3,124,160, 12,124,155, 16, + 124,152, 20,123,141, 34,118,117, 85, 87, 65,237, 30, 49,213, + 105, 84,252,118,117, 85,122,130, 53,122,130, 53,121,125, 63, + 121,127, 58,121,125, 63,117,110,109, 99, 75,247,105, 84,252, + 117,110,109,118,117, 85,117,110,109,114,104,197,118,114, 96, + 123,137, 41,124,145, 29,123,139, 37,122,135, 44,123,141, 34, + 124,161, 11,123,170, 3,122,175, 0,123,172, 2,124,164, 9, + 124,145, 29,120,122, 70,115,107,129, 87, 65,237, 69, 60,230, + 111, 96,248,121,127, 58,124,150, 22,124,161, 11,124,164, 9, + 124,162, 10,124,157, 15,124,150, 22,124,157, 15,124,164, 9, + 123,170, 3,122,175, 0,122,178, 0,122,177, 0,123,171, 3, + 124,161, 11,123,139, 37, 99, 75,247,113,100,242,124,143, 31, + 124,167, 6,121,181, 0,117,190, 0,113,199, 0,108,206, 0, + 103,211, 0, 99,216, 0, 94,220, 0, 92,223, 0, 91,223, 0, + 92,222, 0, 95,219, 0,100,215, 0,103,212, 0,101,214, 0, + 100,215, 0, 97,218, 0, 96,219, 0, 97,217, 0,101,214, 0, + 106,208, 0,108,205, 0,110,202, 0,114,197, 0,118,188, 0, + 122,175, 0,124,162, 10,123,139, 37,107, 89,252, 30, 37,192, + 120,122, 70,124,164, 9,121,181, 0,118,188, 0,118,189, 0, + 119,186, 0,121,181, 0,122,175, 0,121,181, 0,120,184, 0, + 119,185, 0,119,186, 0,119,186, 0,121,181, 0,123,172, 2, + 123,174, 1,123,171, 3,123,170, 3,123,172, 2,123,173, 1, + 123,174, 1,123,174, 1,122,176, 0,122,178, 0,121,179, 0, + 122,175, 0,124,165, 8,124,145, 29,122,135, 44,120,122, 70, + 113,100,242,105, 84,252,114,104,197,121,125, 63,123,141, 34, + 124,150, 22,124,155, 16,124,154, 18,124,147, 26,121,127, 58, + 122,130, 53,121,125, 63,121,127, 58,122,130, 53,123,141, 34, + 124,145, 29,123,139, 37,118,114, 96,118,114, 96,122,132, 48, + 124,143, 31,124,150, 22,124,158, 13,124,165, 8,124,165, 8, + 124,165, 8,123,172, 2,122,177, 0,121,180, 0,122,178, 0, + 124,167, 6,124,143, 31,109, 92,251,102, 80,250,121,125, 63, + 124,150, 22,124,162, 10,123,169, 4,123,171, 3,123,173, 1, + 123,173, 1,122,176, 0,121,179, 0,121,180, 0,121,180, 0, + 122,178, 0,122,176, 0,122,176, 0,121,179, 0,121,182, 0, + 119,185, 0,118,189, 0,117,191, 0,116,192, 0,117,192, 0, + 118,188, 0,120,183, 0,121,181, 0,121,181, 0,122,177, 0, + 123,169, 4,124,154, 18,121,127, 58,118,114, 96,107, 89,252, + 109, 92,251,113,100,242,122,130, 53,124,152, 20,124,165, 8, + 123,171, 3,123,172, 2,123,174, 1,121,181, 0,120,184, 0, + 120,183, 0,121,179, 0,123,172, 2,124,164, 9,124,150, 22, + 122,135, 44,118,117, 85,122,132, 48,123,141, 34,123,139, 37, + 122,130, 53,118,114, 96,120,122, 70,123,139, 37,124,143, 31, + 124,143, 31,124,147, 26,124,149, 24,124,149, 24,124,143, 31, + 122,132, 48,117,110,109, 99, 75,247,107, 89,252,111, 96,248, + 113,100,242,111, 96,248,117,110,109,124,143, 31,124,161, 11, + 124,167, 6,122,175, 0,120,184, 0,118,188, 0,118,189, 0, + 119,187, 0,119,186, 0,119,186, 0,120,183, 0,120,184, 0, + 118,189, 0,116,193, 0,114,196, 0,114,196, 0,115,195, 0, + 116,193, 0,116,193, 0,116,193, 0,116,192, 0,118,189, 0, + 120,183, 0,123,171, 3,124,147, 26,114,104,197, 94, 70,242, + 107, 89,252,121,127, 58,124,154, 18,124,166, 7,123,171, 3, + 123,172, 2,123,171, 3,123,169, 4,124,164, 9,124,157, 15, + 124,157, 15,124,150, 22,123,137, 41,119,119, 77,105, 84,252, + 69, 60,230,118,117, 85,123,141, 34,124,149, 24,124,149, 24, + 124,143, 31,124,145, 29,124,145, 29,123,137, 41,118,117, 85, + 120,122, 70,122,130, 53,123,139, 37,122,135, 44,118,117, 85, + 94, 70,242,105, 84,252,114,104,197,119,119, 77,122,135, 44, + 123,139, 37,124,150, 22,124,162, 10,124,166, 7,124,161, 11, + 124,147, 26,124,149, 24,124,149, 24,123,139, 37,118,114, 96, + 69, 60,230, 30, 43,203,114,104,197,122,135, 44,124,149, 24, + 124,152, 20,124,150, 22,124,143, 31,124,150, 22,124,155, 16, + 124,161, 11,123,168, 5,123,172, 2,123,173, 1,123,171, 3, + 124,164, 9,124,149, 24,118,117, 85, 30, 2,128,107, 89,252, + 123,137, 41,124,165, 8,121,181, 0,117,191, 0,113,199, 0, + 109,203, 0,107,206, 0,103,211, 0, 99,216, 0, 96,219, 0, + 95,220, 0, 95,219, 0, 98,217, 0,102,213, 0,105,209, 0, + 101,213, 0, 97,218, 0, 93,221, 0, 92,222, 0, 93,221, 0, + 97,218, 0,102,212, 0,108,206, 0,109,204, 0,113,199, 0, + 117,191, 0,121,180, 0,123,168, 5,124,147, 26,114,104,197, + 30, 2,128,118,114, 96,124,161, 11,121,179, 0,119,187, 0, + 118,189, 0,119,186, 0,121,180, 0,123,172, 2,121,179, 0, + 119,185, 0,118,188, 0,119,186, 0,120,183, 0,122,178, 0, + 124,167, 6,124,150, 22,124,160, 12,124,164, 9,124,166, 7, + 123,170, 3,122,176, 0,122,178, 0,122,178, 0,123,171, 3, + 123,172, 2,123,168, 5,124,157, 15,123,137, 41,121,125, 63, + 117,110,109, 94, 70,242, 30, 2,128, 87, 65,237,111, 96,248, + 114,104,197,119,119, 77,122,132, 48,123,137, 41,122,132, 48, + 118,114, 96,120,122, 70,118,117, 85,118,114, 96,118,114, 96, + 121,127, 58,122,132, 48,121,127, 58,111, 96,248,122,130, 53, + 124,147, 26,124,157, 15,124,161, 11,123,170, 3,122,177, 0, + 122,178, 0,123,173, 1,121,179, 0,120,184, 0,120,184, 0, + 121,180, 0,123,168, 5,124,143, 31,107, 89,252, 30, 2,128, + 94, 70,242,118,114, 96,122,135, 44,124,145, 29,124,149, 24, + 124,150, 22,124,152, 20,124,155, 16,124,162, 10,124,166, 7, + 124,167, 6,124,167, 6,124,167, 6,124,166, 7,123,168, 5, + 123,168, 5,123,173, 1,121,180, 0,119,185, 0,118,188, 0, + 118,188, 0,119,186, 0,121,182, 0,123,173, 1,123,174, 1, + 123,170, 3,124,162, 10,124,147, 26,120,122, 70,107, 89,252, + 30, 55,222, 30, 2,128, 69, 60,230,118,114, 96,124,143, 31, + 124,160, 12,124,167, 6,123,170, 3,123,169, 4,123,168, 5, + 123,173, 1,123,172, 2,123,170, 3,123,169, 4,124,166, 7, + 124,157, 15,123,139, 37,111, 96,248,121,127, 58,123,141, 34, + 124,145, 29,124,143, 31,122,132, 48,118,117, 85,111, 96,248, + 111, 96,248,109, 92,251,113,100,242,115,107,129,115,107,129, + 114,104,197,111, 96,248, 87, 65,237, 30, 2,128, 87, 65,237, + 109, 92,251,114,104,197,118,117, 85,122,135, 44,124,157, 15, + 123,169, 4,123,173, 1,120,184, 0,117,191, 0,115,194, 0, + 114,196, 0,115,194, 0,117,191, 0,119,186, 0,121,179, 0, + 119,186, 0,117,192, 0,114,197, 0,112,200, 0,111,201, 0, + 111,201, 0,113,198, 0,116,193, 0,116,193, 0,117,192, 0, + 118,189, 0,120,183, 0,123,170, 3,124,145, 29,107, 89,252, + 30, 2,128,102, 80,250,122,130, 53,124,154, 18,124,165, 8, + 123,168, 5,124,167, 6,124,164, 9,124,160, 12,124,161, 11, + 124,162, 10,124,164, 9,124,165, 8,124,160, 12,124,147, 26, + 119,119, 77, 99, 75,247,121,127, 58,124,149, 24,124,158, 13, + 124,160, 12,124,158, 13,124,154, 18,124,150, 22,124,145, 29, + 124,149, 24,124,154, 18,124,155, 16,124,150, 22,124,145, 29, + 121,127, 58,105, 84,252, 30, 2,128,102, 80,250,119,119, 77, + 123,137, 41,124,145, 29,124,147, 26,124,155, 16,124,158, 13, + 124,152, 20,124,160, 12,124,164, 9,124,165, 8,124,158, 13, + 124,147, 26,122,130, 53,114,104,197, 87, 65,237,117,110,109, + 122,130, 53,122,135, 44,123,141, 34,124,145, 29,124,149, 24, + 124,152, 20,124,157, 15,124,164, 9,124,167, 6,124,167, 6, + 124,164, 9,124,154, 18,122,132, 48,105, 84,252, 30, 55,222, + 115,107,129,124,145, 29,124,165, 8,121,181, 0,117,192, 0, + 112,199, 0,109,204, 0,107,207, 0,107,207, 0,102,212, 0, + 100,215, 0, 98,217, 0, 99,216, 0,100,215, 0,100,215, 0, + 101,214, 0,100,215, 0, 95,220, 0, 91,223, 0, 89,225, 0, + 90,225, 0, 93,222, 0, 98,217, 0,103,211, 0,108,205, 0, + 112,200, 0,115,194, 0,119,186, 0,122,177, 0,124,160, 12, + 121,125, 63, 30, 37,192,102, 80,250,124,147, 26,123,172, 2, + 120,182, 0,119,186, 0,120,184, 0,121,179, 0,123,172, 2, + 122,178, 0,120,184, 0,118,188, 0,118,188, 0,119,185, 0, + 122,178, 0,123,169, 4,124,160, 12,124,158, 13,124,162, 10, + 124,167, 6,123,173, 1,122,178, 0,121,179, 0,122,178, 0, + 123,171, 3,123,168, 5,124,166, 7,124,164, 9,124,155, 16, + 124,143, 31,121,127, 58,111, 96,248, 87, 65,237,107, 89,252, + 111, 96,248,107, 89,252,107, 89,252,117,110,109,120,122, 70, + 120,122, 70,115,107,129,117,110,109,115,107,129,111, 96,248, + 113,100,242,119,119, 77,121,125, 63,121,127, 58,121,127, 58, + 123,139, 37,124,155, 16,124,165, 8,123,172, 2,121,180, 0, + 120,184, 0,119,186, 0,120,182, 0,119,187, 0,117,191, 0, + 117,191, 0,118,189, 0,121,181, 0,124,166, 7,122,135, 44, + 102, 80,250, 94, 70,242,111, 96,248,114,104,197,111, 96,248, + 113,100,242,120,122, 70,123,141, 34,124,149, 24,124,147, 26, + 124,143, 31,124,143, 31,124,157, 15,123,168, 5,122,175, 0, + 122,178, 0,121,179, 0,122,177, 0,123,172, 2,122,177, 0, + 120,182, 0,119,185, 0,120,184, 0,121,181, 0,123,173, 1, + 124,166, 7,124,164, 9,124,157, 15,124,145, 29,121,125, 63, + 117,110,109,109, 92,251,102, 80,250,109, 92,251,120,122, 70, + 122,135, 44,124,150, 22,124,161, 11,124,166, 7,124,166, 7, + 124,166, 7,124,166, 7,123,168, 5,123,170, 3,123,170, 3, + 124,167, 6,124,161, 11,124,145, 29,122,130, 53,121,127, 58, + 124,143, 31,124,150, 22,124,152, 20,124,149, 24,123,139, 37, + 121,127, 58,118,114, 96,107, 89,252, 30, 43,203, 30, 31,180, + 94, 70,242,111, 96,248,113,100,242,111, 96,248, 99, 75,247, + 114,104,197,121,125, 63,123,139, 37,124,150, 22,124,160, 12, + 124,167, 6,122,176, 0,120,184, 0,116,192, 0,113,198, 0, + 111,201, 0,111,201, 0,112,200, 0,114,196, 0,117,192, 0, + 117,191, 0,115,194, 0,114,196, 0,111,201, 0,109,204, 0, + 107,207, 0,107,207, 0,108,205, 0,111,201, 0,116,193, 0, + 118,189, 0,119,186, 0,121,181, 0,123,168, 5,124,143, 31, + 114,104,197, 94, 70,242,113,100,242,121,127, 58,124,145, 29, + 124,157, 15,124,158, 13,124,157, 15,124,160, 12,124,167, 6, + 123,171, 3,122,176, 0,121,179, 0,122,178, 0,122,175, 0, + 124,167, 6,124,150, 22,121,125, 63,121,125, 63,124,150, 22, + 124,161, 11,124,166, 7,124,167, 6,124,166, 7,124,164, 9, + 124,164, 9,124,166, 7,123,169, 4,123,170, 3,124,167, 6, + 124,160, 12,123,141, 34,117,110,109, 87, 65,237,102, 80,250, + 119,119, 77,123,137, 41,124,147, 26,124,150, 22,124,152, 20, + 124,154, 18,124,158, 13,124,164, 9,123,168, 5,123,168, 5, + 123,170, 3,124,167, 6,124,158, 13,123,141, 34,120,122, 70, + 118,117, 85,122,132, 48,123,141, 34,124,147, 26,124,150, 22, + 124,161, 11,123,168, 5,123,170, 3,123,169, 4,124,166, 7, + 124,161, 11,124,155, 16,123,141, 34,119,119, 77,107, 89,252, + 107, 89,252,119,119, 77,124,149, 24,124,165, 8,120,182, 0, + 116,193, 0,112,200, 0,109,204, 0,108,206, 0,108,205, 0, + 105,209, 0,103,212, 0,102,213, 0,100,215, 0, 97,218, 0, + 96,219, 0, 97,218, 0, 98,217, 0, 93,221, 0, 89,225, 0, + 87,227, 0, 87,227, 0, 89,225, 0, 93,222, 0, 98,217, 0, + 103,211, 0,108,206, 0,112,200, 0,116,193, 0,120,184, 0, + 123,170, 3,124,145, 29,109, 92,251, 30, 15,143,119,119, 77, + 124,160, 12,123,174, 1,121,181, 0,121,181, 0,122,178, 0, + 123,172, 2,122,175, 0,120,183, 0,118,188, 0,118,189, 0, + 119,186, 0,121,181, 0,123,172, 2,124,164, 9,124,162, 10, + 124,166, 7,123,169, 4,123,174, 1,122,178, 0,122,178, 0, + 122,176, 0,123,169, 4,123,174, 1,122,175, 0,123,173, 1, + 124,166, 7,124,155, 16,123,141, 34,120,122, 70,115,107,129, + 118,114, 96,120,122, 70,119,119, 77,115,107,129,102, 80,250, + 113,100,242,115,107,129,114,104,197,114,104,197,113,100,242, + 118,114, 96,121,127, 58,123,139, 37,124,147, 26,124,152, 20, + 124,154, 18,124,157, 15,124,166, 7,123,174, 1,121,182, 0, + 119,187, 0,117,190, 0,117,191, 0,117,191, 0,115,195, 0, + 113,198, 0,113,199, 0,114,196, 0,117,190, 0,121,179, 0, + 124,161, 11,122,130, 53,118,114, 96,115,107,129,115,107,129, + 107, 89,252, 87, 65,237,105, 84,252,119,119, 77,122,132, 48, + 122,135, 44,123,137, 41,124,143, 31,124,157, 15,123,172, 2, + 121,181, 0,119,185, 0,119,186, 0,120,183, 0,122,177, 0, + 121,179, 0,120,182, 0,120,183, 0,120,183, 0,121,180, 0, + 123,173, 1,124,162, 10,124,160, 12,124,157, 15,124,154, 18, + 124,147, 26,123,137, 41,121,125, 63,118,117, 85,115,107,129, + 120,122, 70,123,137, 41,123,141, 34,124,152, 20,124,158, 13, + 124,160, 12,124,160, 12,124,160, 12,124,164, 9,124,166, 7, + 124,167, 6,124,166, 7,124,161, 11,124,154, 18,124,155, 16, + 124,154, 18,124,147, 26,124,155, 16,124,158, 13,124,158, 13, + 124,154, 18,124,147, 26,123,137, 41,119,119, 77,102, 80,250, + 30, 49,213,105, 84,252,118,117, 85,122,132, 48,122,135, 44, + 121,125, 63,124,143, 31,124,155, 16,124,164, 9,123,170, 3, + 122,175, 0,121,180, 0,119,186, 0,117,192, 0,113,198, 0, + 110,202, 0,108,205, 0,108,205, 0,109,203, 0,112,200, 0, + 113,198, 0,112,200, 0,111,202, 0,111,202, 0,107,206, 0, + 104,210, 0,102,212, 0,101,213, 0,103,212, 0,106,208, 0, + 111,201, 0,117,191, 0,120,184, 0,122,178, 0,123,169, 4, + 124,150, 22,123,141, 34,121,125, 63,121,125, 63,123,141, 34, + 124,152, 20,124,158, 13,124,160, 12,124,166, 7,123,174, 1, + 122,178, 0,121,182, 0,120,184, 0,119,186, 0,119,186, 0, + 120,184, 0,121,179, 0,123,170, 3,124,155, 16,123,141, 34, + 124,150, 22,124,165, 8,123,171, 3,123,173, 1,123,174, 1, + 123,173, 1,123,172, 2,123,174, 1,122,176, 0,122,177, 0, + 123,174, 1,123,168, 5,124,155, 16,122,132, 48,115,107,129, + 120,122, 70,122,130, 53,122,135, 44,123,141, 34,124,145, 29, + 124,147, 26,124,149, 24,124,152, 20,124,161, 11,123,170, 3, + 122,175, 0,122,177, 0,122,175, 0,123,170, 3,124,162, 10, + 124,154, 18,124,149, 24,124,149, 24,124,152, 20,124,160, 12, + 124,166, 7,123,172, 2,122,177, 0,122,178, 0,122,177, 0, + 123,174, 1,123,170, 3,124,162, 10,124,152, 20,122,132, 48, + 113,100,242,113,100,242,120,122, 70,124,147, 26,123,168, 5, + 120,184, 0,115,194, 0,112,200, 0,110,202, 0,109,204, 0, + 110,203, 0,107,206, 0,106,208, 0,103,211, 0, 98,216, 0, + 95,220, 0, 92,222, 0, 92,222, 0, 93,221, 0, 92,222, 0, + 89,225, 0, 86,228, 0, 85,228, 0, 87,227, 0, 89,225, 0, + 94,221, 0, 99,216, 0,103,211, 0,109,204, 0,113,198, 0, + 118,189, 0,122,178, 0,124,160, 12,121,127, 58, 30, 49,213, + 87, 65,237,122,135, 44,124,161, 11,123,172, 2,122,176, 0, + 122,175, 0,123,171, 3,123,172, 2,121,182, 0,119,187, 0, + 118,189, 0,119,187, 0,120,182, 0,123,174, 1,124,166, 7, + 123,169, 4,123,171, 3,123,171, 3,123,172, 2,122,176, 0, + 122,176, 0,123,174, 1,122,176, 0,121,181, 0,121,182, 0, + 121,179, 0,123,173, 1,124,164, 9,124,152, 20,123,137, 41, + 121,125, 63,122,130, 53,123,141, 34,123,141, 34,122,135, 44, + 119,119, 77,109, 92,251,109, 92,251,111, 96,248,113,100,242, + 118,117, 85,122,132, 48,124,147, 26,124,157, 15,124,164, 9, + 123,168, 5,123,170, 3,123,173, 1,122,178, 0,120,184, 0, + 118,189, 0,116,192, 0,115,194, 0,114,196, 0,113,199, 0, + 111,202, 0,109,204, 0,108,205, 0,110,202, 0,114,197, 0, + 118,188, 0,122,176, 0,124,158, 13,124,150, 22,124,147, 26, + 122,135, 44,117,110,109,102, 80,250,109, 92,251,114,104,197, + 121,127, 58,124,143, 31,124,149, 24,124,152, 20,124,157, 15, + 123,174, 1,120,183, 0,118,188, 0,118,189, 0,119,185, 0, + 122,178, 0,121,182, 0,119,186, 0,119,187, 0,119,185, 0, + 121,180, 0,123,173, 1,124,162, 10,124,164, 9,124,165, 8, + 124,164, 9,124,160, 12,124,150, 22,123,139, 37,121,125, 63, + 118,114, 96,117,110,109,121,127, 58,122,135, 44,123,137, 41, + 124,147, 26,124,150, 22,124,149, 24,124,149, 24,124,152, 20, + 124,157, 15,124,160, 12,124,160, 12,124,164, 9,123,169, 4, + 123,171, 3,123,169, 4,124,165, 8,124,160, 12,124,164, 9, + 124,164, 9,124,161, 11,124,157, 15,124,150, 22,123,137, 41, + 115,107,129,115,107,129,115,107,129,123,137, 41,124,152, 20, + 124,157, 15,124,154, 18,124,165, 8,123,172, 2,122,178, 0, + 121,182, 0,119,185, 0,118,188, 0,116,192, 0,114,197, 0, + 110,202, 0,107,206, 0,106,208, 0,106,208, 0,108,206, 0, + 109,204, 0,108,205, 0,107,206, 0,107,207, 0,107,207, 0, + 102,212, 0, 99,215, 0, 97,218, 0, 97,218, 0, 98,217, 0, + 101,214, 0,106,208, 0,113,199, 0,119,186, 0,122,178, 0, + 123,171, 3,124,162, 10,124,162, 10,124,155, 16,124,149, 24, + 124,149, 24,124,155, 16,124,162, 10,123,170, 3,122,178, 0, + 120,182, 0,120,184, 0,119,186, 0,118,188, 0,117,190, 0, + 117,191, 0,118,189, 0,119,186, 0,121,181, 0,123,171, 3, + 124,161, 11,124,162, 10,123,169, 4,122,176, 0,121,179, 0, + 121,179, 0,121,179, 0,122,178, 0,122,178, 0,121,180, 0, + 121,179, 0,122,178, 0,123,172, 2,124,162, 10,124,145, 29, + 123,139, 37,124,147, 26,124,152, 20,124,154, 18,124,152, 20, + 124,150, 22,124,152, 20,124,158, 13,124,160, 12,124,167, 6, + 123,173, 1,122,178, 0,121,179, 0,121,179, 0,122,177, 0, + 123,173, 1,123,170, 3,123,168, 5,123,169, 4,123,170, 3, + 123,173, 1,122,177, 0,121,179, 0,121,182, 0,120,182, 0, + 120,182, 0,121,180, 0,122,177, 0,123,170, 3,124,160, 12, + 123,139, 37,114,104,197,113,100,242,119,119, 77,124,149, 24, + 122,175, 0,118,188, 0,114,196, 0,112,200, 0,111,201, 0, + 111,202, 0,110,202, 0,109,204, 0,108,205, 0,103,212, 0, + 97,217, 0, 93,221, 0, 91,224, 0, 89,225, 0, 89,225, 0, + 90,224, 0, 89,225, 0, 87,227, 0, 86,228, 0, 86,228, 0, + 87,227, 0, 91,223, 0, 96,219, 0,101,214, 0,107,207, 0, + 112,200, 0,117,192, 0,121,181, 0,124,167, 6,124,147, 26, + 114,104,197, 30, 31,180,109, 92,251,123,139, 37,124,161, 11, + 123,169, 4,123,171, 3,123,169, 4,123,169, 4,121,180, 0, + 119,186, 0,118,189, 0,118,188, 0,120,183, 0,122,176, 0, + 123,170, 3,123,174, 1,122,176, 0,122,175, 0,123,174, 1, + 123,172, 2,123,172, 2,122,177, 0,120,182, 0,119,186, 0, + 119,186, 0,120,182, 0,122,177, 0,123,168, 5,124,158, 13, + 124,143, 31,122,135, 44,123,137, 41,124,152, 20,124,155, 16, + 124,154, 18,124,143, 31,120,122, 70,109, 92,251,109, 92,251, + 117,110,109,122,130, 53,124,147, 26,124,160, 12,123,168, 5, + 123,173, 1,122,177, 0,121,179, 0,120,183, 0,119,187, 0, + 117,191, 0,115,194, 0,114,197, 0,113,198, 0,110,202, 0, + 108,205, 0,106,207, 0,105,209, 0,105,209, 0,106,207, 0, + 110,203, 0,115,195, 0,119,185, 0,123,174, 1,123,172, 2, + 123,172, 2,123,168, 5,124,157, 15,122,135, 44,123,137, 41, + 124,145, 29,124,147, 26,124,149, 24,124,155, 16,124,155, 16, + 124,155, 16,123,173, 1,120,183, 0,118,188, 0,118,188, 0, + 120,184, 0,122,178, 0,120,184, 0,118,188, 0,118,189, 0, + 119,186, 0,121,182, 0,123,174, 1,124,164, 9,123,168, 5, + 123,171, 3,123,170, 3,124,166, 7,124,157, 15,124,143, 31, + 121,125, 63,117,110,109,109, 92,251,115,107,129,119,119, 77, + 121,125, 63,122,130, 53,122,135, 44,122,132, 48,122,130, 53, + 122,135, 44,123,141, 34,124,149, 24,124,164, 9,123,172, 2, + 122,178, 0,121,179, 0,122,178, 0,123,174, 1,123,168, 5, + 124,167, 6,124,167, 6,124,165, 8,124,161, 11,124,155, 16, + 124,143, 31,121,127, 58,122,132, 48,123,137, 41,124,149, 24, + 124,164, 9,123,169, 4,123,169, 4,122,178, 0,120,182, 0, + 119,186, 0,118,189, 0,117,191, 0,116,193, 0,114,196, 0, + 112,200, 0,108,205, 0,106,208, 0,105,209, 0,105,209, 0, + 105,209, 0,104,210, 0,104,210, 0,103,211, 0,103,211, 0, + 102,213, 0, 98,217, 0, 94,220, 0, 93,222, 0, 93,222, 0, + 94,220, 0, 97,217, 0,102,212, 0,109,204, 0,116,193, 0, + 121,181, 0,122,176, 0,123,173, 1,122,175, 0,123,171, 3, + 123,168, 5,123,168, 5,123,172, 2,122,176, 0,121,181, 0, + 120,184, 0,119,186, 0,119,187, 0,118,188, 0,118,189, 0, + 117,191, 0,116,192, 0,116,192, 0,117,190, 0,119,186, 0, + 121,180, 0,123,174, 1,123,174, 1,122,175, 0,121,181, 0, + 120,184, 0,120,184, 0,120,184, 0,121,182, 0,121,182, 0, + 121,182, 0,121,181, 0,122,178, 0,123,174, 1,124,166, 7, + 124,154, 18,124,157, 15,124,162, 10,124,165, 8,124,165, 8, + 124,164, 9,124,160, 12,124,164, 9,123,170, 3,123,171, 3, + 123,169, 4,123,173, 1,122,177, 0,121,179, 0,121,180, 0, + 121,179, 0,121,179, 0,121,179, 0,121,180, 0,121,180, 0, + 121,181, 0,120,182, 0,120,183, 0,120,184, 0,120,184, 0, + 119,185, 0,119,185, 0,120,184, 0,121,181, 0,122,175, 0, + 124,164, 9,123,141, 34,115,107,129,107, 89,252,118,117, 85, + 124,162, 10,121,182, 0,117,191, 0,114,197, 0,112,200, 0, + 112,200, 0,112,199, 0,110,202, 0,110,203, 0,109,204, 0, + 103,212, 0, 98,217, 0, 93,221, 0, 90,225, 0, 87,227, 0, + 86,228, 0, 86,228, 0, 87,227, 0, 89,225, 0, 88,226, 0, + 87,227, 0, 88,226, 0, 91,223, 0, 95,219, 0,101,214, 0, + 106,207, 0,112,200, 0,117,192, 0,121,182, 0,123,170, 3, + 124,155, 16,122,130, 53, 94, 70,242, 87, 65,237,115,107,129, + 124,143, 31,124,160, 12,124,165, 8,124,166, 7,124,167, 6, + 121,179, 0,119,186, 0,118,189, 0,118,188, 0,120,184, 0, + 122,176, 0,122,176, 0,122,178, 0,122,178, 0,122,178, 0, + 122,176, 0,123,173, 1,122,176, 0,121,181, 0,119,186, 0, + 118,188, 0,118,188, 0,120,184, 0,122,178, 0,123,171, 3, + 124,165, 8,124,150, 22,123,137, 41,123,141, 34,124,158, 13, + 124,165, 8,124,165, 8,124,158, 13,124,143, 31,118,117, 85, + 107, 89,252,118,117, 85,123,141, 34,124,157, 15,123,168, 5, + 123,174, 1,122,178, 0,121,182, 0,120,184, 0,118,188, 0, + 116,192, 0,114,196, 0,113,198, 0,112,200, 0,110,203, 0, + 107,206, 0,105,209, 0,103,211, 0,102,213, 0,101,213, 0, + 103,211, 0,107,207, 0,111,201, 0,116,192, 0,120,184, 0, + 119,186, 0,119,187, 0,119,186, 0,121,181, 0,123,171, 3, + 124,158, 13,124,165, 8,124,166, 7,124,161, 11,124,155, 16, + 124,157, 15,124,155, 16,123,171, 3,121,181, 0,119,185, 0, + 120,184, 0,121,179, 0,121,181, 0,119,187, 0,117,190, 0, + 117,190, 0,119,186, 0,121,181, 0,123,172, 2,124,166, 7, + 123,172, 2,122,175, 0,123,174, 1,123,169, 4,124,160, 12, + 123,141, 34,118,117, 85,111, 96,248, 94, 70,242, 94, 70,242, + 109, 92,251,115,107,129,118,114, 96,118,117, 85,117,110,109, + 117,110,109,118,114, 96,122,135, 44,124,154, 18,124,167, 6, + 122,177, 0,121,182, 0,120,182, 0,121,181, 0,122,178, 0, + 122,175, 0,123,169, 4,124,167, 6,124,165, 8,124,160, 12, + 124,154, 18,124,143, 31,122,135, 44,124,143, 31,124,149, 24, + 124,158, 13,123,171, 3,122,177, 0,122,178, 0,119,185, 0, + 118,189, 0,117,192, 0,116,193, 0,115,194, 0,114,196, 0, + 113,198, 0,111,202, 0,108,205, 0,105,209, 0,104,210, 0, + 102,212, 0,100,214, 0,100,215, 0,100,215, 0,100,215, 0, + 100,214, 0, 96,219, 0, 93,222, 0, 91,224, 0, 90,224, 0, + 91,223, 0, 93,222, 0, 96,219, 0,100,214, 0,106,208, 0, + 113,199, 0,118,188, 0,121,180, 0,121,180, 0,121,182, 0, + 121,180, 0,122,178, 0,121,179, 0,121,182, 0,120,184, 0, + 119,187, 0,118,188, 0,118,189, 0,118,188, 0,118,188, 0, + 118,189, 0,117,191, 0,117,192, 0,116,192, 0,117,192, 0, + 118,189, 0,120,184, 0,121,181, 0,121,182, 0,120,182, 0, + 119,187, 0,118,189, 0,118,189, 0,118,188, 0,118,188, 0, + 119,187, 0,120,183, 0,121,182, 0,121,180, 0,122,176, 0, + 123,170, 3,124,164, 9,124,167, 6,123,170, 3,123,172, 2, + 123,171, 3,123,169, 4,124,166, 7,123,170, 3,122,177, 0, + 122,178, 0,122,178, 0,122,178, 0,122,178, 0,122,178, 0, + 122,178, 0,121,180, 0,121,182, 0,120,184, 0,119,186, 0, + 119,187, 0,118,188, 0,118,188, 0,119,187, 0,119,186, 0, + 119,186, 0,119,186, 0,119,186, 0,119,186, 0,120,184, 0, + 122,178, 0,124,166, 7,123,139, 37,114,104,197, 99, 75,247, + 123,137, 41,123,171, 3,119,186, 0,115,195, 0,112,199, 0, + 111,201, 0,112,200, 0,112,200, 0,110,202, 0,110,202, 0, + 109,204, 0,104,210, 0, 99,216, 0, 94,220, 0, 91,224, 0, + 87,227, 0, 84,229, 0, 83,230, 0, 83,230, 0, 85,228, 0, + 89,225, 0, 91,224, 0, 91,223, 0, 93,221, 0, 97,217, 0, + 102,212, 0,108,206, 0,113,198, 0,118,189, 0,121,179, 0, + 123,169, 4,124,160, 12,124,143, 31,114,104,197, 69, 60,230, + 102, 80,250,119,119, 77,124,145, 29,124,155, 16,124,158, 13, + 124,165, 8,122,178, 0,119,186, 0,118,189, 0,118,188, 0, + 120,184, 0,122,177, 0,122,178, 0,121,179, 0,121,179, 0, + 122,178, 0,122,175, 0,122,175, 0,122,178, 0,120,182, 0, + 119,186, 0,118,189, 0,118,188, 0,120,183, 0,122,177, 0, + 123,174, 1,123,169, 4,124,157, 15,122,135, 44,124,145, 29, + 124,164, 9,123,171, 3,123,172, 2,124,167, 6,124,154, 18, + 121,127, 58,107, 89,252,120,122, 70,124,147, 26,124,162, 10, + 123,171, 3,122,177, 0,121,180, 0,120,182, 0,119,186, 0, + 117,191, 0,115,194, 0,113,199, 0,111,201, 0,110,203, 0, + 107,206, 0,105,209, 0,103,211, 0,101,213, 0,100,215, 0, + 100,215, 0,101,214, 0,104,210, 0,108,205, 0,113,198, 0, + 116,192, 0,115,195, 0,113,198, 0,113,198, 0,115,195, 0, + 118,189, 0,121,180, 0,123,174, 1,122,176, 0,123,172, 2, + 124,162, 10,124,154, 18,124,152, 20,124,167, 6,122,176, 0, + 121,180, 0,122,178, 0,123,174, 1,120,183, 0,118,188, 0, + 117,191, 0,118,189, 0,119,185, 0,122,178, 0,123,169, 4, + 123,170, 3,122,175, 0,122,178, 0,122,177, 0,123,171, 3, + 124,160, 12,123,137, 41,119,119, 77,114,104,197, 94, 70,242, + 30, 31,180, 69, 60,230,105, 84,252,113,100,242,114,104,197, + 113,100,242,120,122, 70,122,130, 53,122,135, 44,124,155, 16, + 123,168, 5,122,178, 0,120,182, 0,120,183, 0,121,182, 0, + 121,181, 0,122,178, 0,122,175, 0,123,169, 4,124,161, 11, + 124,155, 16,124,149, 24,123,139, 37,122,132, 48,124,145, 29, + 124,154, 18,124,164, 9,122,176, 0,121,182, 0,120,183, 0, + 118,189, 0,116,193, 0,115,195, 0,114,196, 0,114,196, 0, + 114,197, 0,113,198, 0,111,201, 0,108,205, 0,106,208, 0, + 104,210, 0, 99,215, 0, 97,218, 0, 96,219, 0, 97,218, 0, + 97,217, 0, 95,220, 0, 91,223, 0, 89,225, 0, 88,226, 0, + 89,225, 0, 91,224, 0, 93,221, 0, 96,219, 0,100,215, 0, + 105,209, 0,110,202, 0,116,193, 0,120,184, 0,120,184, 0, + 119,186, 0,120,184, 0,120,184, 0,119,185, 0,119,187, 0, + 118,189, 0,117,191, 0,117,191, 0,117,190, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,117,190, 0,117,191, 0, + 117,191, 0,118,189, 0,119,186, 0,119,185, 0,119,186, 0, + 117,190, 0,116,193, 0,115,195, 0,115,194, 0,116,193, 0, + 115,194, 0,116,193, 0,117,190, 0,119,186, 0,121,182, 0, + 121,179, 0,123,174, 1,123,170, 3,123,172, 2,122,176, 0, + 122,177, 0,122,176, 0,123,174, 1,123,171, 3,123,174, 1, + 121,179, 0,121,182, 0,120,182, 0,120,184, 0,119,186, 0, + 119,186, 0,119,186, 0,119,185, 0,120,183, 0,119,187, 0, + 117,190, 0,117,192, 0,117,192, 0,117,191, 0,117,191, 0, + 116,192, 0,117,191, 0,118,188, 0,118,188, 0,118,188, 0, + 119,186, 0,121,180, 0,124,167, 6,122,135, 44,109, 92,251, + 105, 84,252,124,149, 24,122,177, 0,117,191, 0,113,198, 0, + 111,202, 0,110,202, 0,112,200, 0,111,201, 0,110,202, 0, + 111,201, 0,110,202, 0,106,208, 0,100,215, 0, 94,220, 0, + 91,223, 0, 88,226, 0, 84,229, 0, 82,231, 0, 82,231, 0, + 83,230, 0, 87,227, 0, 92,223, 0, 95,220, 0, 97,218, 0, + 101,214, 0,106,208, 0,111,201, 0,116,193, 0,120,184, 0, + 123,174, 1,124,164, 9,124,157, 15,124,145, 29,119,119, 77, + 87, 65,237,102, 80,250,105, 84,252,121,125, 63,123,141, 34, + 124,147, 26,124,162, 10,122,178, 0,119,186, 0,118,189, 0, + 118,189, 0,120,184, 0,122,178, 0,122,177, 0,122,178, 0, + 122,177, 0,123,174, 1,123,171, 3,123,174, 1,122,177, 0, + 121,181, 0,120,184, 0,119,187, 0,119,185, 0,121,181, 0, + 122,178, 0,122,176, 0,123,171, 3,124,160, 12,122,132, 48, + 124,147, 26,124,166, 7,122,175, 0,122,177, 0,123,172, 2, + 124,160, 12,122,135, 44,107, 89,252,121,125, 63,124,150, 22, + 124,165, 8,123,172, 2,122,177, 0,122,178, 0,121,181, 0, + 119,185, 0,117,190, 0,114,196, 0,112,200, 0,109,203, 0, + 108,206, 0,106,207, 0,104,210, 0,103,212, 0,101,214, 0, + 99,215, 0, 98,216, 0, 99,215, 0,102,213, 0,106,208, 0, + 110,202, 0,113,199, 0,110,202, 0,108,205, 0,107,207, 0, + 108,206, 0,111,202, 0,115,195, 0,119,185, 0,121,182, 0, + 122,178, 0,123,170, 3,124,154, 18,124,149, 24,124,162, 10, + 123,170, 3,123,172, 2,123,169, 4,122,175, 0,120,184, 0, + 118,189, 0,117,190, 0,118,188, 0,119,185, 0,122,178, 0, + 123,170, 3,122,176, 0,121,181, 0,121,182, 0,122,178, 0, + 123,172, 2,124,160, 12,122,135, 44,118,114, 96,113,100,242, + 94, 70,242, 30, 26,168, 30, 31,180, 87, 65,237,107, 89,252, + 113,100,242,115,107,129,122,132, 48,124,143, 31,124,147, 26, + 124,154, 18,123,168, 5,122,176, 0,121,181, 0,121,181, 0, + 121,180, 0,121,180, 0,121,180, 0,122,178, 0,122,175, 0, + 123,169, 4,124,155, 16,124,143, 31,122,132, 48,120,122, 70, + 123,139, 37,124,152, 20,124,167, 6,122,178, 0,120,184, 0, + 119,186, 0,117,192, 0,115,195, 0,114,197, 0,114,197, 0, + 114,197, 0,113,199, 0,113,199, 0,112,200, 0,109,204, 0, + 107,207, 0,102,212, 0, 97,218, 0, 94,221, 0, 93,222, 0, + 94,221, 0, 94,220, 0, 91,224, 0, 88,226, 0, 87,227, 0, + 87,227, 0, 89,225, 0, 92,222, 0, 95,219, 0, 98,216, 0, + 101,214, 0,105,209, 0,109,204, 0,114,197, 0,117,190, 0, + 119,186, 0,119,187, 0,119,186, 0,119,185, 0,119,187, 0, + 118,189, 0,117,192, 0,116,193, 0,116,192, 0,117,191, 0, + 117,191, 0,115,194, 0,115,195, 0,116,193, 0,117,190, 0, + 118,189, 0,117,190, 0,118,188, 0,119,186, 0,118,188, 0, + 116,192, 0,113,198, 0,112,200, 0,111,201, 0,112,200, 0, + 114,197, 0,113,198, 0,114,197, 0,115,194, 0,117,190, 0, + 119,186, 0,120,183, 0,121,181, 0,122,176, 0,122,175, 0, + 122,178, 0,121,179, 0,121,179, 0,122,178, 0,122,175, 0, + 122,175, 0,121,180, 0,121,182, 0,120,183, 0,119,186, 0, + 118,189, 0,117,191, 0,117,192, 0,117,191, 0,118,189, 0, + 118,188, 0,116,192, 0,115,194, 0,115,195, 0,115,194, 0, + 115,194, 0,114,196, 0,114,196, 0,117,191, 0,117,190, 0, + 117,190, 0,118,188, 0,121,182, 0,124,167, 6,122,132, 48, + 102, 80,250,107, 89,252,124,152, 20,121,179, 0,116,193, 0, + 112,200, 0,109,204, 0,109,204, 0,110,202, 0,112,200, 0, + 111,201, 0,112,199, 0,112,199, 0,106,208, 0, 99,215, 0, + 94,221, 0, 91,224, 0, 90,224, 0, 86,228, 0, 83,230, 0, + 82,231, 0, 83,230, 0, 86,228, 0, 91,224, 0, 97,218, 0, + 102,212, 0,106,208, 0,110,202, 0,114,197, 0,117,191, 0, + 120,184, 0,122,176, 0,124,165, 8,124,150, 22,124,143, 31, + 120,122, 70,111, 96,248,107, 89,252,105, 84,252,113,100,242, + 120,122, 70,122,130, 53,124,161, 11,122,177, 0,119,186, 0, + 118,189, 0,118,189, 0,119,186, 0,121,179, 0,123,170, 3, + 123,171, 3,123,169, 4,124,166, 7,124,166, 7,123,173, 1, + 122,178, 0,121,180, 0,121,179, 0,121,182, 0,121,180, 0, + 122,178, 0,122,178, 0,122,176, 0,123,170, 3,124,158, 13, + 122,130, 53,124,149, 24,123,168, 5,122,177, 0,122,178, 0, + 123,174, 1,124,161, 11,122,135, 44,105, 84,252,121,125, 63, + 124,150, 22,124,164, 9,123,170, 3,123,173, 1,122,175, 0, + 122,177, 0,121,182, 0,117,190, 0,114,196, 0,112,200, 0, + 108,205, 0,106,208, 0,104,210, 0,103,211, 0,103,211, 0, + 102,213, 0,100,215, 0, 98,216, 0, 98,216, 0,100,214, 0, + 103,211, 0,107,207, 0,110,202, 0,107,207, 0,104,210, 0, + 102,212, 0,102,212, 0,105,209, 0,109,204, 0,114,196, 0, + 119,187, 0,120,184, 0,122,177, 0,124,164, 9,124,145, 29, + 124,158, 13,124,164, 9,124,165, 8,124,160, 12,123,172, 2, + 121,182, 0,119,186, 0,118,188, 0,118,189, 0,119,186, 0, + 121,181, 0,123,172, 2,121,179, 0,120,184, 0,119,186, 0, + 120,183, 0,122,176, 0,124,161, 11,122,135, 44,107, 89,252, + 99, 75,247, 30, 49,213, 30, 15,143, 30, 43,203,102, 80,250, + 111, 96,248,114,104,197,114,104,197,122,132, 48,124,147, 26, + 124,154, 18,124,158, 13,124,167, 6,123,174, 1,122,178, 0, + 122,178, 0,120,183, 0,120,183, 0,121,180, 0,121,179, 0, + 122,178, 0,123,173, 1,124,164, 9,123,139, 37,122,130, 53, + 115,107,129,122,130, 53,124,147, 26,124,167, 6,121,179, 0, + 119,185, 0,119,186, 0,116,193, 0,114,196, 0,114,197, 0, + 114,197, 0,112,200, 0,111,202, 0,111,202, 0,113,199, 0, + 110,202, 0,107,206, 0,100,214, 0, 94,220, 0, 91,224, 0, + 89,225, 0, 90,224, 0, 92,223, 0, 88,226, 0, 86,228, 0, + 86,228, 0, 88,226, 0, 91,224, 0, 94,221, 0, 98,217, 0, + 101,214, 0,103,212, 0,105,209, 0,108,205, 0,111,201, 0, + 114,196, 0,117,191, 0,118,188, 0,119,185, 0,120,184, 0, + 119,186, 0,118,189, 0,116,192, 0,115,194, 0,115,194, 0, + 116,193, 0,117,191, 0,115,194, 0,114,196, 0,114,196, 0, + 115,194, 0,117,191, 0,118,188, 0,119,186, 0,119,187, 0, + 115,194, 0,111,201, 0,108,205, 0,107,207, 0,107,207, 0, + 108,205, 0,110,202, 0,113,199, 0,113,198, 0,114,196, 0, + 116,192, 0,117,191, 0,117,191, 0,117,191, 0,118,188, 0, + 121,181, 0,121,179, 0,121,181, 0,121,182, 0,121,181, 0, + 121,179, 0,122,178, 0,122,177, 0,122,178, 0,121,180, 0, + 120,184, 0,118,189, 0,117,192, 0,116,193, 0,116,193, 0, + 116,192, 0,118,189, 0,115,194, 0,114,197, 0,113,198, 0, + 114,197, 0,114,197, 0,113,198, 0,114,197, 0,116,192, 0, + 116,193, 0,116,193, 0,117,191, 0,120,184, 0,123,169, 4, + 122,132, 48, 99, 75,247,114,104,197,124,152, 20,121,180, 0, + 115,194, 0,110,202, 0,107,206, 0,106,208, 0,107,207, 0, + 108,205, 0,110,203, 0,111,201, 0,112,200, 0,106,208, 0, + 100,215, 0, 95,220, 0, 92,222, 0, 92,222, 0, 88,226, 0, + 85,229, 0, 83,230, 0, 83,230, 0, 86,228, 0, 91,223, 0, + 97,218, 0,102,212, 0,107,207, 0,110,202, 0,114,197, 0, + 117,191, 0,120,184, 0,122,177, 0,124,167, 6,124,157, 15, + 124,145, 29,122,132, 48,119,119, 77,111, 96,248,102, 80,250, + 87, 65,237,109, 92,251,121,125, 63,124,158, 13,122,176, 0, + 119,185, 0,118,189, 0,118,189, 0,119,186, 0,121,179, 0, + 123,170, 3,124,162, 10,124,160, 12,124,160, 12,123,168, 5, + 123,174, 1,122,178, 0,121,180, 0,121,179, 0,122,177, 0, + 122,177, 0,122,178, 0,122,177, 0,123,174, 1,123,168, 5, + 124,155, 16,121,127, 58,124,150, 22,123,168, 5,122,177, 0, + 122,178, 0,123,172, 2,124,157, 15,121,125, 63,107, 89,252, + 121,127, 58,124,150, 22,124,162, 10,123,168, 5,123,170, 3, + 123,171, 3,123,174, 1,119,186, 0,116,193, 0,113,198, 0, + 111,202, 0,107,207, 0,103,211, 0,101,214, 0,100,215, 0, + 100,215, 0,102,213, 0,102,213, 0,100,215, 0, 99,215, 0, + 100,215, 0,102,213, 0,105,209, 0,107,207, 0,105,209, 0, + 101,213, 0, 99,216, 0, 98,216, 0,101,214, 0,105,209, 0, + 110,202, 0,115,194, 0,117,190, 0,120,183, 0,123,170, 3, + 124,157, 15,124,154, 18,124,158, 13,124,158, 13,124,152, 20, + 124,165, 8,122,176, 0,120,182, 0,118,189, 0,117,192, 0, + 118,189, 0,120,184, 0,122,178, 0,121,179, 0,119,185, 0, + 119,187, 0,119,186, 0,121,179, 0,124,167, 6,124,145, 29, + 117,110,109, 99, 75,247, 30, 49,213, 30, 43,203,105, 84,252, + 115,107,129,119,119, 77,119,119, 77,120,122, 70,121,125, 63, + 123,139, 37,124,150, 22,124,160, 12,123,168, 5,123,174, 1, + 122,177, 0,121,180, 0,120,184, 0,120,184, 0,121,181, 0, + 122,178, 0,122,178, 0,123,173, 1,124,164, 9,124,143, 31, + 122,135, 44,117,110,109,120,122, 70,123,141, 34,124,165, 8, + 122,178, 0,120,184, 0,119,186, 0,117,191, 0,115,195, 0, + 114,196, 0,113,198, 0,111,202, 0,109,203, 0,109,203, 0, + 111,201, 0,111,201, 0,108,206, 0,100,215, 0, 93,221, 0, + 89,225, 0, 87,227, 0, 88,226, 0, 89,225, 0, 87,227, 0, + 86,228, 0, 87,227, 0, 89,225, 0, 92,222, 0, 94,221, 0, + 95,220, 0, 98,217, 0,101,213, 0,105,209, 0,107,206, 0, + 109,204, 0,111,201, 0,113,198, 0,114,196, 0,116,193, 0, + 117,190, 0,119,185, 0,118,188, 0,117,192, 0,115,194, 0, + 115,195, 0,115,194, 0,116,193, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,115,195, 0,116,193, 0,117,191, 0, + 114,197, 0,110,202, 0,106,208, 0,103,212, 0,101,213, 0, + 101,213, 0,103,212, 0,105,209, 0,108,206, 0,110,203, 0, + 111,201, 0,112,199, 0,113,199, 0,112,200, 0,112,200, 0, + 113,198, 0,116,192, 0,120,184, 0,120,184, 0,120,184, 0, + 120,184, 0,120,184, 0,120,184, 0,120,184, 0,120,184, 0, + 120,184, 0,120,182, 0,119,185, 0,118,189, 0,116,192, 0, + 116,193, 0,116,193, 0,117,191, 0,115,195, 0,113,198, 0, + 112,200, 0,112,200, 0,113,199, 0,113,199, 0,114,197, 0, + 114,196, 0,114,196, 0,115,195, 0,117,192, 0,120,184, 0, + 123,169, 4,122,132, 48, 99, 75,247,115,107,129,124,154, 18, + 121,180, 0,115,194, 0,109,203, 0,105,209, 0,103,211, 0, + 103,211, 0,104,210, 0,106,208, 0,107,206, 0,109,203, 0, + 107,207, 0,102,212, 0, 98,216, 0, 96,219, 0, 95,220, 0, + 91,224, 0, 87,227, 0, 85,229, 0, 85,229, 0, 88,226, 0, + 92,222, 0, 98,217, 0,103,211, 0,108,206, 0,112,200, 0, + 116,193, 0,119,187, 0,121,179, 0,123,171, 3,124,162, 10, + 124,154, 18,124,147, 26,123,139, 37,121,127, 58,117,110,109, + 99, 75,247, 30, 43,203, 30, 55,222,118,117, 85,124,154, 18, + 123,172, 2,121,182, 0,119,186, 0,119,185, 0,121,182, 0, + 123,173, 1,124,164, 9,124,155, 16,124,152, 20,124,158, 13, + 124,166, 7,123,172, 2,122,177, 0,122,178, 0,122,176, 0, + 123,174, 1,123,174, 1,123,174, 1,123,174, 1,123,172, 2, + 124,167, 6,124,155, 16,121,127, 58,124,145, 29,124,165, 8, + 123,173, 1,123,174, 1,124,167, 6,124,149, 24,117,110,109, + 113,100,242,122,132, 48,124,152, 20,124,162, 10,124,167, 6, + 123,169, 4,123,170, 3,122,175, 0,119,187, 0,115,194, 0, + 113,198, 0,109,203, 0,105,209, 0,101,214, 0, 98,217, 0, + 96,219, 0, 96,219, 0, 98,217, 0,101,213, 0,103,211, 0, + 102,213, 0,101,213, 0,102,212, 0,103,211, 0,105,209, 0, + 103,211, 0,100,215, 0, 98,217, 0, 97,218, 0, 99,216, 0, + 102,212, 0,108,206, 0,112,199, 0,114,196, 0,118,188, 0, + 122,176, 0,124,164, 9,124,147, 26,124,152, 20,124,152, 20, + 124,143, 31,124,154, 18,123,169, 4,120,184, 0,117,192, 0, + 115,194, 0,116,193, 0,118,189, 0,120,184, 0,122,178, 0, + 120,184, 0,119,186, 0,119,185, 0,121,180, 0,123,170, 3, + 124,150, 22,120,122, 70,109, 92,251, 94, 70,242, 99, 75,247, + 115,107,129,122,130, 53,123,139, 37,123,139, 37,123,137, 41, + 123,139, 37,124,145, 29,124,154, 18,124,162, 10,123,169, 4, + 123,174, 1,122,177, 0,122,178, 0,121,181, 0,121,180, 0, + 122,177, 0,122,178, 0,122,176, 0,123,171, 3,124,161, 11, + 124,150, 22,124,145, 29,121,125, 63,115,107,129,122,130, 53, + 124,158, 13,123,174, 1,120,182, 0,120,184, 0,118,188, 0, + 116,192, 0,115,194, 0,112,199, 0,110,202, 0,108,205, 0, + 108,206, 0,109,203, 0,113,199, 0,109,204, 0,101,214, 0, + 94,220, 0, 90,224, 0, 88,226, 0, 88,226, 0, 89,226, 0, + 87,227, 0, 87,227, 0, 88,226, 0, 91,223, 0, 91,223, 0, + 92,222, 0, 94,221, 0, 96,219, 0,100,215, 0,104,210, 0, + 106,207, 0,107,206, 0,109,204, 0,110,203, 0,111,202, 0, + 112,200, 0,113,198, 0,115,194, 0,118,189, 0,118,189, 0, + 117,192, 0,116,193, 0,115,194, 0,116,193, 0,115,195, 0, + 114,197, 0,113,199, 0,112,200, 0,112,199, 0,113,199, 0, + 112,200, 0,108,205, 0,104,210, 0,100,214, 0, 97,217, 0, + 96,219, 0, 96,219, 0, 97,217, 0,100,215, 0,102,212, 0, + 105,209, 0,106,208, 0,107,206, 0,107,207, 0,106,208, 0, + 106,207, 0,108,206, 0,111,201, 0,115,194, 0,118,189, 0, + 117,191, 0,116,193, 0,116,193, 0,116,193, 0,117,191, 0, + 117,190, 0,118,189, 0,119,186, 0,121,182, 0,119,187, 0, + 117,191, 0,116,193, 0,116,193, 0,117,191, 0,114,196, 0, + 112,199, 0,111,201, 0,111,201, 0,112,200, 0,112,200, 0, + 113,199, 0,114,197, 0,114,197, 0,115,195, 0,117,191, 0, + 120,182, 0,124,165, 8,120,122, 70, 99, 75,247,117,110,109, + 124,155, 16,121,180, 0,115,194, 0,109,203, 0,105,209, 0, + 101,213, 0,100,215, 0,100,214, 0,102,213, 0,103,211, 0, + 106,207, 0,109,203, 0,106,208, 0,103,212, 0,101,214, 0, + 97,217, 0, 93,221, 0, 90,225, 0, 88,226, 0, 88,226, 0, + 90,224, 0, 95,220, 0,100,214, 0,106,208, 0,111,202, 0, + 115,194, 0,119,187, 0,122,178, 0,123,169, 4,124,160, 12, + 124,152, 20,124,145, 29,123,141, 34,123,137, 41,122,130, 53, + 118,117, 85,107, 89,252, 30, 31,180, 30, 31,180,115,107,129, + 124,147, 26,124,166, 7,122,175, 0,122,178, 0,122,178, 0, + 123,172, 2,124,162, 10,124,149, 24,123,139, 37,123,141, 34, + 124,150, 22,124,161, 11,124,167, 6,123,171, 3,123,172, 2, + 123,169, 4,124,167, 6,124,167, 6,123,169, 4,123,170, 3, + 123,169, 4,124,165, 8,124,154, 18,122,130, 53,123,137, 41, + 124,160, 12,123,168, 5,123,168, 5,124,160, 12,123,137, 41, + 107, 89,252,115,107,129,123,137, 41,124,152, 20,124,162, 10, + 124,166, 7,123,168, 5,123,170, 3,123,173, 1,119,186, 0, + 116,192, 0,113,198, 0,108,206, 0,103,212, 0, 99,216, 0, + 95,219, 0, 94,221, 0, 94,221, 0, 95,219, 0, 98,216, 0, + 101,213, 0,104,210, 0,104,210, 0,104,210, 0,104,210, 0, + 104,210, 0,102,212, 0,100,215, 0, 98,217, 0, 97,217, 0, + 98,216, 0,102,213, 0,107,207, 0,109,204, 0,112,200, 0, + 116,193, 0,121,181, 0,124,167, 6,124,150, 22,124,147, 26, + 124,145, 29,122,135, 44,123,139, 37,123,169, 4,119,186, 0, + 116,193, 0,114,197, 0,114,196, 0,116,193, 0,118,189, 0, + 121,182, 0,121,181, 0,120,184, 0,120,184, 0,121,179, 0, + 123,169, 4,124,152, 20,121,125, 63,111, 96,248,102, 80,250, + 109, 92,251,120,122, 70,124,143, 31,124,152, 20,124,154, 18, + 124,147, 26,124,149, 24,124,152, 20,124,157, 15,124,162, 10, + 123,168, 5,123,172, 2,122,175, 0,122,175, 0,123,174, 1, + 122,175, 0,122,177, 0,122,177, 0,123,174, 1,123,168, 5, + 124,157, 15,124,157, 15,124,152, 20,122,135, 44,113,100,242, + 117,110,109,124,149, 24,123,168, 5,122,178, 0,121,181, 0, + 120,184, 0,118,188, 0,116,193, 0,113,199, 0,110,203, 0, + 108,206, 0,107,207, 0,108,205, 0,111,202, 0,110,202, 0, + 103,211, 0, 97,218, 0, 93,222, 0, 91,224, 0, 90,225, 0, + 87,227, 0, 87,227, 0, 87,227, 0, 90,224, 0, 91,224, 0, + 91,223, 0, 92,223, 0, 94,221, 0, 97,218, 0,100,214, 0, + 103,211, 0,106,208, 0,106,207, 0,107,207, 0,108,205, 0, + 108,205, 0,109,204, 0,109,203, 0,111,201, 0,113,198, 0, + 115,194, 0,117,191, 0,117,191, 0,117,192, 0,116,193, 0, + 114,196, 0,113,198, 0,112,200, 0,111,202, 0,110,202, 0, + 110,203, 0,108,206, 0,104,210, 0,100,215, 0, 96,219, 0, + 93,221, 0, 92,222, 0, 92,222, 0, 93,221, 0, 95,219, 0, + 98,217, 0,100,215, 0,102,213, 0,103,211, 0,102,213, 0, + 101,214, 0,101,213, 0,103,211, 0,106,207, 0,111,202, 0, + 115,194, 0,113,198, 0,111,201, 0,110,202, 0,110,203, 0, + 110,202, 0,111,201, 0,113,198, 0,117,192, 0,120,184, 0, + 119,185, 0,117,190, 0,117,192, 0,117,192, 0,117,191, 0, + 114,196, 0,112,199, 0,111,201, 0,111,201, 0,111,201, 0, + 111,202, 0,112,200, 0,114,197, 0,114,196, 0,116,193, 0, + 118,188, 0,122,178, 0,124,155, 16,114,104,197, 69, 60,230, + 119,119, 77,124,155, 16,122,178, 0,116,192, 0,110,202, 0, + 105,209, 0,101,214, 0, 98,217, 0, 98,217, 0, 99,216, 0, + 101,214, 0,104,210, 0,108,205, 0,110,203, 0,108,206, 0, + 105,209, 0,101,214, 0, 97,218, 0, 94,221, 0, 92,223, 0, + 92,222, 0, 94,220, 0, 99,216, 0,104,210, 0,110,203, 0, + 115,195, 0,119,186, 0,122,176, 0,124,164, 9,124,150, 22, + 124,143, 31,123,139, 37,123,141, 34,124,143, 31,122,135, 44, + 122,132, 48,120,122, 70,114,104,197, 30, 55,222, 30, 20,155, + 107, 89,252,122,132, 48,124,155, 16,124,165, 8,123,168, 5, + 124,165, 8,124,157, 15,123,141, 34,122,135, 44,118,117, 85, + 120,122, 70,123,139, 37,124,150, 22,124,158, 13,124,162, 10, + 124,161, 11,124,158, 13,124,155, 16,124,155, 16,124,160, 12, + 124,162, 10,124,164, 9,124,161, 11,124,152, 20,122,132, 48, + 121,127, 58,124,154, 18,124,162, 10,124,162, 10,124,152, 20, + 121,125, 63, 99, 75,247,115,107,129,122,135, 44,124,150, 22, + 124,160, 12,124,165, 8,124,167, 6,123,169, 4,123,174, 1, + 120,182, 0,117,192, 0,112,200, 0,107,207, 0,102,212, 0, + 98,217, 0, 95,220, 0, 94,221, 0, 94,221, 0, 95,220, 0, + 97,218, 0,100,215, 0,102,213, 0,103,211, 0,105,209, 0, + 105,209, 0,104,210, 0,102,212, 0,100,214, 0, 99,216, 0, + 98,216, 0, 99,215, 0,102,212, 0,105,209, 0,106,208, 0, + 109,204, 0,114,196, 0,120,183, 0,123,169, 4,124,154, 18, + 123,139, 37,123,137, 41,120,122, 70,123,137, 41,123,171, 3, + 119,186, 0,115,194, 0,113,198, 0,113,198, 0,114,196, 0, + 117,192, 0,119,186, 0,121,179, 0,120,182, 0,121,182, 0, + 122,177, 0,124,167, 6,124,149, 24,119,119, 77,114,104,197, + 105, 84,252,113,100,242,122,130, 53,124,149, 24,124,158, 13, + 124,161, 11,124,155, 16,124,150, 22,124,152, 20,124,155, 16, + 124,160, 12,124,164, 9,123,168, 5,123,171, 3,123,172, 2, + 123,172, 2,123,174, 1,122,175, 0,122,175, 0,123,171, 3, + 124,165, 8,124,158, 13,124,161, 11,124,157, 15,124,143, 31, + 118,117, 85, 99, 75,247,122,130, 53,124,157, 15,123,170, 3, + 122,175, 0,122,178, 0,120,184, 0,117,190, 0,114,196, 0, + 111,201, 0,108,205, 0,107,207, 0,107,206, 0,109,203, 0, + 109,203, 0,106,208, 0,100,215, 0, 96,219, 0, 93,221, 0, + 89,225, 0, 87,227, 0, 87,227, 0, 90,225, 0, 91,223, 0, + 91,223, 0, 91,223, 0, 93,222, 0, 95,220, 0, 97,218, 0, + 99,216, 0,101,213, 0,103,212, 0,105,209, 0,107,207, 0, + 107,207, 0,107,206, 0,107,207, 0,107,207, 0,107,206, 0, + 109,204, 0,110,202, 0,112,199, 0,114,196, 0,115,194, 0, + 114,196, 0,114,197, 0,113,199, 0,111,201, 0,110,202, 0, + 109,203, 0,108,205, 0,106,208, 0,102,213, 0, 98,217, 0, + 94,220, 0, 92,223, 0, 91,224, 0, 90,224, 0, 91,223, 0, + 94,221, 0, 95,219, 0, 97,217, 0, 99,216, 0,100,214, 0, + 99,216, 0, 98,217, 0, 98,217, 0,100,215, 0,103,211, 0, + 108,205, 0,113,199, 0,110,203, 0,107,207, 0,105,209, 0, + 104,210, 0,105,209, 0,106,208, 0,109,204, 0,113,199, 0, + 117,191, 0,117,192, 0,117,191, 0,117,191, 0,117,190, 0, + 117,190, 0,114,196, 0,113,199, 0,111,201, 0,111,201, 0, + 111,201, 0,110,202, 0,111,202, 0,114,197, 0,115,195, 0, + 117,191, 0,120,184, 0,123,171, 3,124,143, 31,113,100,242, + 87, 65,237,121,125, 63,124,155, 16,122,176, 0,118,189, 0, + 112,200, 0,106,207, 0,102,213, 0, 98,216, 0, 97,218, 0, + 98,217, 0,100,215, 0,102,212, 0,107,207, 0,112,200, 0, + 113,199, 0,109,203, 0,105,209, 0,102,213, 0, 98,216, 0, + 97,218, 0, 97,217, 0,100,214, 0,105,209, 0,110,202, 0, + 115,194, 0,120,184, 0,123,172, 2,124,160, 12,124,154, 18, + 124,147, 26,123,139, 37,122,135, 44,123,139, 37,123,141, 34, + 122,132, 48,122,132, 48,122,130, 53,118,117, 85,102, 80,250, + 30, 37,192, 99, 75,247,119,119, 77,123,139, 37,124,149, 24, + 124,150, 22,124,143, 31,122,130, 53,121,125, 63,119,119, 77, + 111, 96,248,113,100,242,119,119, 77,122,135, 44,124,143, 31, + 124,147, 26,124,145, 29,123,137, 41,122,135, 44,123,137, 41, + 124,143, 31,124,150, 22,124,154, 18,124,154, 18,124,147, 26, + 122,130, 53,121,125, 63,124,147, 26,124,158, 13,124,158, 13, + 124,147, 26,118,114, 96, 69, 60,230,114,104,197,122,130, 53, + 124,147, 26,124,160, 12,124,167, 6,123,169, 4,123,168, 5, + 123,174, 1,120,183, 0,116,193, 0,111,201, 0,107,207, 0, + 102,212, 0, 99,215, 0, 97,218, 0, 96,219, 0, 96,219, 0, + 97,218, 0, 98,217, 0, 99,215, 0,101,214, 0,102,212, 0, + 103,211, 0,104,210, 0,104,210, 0,102,212, 0,101,214, 0, + 100,215, 0,100,215, 0,100,215, 0,101,214, 0,102,212, 0, + 104,210, 0,108,206, 0,114,197, 0,120,184, 0,123,171, 3, + 124,154, 18,122,132, 48,121,127, 58,117,110,109,123,141, 34, + 123,172, 2,119,187, 0,115,195, 0,113,198, 0,113,198, 0, + 114,196, 0,116,192, 0,119,186, 0,122,178, 0,121,180, 0, + 122,178, 0,123,173, 1,124,162, 10,123,141, 34,118,114, 96, + 117,110,109,109, 92,251,111, 96,248,121,127, 58,124,147, 26, + 124,158, 13,124,162, 10,124,158, 13,124,147, 26,124,143, 31, + 124,145, 29,124,149, 24,124,154, 18,124,160, 12,124,165, 8, + 124,167, 6,123,168, 5,123,171, 3,123,173, 1,123,172, 2, + 123,168, 5,124,161, 11,124,155, 16,124,161, 11,124,160, 12, + 124,149, 24,121,125, 63,111, 96,248,113,100,242,123,139, 37, + 124,157, 15,124,165, 8,123,169, 4,122,178, 0,119,185, 0, + 116,192, 0,113,198, 0,110,202, 0,108,205, 0,108,205, 0, + 109,203, 0,109,204, 0,105,209, 0,101,214, 0, 97,218, 0, + 92,222, 0, 89,225, 0, 88,226, 0, 89,225, 0, 92,222, 0, + 92,223, 0, 92,223, 0, 92,223, 0, 91,223, 0, 92,223, 0, + 94,221, 0, 96,219, 0, 97,218, 0, 99,215, 0,102,213, 0, + 105,209, 0,107,207, 0,107,206, 0,107,207, 0,106,208, 0, + 105,209, 0,105,209, 0,107,207, 0,108,205, 0,111,202, 0, + 113,199, 0,114,197, 0,113,198, 0,113,199, 0,112,200, 0, + 111,202, 0,110,203, 0,109,204, 0,106,208, 0,102,212, 0, + 98,216, 0, 95,219, 0, 93,222, 0, 92,223, 0, 91,223, 0, + 92,222, 0, 94,221, 0, 95,219, 0, 97,218, 0, 98,217, 0, + 99,215, 0, 98,217, 0, 97,218, 0, 97,217, 0, 99,215, 0, + 102,212, 0,107,207, 0,110,202, 0,106,207, 0,103,211, 0, + 101,214, 0,101,214, 0,101,213, 0,103,211, 0,106,208, 0, + 110,202, 0,112,200, 0,112,200, 0,113,198, 0,116,193, 0, + 118,189, 0,118,189, 0,115,194, 0,113,198, 0,112,200, 0, + 112,200, 0,111,202, 0,109,203, 0,110,203, 0,113,199, 0, + 116,193, 0,118,189, 0,121,180, 0,124,162, 10,121,125, 63, + 117,110,109,114,104,197,121,127, 58,124,152, 20,123,172, 2, + 119,186, 0,114,196, 0,109,204, 0,104,210, 0,100,215, 0, + 98,216, 0, 98,216, 0,100,215, 0,102,212, 0,106,208, 0, + 111,202, 0,116,193, 0,114,197, 0,110,202, 0,107,207, 0, + 105,209, 0,103,211, 0,104,210, 0,107,207, 0,111,201, 0, + 116,193, 0,120,182, 0,123,168, 5,124,157, 15,124,154, 18, + 124,149, 24,123,141, 34,122,130, 53,121,125, 63,122,130, 53, + 122,132, 48,121,125, 63,122,135, 44,123,137, 41,121,127, 58, + 114,104,197, 30, 55,222, 87, 65,237,115,107,129,121,127, 58, + 122,132, 48,122,130, 53,119,119, 77,115,107,129,118,114, 96, + 113,100,242, 69, 60,230, 87, 65,237,111, 96,248,118,117, 85, + 121,125, 63,121,125, 63,119,119, 77,115,107,129,120,122, 70, + 122,130, 53,122,130, 53,122,132, 48,123,139, 37,124,143, 31, + 123,137, 41,119,119, 77,118,117, 85,124,143, 31,124,155, 16, + 124,155, 16,124,143, 31,117,110,109, 30, 43,203,109, 92,251, + 119,119, 77,124,149, 24,124,164, 9,123,171, 3,123,174, 1, + 122,175, 0,123,174, 1,120,183, 0,116,193, 0,112,200, 0, + 107,206, 0,104,210, 0,102,213, 0,100,214, 0,100,215, 0, + 100,214, 0,100,214, 0,101,214, 0,101,214, 0,100,215, 0, + 100,215, 0,102,213, 0,103,211, 0,104,210, 0,102,213, 0, + 99,215, 0, 98,217, 0, 97,218, 0, 98,217, 0, 99,215, 0, + 101,214, 0,103,211, 0,107,206, 0,113,198, 0,120,184, 0, + 123,172, 2,124,155, 16,121,127, 58,119,119, 77,117,110,109, + 124,145, 29,123,173, 1,119,187, 0,115,194, 0,114,197, 0, + 114,197, 0,115,194, 0,117,190, 0,120,184, 0,122,178, 0, + 122,178, 0,122,176, 0,123,169, 4,124,155, 16,122,130, 53, + 115,107,129,115,107,129,114,104,197,117,110,109,119,119, 77, + 123,141, 34,124,154, 18,124,158, 13,124,155, 16,124,145, 29, + 121,127, 58,121,125, 63,122,130, 53,123,137, 41,124,143, 31, + 124,152, 20,124,155, 16,124,160, 12,124,165, 8,123,168, 5, + 123,168, 5,124,165, 8,124,157, 15,124,149, 24,124,157, 15, + 124,157, 15,124,149, 24,122,130, 53,117,110,109,118,114, 96, + 117,110,109,122,135, 44,124,147, 26,124,160, 12,123,172, 2, + 122,178, 0,119,186, 0,116,193, 0,113,199, 0,110,202, 0, + 110,202, 0,110,202, 0,109,203, 0,106,208, 0,101,214, 0, + 97,218, 0, 93,221, 0, 91,223, 0, 91,224, 0, 93,222, 0, + 94,221, 0, 92,222, 0, 89,226, 0, 87,227, 0, 86,228, 0, + 88,226, 0, 91,223, 0, 91,223, 0, 92,222, 0, 95,219, 0, + 99,215, 0,102,212, 0,105,209, 0,105,209, 0,106,207, 0, + 105,209, 0,104,210, 0,103,211, 0,104,210, 0,106,208, 0, + 108,205, 0,111,201, 0,113,198, 0,113,198, 0,113,198, 0, + 113,198, 0,112,199, 0,111,201, 0,110,202, 0,108,205, 0, + 105,209, 0,102,213, 0, 99,216, 0, 97,218, 0, 95,219, 0, + 95,220, 0, 95,219, 0, 96,219, 0, 98,217, 0, 98,216, 0, + 99,215, 0,100,215, 0,100,215, 0, 98,216, 0, 99,216, 0, + 101,214, 0,104,210, 0,108,205, 0,107,206, 0,104,210, 0, + 101,214, 0, 99,215, 0, 99,215, 0,100,214, 0,102,212, 0, + 106,208, 0,108,205, 0,106,207, 0,106,207, 0,108,205, 0, + 112,199, 0,117,190, 0,119,187, 0,116,193, 0,114,197, 0, + 112,199, 0,112,200, 0,111,202, 0,109,203, 0,110,203, 0, + 112,199, 0,116,192, 0,119,187, 0,122,176, 0,124,158, 13, + 123,139, 37,122,135, 44,121,127, 58,121,127, 58,124,155, 16, + 122,178, 0,118,189, 0,114,197, 0,111,201, 0,107,207, 0, + 103,212, 0,101,214, 0,100,214, 0,101,214, 0,103,212, 0, + 106,208, 0,110,203, 0,114,196, 0,118,189, 0,116,193, 0, + 113,198, 0,111,202, 0,109,203, 0,110,202, 0,113,198, 0, + 117,191, 0,121,180, 0,124,164, 9,124,152, 20,124,160, 12, + 124,162, 10,124,161, 11,124,154, 18,123,137, 41,117,110,109, + 117,110,109,118,114, 96,121,125, 63,123,139, 37,124,143, 31, + 123,139, 37,120,122, 70,105, 84,252, 30, 55,222,109, 92,251, + 118,114, 96,119,119, 77,118,114, 96,113,100,242,109, 92,251, + 109, 92,251, 87, 65,237, 30, 15,143, 87, 65,237,113,100,242, + 117,110,109,115,107,129,113,100,242,107, 89,252, 94, 70,242, + 115,107,129,120,122, 70,121,125, 63,118,117, 85,119,119, 77, + 121,125, 63,119,119, 77,111, 96,248,118,114, 96,124,143, 31, + 124,154, 18,124,154, 18,123,141, 34,115,107,129, 69, 60,230, + 94, 70,242,119,119, 77,124,149, 24,124,164, 9,123,172, 2, + 122,176, 0,122,177, 0,122,178, 0,121,181, 0,117,191, 0, + 113,198, 0,109,203, 0,107,207, 0,105,209, 0,105,209, 0, + 105,209, 0,105,209, 0,105,209, 0,102,212, 0, 99,215, 0, + 97,217, 0, 97,217, 0, 99,216, 0,102,212, 0,105,209, 0, + 102,212, 0, 99,216, 0, 97,218, 0, 96,219, 0, 97,217, 0, + 99,215, 0,102,213, 0,105,209, 0,108,205, 0,114,196, 0, + 119,185, 0,123,174, 1,124,155, 16,121,125, 63,118,114, 96, + 114,104,197,124,147, 26,123,174, 1,119,186, 0,116,192, 0, + 115,194, 0,116,193, 0,117,190, 0,119,185, 0,122,178, 0, + 122,178, 0,122,178, 0,123,174, 1,124,165, 8,124,147, 26, + 118,117, 85,107, 89,252,107, 89,252,114,104,197,119,119, 77, + 120,122, 70,121,127, 58,124,143, 31,124,150, 22,124,149, 24, + 123,137, 41,121,127, 58,122,132, 48,122,132, 48,122,135, 44, + 122,130, 53,121,125, 63,122,135, 44,123,141, 34,124,152, 20, + 124,158, 13,124,161, 11,124,160, 12,124,152, 20,123,141, 34, + 124,147, 26,124,150, 22,124,143, 31,121,127, 58,120,122, 70, + 121,125, 63,119,119, 77,115,107,129,119,119, 77,124,149, 24, + 124,166, 7,122,175, 0,121,180, 0,119,186, 0,116,193, 0, + 113,198, 0,112,199, 0,112,200, 0,111,201, 0,108,206, 0, + 103,211, 0, 99,215, 0, 96,219, 0, 94,221, 0, 94,220, 0, + 97,218, 0, 92,222, 0, 87,227, 0, 83,230, 0, 82,231, 0, + 82,231, 0, 85,229, 0, 87,227, 0, 87,227, 0, 89,226, 0, + 92,222, 0, 97,218, 0,100,215, 0,102,212, 0,105,209, 0, + 106,207, 0,106,208, 0,104,210, 0,103,211, 0,104,210, 0, + 106,208, 0,108,205, 0,112,200, 0,113,199, 0,113,198, 0, + 113,199, 0,112,200, 0,112,200, 0,113,199, 0,113,199, 0, + 112,199, 0,110,203, 0,107,207, 0,104,210, 0,102,213, 0, + 100,214, 0,100,215, 0,100,215, 0,100,214, 0,101,214, 0, + 101,213, 0,101,213, 0,101,213, 0,102,212, 0,102,213, 0, + 102,212, 0,104,210, 0,107,207, 0,109,204, 0,106,208, 0, + 102,212, 0,100,215, 0, 99,216, 0,100,215, 0,101,213, 0, + 103,211, 0,106,208, 0,103,212, 0,101,213, 0,102,213, 0, + 105,209, 0,109,203, 0,115,194, 0,119,186, 0,116,192, 0, + 114,196, 0,113,199, 0,112,199, 0,111,201, 0,110,203, 0, + 110,202, 0,113,199, 0,116,192, 0,119,186, 0,122,175, 0, + 124,160, 12,124,150, 22,124,145, 29,123,137, 41,120,122, 70, + 124,162, 10,120,182, 0,116,193, 0,111,201, 0,108,206, 0, + 106,208, 0,105,209, 0,104,210, 0,103,212, 0,103,212, 0, + 104,210, 0,106,208, 0,109,204, 0,113,198, 0,117,191, 0, + 120,182, 0,118,188, 0,116,192, 0,115,194, 0,116,193, 0, + 118,189, 0,121,180, 0,124,165, 8,124,150, 22,124,160, 12, + 124,165, 8,124,166, 7,124,162, 10,124,154, 18,122,135, 44, + 114,104,197, 69, 60,230,114,104,197,122,130, 53,124,145, 29, + 124,149, 24,124,147, 26,122,132, 48,114,104,197, 30, 55,222, + 102, 80,250,114,104,197,117,110,109,115,107,129,109, 92,251, + 94, 70,242, 30, 49,213, 30, 10,132, 94, 70,242,117,110,109, + 121,127, 58,122,132, 48,121,125, 63,113,100,242, 87, 65,237, + 30, 43,203,107, 89,252,117,110,109,118,117, 85,118,114, 96, + 114,104,197,114,104,197,109, 92,251, 30, 55,222,118,117, 85, + 124,143, 31,124,155, 16,124,155, 16,124,143, 31,117,110,109, + 105, 84,252, 30, 49,213,118,114, 96,124,145, 29,124,161, 11, + 123,169, 4,123,173, 1,122,175, 0,122,175, 0,122,176, 0, + 119,187, 0,115,195, 0,112,200, 0,110,203, 0,109,204, 0, + 108,205, 0,109,204, 0,109,204, 0,107,207, 0,103,211, 0, + 100,215, 0, 97,217, 0, 97,218, 0, 98,217, 0,101,214, 0, + 106,207, 0,102,212, 0, 99,215, 0, 98,217, 0, 97,217, 0, + 99,216, 0,101,214, 0,103,211, 0,107,207, 0,110,202, 0, + 115,195, 0,118,188, 0,122,177, 0,124,157, 15,122,130, 53, + 118,117, 85,107, 89,252,124,149, 24,123,173, 1,120,184, 0, + 118,189, 0,117,190, 0,118,188, 0,120,182, 0,122,176, 0, + 122,177, 0,122,178, 0,122,176, 0,123,171, 3,124,160, 12, + 123,137, 41,114,104,197, 30, 31,180, 30, 37,192,107, 89,252, + 118,114, 96,121,125, 63,121,127, 58,121,127, 58,123,137, 41, + 122,135, 44,124,145, 29,124,152, 20,124,155, 16,124,154, 18, + 124,147, 26,122,135, 44,118,114, 96,107, 89,252,114,104,197, + 121,125, 63,123,141, 34,124,150, 22,124,152, 20,124,149, 24, + 123,139, 37,121,127, 58,122,135, 44,122,132, 48,121,125, 63, + 122,130, 53,122,130, 53,121,125, 63,117,110,109,102, 80,250, + 123,137, 41,124,161, 11,123,172, 2,122,178, 0,120,183, 0, + 118,188, 0,116,193, 0,115,195, 0,112,200, 0,110,203, 0, + 108,205, 0,107,207, 0,103,211, 0,100,214, 0, 99,216, 0, + 99,215, 0, 94,221, 0, 87,227, 0, 82,231, 0, 78,234, 0, + 77,235, 0, 78,234, 0, 81,232, 0, 85,229, 0, 84,229, 0, + 86,228, 0, 89,225, 0, 94,221, 0, 98,217, 0,101,214, 0, + 104,210, 0,109,204, 0,107,207, 0,105,209, 0,104,210, 0, + 105,209, 0,108,206, 0,110,202, 0,112,200, 0,113,198, 0, + 111,201, 0,109,203, 0,108,205, 0,108,205, 0,109,204, 0, + 110,202, 0,113,199, 0,115,195, 0,112,200, 0,109,203, 0, + 107,206, 0,106,208, 0,105,209, 0,104,210, 0,104,210, 0, + 105,209, 0,104,210, 0,103,211, 0,103,211, 0,103,211, 0, + 104,210, 0,106,208, 0,108,205, 0,111,202, 0,109,204, 0, + 106,208, 0,102,212, 0,101,214, 0,100,214, 0,101,213, 0, + 103,211, 0,106,208, 0,102,212, 0,100,215, 0, 98,217, 0, + 98,216, 0,102,213, 0,107,206, 0,114,197, 0,119,185, 0, + 117,191, 0,114,196, 0,113,199, 0,112,200, 0,112,200, 0, + 111,201, 0,111,201, 0,114,196, 0,115,194, 0,118,188, 0, + 122,175, 0,124,155, 16,124,152, 20,124,147, 26,123,139, 37, + 124,145, 29,123,169, 4,119,186, 0,114,197, 0,109,203, 0, + 106,208, 0,103,211, 0,102,213, 0,101,214, 0,101,214, 0, + 103,211, 0,105,209, 0,106,208, 0,108,205, 0,111,201, 0, + 114,196, 0,118,188, 0,122,178, 0,121,179, 0,120,182, 0, + 121,182, 0,122,177, 0,123,170, 3,124,160, 12,124,155, 16, + 124,164, 9,124,167, 6,124,167, 6,124,162, 10,124,150, 22, + 121,127, 58,114,104,197,111, 96,248,114,104,197,122,130, 53, + 124,145, 29,124,149, 24,124,145, 29,122,132, 48,115,107,129, + 87, 65,237,107, 89,252,115,107,129,118,117, 85,118,117, 85, + 115,107,129,109, 92,251, 87, 65,237, 99, 75,247,117,110,109, + 122,132, 48,124,143, 31,123,141, 34,122,130, 53,113,100,242, + 30, 43,203, 30, 20,155, 99, 75,247,113,100,242,117,110,109, + 117,110,109,114,104,197,111, 96,248,105, 84,252, 99, 75,247, + 117,110,109,124,143, 31,124,155, 16,124,155, 16,124,145, 29, + 121,125, 63,114,104,197,109, 92,251,114,104,197,123,137, 41, + 124,154, 18,124,164, 9,123,169, 4,122,178, 0,120,182, 0, + 120,183, 0,117,190, 0,115,194, 0,114,197, 0,112,200, 0, + 110,202, 0,109,203, 0,108,205, 0,107,206, 0,106,208, 0, + 105,209, 0,101,213, 0, 98,216, 0, 97,217, 0, 98,217, 0, + 101,214, 0,106,208, 0,104,210, 0,101,214, 0,100,215, 0, + 100,214, 0,102,213, 0,101,213, 0,103,212, 0,106,208, 0, + 110,202, 0,114,196, 0,118,188, 0,122,177, 0,124,157, 15, + 122,135, 44,119,119, 77,117,110,109,124,150, 22,123,172, 2, + 121,182, 0,119,185, 0,120,184, 0,121,179, 0,122,177, 0, + 122,178, 0,122,178, 0,122,178, 0,122,175, 0,123,170, 3, + 124,167, 6,124,157, 15,122,135, 44,107, 89,252, 30, 31,180, + 94, 70,242,109, 92,251,118,117, 85,122,130, 53,123,137, 41, + 123,141, 34,124,149, 24,124,157, 15,124,164, 9,123,168, 5, + 123,168, 5,124,165, 8,124,158, 13,124,147, 26,122,132, 48, + 114,104,197,111, 96,248,118,114, 96,122,135, 44,124,145, 29, + 124,147, 26,124,143, 31,122,135, 44,122,135, 44,123,137, 41, + 123,137, 41,123,137, 41,122,135, 44,122,130, 53,118,117, 85, + 109, 92,251,118,114, 96,124,149, 24,124,165, 8,122,175, 0, + 121,181, 0,119,186, 0,117,191, 0,114,197, 0,110,202, 0, + 108,205, 0,106,207, 0,105,209, 0,103,211, 0,101,213, 0, + 99,216, 0, 95,220, 0, 89,225, 0, 83,230, 0, 78,235, 0, + 74,237, 0, 72,239, 0, 74,238, 0, 76,236, 0, 80,232, 0, + 83,230, 0, 85,229, 0, 87,227, 0, 91,223, 0, 95,219, 0, + 100,215, 0,103,211, 0,105,209, 0,105,209, 0,105,209, 0, + 105,209, 0,106,208, 0,107,207, 0,109,203, 0,111,201, 0, + 110,203, 0,108,205, 0,106,208, 0,105,209, 0,105,209, 0, + 106,208, 0,107,206, 0,110,203, 0,112,200, 0,114,197, 0, + 113,199, 0,111,202, 0,109,204, 0,107,207, 0,105,209, 0, + 104,210, 0,103,211, 0,104,210, 0,105,209, 0,105,209, 0, + 104,210, 0,105,209, 0,106,207, 0,108,205, 0,106,207, 0, + 104,210, 0,103,211, 0,103,211, 0,102,212, 0,102,212, 0, + 103,211, 0,105,209, 0,105,209, 0,101,213, 0, 98,216, 0, + 97,218, 0, 97,217, 0,101,214, 0,106,208, 0,112,200, 0, + 118,189, 0,118,189, 0,115,194, 0,114,197, 0,112,199, 0, + 112,200, 0,112,200, 0,113,199, 0,114,197, 0,115,194, 0, + 118,188, 0,122,178, 0,124,161, 11,124,154, 18,124,145, 29, + 123,141, 34,124,164, 9,123,174, 1,118,188, 0,114,197, 0, + 109,204, 0,105,209, 0,102,212, 0,101,214, 0, 99,216, 0, + 99,216, 0,101,213, 0,106,208, 0,106,208, 0,107,207, 0, + 109,204, 0,112,200, 0,115,194, 0,119,185, 0,123,173, 1, + 123,174, 1,122,175, 0,123,172, 2,124,166, 7,124,154, 18, + 124,162, 10,123,168, 5,123,170, 3,123,169, 4,124,164, 9, + 124,150, 22,121,127, 58,118,114, 96,115,107,129,113,100,242, + 119,119, 77,122,132, 48,123,139, 37,122,135, 44,120,122, 70, + 109, 92,251,105, 84,252,114,104,197,120,122, 70,122,130, 53, + 122,132, 48,121,127, 58,118,114, 96,111, 96,248,118,114, 96, + 122,132, 48,124,143, 31,124,145, 29,123,141, 34,121,127, 58, + 111, 96,248, 30, 37,192, 30, 55,222,113,100,242,119,119, 77, + 121,125, 63,120,122, 70,118,114, 96,113,100,242,109, 92,251, + 109, 92,251,114,104,197,122,132, 48,124,149, 24,124,154, 18, + 124,145, 29,123,137, 41,121,127, 58,121,127, 58,122,135, 44, + 124,143, 31,124,154, 18,124,165, 8,123,174, 1,121,182, 0, + 119,186, 0,118,189, 0,114,196, 0,112,199, 0,111,202, 0, + 109,203, 0,109,204, 0,108,205, 0,107,206, 0,107,207, 0, + 106,208, 0,105,209, 0,103,211, 0,100,214, 0, 98,216, 0, + 99,216, 0,101,214, 0,106,208, 0,107,207, 0,105,209, 0, + 104,210, 0,104,210, 0,103,212, 0,103,212, 0,104,210, 0, + 108,205, 0,112,199, 0,116,193, 0,120,184, 0,123,172, 2, + 124,149, 24,123,137, 41,120,122, 70,118,117, 85,124,152, 20, + 123,171, 3,122,178, 0,121,180, 0,122,177, 0,122,176, 0, + 121,182, 0,120,182, 0,121,180, 0,122,178, 0,122,178, 0, + 121,180, 0,122,178, 0,123,170, 3,124,155, 16,121,125, 63, + 114,104,197,109, 92,251,114,104,197,118,117, 85,122,130, 53, + 123,141, 34,124,147, 26,124,154, 18,124,161, 11,123,168, 5, + 123,172, 2,123,174, 1,123,173, 1,123,170, 3,124,164, 9, + 124,154, 18,122,135, 44,105, 84,252,111, 96,248,119,119, 77, + 123,139, 37,124,147, 26,124,152, 20,124,154, 18,124,154, 18, + 124,155, 16,124,155, 16,124,154, 18,124,147, 26,123,137, 41, + 122,130, 53,118,114, 96,113,100,242,119,119, 77,124,149, 24, + 124,165, 8,122,175, 0,120,182, 0,118,189, 0,114,196, 0, + 111,202, 0,108,206, 0,105,209, 0,103,211, 0,101,213, 0, + 99,216, 0, 96,219, 0, 92,223, 0, 86,228, 0, 79,233, 0, + 74,238, 0, 70,241, 0, 69,242, 0, 70,241, 0, 72,239, 0, + 76,236, 0, 80,232, 0, 84,229, 0, 86,228, 0, 89,225, 0, + 92,222, 0, 96,219, 0,100,215, 0,102,213, 0,102,212, 0, + 103,212, 0,103,211, 0,104,210, 0,106,208, 0,108,205, 0, + 110,203, 0,107,206, 0,105,209, 0,103,211, 0,102,212, 0, + 102,212, 0,103,211, 0,105,209, 0,108,206, 0,110,202, 0, + 112,199, 0,114,197, 0,114,197, 0,111,201, 0,109,204, 0, + 107,207, 0,105,209, 0,104,210, 0,105,209, 0,106,208, 0, + 107,207, 0,106,208, 0,106,207, 0,107,207, 0,105,209, 0, + 103,212, 0,101,214, 0,100,214, 0,101,213, 0,103,211, 0, + 104,210, 0,105,209, 0,107,207, 0,104,210, 0,101,213, 0, + 98,216, 0, 97,218, 0, 97,217, 0,100,215, 0,104,210, 0, + 110,202, 0,116,193, 0,117,190, 0,117,192, 0,115,194, 0, + 114,197, 0,113,198, 0,113,199, 0,114,197, 0,114,196, 0, + 116,193, 0,118,188, 0,122,178, 0,124,167, 6,124,165, 8, + 124,162, 10,124,161, 11,122,175, 0,122,176, 0,119,187, 0, + 115,195, 0,111,202, 0,107,207, 0,104,210, 0,101,214, 0, + 99,216, 0, 98,217, 0,101,214, 0,105,209, 0,106,207, 0, + 106,208, 0,107,207, 0,109,204, 0,113,199, 0,117,191, 0, + 121,180, 0,123,171, 3,123,172, 2,123,169, 4,124,162, 10, + 124,160, 12,124,167, 6,123,172, 2,123,174, 1,123,171, 3, + 124,166, 7,124,154, 18,122,132, 48,117,110,109,114,104,197, + 109, 92,251,109, 92,251,117,110,109,118,117, 85,118,114, 96, + 113,100,242,111, 96,248,115,107,129,120,122, 70,122,135, 44, + 124,143, 31,124,145, 29,123,141, 34,122,130, 53,119,119, 77, + 121,127, 58,123,137, 41,123,141, 34,123,139, 37,122,135, 44, + 119,119, 77,105, 84,252, 30, 26,168,107, 89,252,120,122, 70, + 123,137, 41,123,141, 34,123,141, 34,122,135, 44,121,127, 58, + 118,114, 96,114,104,197,118,114, 96,122,132, 48,124,143, 31, + 124,147, 26,124,150, 22,124,150, 22,124,149, 24,124,149, 24, + 124,150, 22,124,154, 18,124,160, 12,123,168, 5,122,175, 0, + 121,182, 0,119,186, 0,116,193, 0,112,200, 0,110,203, 0, + 108,205, 0,108,206, 0,107,206, 0,107,206, 0,107,206, 0, + 107,207, 0,107,206, 0,107,207, 0,106,208, 0,103,212, 0, + 101,214, 0,100,214, 0,102,213, 0,105,209, 0,108,206, 0, + 107,207, 0,106,208, 0,105,209, 0,105,209, 0,106,208, 0, + 108,205, 0,112,200, 0,116,193, 0,119,186, 0,122,177, 0, + 124,161, 11,123,139, 37,122,135, 44,120,122, 70,120,122, 70, + 124,157, 15,123,171, 3,122,177, 0,122,175, 0,123,173, 1, + 121,181, 0,120,184, 0,120,184, 0,121,180, 0,121,182, 0, + 120,184, 0,120,184, 0,120,183, 0,122,178, 0,124,166, 7, + 124,145, 29,123,137, 41,121,125, 63,118,117, 85,122,130, 53, + 122,132, 48,124,143, 31,124,150, 22,124,154, 18,124,161, 11, + 124,167, 6,123,172, 2,122,176, 0,122,177, 0,122,175, 0, + 123,170, 3,124,164, 9,124,145, 29,113,100,242,111, 96,248, + 115,107,129,122,135, 44,124,154, 18,124,161, 11,124,164, 9, + 124,165, 8,124,166, 7,124,166, 7,124,164, 9,124,160, 12, + 124,150, 22,123,139, 37,122,130, 53,120,122, 70,120,122, 70, + 122,130, 53,124,150, 22,123,168, 5,121,180, 0,118,188, 0, + 116,193, 0,112,200, 0,108,205, 0,106,208, 0,103,212, 0, + 100,215, 0, 97,218, 0, 94,221, 0, 89,225, 0, 83,230, 0, + 77,235, 0, 72,239, 0, 68,242, 0, 66,243, 0, 67,243, 0, + 70,241, 0, 74,237, 0, 78,234, 0, 83,230, 0, 85,229, 0, + 87,227, 0, 90,224, 0, 94,221, 0, 97,218, 0,100,215, 0, + 101,214, 0,102,213, 0,102,212, 0,103,211, 0,105,209, 0, + 106,207, 0,108,205, 0,106,208, 0,104,210, 0,102,212, 0, + 101,214, 0,101,214, 0,102,213, 0,104,210, 0,107,207, 0, + 109,203, 0,111,201, 0,113,199, 0,114,197, 0,114,197, 0, + 113,198, 0,111,202, 0,109,204, 0,108,206, 0,108,206, 0, + 108,205, 0,109,204, 0,108,205, 0,108,205, 0,106,207, 0, + 104,210, 0,101,213, 0,100,215, 0,100,215, 0,102,213, 0, + 105,209, 0,105,209, 0,106,207, 0,107,207, 0,104,210, 0, + 102,213, 0,100,215, 0, 98,216, 0, 99,216, 0,100,214, 0, + 103,211, 0,108,205, 0,114,197, 0,116,192, 0,115,194, 0, + 114,196, 0,114,197, 0,113,198, 0,113,198, 0,114,197, 0, + 116,193, 0,117,190, 0,120,184, 0,122,176, 0,123,171, 3, + 123,172, 2,123,172, 2,123,173, 1,121,181, 0,121,180, 0, + 120,182, 0,117,190, 0,114,196, 0,111,202, 0,107,207, 0, + 103,211, 0,100,214, 0, 99,215, 0,101,214, 0,105,209, 0, + 107,206, 0,106,208, 0,106,208, 0,108,206, 0,111,201, 0, + 116,193, 0,120,183, 0,123,171, 3,123,172, 2,123,169, 4, + 124,162, 10,124,164, 9,123,170, 3,123,174, 1,122,175, 0, + 123,172, 2,124,167, 6,124,155, 16,123,137, 41,118,114, 96, + 109, 92,251,102, 80,250, 94, 70,242, 99, 75,247,105, 84,252, + 111, 96,248,115,107,129,118,114, 96,119,119, 77,122,132, 48, + 124,143, 31,124,150, 22,124,152, 20,124,149, 24,123,139, 37, + 121,127, 58,122,130, 53,122,135, 44,122,132, 48,121,127, 58, + 119,119, 77,113,100,242, 87, 65,237, 87, 65,237,118,114, 96, + 123,139, 37,124,149, 24,124,152, 20,124,152, 20,124,149, 24, + 123,141, 34,122,135, 44,121,127, 58,122,130, 53,123,141, 34, + 124,152, 20,124,160, 12,124,162, 10,124,162, 10,124,160, 12, + 124,157, 15,124,157, 15,124,157, 15,124,160, 12,124,165, 8, + 123,171, 3,122,178, 0,119,186, 0,115,195, 0,111,202, 0, + 108,205, 0,107,207, 0,107,207, 0,107,207, 0,108,206, 0, + 108,205, 0,108,206, 0,108,206, 0,109,204, 0,109,204, 0, + 106,208, 0,104,210, 0,103,212, 0,103,211, 0,106,208, 0, + 108,205, 0,107,206, 0,107,207, 0,107,207, 0,107,206, 0, + 109,203, 0,113,199, 0,117,192, 0,120,184, 0,122,175, 0, + 124,162, 10,123,141, 34,122,135, 44,122,132, 48,120,122, 70, + 122,135, 44,124,164, 9,123,173, 1,122,176, 0,123,172, 2, + 122,178, 0,120,183, 0,119,185, 0,120,182, 0,121,181, 0, + 120,184, 0,119,186, 0,119,186, 0,120,184, 0,121,179, 0, + 123,170, 3,124,158, 13,124,152, 20,123,139, 37,121,127, 58, + 123,139, 37,124,143, 31,124,145, 29,124,149, 24,124,150, 22, + 124,155, 16,124,162, 10,123,168, 5,123,172, 2,122,175, 0, + 123,174, 1,123,171, 3,124,165, 8,124,147, 26,113,100,242, + 111, 96,248,115,107,129,123,137, 41,124,155, 16,124,165, 8, + 124,167, 6,123,168, 5,123,169, 4,123,169, 4,123,168, 5, + 124,165, 8,124,160, 12,124,149, 24,123,141, 34,123,139, 37, + 123,141, 34,124,147, 26,124,154, 18,124,167, 6,121,179, 0, + 118,188, 0,115,194, 0,112,200, 0,108,206, 0,103,211, 0, + 100,215, 0, 96,219, 0, 93,221, 0, 92,223, 0, 88,226, 0, + 82,231, 0, 76,236, 0, 71,240, 0, 67,243, 0, 66,244, 0, + 67,243, 0, 70,241, 0, 74,238, 0, 78,234, 0, 83,230, 0, + 85,229, 0, 86,228, 0, 88,226, 0, 92,223, 0, 95,219, 0, + 99,216, 0,101,214, 0,102,213, 0,102,212, 0,103,212, 0, + 104,210, 0,105,209, 0,107,207, 0,106,208, 0,104,210, 0, + 102,213, 0,100,214, 0,100,214, 0,102,213, 0,104,210, 0, + 107,207, 0,110,203, 0,112,200, 0,113,199, 0,113,198, 0, + 113,198, 0,113,198, 0,113,198, 0,113,198, 0,112,199, 0, + 112,199, 0,113,199, 0,110,202, 0,110,203, 0,110,202, 0, + 108,205, 0,105,209, 0,102,213, 0,100,214, 0,101,214, 0, + 103,211, 0,107,207, 0,107,207, 0,108,206, 0,107,207, 0, + 105,209, 0,103,211, 0,102,212, 0,101,213, 0,101,214, 0, + 102,212, 0,104,210, 0,108,206, 0,112,200, 0,115,194, 0, + 115,195, 0,114,196, 0,114,197, 0,113,198, 0,114,197, 0, + 114,196, 0,116,193, 0,118,188, 0,121,181, 0,123,172, 2, + 123,171, 3,122,175, 0,122,178, 0,121,179, 0,120,183, 0, + 121,181, 0,122,175, 0,121,180, 0,119,187, 0,116,193, 0, + 112,200, 0,108,205, 0,104,210, 0,102,212, 0,103,212, 0, + 106,208, 0,109,204, 0,107,206, 0,107,207, 0,108,205, 0, + 112,200, 0,117,192, 0,121,182, 0,122,175, 0,122,175, 0, + 123,172, 2,124,164, 9,124,164, 9,123,169, 4,123,173, 1, + 123,174, 1,123,171, 3,124,167, 6,124,158, 13,123,139, 37, + 119,119, 77,114,104,197,111, 96,248,109, 92,251,111, 96,248, + 117,110,109,119,119, 77,120,122, 70,121,125, 63,122,130, 53, + 123,139, 37,124,149, 24,124,154, 18,124,155, 16,124,152, 20, + 123,141, 34,120,122, 70,119,119, 77,119,119, 77,117,110,109, + 114,104,197,111, 96,248, 99, 75,247, 94, 70,242,107, 89,252, + 121,127, 58,124,149, 24,124,157, 15,124,158, 13,124,158, 13, + 124,157, 15,124,158, 13,124,150, 22,124,143, 31,124,145, 29, + 124,154, 18,124,161, 11,124,166, 7,124,167, 6,124,166, 7, + 124,162, 10,124,158, 13,124,157, 15,124,154, 18,124,152, 20, + 124,155, 16,124,161, 11,123,170, 3,120,184, 0,115,194, 0, + 111,202, 0,108,205, 0,107,207, 0,107,207, 0,107,207, 0, + 108,205, 0,107,207, 0,107,207, 0,108,205, 0,109,204, 0, + 110,203, 0,110,202, 0,108,205, 0,107,207, 0,106,207, 0, + 108,206, 0,109,203, 0,109,204, 0,108,205, 0,109,203, 0, + 111,201, 0,114,197, 0,118,189, 0,121,181, 0,123,174, 1, + 124,158, 13,122,135, 44,119,119, 77,122,130, 53,122,132, 48, + 122,135, 44,124,149, 24,123,169, 4,122,176, 0,122,177, 0, + 123,174, 1,121,179, 0,120,182, 0,120,183, 0,121,179, 0, + 121,180, 0,121,182, 0,120,183, 0,120,184, 0,121,182, 0, + 122,178, 0,123,168, 5,124,161, 11,124,155, 16,123,141, 34, + 123,141, 34,124,149, 24,124,150, 22,124,147, 26,124,147, 26, + 124,145, 29,124,147, 26,124,152, 20,124,158, 13,124,165, 8, + 123,168, 5,123,168, 5,124,166, 7,124,158, 13,123,137, 41, + 105, 84,252,111, 96,248,118,117, 85,122,135, 44,124,154, 18, + 124,162, 10,124,165, 8,124,166, 7,124,167, 6,124,167, 6, + 124,167, 6,124,166, 7,124,162, 10,124,157, 15,124,143, 31, + 124,147, 26,124,152, 20,124,157, 15,124,161, 11,124,165, 8, + 122,178, 0,119,187, 0,115,194, 0,111,201, 0,106,208, 0, + 101,214, 0, 96,219, 0, 93,222, 0, 90,224, 0, 89,226, 0, + 88,226, 0, 82,231, 0, 76,236, 0, 72,239, 0, 68,242, 0, + 67,243, 0, 69,242, 0, 72,239, 0, 76,236, 0, 81,232, 0, + 85,229, 0, 84,229, 0, 85,229, 0, 87,227, 0, 91,223, 0, + 95,219, 0,100,215, 0,102,212, 0,103,211, 0,103,211, 0, + 103,211, 0,103,211, 0,104,210, 0,105,209, 0,106,207, 0, + 104,210, 0,102,212, 0,101,214, 0,101,214, 0,102,212, 0, + 105,209, 0,108,205, 0,111,202, 0,111,201, 0,113,199, 0, + 114,197, 0,114,197, 0,113,198, 0,113,198, 0,114,197, 0, + 115,194, 0,117,191, 0,114,196, 0,112,199, 0,111,201, 0, + 111,201, 0,111,201, 0,108,206, 0,105,209, 0,103,211, 0, + 105,209, 0,107,207, 0,109,204, 0,109,204, 0,109,204, 0, + 107,207, 0,105,209, 0,105,209, 0,105,209, 0,105,209, 0, + 105,209, 0,105,209, 0,106,208, 0,108,205, 0,112,200, 0, + 116,193, 0,115,194, 0,114,196, 0,114,196, 0,114,196, 0, + 114,196, 0,115,194, 0,116,192, 0,118,188, 0,121,179, 0, + 124,167, 6,123,168, 5,122,175, 0,121,179, 0,121,182, 0, + 121,181, 0,122,178, 0,123,170, 3,124,162, 10,122,178, 0, + 119,186, 0,117,191, 0,113,198, 0,109,204, 0,106,208, 0, + 106,208, 0,108,206, 0,111,201, 0,110,203, 0,109,203, 0, + 111,202, 0,114,196, 0,118,188, 0,122,178, 0,121,179, 0, + 121,179, 0,122,176, 0,123,168, 5,124,158, 13,124,166, 7, + 123,171, 3,122,176, 0,122,176, 0,123,172, 2,124,164, 9, + 124,149, 24,120,122, 70,115,107,129,115,107,129,115,107,129, + 118,114, 96,120,122, 70,122,130, 53,122,130, 53,121,127, 58, + 122,132, 48,123,139, 37,124,147, 26,124,154, 18,124,155, 16, + 124,152, 20,123,141, 34,118,117, 85,113,100,242,107, 89,252, + 94, 70,242,105, 84,252,109, 92,251,109, 92,251,107, 89,252, + 113,100,242,122,132, 48,124,152, 20,124,158, 13,124,161, 11, + 124,162, 10,123,169, 4,123,171, 3,124,166, 7,124,152, 20, + 124,155, 16,124,161, 11,124,166, 7,123,169, 4,123,168, 5, + 124,165, 8,124,160, 12,124,154, 18,124,149, 24,123,141, 34, + 123,137, 41,124,147, 26,124,149, 24,124,162, 10,121,179, 0, + 117,191, 0,112,199, 0,109,203, 0,108,206, 0,107,206, 0, + 108,206, 0,107,207, 0,106,208, 0,106,208, 0,108,206, 0, + 108,205, 0,109,204, 0,110,202, 0,113,199, 0,111,201, 0, + 110,202, 0,111,202, 0,112,200, 0,111,201, 0,111,201, 0, + 113,199, 0,115,194, 0,118,188, 0,122,178, 0,123,168, 5, + 124,158, 13,122,132, 48,102, 80,250,115,107,129,121,125, 63, + 122,135, 44,124,154, 18,124,161, 11,123,174, 1,121,180, 0, + 121,179, 0,122,176, 0,122,178, 0,121,179, 0,122,178, 0, + 122,176, 0,122,176, 0,122,177, 0,122,178, 0,122,178, 0, + 122,176, 0,123,171, 3,124,161, 11,124,157, 15,124,150, 22, + 123,137, 41,124,152, 20,124,158, 13,124,157, 15,124,150, 22, + 124,145, 29,123,139, 37,124,145, 29,123,141, 34,124,145, 29, + 124,150, 22,124,155, 16,124,155, 16,124,152, 20,123,141, 34, + 118,114, 96,105, 84,252,114,104,197,121,125, 63,122,135, 44, + 124,145, 29,124,152, 20,124,155, 16,124,157, 15,124,158, 13, + 124,158, 13,124,160, 12,124,161, 11,124,161, 11,124,158, 13, + 124,149, 24,124,149, 24,124,155, 16,124,160, 12,124,164, 9, + 124,167, 6,123,174, 1,119,185, 0,116,193, 0,111,202, 0, + 105,209, 0,100,215, 0, 94,220, 0, 91,224, 0, 88,226, 0, + 87,227, 0, 87,227, 0, 83,230, 0, 78,234, 0, 74,238, 0, + 71,240, 0, 71,240, 0, 73,238, 0, 76,236, 0, 81,232, 0, + 85,228, 0, 86,228, 0, 85,229, 0, 86,228, 0, 88,226, 0, + 92,222, 0, 97,218, 0,101,214, 0,103,211, 0,104,210, 0, + 105,209, 0,105,209, 0,105,209, 0,104,210, 0,105,209, 0, + 106,207, 0,106,208, 0,103,211, 0,102,212, 0,102,212, 0, + 104,210, 0,107,207, 0,110,202, 0,111,202, 0,111,202, 0, + 112,200, 0,114,197, 0,114,196, 0,114,196, 0,114,196, 0, + 115,194, 0,117,192, 0,116,193, 0,114,197, 0,113,199, 0, + 113,199, 0,113,198, 0,114,196, 0,113,199, 0,109,203, 0, + 108,205, 0,109,203, 0,112,200, 0,112,200, 0,111,201, 0, + 109,203, 0,107,207, 0,106,207, 0,103,211, 0,102,212, 0, + 102,212, 0,104,210, 0,108,206, 0,109,204, 0,110,202, 0, + 113,199, 0,117,192, 0,117,192, 0,116,193, 0,116,193, 0, + 116,193, 0,116,193, 0,117,192, 0,117,191, 0,119,187, 0, + 121,179, 0,124,166, 7,124,162, 10,123,171, 3,122,177, 0, + 121,180, 0,122,176, 0,123,172, 2,123,172, 2,123,174, 1, + 121,180, 0,119,187, 0,117,191, 0,117,192, 0,114,196, 0, + 111,201, 0,110,203, 0,110,202, 0,112,199, 0,113,199, 0, + 113,198, 0,114,196, 0,117,191, 0,121,181, 0,120,182, 0, + 120,184, 0,120,183, 0,121,179, 0,123,171, 3,124,164, 9, + 123,168, 5,122,175, 0,122,178, 0,122,178, 0,123,174, 1, + 124,166, 7,124,150, 22,121,125, 63,113,100,242,114,104,197, + 115,107,129,118,114, 96,121,125, 63,122,130, 53,121,127, 58, + 121,125, 63,121,125, 63,122,132, 48,124,143, 31,124,150, 22, + 124,152, 20,124,149, 24,123,137, 41,115,107,129, 87, 65,237, + 87, 65,237,105, 84,252,114,104,197,117,110,109,114,104,197, + 109, 92,251,113,100,242,122,130, 53,124,149, 24,124,157, 15, + 124,160, 12,123,171, 3,122,178, 0,122,178, 0,123,174, 1, + 124,162, 10,124,161, 11,124,166, 7,123,169, 4,123,169, 4, + 124,167, 6,124,161, 11,124,150, 22,124,143, 31,122,132, 48, + 123,137, 41,124,149, 24,124,155, 16,124,162, 10,123,169, 4, + 123,173, 1,119,186, 0,115,195, 0,111,201, 0,109,204, 0, + 108,205, 0,108,205, 0,107,207, 0,106,208, 0,106,208, 0, + 108,205, 0,108,206, 0,108,205, 0,109,203, 0,112,200, 0, + 114,196, 0,115,194, 0,115,195, 0,115,194, 0,115,195, 0, + 115,194, 0,117,192, 0,119,186, 0,122,176, 0,124,158, 13, + 124,143, 31,121,125, 63,102, 80,250, 94, 70,242,113,100,242, + 122,132, 48,124,157, 15,124,167, 6,123,171, 3,121,180, 0, + 120,184, 0,120,184, 0,121,179, 0,122,176, 0,123,174, 1, + 123,170, 3,123,170, 3,123,169, 4,123,169, 4,123,169, 4, + 123,168, 5,124,166, 7,124,160, 12,124,145, 29,124,147, 26, + 123,137, 41,124,150, 22,124,161, 11,124,164, 9,124,162, 10, + 124,154, 18,124,147, 26,123,141, 34,124,149, 24,124,147, 26, + 123,139, 37,122,130, 53,122,135, 44,122,135, 44,121,127, 58, + 117,110,109, 99, 75,247,111, 96,248,118,117, 85,122,132, 48, + 122,135, 44,122,132, 48,122,135, 44,122,135, 44,123,137, 41, + 123,139, 37,124,150, 22,124,155, 16,124,155, 16,124,152, 20, + 124,154, 18,124,149, 24,123,141, 34,124,150, 22,124,158, 13, + 124,162, 10,124,166, 7,124,167, 6,121,180, 0,117,191, 0, + 112,200, 0,106,208, 0,100,215, 0, 94,220, 0, 90,224, 0, + 87,227, 0, 87,227, 0, 87,227, 0, 85,229, 0, 80,233, 0, + 77,235, 0, 75,237, 0, 76,236, 0, 79,234, 0, 83,230, 0, + 87,227, 0, 90,224, 0, 88,226, 0, 87,227, 0, 87,227, 0, + 90,224, 0, 94,221, 0, 99,215, 0,103,211, 0,103,211, 0, + 103,211, 0,105,209, 0,107,207, 0,106,207, 0,106,208, 0, + 106,208, 0,107,206, 0,107,206, 0,105,209, 0,104,210, 0, + 104,210, 0,106,208, 0,109,204, 0,112,200, 0,112,199, 0, + 112,200, 0,113,198, 0,115,195, 0,115,194, 0,115,194, 0, + 115,194, 0,116,192, 0,117,190, 0,115,194, 0,113,198, 0, + 112,200, 0,112,200, 0,113,198, 0,115,194, 0,117,190, 0, + 115,195, 0,114,197, 0,115,195, 0,115,195, 0,114,196, 0, + 112,200, 0,109,204, 0,107,206, 0,107,207, 0,103,211, 0, + 101,214, 0,100,215, 0,101,214, 0,105,209, 0,109,203, 0, + 113,199, 0,114,196, 0,118,189, 0,118,188, 0,118,189, 0, + 118,189, 0,118,189, 0,118,189, 0,118,188, 0,119,187, 0, + 120,184, 0,122,178, 0,124,165, 8,124,155, 16,124,164, 9, + 123,170, 3,123,174, 1,123,168, 5,122,177, 0,121,182, 0, + 120,184, 0,121,182, 0,119,186, 0,118,188, 0,118,188, 0, + 118,188, 0,115,194, 0,114,197, 0,113,198, 0,114,196, 0, + 116,193, 0,116,192, 0,118,189, 0,120,183, 0,120,183, 0, + 120,184, 0,119,186, 0,119,185, 0,120,184, 0,121,182, 0, + 122,178, 0,123,171, 3,122,177, 0,121,179, 0,122,178, 0, + 123,173, 1,124,164, 9,124,145, 29,118,114, 96, 94, 70,242, + 99, 75,247,105, 84,252,113,100,242,118,117, 85,119,119, 77, + 118,117, 85,117,110,109,117,110,109,119,119, 77,122,132, 48, + 124,143, 31,124,149, 24,124,147, 26,122,135, 44,114,104,197, + 30, 31,180,102, 80,250,109, 92,251,115,107,129,115,107,129, + 113,100,242,102, 80,250,107, 89,252,118,117, 85,123,139, 37, + 124,154, 18,123,168, 5,122,177, 0,121,181, 0,121,182, 0, + 122,178, 0,124,166, 7,123,168, 5,123,170, 3,123,171, 3, + 123,170, 3,124,165, 8,124,154, 18,123,139, 37,121,125, 63, + 123,137, 41,124,149, 24,124,157, 15,124,161, 11,123,170, 3, + 122,175, 0,122,178, 0,121,181, 0,117,191, 0,113,198, 0, + 111,202, 0,109,204, 0,108,205, 0,108,205, 0,107,206, 0, + 108,205, 0,107,207, 0,106,207, 0,107,207, 0,108,205, 0, + 111,202, 0,114,197, 0,117,191, 0,119,186, 0,119,186, 0, + 119,186, 0,119,186, 0,120,182, 0,123,174, 1,124,160, 12, + 122,132, 48, 99, 75,247, 30, 37,192, 30, 43,203,109, 92,251, + 122,135, 44,124,158, 13,123,170, 3,122,177, 0,122,178, 0, + 119,186, 0,118,189, 0,118,188, 0,120,184, 0,122,177, 0, + 123,168, 5,124,166, 7,124,166, 7,123,170, 3,123,170, 3, + 124,167, 6,124,160, 12,124,154, 18,124,143, 31,121,127, 58, + 121,127, 58,124,145, 29,124,158, 13,124,167, 6,123,169, 4, + 124,166, 7,124,161, 11,124,152, 20,123,139, 37,124,147, 26, + 124,145, 29,123,137, 41,120,122, 70,117,110,109,115,107,129, + 109, 92,251, 69, 60,230,105, 84,252,113,100,242,121,125, 63, + 122,135, 44,122,135, 44,121,125, 63,115,107,129,114,104,197, + 122,132, 48,124,149, 24,124,160, 12,124,164, 9,124,165, 8, + 124,161, 11,124,152, 20,123,139, 37,121,127, 58,123,139, 37, + 124,149, 24,124,155, 16,124,158, 13,124,160, 12,123,171, 3, + 120,184, 0,115,195, 0,109,204, 0,102,212, 0, 97,218, 0, + 92,222, 0, 89,225, 0, 89,226, 0, 89,225, 0, 85,228, 0, + 82,231, 0, 80,233, 0, 80,233, 0, 82,231, 0, 85,228, 0, + 90,224, 0, 95,220, 0, 93,221, 0, 91,223, 0, 90,224, 0, + 91,223, 0, 93,221, 0, 97,217, 0,102,212, 0,106,207, 0, + 105,209, 0,105,209, 0,106,207, 0,108,206, 0,109,203, 0, + 109,204, 0,109,204, 0,111,202, 0,109,204, 0,107,207, 0, + 106,208, 0,106,208, 0,108,206, 0,110,202, 0,114,197, 0, + 116,193, 0,115,194, 0,116,192, 0,117,191, 0,116,192, 0, + 116,193, 0,116,193, 0,117,191, 0,119,187, 0,117,192, 0, + 114,196, 0,112,199, 0,112,200, 0,113,198, 0,115,194, 0, + 118,189, 0,120,184, 0,119,186, 0,118,188, 0,117,190, 0, + 114,196, 0,111,201, 0,109,204, 0,107,206, 0,108,206, 0, + 104,210, 0,101,214, 0, 99,215, 0,100,215, 0,103,212, 0, + 107,207, 0,112,199, 0,117,191, 0,120,184, 0,121,182, 0, + 119,186, 0,118,188, 0,117,190, 0,118,189, 0,119,186, 0, + 121,181, 0,122,178, 0,123,172, 2,124,161, 11,124,152, 20, + 124,154, 18,124,161, 11,124,166, 7,123,172, 2,121,180, 0, + 119,185, 0,119,187, 0,119,185, 0,121,179, 0,121,182, 0, + 121,182, 0,121,180, 0,119,187, 0,117,191, 0,116,192, 0, + 117,192, 0,118,189, 0,119,186, 0,120,183, 0,121,180, 0, + 121,180, 0,120,182, 0,120,184, 0,118,188, 0,118,189, 0, + 118,188, 0,119,185, 0,121,181, 0,122,178, 0,122,178, 0, + 122,177, 0,123,170, 3,124,158, 13,124,147, 26,122,130, 53, + 109, 92,251, 94, 70,242, 87, 65,237,102, 80,250,113,100,242, + 115,107,129,117,110,109,118,117, 85,122,130, 53,122,132, 48, + 121,127, 58,123,139, 37,124,145, 29,124,143, 31,122,132, 48, + 113,100,242, 94, 70,242,107, 89,252,113,100,242,115,107,129, + 114,104,197,109, 92,251,107, 89,252,117,110,109,122,130, 53, + 124,147, 26,124,161, 11,123,172, 2,121,179, 0,120,183, 0, + 120,183, 0,121,179, 0,123,168, 5,123,170, 3,123,171, 3, + 123,171, 3,123,168, 5,124,160, 12,124,145, 29,122,132, 48, + 123,141, 34,124,149, 24,124,157, 15,124,161, 11,124,164, 9, + 123,171, 3,122,175, 0,122,178, 0,121,179, 0,118,188, 0, + 115,194, 0,112,200, 0,110,203, 0,108,205, 0,107,206, 0, + 107,207, 0,106,208, 0,105,209, 0,105,209, 0,105,209, 0, + 107,206, 0,110,202, 0,114,197, 0,117,190, 0,120,183, 0, + 122,175, 0,122,176, 0,122,176, 0,123,173, 1,124,166, 7, + 124,152, 20,121,127, 58,105, 84,252,107, 89,252,120,122, 70, + 124,149, 24,124,165, 8,122,175, 0,121,182, 0,120,184, 0, + 120,184, 0,118,189, 0,117,191, 0,117,190, 0,119,186, 0, + 121,179, 0,123,169, 4,123,170, 3,123,174, 1,122,177, 0, + 122,176, 0,123,172, 2,124,166, 7,124,154, 18,123,137, 41, + 120,122, 70,123,137, 41,124,150, 22,124,162, 10,123,169, 4, + 123,172, 2,123,172, 2,123,170, 3,124,162, 10,124,150, 22, + 123,137, 41,122,135, 44,121,125, 63,118,114, 96,113,100,242, + 107, 89,252, 99, 75,247, 94, 70,242,105, 84,252,113,100,242, + 121,125, 63,122,132, 48,122,130, 53,119,119, 77,118,117, 85, + 122,132, 48,123,141, 34,124,155, 16,124,164, 9,123,168, 5, + 123,169, 4,123,168, 5,124,164, 9,124,155, 16,124,143, 31, + 124,143, 31,124,149, 24,124,155, 16,124,160, 12,124,162, 10, + 123,171, 3,119,185, 0,115,194, 0,112,200, 0,106,208, 0, + 100,215, 0, 95,219, 0, 92,222, 0, 92,223, 0, 91,223, 0, + 88,226, 0, 85,228, 0, 85,229, 0, 85,228, 0, 88,226, 0, + 92,222, 0, 98,217, 0,100,215, 0, 99,216, 0, 97,218, 0, + 96,219, 0, 96,219, 0, 98,217, 0,101,213, 0,106,208, 0, + 109,203, 0,109,204, 0,109,204, 0,109,203, 0,110,202, 0, + 112,200, 0,112,199, 0,113,198, 0,113,198, 0,111,201, 0, + 110,203, 0,109,204, 0,109,203, 0,111,202, 0,113,198, 0, + 116,193, 0,118,189, 0,119,187, 0,119,187, 0,118,189, 0, + 117,190, 0,117,191, 0,117,191, 0,118,189, 0,119,185, 0, + 118,188, 0,116,193, 0,114,196, 0,113,198, 0,114,197, 0, + 116,193, 0,118,188, 0,120,184, 0,120,182, 0,119,186, 0, + 117,191, 0,114,197, 0,111,201, 0,108,205, 0,107,207, 0, + 107,206, 0,106,207, 0,103,211, 0,101,214, 0,101,214, 0, + 103,211, 0,107,207, 0,112,200, 0,116,193, 0,119,185, 0, + 120,184, 0,119,187, 0,118,189, 0,117,190, 0,118,189, 0, + 119,186, 0,121,181, 0,123,171, 3,124,166, 7,124,155, 16, + 124,143, 31,124,150, 22,124,157, 15,124,164, 9,123,170, 3, + 122,178, 0,120,184, 0,119,186, 0,120,184, 0,122,178, 0, + 123,170, 3,123,169, 4,123,169, 4,121,179, 0,120,184, 0, + 119,186, 0,119,185, 0,120,183, 0,121,179, 0,122,175, 0, + 123,171, 3,123,171, 3,122,178, 0,119,185, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,119,186, 0,120,183, 0, + 121,179, 0,122,175, 0,123,169, 4,124,162, 10,124,152, 20, + 122,135, 44,111, 96,248,102, 80,250,105, 84,252,114,104,197, + 118,117, 85,120,122, 70,120,122, 70,120,122, 70,122,132, 48, + 123,139, 37,123,137, 41,123,139, 37,124,143, 31,123,139, 37, + 121,125, 63,118,114, 96,117,110,109,115,107,129,118,114, 96, + 118,114, 96,118,114, 96,111, 96,248,111, 96,248,118,114, 96, + 122,135, 44,124,150, 22,124,165, 8,122,175, 0,121,182, 0, + 120,184, 0,120,184, 0,121,180, 0,123,171, 3,124,166, 7, + 124,167, 6,124,167, 6,124,162, 10,124,152, 20,122,132, 48, + 124,145, 29,124,152, 20,124,157, 15,124,161, 11,124,161, 11, + 124,160, 12,124,167, 6,123,170, 3,123,171, 3,123,174, 1, + 120,184, 0,117,192, 0,114,197, 0,111,201, 0,109,204, 0, + 107,206, 0,106,208, 0,105,209, 0,104,210, 0,104,210, 0, + 105,209, 0,108,206, 0,111,201, 0,115,194, 0,119,186, 0, + 122,178, 0,123,168, 5,124,165, 8,124,167, 6,124,166, 7, + 124,162, 10,124,154, 18,123,137, 41,123,141, 34,124,154, 18, + 124,167, 6,122,176, 0,120,183, 0,118,188, 0,117,190, 0, + 117,191, 0,117,191, 0,117,190, 0,117,191, 0,118,189, 0, + 119,185, 0,122,178, 0,123,172, 2,123,174, 1,122,178, 0, + 121,179, 0,122,178, 0,123,173, 1,124,166, 7,124,154, 18, + 122,135, 44,120,122, 70,123,137, 41,124,150, 22,124,162, 10, + 123,169, 4,122,177, 0,121,179, 0,122,177, 0,123,171, 3, + 124,162, 10,124,152, 20,123,137, 41,119,119, 77,118,117, 85, + 119,119, 77,118,117, 85,117,110,109,114,104,197,109, 92,251, + 109, 92,251,118,114, 96,119,119, 77,118,117, 85,122,132, 48, + 124,154, 18,124,162, 10,124,165, 8,124,166, 7,124,165, 8, + 123,169, 4,123,170, 3,123,170, 3,123,168, 5,124,166, 7, + 124,158, 13,124,155, 16,124,160, 12,124,164, 9,124,165, 8, + 124,167, 6,123,169, 4,120,183, 0,116,193, 0,112,199, 0, + 108,205, 0,103,212, 0, 98,216, 0, 96,219, 0, 95,220, 0, + 94,220, 0, 93,222, 0, 92,223, 0, 92,223, 0, 93,221, 0, + 96,219, 0,100,214, 0,105,209, 0,107,207, 0,106,208, 0, + 105,209, 0,103,211, 0,103,211, 0,104,210, 0,107,207, 0, + 110,203, 0,113,198, 0,114,197, 0,113,198, 0,113,198, 0, + 114,196, 0,115,194, 0,116,193, 0,117,192, 0,117,191, 0, + 116,193, 0,114,196, 0,114,196, 0,114,196, 0,116,193, 0, + 118,189, 0,120,184, 0,121,179, 0,122,178, 0,121,180, 0, + 120,183, 0,119,185, 0,119,186, 0,119,186, 0,120,184, 0, + 121,180, 0,120,183, 0,118,188, 0,117,192, 0,116,193, 0, + 116,192, 0,118,189, 0,120,184, 0,121,181, 0,121,181, 0, + 119,185, 0,117,191, 0,114,197, 0,110,202, 0,108,205, 0, + 106,207, 0,107,207, 0,108,205, 0,107,207, 0,104,210, 0, + 104,210, 0,105,209, 0,108,205, 0,112,199, 0,117,192, 0, + 120,184, 0,120,182, 0,119,185, 0,119,187, 0,118,188, 0, + 119,186, 0,120,183, 0,122,177, 0,124,167, 6,124,161, 11, + 124,150, 22,122,132, 48,124,143, 31,124,152, 20,124,161, 11, + 124,162, 10,123,172, 2,121,179, 0,120,182, 0,121,181, 0, + 122,176, 0,124,164, 9,124,149, 24,124,157, 15,123,169, 4, + 122,175, 0,122,177, 0,122,176, 0,123,172, 2,123,168, 5, + 124,166, 7,124,157, 15,124,164, 9,123,174, 1,121,182, 0, + 119,186, 0,118,189, 0,117,190, 0,118,189, 0,118,188, 0, + 119,186, 0,120,184, 0,121,179, 0,123,173, 1,124,165, 8, + 124,152, 20,122,130, 53,109, 92,251, 94, 70,242,113,100,242, + 119,119, 77,121,127, 58,122,130, 53,122,130, 53,121,125, 63, + 122,130, 53,123,137, 41,123,137, 41,123,141, 34,124,143, 31, + 123,137, 41,123,137, 41,123,137, 41,122,132, 48,120,122, 70, + 121,127, 58,121,125, 63,121,125, 63,118,114, 96,109, 92,251, + 118,117, 85,122,130, 53,124,149, 24,124,164, 9,122,175, 0, + 121,181, 0,120,184, 0,120,184, 0,121,181, 0,123,172, 2, + 124,158, 13,124,158, 13,124,158, 13,124,152, 20,123,141, 34, + 122,135, 44,124,147, 26,124,155, 16,124,160, 12,124,160, 12, + 124,158, 13,124,150, 22,124,157, 15,124,158, 13,124,160, 12, + 123,169, 4,121,180, 0,119,187, 0,116,193, 0,114,197, 0, + 111,201, 0,109,204, 0,107,207, 0,105,209, 0,104,210, 0, + 105,209, 0,106,207, 0,110,203, 0,114,197, 0,118,189, 0, + 122,178, 0,123,168, 5,124,157, 15,124,149, 24,124,154, 18, + 124,154, 18,124,154, 18,124,147, 26,124,161, 11,123,171, 3, + 121,179, 0,119,186, 0,117,191, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,114,197, 0,115,195, 0,116,193, 0, + 118,189, 0,119,185, 0,121,179, 0,121,179, 0,121,180, 0, + 121,179, 0,121,179, 0,122,177, 0,123,171, 3,124,162, 10, + 124,147, 26,121,127, 58,118,114, 96,122,130, 53,124,145, 29, + 124,158, 13,123,173, 1,121,181, 0,120,183, 0,121,182, 0, + 122,177, 0,123,170, 3,124,161, 11,124,145, 29,122,130, 53, + 120,122, 70,122,130, 53,122,135, 44,122,132, 48,120,122, 70, + 113,100,242,102, 80,250,109, 92,251,111, 96,248,122,132, 48, + 124,158, 13,123,171, 3,122,177, 0,122,178, 0,122,177, 0, + 123,173, 1,124,167, 6,124,166, 7,123,168, 5,123,169, 4, + 123,168, 5,124,164, 9,124,161, 11,124,166, 7,124,167, 6, + 124,167, 6,124,167, 6,124,167, 6,122,178, 0,118,189, 0, + 114,197, 0,110,202, 0,106,208, 0,102,213, 0, 99,216, 0, + 98,217, 0, 98,216, 0, 99,216, 0, 99,216, 0,100,215, 0, + 102,212, 0,105,209, 0,108,205, 0,112,200, 0,114,197, 0, + 114,197, 0,113,198, 0,112,200, 0,111,201, 0,111,201, 0, + 113,199, 0,114,196, 0,117,191, 0,118,188, 0,117,190, 0, + 118,189, 0,118,188, 0,119,186, 0,119,186, 0,120,184, 0, + 121,181, 0,120,182, 0,120,184, 0,120,184, 0,120,182, 0, + 122,178, 0,123,172, 2,124,166, 7,124,161, 11,124,162, 10, + 124,167, 6,123,172, 2,122,177, 0,122,178, 0,122,178, 0, + 122,176, 0,123,170, 3,122,177, 0,120,182, 0,119,186, 0, + 119,186, 0,119,185, 0,120,182, 0,122,178, 0,123,174, 1, + 122,176, 0,120,182, 0,117,190, 0,114,196, 0,111,202, 0, + 108,205, 0,107,207, 0,107,207, 0,108,205, 0,110,202, 0, + 108,205, 0,107,207, 0,108,205, 0,111,202, 0,114,196, 0, + 118,189, 0,121,182, 0,121,180, 0,120,182, 0,120,184, 0, + 120,184, 0,121,182, 0,122,178, 0,123,171, 3,124,166, 7, + 124,158, 13,124,147, 26,122,130, 53,121,127, 58,123,139, 37, + 124,150, 22,124,147, 26,124,162, 10,123,170, 3,122,175, 0, + 122,175, 0,123,170, 3,124,160, 12,123,137, 41,123,139, 37, + 124,155, 16,124,162, 10,124,164, 9,124,161, 11,124,155, 16, + 124,154, 18,124,154, 18,124,157, 15,124,155, 16,124,165, 8, + 123,173, 1,121,180, 0,120,184, 0,119,186, 0,118,188, 0, + 118,189, 0,118,188, 0,119,186, 0,121,181, 0,123,174, 1, + 124,164, 9,124,147, 26,119,119, 77, 99, 75,247, 94, 70,242, + 117,110,109,122,130, 53,123,137, 41,123,137, 41,122,132, 48, + 121,125, 63,120,122, 70,122,132, 48,124,143, 31,124,147, 26, + 124,147, 26,124,145, 29,124,147, 26,124,147, 26,124,143, 31, + 122,135, 44,123,139, 37,122,135, 44,122,135, 44,121,127, 58, + 117,110,109,118,114, 96,121,127, 58,123,139, 37,124,158, 13, + 123,171, 3,122,178, 0,121,182, 0,121,182, 0,122,178, 0, + 123,170, 3,124,157, 15,124,143, 31,124,143, 31,123,141, 34, + 121,127, 58,122,130, 53,124,143, 31,124,152, 20,124,155, 16, + 124,155, 16,124,150, 22,123,137, 41,122,135, 44,123,137, 41, + 124,145, 29,124,164, 9,123,173, 1,121,180, 0,119,186, 0, + 117,191, 0,114,196, 0,112,200, 0,109,204, 0,107,207, 0, + 106,208, 0,107,207, 0,109,204, 0,113,199, 0,117,191, 0, + 121,181, 0,124,167, 6,124,152, 20,123,139, 37,122,135, 44, + 122,130, 53,122,132, 48,122,135, 44,124,161, 11,122,177, 0, + 119,185, 0,117,191, 0,114,196, 0,112,199, 0,111,201, 0, + 110,202, 0,110,202, 0,111,201, 0,111,201, 0,112,200, 0, + 114,197, 0,115,194, 0,118,189, 0,119,186, 0,119,187, 0, + 119,187, 0,119,185, 0,121,182, 0,122,178, 0,123,173, 1, + 124,165, 8,124,147, 26,118,114, 96,107, 89,252,118,114, 96, + 122,132, 48,124,161, 11,122,175, 0,121,182, 0,120,184, 0, + 120,182, 0,121,179, 0,123,174, 1,124,166, 7,124,150, 22, + 122,135, 44,121,125, 63,123,137, 41,124,143, 31,123,141, 34, + 121,127, 58,111, 96,248, 30, 55,222, 87, 65,237,120,122, 70, + 124,155, 16,123,172, 2,121,181, 0,120,184, 0,120,184, 0, + 120,182, 0,122,178, 0,123,170, 3,124,160, 12,124,157, 15, + 124,162, 10,124,165, 8,124,164, 9,124,162, 10,124,167, 6, + 123,168, 5,124,167, 6,124,166, 7,124,166, 7,123,172, 2, + 119,185, 0,116,193, 0,112,199, 0,109,204, 0,105,209, 0, + 102,212, 0,101,214, 0,101,213, 0,103,211, 0,106,207, 0, + 109,204, 0,111,201, 0,113,198, 0,114,196, 0,115,194, 0, + 116,193, 0,118,189, 0,118,189, 0,119,187, 0,118,188, 0, + 118,189, 0,118,188, 0,119,185, 0,121,181, 0,122,177, 0, + 121,179, 0,122,178, 0,122,176, 0,122,177, 0,122,177, 0, + 123,174, 1,124,167, 6,124,165, 8,123,171, 3,123,173, 1, + 123,172, 2,123,169, 4,124,165, 8,124,158, 13,124,157, 15, + 124,157, 15,124,162, 10,124,166, 7,124,166, 7,124,166, 7, + 124,166, 7,124,161, 11,124,157, 15,124,167, 6,123,173, 1, + 122,177, 0,122,177, 0,123,174, 1,123,170, 3,124,166, 7, + 124,164, 9,123,168, 5,122,178, 0,119,187, 0,115,194, 0, + 112,200, 0,109,203, 0,108,205, 0,108,206, 0,109,204, 0, + 111,202, 0,111,201, 0,110,202, 0,111,201, 0,114,197, 0, + 117,192, 0,119,185, 0,121,179, 0,121,181, 0,121,182, 0, + 121,182, 0,121,180, 0,122,178, 0,123,174, 1,123,170, 3, + 124,165, 8,124,157, 15,124,145, 29,121,125, 63,114,104,197, + 118,117, 85,122,132, 48,122,135, 44,124,152, 20,124,157, 15, + 124,164, 9,124,167, 6,124,164, 9,124,155, 16,123,141, 34, + 123,137, 41,122,132, 48,123,141, 34,124,143, 31,123,137, 41, + 121,125, 63,122,130, 53,124,150, 22,124,155, 16,124,157, 15, + 124,160, 12,124,165, 8,123,169, 4,122,175, 0,121,180, 0, + 120,184, 0,119,187, 0,118,188, 0,119,186, 0,121,181, 0, + 123,172, 2,124,160, 12,123,137, 41,113,100,242, 30, 43,203, + 99, 75,247,119,119, 77,123,137, 41,124,143, 31,123,141, 34, + 123,137, 41,123,139, 37,122,135, 44,124,143, 31,124,150, 22, + 124,154, 18,124,152, 20,124,147, 26,124,150, 22,124,152, 20, + 124,147, 26,123,137, 41,124,145, 29,124,147, 26,124,143, 31, + 123,141, 34,122,130, 53,115,107,129,121,127, 58,123,137, 41, + 124,145, 29,124,162, 10,123,171, 3,122,175, 0,122,175, 0, + 123,170, 3,124,162, 10,124,149, 24,121,127, 58,121,125, 63, + 121,125, 63,118,117, 85,118,114, 96,121,127, 58,123,139, 37, + 124,145, 29,124,145, 29,123,137, 41,118,117, 85,111, 96,248, + 111, 96,248,122,132, 48,124,152, 20,124,162, 10,123,169, 4, + 122,176, 0,120,184, 0,118,188, 0,116,193, 0,113,198, 0, + 110,202, 0,109,204, 0,110,202, 0,112,199, 0,116,192, 0, + 120,183, 0,123,169, 4,124,149, 24,121,127, 58,118,114, 96, + 119,119, 77,119,119, 77,118,114, 96,124,145, 29,123,170, 3, + 120,184, 0,116,192, 0,113,198, 0,111,202, 0,109,204, 0, + 108,205, 0,108,205, 0,109,204, 0,109,203, 0,110,202, 0, + 110,202, 0,112,200, 0,114,197, 0,117,192, 0,116,193, 0, + 116,193, 0,116,193, 0,117,191, 0,118,188, 0,120,184, 0, + 121,179, 0,123,171, 3,124,157, 15,121,125, 63, 30, 55,222, + 105, 84,252,122,130, 53,124,160, 12,123,174, 1,121,180, 0, + 120,182, 0,121,181, 0,122,178, 0,123,174, 1,124,166, 7, + 124,150, 22,122,130, 53,121,125, 63,123,139, 37,124,145, 29, + 123,141, 34,121,125, 63,102, 80,250, 30, 15,143,113,100,242, + 124,147, 26,123,168, 5,122,178, 0,120,184, 0,119,186, 0, + 119,186, 0,120,182, 0,122,176, 0,123,168, 5,124,157, 15, + 123,141, 34,124,143, 31,124,154, 18,124,154, 18,124,158, 13, + 124,165, 8,124,166, 7,124,166, 7,124,165, 8,123,168, 5, + 123,170, 3,121,180, 0,118,189, 0,114,196, 0,111,201, 0, + 109,204, 0,106,207, 0,105,209, 0,105,209, 0,107,207, 0, + 109,204, 0,112,200, 0,113,198, 0,113,198, 0,113,198, 0, + 113,198, 0,114,197, 0,116,193, 0,118,188, 0,119,185, 0, + 121,180, 0,123,172, 2,123,171, 3,123,170, 3,124,165, 8, + 124,160, 12,124,164, 9,124,162, 10,124,158, 13,124,161, 11, + 124,162, 10,123,168, 5,123,168, 5,124,165, 8,123,168, 5, + 123,169, 4,124,167, 6,124,164, 9,124,162, 10,124,164, 9, + 124,164, 9,124,160, 12,124,167, 6,123,171, 3,123,173, 1, + 123,171, 3,124,164, 9,124,149, 24,123,137, 41,124,152, 20, + 124,160, 12,124,162, 10,124,161, 11,124,155, 16,124,149, 24, + 124,149, 24,124,149, 24,124,155, 16,123,170, 3,120,182, 0, + 117,190, 0,114,196, 0,112,200, 0,111,202, 0,111,202, 0, + 111,201, 0,113,199, 0,114,197, 0,113,198, 0,114,196, 0, + 116,192, 0,118,188, 0,119,185, 0,121,180, 0,120,184, 0, + 119,185, 0,119,185, 0,120,183, 0,121,180, 0,122,176, 0, + 123,171, 3,124,165, 8,124,155, 16,123,141, 34,120,122, 70, + 109, 92,251,102, 80,250,113,100,242,118,117, 85,124,143, 31, + 124,152, 20,124,154, 18,124,155, 16,124,157, 15,124,150, 22, + 123,141, 34,123,139, 37,122,132, 48,118,114, 96,117,110,109, + 113,100,242,105, 84,252,118,117, 85,123,141, 34,124,147, 26, + 124,152, 20,124,160, 12,124,167, 6,123,173, 1,122,177, 0, + 122,178, 0,122,178, 0,120,183, 0,119,185, 0,120,184, 0, + 122,178, 0,123,169, 4,124,154, 18,123,139, 37,114,104,197, + 30, 15,143, 99, 75,247,121,125, 63,124,143, 31,124,149, 24, + 124,149, 24,124,143, 31,124,149, 24,124,147, 26,124,149, 24, + 124,155, 16,124,158, 13,124,158, 13,124,152, 20,124,149, 24, + 124,149, 24,124,143, 31,122,135, 44,124,149, 24,124,155, 16, + 124,154, 18,124,150, 22,124,143, 31,122,130, 53,121,125, 63, + 123,139, 37,124,147, 26,124,150, 22,124,158, 13,124,162, 10, + 124,161, 11,124,155, 16,124,143, 31,121,125, 63,111, 96,248, + 115,107,129,121,125, 63,122,130, 53,120,122, 70,120,122, 70, + 122,135, 44,123,141, 34,123,137, 41,119,119, 77,109, 92,251, + 30, 49,213, 30, 55,222,115,107,129,122,130, 53,123,139, 37, + 124,152, 20,123,171, 3,121,181, 0,119,186, 0,118,188, 0, + 118,189, 0,115,195, 0,114,197, 0,114,196, 0,117,192, 0, + 120,184, 0,123,172, 2,124,155, 16,123,139, 37,114,104,197, + 105, 84,252,115,107,129,122,130, 53,124,157, 15,123,169, 4, + 122,175, 0,119,187, 0,114,196, 0,111,201, 0,108,205, 0, + 107,207, 0,107,207, 0,107,206, 0,108,205, 0,110,203, 0, + 110,202, 0,110,202, 0,111,201, 0,114,197, 0,113,198, 0, + 112,199, 0,113,199, 0,113,198, 0,115,195, 0,116,192, 0, + 118,189, 0,119,185, 0,122,178, 0,124,164, 9,123,137, 41, + 94, 70,242, 30, 43,203,120,122, 70,124,155, 16,123,169, 4, + 122,176, 0,122,178, 0,122,176, 0,123,173, 1,123,169, 4, + 124,162, 10,124,147, 26,119,119, 77,121,125, 63,123,139, 37, + 123,141, 34,122,135, 44,117,110,109, 30, 37,192, 69, 60,230, + 121,127, 58,124,160, 12,123,174, 1,121,181, 0,120,184, 0, + 119,185, 0,120,184, 0,122,178, 0,123,170, 3,124,160, 12, + 124,145, 29,121,127, 58,115,107,129,121,125, 63,122,132, 48, + 124,150, 22,124,161, 11,124,164, 9,124,164, 9,124,167, 6, + 123,174, 1,122,176, 0,122,177, 0,119,186, 0,116,193, 0, + 113,198, 0,111,201, 0,111,202, 0,109,204, 0,109,204, 0, + 110,202, 0,112,200, 0,111,202, 0,109,203, 0,109,204, 0, + 110,202, 0,112,200, 0,112,200, 0,114,197, 0,117,190, 0, + 120,183, 0,122,178, 0,123,168, 5,124,150, 22,123,141, 34, + 122,135, 44,122,132, 48,123,139, 37,123,139, 37,122,135, 44, + 124,154, 18,124,165, 8,123,171, 3,123,171, 3,123,168, 5, + 124,167, 6,124,166, 7,124,166, 7,124,167, 6,123,168, 5, + 123,168, 5,124,167, 6,124,162, 10,123,170, 3,122,176, 0, + 122,178, 0,122,176, 0,123,169, 4,124,157, 15,124,149, 24, + 124,149, 24,123,141, 34,122,135, 44,122,130, 53,120,122, 70, + 121,127, 58,122,132, 48,122,130, 53,122,135, 44,124,158, 13, + 123,174, 1,120,184, 0,118,189, 0,116,193, 0,115,195, 0, + 114,196, 0,115,195, 0,115,194, 0,116,193, 0,115,194, 0, + 116,192, 0,118,188, 0,119,186, 0,120,184, 0,120,182, 0, + 119,186, 0,119,187, 0,119,187, 0,119,185, 0,121,182, 0, + 122,177, 0,123,170, 3,124,164, 9,124,154, 18,123,139, 37, + 118,117, 85,105, 84,252, 30, 37,192, 87, 65,237,102, 80,250, + 120,122, 70,123,137, 41,124,143, 31,124,143, 31,124,149, 24, + 124,147, 26,123,137, 41,122,132, 48,122,130, 53,118,117, 85, + 107, 89,252, 30, 49,213, 87, 65,237,107, 89,252,119,119, 77, + 122,130, 53,123,137, 41,124,152, 20,124,165, 8,123,172, 2, + 122,178, 0,121,179, 0,122,178, 0,122,178, 0,121,182, 0, + 121,181, 0,122,176, 0,124,166, 7,124,154, 18,123,139, 37, + 114,104,197, 30, 15,143,105, 84,252,122,132, 48,124,150, 22, + 124,157, 15,124,157, 15,124,150, 22,124,152, 20,124,152, 20, + 124,147, 26,124,155, 16,124,161, 11,124,162, 10,124,158, 13, + 124,149, 24,123,139, 37,122,130, 53,122,135, 44,124,152, 20, + 124,161, 11,124,164, 9,124,158, 13,124,150, 22,123,141, 34, + 121,125, 63,123,141, 34,124,150, 22,124,154, 18,124,149, 24, + 123,137, 41,122,132, 48,119,119, 77,113,100,242, 87, 65,237, + 102, 80,250,118,117, 85,122,135, 44,124,143, 31,123,141, 34, + 122,130, 53,122,132, 48,123,141, 34,123,141, 34,122,130, 53, + 115,107,129, 87, 65,237, 30, 26,168, 30, 49,213,102, 80,250, + 109, 92,251,123,139, 37,124,165, 8,122,178, 0,120,184, 0, + 119,185, 0,120,184, 0,119,186, 0,118,189, 0,118,188, 0, + 120,184, 0,123,174, 1,124,157, 15,124,145, 29,120,122, 70, + 87, 65,237, 69, 60,230,121,125, 63,124,161, 11,122,178, 0, + 119,185, 0,118,189, 0,118,189, 0,114,196, 0,111,202, 0, + 108,205, 0,107,207, 0,107,207, 0,108,205, 0,110,203, 0, + 111,201, 0,111,201, 0,112,200, 0,113,199, 0,111,201, 0, + 110,202, 0,110,203, 0,110,202, 0,111,201, 0,113,198, 0, + 115,195, 0,116,192, 0,118,189, 0,120,182, 0,123,170, 3, + 124,147, 26,107, 89,252, 30, 20,155,118,114, 96,124,149, 24, + 124,162, 10,123,169, 4,123,170, 3,123,168, 5,124,165, 8, + 124,161, 11,124,155, 16,123,139, 37,117,110,109,119,119, 77, + 122,130, 53,122,132, 48,119,119, 77,102, 80,250, 30, 31,180, + 113,100,242,124,145, 29,124,167, 6,122,176, 0,121,180, 0, + 121,182, 0,121,181, 0,122,178, 0,122,176, 0,122,177, 0, + 123,174, 1,124,164, 9,123,139, 37,102, 80,250, 94, 70,242, + 115,107,129,123,141, 34,124,155, 16,124,162, 10,124,165, 8, + 123,171, 3,122,178, 0,121,179, 0,122,178, 0,119,185, 0, + 116,192, 0,114,197, 0,112,199, 0,113,199, 0,113,198, 0, + 113,198, 0,114,197, 0,111,202, 0,108,206, 0,106,208, 0, + 106,208, 0,107,206, 0,110,202, 0,111,201, 0,113,199, 0, + 116,192, 0,120,183, 0,122,176, 0,124,165, 8,124,145, 29, + 114,104,197, 99, 75,247,109, 92,251,118,114, 96,122,135, 44, + 124,143, 31,124,154, 18,124,165, 8,123,170, 3,123,170, 3, + 123,168, 5,124,167, 6,124,167, 6,124,167, 6,123,168, 5, + 123,168, 5,123,168, 5,124,166, 7,124,164, 9,123,173, 1, + 121,179, 0,121,182, 0,121,180, 0,123,174, 1,124,162, 10, + 124,154, 18,124,154, 18,124,147, 26,122,130, 53,113,100,242, + 102, 80,250,111, 96,248,113,100,242,107, 89,252,113,100,242, + 123,137, 41,124,160, 12,123,173, 1,121,180, 0,120,184, 0, + 119,185, 0,119,186, 0,119,186, 0,118,188, 0,117,191, 0, + 117,192, 0,117,190, 0,119,186, 0,120,183, 0,121,180, 0, + 120,183, 0,119,186, 0,118,188, 0,118,188, 0,119,186, 0, + 121,182, 0,122,176, 0,123,169, 4,124,161, 11,124,150, 22, + 122,135, 44,120,122, 70,114,104,197, 69, 60,230, 30, 2,128, + 30, 2,128, 87, 65,237,113,100,242,118,117, 85,122,135, 44, + 124,143, 31,124,145, 29,123,137, 41,118,117, 85,118,117, 85, + 117,110,109,113,100,242,111, 96,248,107, 89,252,102, 80,250, + 105, 84,252,109, 92,251,117,110,109,122,132, 48,124,152, 20, + 124,166, 7,123,174, 1,122,177, 0,122,177, 0,123,172, 2, + 122,178, 0,122,178, 0,123,174, 1,124,164, 9,124,149, 24, + 122,132, 48,109, 92,251, 30, 2,128,113,100,242,123,141, 34, + 124,160, 12,124,166, 7,124,166, 7,124,162, 10,124,155, 16, + 124,149, 24,124,147, 26,124,150, 22,124,158, 13,124,162, 10, + 124,162, 10,124,155, 16,123,141, 34,117,110,109,122,135, 44, + 124,155, 16,124,166, 7,123,170, 3,123,168, 5,124,158, 13, + 124,143, 31,121,127, 58,124,145, 29,124,154, 18,124,154, 18, + 124,147, 26,122,130, 53,111, 96,248, 30, 43,203, 30, 2,128, + 30, 43,203,111, 96,248,122,130, 53,124,147, 26,124,154, 18, + 124,154, 18,124,147, 26,122,135, 44,124,147, 26,124,150, 22, + 124,147, 26,123,137, 41,118,117, 85,105, 84,252, 30, 55,222, + 30, 10,132, 69, 60,230,120,122, 70,124,157, 15,123,172, 2, + 121,179, 0,121,181, 0,121,179, 0,123,174, 1,121,179, 0, + 121,179, 0,123,174, 1,124,164, 9,124,143, 31,119,119, 77, + 102, 80,250, 30, 2,128,113,100,242,124,154, 18,122,177, 0, + 118,188, 0,115,194, 0,114,196, 0,114,197, 0,114,196, 0, + 111,201, 0,109,204, 0,108,205, 0,109,204, 0,110,202, 0, + 112,199, 0,114,197, 0,114,196, 0,113,199, 0,111,202, 0, + 109,204, 0,108,205, 0,108,206, 0,108,205, 0,110,202, 0, + 112,200, 0,114,197, 0,115,194, 0,117,191, 0,120,184, 0, + 123,174, 1,124,154, 18,115,107,129, 30, 2,128,111, 96,248, + 123,137, 41,124,154, 18,124,160, 12,124,160, 12,124,157, 15, + 124,155, 16,124,150, 22,124,145, 29,122,135, 44,122,130, 53, + 121,125, 63,118,117, 85,117,110,109,107, 89,252, 30, 26,168, + 102, 80,250,121,125, 63,124,155, 16,123,170, 3,122,177, 0, + 122,178, 0,122,177, 0,122,175, 0,121,181, 0,120,184, 0, + 120,184, 0,121,182, 0,123,172, 2,124,152, 20,114,104,197, + 30, 2,128,109, 92,251,122,135, 44,124,155, 16,124,165, 8, + 123,169, 4,123,172, 2,122,178, 0,121,179, 0,120,184, 0, + 118,189, 0,115,194, 0,113,198, 0,112,199, 0,113,199, 0, + 114,196, 0,117,191, 0,114,196, 0,110,202, 0,107,207, 0, + 104,210, 0,104,210, 0,105,209, 0,108,205, 0,112,200, 0, + 114,197, 0,117,191, 0,120,183, 0,122,175, 0,124,162, 10, + 123,139, 37,107, 89,252, 30, 2,128,105, 84,252,121,125, 63, + 124,145, 29,124,154, 18,124,157, 15,124,161, 11,124,167, 6, + 124,167, 6,123,171, 3,123,171, 3,123,169, 4,124,165, 8, + 124,164, 9,124,164, 9,124,165, 8,124,160, 12,123,169, 4, + 122,178, 0,120,182, 0,120,184, 0,120,183, 0,122,178, 0, + 124,167, 6,124,152, 20,124,152, 20,124,145, 29,122,130, 53, + 114,104,197,105, 84,252, 87, 65,237, 30, 37,192, 30, 2,128, + 30, 49,213,114,104,197,123,137, 41,124,155, 16,124,165, 8, + 123,168, 5,123,170, 3,123,172, 2,121,181, 0,119,187, 0, + 117,191, 0,117,191, 0,117,190, 0,119,186, 0,121,180, 0, + 123,172, 2,121,180, 0,120,184, 0,119,186, 0,119,186, 0, + 120,183, 0,121,179, 0,123,172, 2,124,164, 9,124,154, 18, + 124,147, 26,124,147, 26,124,145, 29,122,135, 44,118,117, 85, + 99, 75,247,111, 96,248, 30, 31,180, 94, 70,242,115,107,129, + 121,127, 58,123,139, 37,124,143, 31,123,139, 37,121,125, 63, + 113,100,242,114,104,197,118,117, 85,120,122, 70,121,125, 63, + 121,125, 63,121,127, 58,123,137, 41,123,141, 34,122,135, 44, + 124,143, 31,124,157, 15,123,168, 5,123,173, 1,123,174, 1, + 123,171, 3,123,171, 3,123,173, 1,123,169, 4,124,160, 12, + 124,143, 31,120,122, 70, 94, 70,242, 30, 26,168,118,117, 85, + 124,154, 18,123,168, 5,122,175, 0,122,177, 0,122,175, 0, + 123,172, 2,123,169, 4,124,165, 8,124,154, 18,124,149, 24, + 124,158, 13,124,162, 10,124,160, 12,124,152, 20,124,150, 22, + 124,143, 31,124,161, 11,123,171, 3,122,176, 0,122,176, 0, + 123,170, 3,124,158, 13,123,139, 37,124,145, 29,124,150, 22, + 124,150, 22,123,139, 37,118,117, 85, 94, 70,242,105, 84,252, + 117,110,109,122,130, 53,124,143, 31,124,149, 24,124,155, 16, + 124,160, 12,124,160, 12,124,154, 18,124,145, 29,124,158, 13, + 124,164, 9,124,164, 9,124,161, 11,124,154, 18,123,141, 34, + 121,127, 58,118,114, 96,115,107,129,122,132, 48,124,152, 20, + 124,162, 10,123,169, 4,123,170, 3,124,167, 6,124,160, 12, + 124,167, 6,124,167, 6,124,162, 10,124,149, 24,120,122, 70, + 99, 75,247, 30, 49,213,102, 80,250,123,141, 34,123,171, 3, + 119,186, 0,115,194, 0,113,199, 0,112,200, 0,112,200, 0, + 113,199, 0,111,201, 0,110,203, 0,110,203, 0,111,202, 0, + 113,199, 0,112,199, 0,109,204, 0,108,206, 0,108,206, 0, + 107,206, 0,107,207, 0,106,208, 0,105,209, 0,105,209, 0, + 105,209, 0,107,207, 0,109,203, 0,114,197, 0,117,192, 0, + 119,186, 0,122,175, 0,124,155, 16,117,110,109, 30, 49,213, + 105, 84,252,121,125, 63,124,143, 31,124,147, 26,124,149, 24, + 124,161, 11,124,166, 7,124,164, 9,124,157, 15,124,152, 20, + 124,149, 24,124,143, 31,122,132, 48,119,119, 77,111, 96,248, + 94, 70,242,115,107,129,122,132, 48,124,155, 16,124,167, 6, + 123,171, 3,123,171, 3,122,176, 0,120,182, 0,119,186, 0, + 118,188, 0,118,188, 0,120,184, 0,122,176, 0,124,157, 15, + 117,110,109, 94, 70,242,107, 89,252,123,137, 41,124,160, 12, + 123,171, 3,122,178, 0,120,182, 0,119,186, 0,118,189, 0, + 116,193, 0,114,196, 0,113,199, 0,112,200, 0,112,200, 0, + 112,199, 0,114,197, 0,114,197, 0,114,197, 0,111,201, 0, + 107,207, 0,104,210, 0,103,212, 0,103,211, 0,106,207, 0, + 110,202, 0,114,197, 0,117,191, 0,119,185, 0,122,176, 0, + 124,161, 11,122,135, 44,113,100,242, 87, 65,237,107, 89,252, + 122,130, 53,124,149, 24,124,158, 13,124,164, 9,124,166, 7, + 123,168, 5,123,172, 2,122,175, 0,122,175, 0,123,173, 1, + 123,174, 1,122,177, 0,121,179, 0,121,181, 0,120,182, 0, + 120,184, 0,119,186, 0,119,187, 0,119,187, 0,119,185, 0, + 121,180, 0,123,170, 3,124,157, 15,124,143, 31,123,137, 41, + 121,125, 63,115,107,129,105, 84,252,102, 80,250,107, 89,252, + 114,104,197,118,117, 85,118,117, 85,118,114, 96,122,130, 53, + 123,141, 34,124,145, 29,124,149, 24,124,166, 7,122,177, 0, + 120,184, 0,118,189, 0,117,191, 0,117,191, 0,118,188, 0, + 120,183, 0,121,181, 0,120,184, 0,119,186, 0,119,187, 0, + 119,187, 0,119,186, 0,120,184, 0,121,181, 0,122,175, 0, + 124,165, 8,124,157, 15,124,154, 18,124,157, 15,124,157, 15, + 124,152, 20,122,135, 44,124,149, 24,118,117, 85,102, 80,250, + 107, 89,252,118,114, 96,122,130, 53,123,139, 37,123,141, 34, + 122,135, 44,121,127, 58,120,122, 70,119,119, 77,122,130, 53, + 123,139, 37,124,145, 29,124,150, 22,124,157, 15,124,157, 15, + 124,152, 20,124,158, 13,124,160, 12,124,164, 9,123,171, 3, + 123,173, 1,123,171, 3,123,168, 5,124,165, 8,124,160, 12, + 124,154, 18,123,139, 37,118,114, 96, 69, 60,230, 30, 49,213, + 122,130, 53,124,161, 11,122,175, 0,121,182, 0,120,184, 0, + 119,185, 0,120,184, 0,120,183, 0,121,180, 0,123,172, 2, + 124,161, 11,124,160, 12,124,167, 6,123,170, 3,123,172, 2, + 123,172, 2,123,168, 5,123,168, 5,122,178, 0,120,184, 0, + 120,184, 0,121,182, 0,122,175, 0,124,164, 9,124,160, 12, + 124,161, 11,124,157, 15,124,145, 29,121,127, 58,122,130, 53, + 124,147, 26,124,160, 12,123,168, 5,123,172, 2,123,173, 1, + 123,170, 3,124,162, 10,124,160, 12,124,154, 18,124,157, 15, + 124,167, 6,123,172, 2,122,175, 0,122,175, 0,123,173, 1, + 123,170, 3,124,166, 7,124,161, 11,124,158, 13,124,152, 20, + 124,161, 11,124,167, 6,124,167, 6,124,162, 10,124,152, 20, + 124,143, 31,124,152, 20,124,154, 18,124,147, 26,122,135, 44, + 118,117, 85,109, 92,251,105, 84,252,122,135, 44,124,166, 7, + 120,183, 0,116,193, 0,112,199, 0,110,202, 0,110,202, 0, + 111,202, 0,112,200, 0,111,202, 0,110,203, 0,110,202, 0, + 112,200, 0,111,202, 0,107,207, 0,103,211, 0,102,213, 0, + 101,213, 0,101,214, 0,100,215, 0, 99,216, 0, 98,216, 0, + 99,216, 0,100,215, 0,102,213, 0,105,209, 0,110,202, 0, + 115,194, 0,119,187, 0,122,176, 0,124,155, 16,122,130, 53, + 113,100,242,109, 92,251,117,110,109,120,122, 70,122,132, 48, + 124,155, 16,123,168, 5,123,174, 1,123,173, 1,123,169, 4, + 124,166, 7,124,161, 11,124,155, 16,124,147, 26,123,137, 41, + 120,122, 70,114,104,197,117,110,109,122,132, 48,124,145, 29, + 124,155, 16,124,160, 12,123,172, 2,120,183, 0,118,188, 0, + 117,190, 0,117,192, 0,117,191, 0,119,186, 0,122,178, 0, + 124,165, 8,124,147, 26,121,125, 63,119,119, 77,123,141, 34, + 124,167, 6,121,179, 0,119,187, 0,117,192, 0,115,195, 0, + 113,198, 0,111,201, 0,110,202, 0,109,204, 0,109,204, 0, + 109,204, 0,110,203, 0,110,202, 0,110,202, 0,110,202, 0, + 112,200, 0,108,205, 0,105,209, 0,103,212, 0,103,212, 0, + 105,209, 0,108,205, 0,112,199, 0,116,193, 0,119,185, 0, + 122,175, 0,124,160, 12,124,147, 26,123,139, 37,121,125, 63, + 118,117, 85,121,125, 63,124,145, 29,124,157, 15,124,164, 9, + 124,167, 6,123,170, 3,123,173, 1,122,175, 0,121,179, 0, + 120,184, 0,119,187, 0,117,190, 0,116,192, 0,115,195, 0, + 114,197, 0,113,198, 0,113,199, 0,113,198, 0,114,196, 0, + 117,192, 0,119,185, 0,122,176, 0,124,165, 8,124,154, 18, + 124,143, 31,122,135, 44,122,130, 53,122,132, 48,123,137, 41, + 124,143, 31,124,152, 20,124,157, 15,124,155, 16,124,143, 31, + 118,114, 96,117,110,109,118,117, 85,121,125, 63,124,154, 18, + 123,168, 5,122,178, 0,119,185, 0,118,188, 0,118,189, 0, + 118,188, 0,120,184, 0,119,186, 0,118,189, 0,117,191, 0, + 117,192, 0,117,191, 0,117,191, 0,118,189, 0,119,187, 0, + 120,183, 0,122,177, 0,123,172, 2,124,164, 9,124,166, 7, + 123,171, 3,123,171, 3,124,166, 7,123,171, 3,124,152, 20, + 121,127, 58,113,100,242,105, 84,252,117,110,109,121,127, 58, + 123,137, 41,124,145, 29,124,149, 24,124,149, 24,124,149, 24, + 124,147, 26,124,147, 26,124,154, 18,124,160, 12,124,165, 8, + 124,164, 9,124,165, 8,123,169, 4,123,170, 3,123,168, 5, + 123,169, 4,123,173, 1,123,172, 2,123,169, 4,124,167, 6, + 124,162, 10,124,155, 16,123,139, 37,115,107,129, 30, 37,192, + 87, 65,237,123,137, 41,124,166, 7,121,179, 0,119,186, 0, + 118,189, 0,117,191, 0,117,191, 0,117,191, 0,118,189, 0, + 120,183, 0,123,173, 1,123,168, 5,122,176, 0,121,182, 0, + 120,184, 0,120,184, 0,121,182, 0,122,177, 0,119,186, 0, + 117,191, 0,116,192, 0,117,191, 0,119,186, 0,121,179, 0, + 123,170, 3,123,170, 3,124,167, 6,124,160, 12,124,152, 20, + 124,162, 10,123,171, 3,121,180, 0,120,184, 0,119,186, 0, + 119,186, 0,121,182, 0,123,174, 1,124,160, 12,124,152, 20, + 124,167, 6,123,174, 1,122,178, 0,121,182, 0,120,183, 0, + 120,184, 0,120,183, 0,120,182, 0,121,181, 0,121,179, 0, + 122,175, 0,123,168, 5,123,168, 5,124,166, 7,124,158, 13, + 124,143, 31,124,147, 26,124,154, 18,124,154, 18,124,147, 26, + 122,135, 44,118,117, 85,115,107,129,123,137, 41,124,162, 10, + 121,180, 0,117,191, 0,113,198, 0,110,202, 0,109,204, 0, + 109,204, 0,110,202, 0,110,202, 0,109,203, 0,109,203, 0, + 110,202, 0,108,205, 0,105,209, 0,102,213, 0, 98,217, 0, + 96,219, 0, 95,220, 0, 94,221, 0, 92,222, 0, 92,223, 0, + 92,223, 0, 92,222, 0, 94,220, 0, 97,217, 0,102,213, 0, + 107,207, 0,112,200, 0,117,192, 0,120,182, 0,123,169, 4, + 124,154, 18,122,130, 53,118,117, 85,115,107,129,109, 92,251, + 123,137, 41,124,161, 11,123,173, 1,121,179, 0,122,178, 0, + 122,176, 0,123,172, 2,123,168, 5,124,162, 10,124,155, 16, + 124,147, 26,123,137, 41,121,127, 58,120,122, 70,121,127, 58, + 123,137, 41,124,152, 20,124,166, 7,122,178, 0,119,186, 0, + 118,189, 0,117,191, 0,117,192, 0,117,192, 0,117,190, 0, + 119,186, 0,122,178, 0,124,167, 6,124,154, 18,123,141, 34, + 124,150, 22,123,174, 1,119,187, 0,115,194, 0,112,200, 0, + 110,202, 0,108,205, 0,107,206, 0,107,207, 0,107,207, 0, + 107,207, 0,107,206, 0,108,206, 0,108,205, 0,108,206, 0, + 108,205, 0,110,203, 0,109,203, 0,106,208, 0,103,211, 0, + 103,212, 0,105,209, 0,108,205, 0,112,199, 0,117,192, 0, + 120,184, 0,123,172, 2,124,165, 8,124,165, 8,124,162, 10, + 124,157, 15,124,149, 24,124,147, 26,124,155, 16,124,161, 11, + 124,166, 7,123,170, 3,122,175, 0,121,180, 0,120,184, 0, + 118,188, 0,117,191, 0,115,194, 0,113,198, 0,111,201, 0, + 109,204, 0,107,206, 0,107,207, 0,107,207, 0,107,206, 0, + 110,203, 0,113,199, 0,116,193, 0,119,186, 0,121,179, 0, + 123,173, 1,124,167, 6,124,160, 12,124,149, 24,124,152, 20, + 124,160, 12,124,166, 7,123,172, 2,123,174, 1,123,171, 3, + 124,161, 11,123,139, 37,107, 89,252, 99, 75,247,107, 89,252, + 122,132, 48,124,154, 18,123,168, 5,122,178, 0,120,184, 0, + 119,186, 0,119,186, 0,120,184, 0,118,189, 0,117,191, 0, + 116,192, 0,116,193, 0,116,193, 0,116,193, 0,116,192, 0, + 117,191, 0,118,188, 0,120,184, 0,121,181, 0,123,173, 1, + 123,171, 3,121,179, 0,120,182, 0,121,180, 0,121,181, 0, + 124,167, 6,124,147, 26,119,119, 77,107, 89,252, 94, 70,242, + 115,107,129,122,132, 48,124,149, 24,124,157, 15,124,161, 11, + 124,164, 9,124,165, 8,124,165, 8,124,165, 8,124,165, 8, + 123,168, 5,123,171, 3,123,174, 1,122,176, 0,122,176, 0, + 123,174, 1,123,169, 4,123,171, 3,123,171, 3,123,170, 3, + 123,168, 5,124,165, 8,124,157, 15,123,141, 34,115,107,129, + 30, 49,213, 99, 75,247,123,141, 34,123,168, 5,121,181, 0, + 118,188, 0,117,191, 0,116,193, 0,115,194, 0,115,195, 0, + 116,193, 0,118,189, 0,121,181, 0,122,175, 0,120,183, 0, + 118,188, 0,117,191, 0,117,191, 0,118,189, 0,119,185, 0, + 116,193, 0,114,197, 0,113,199, 0,114,197, 0,115,194, 0, + 118,189, 0,120,182, 0,122,177, 0,122,176, 0,123,171, 3, + 123,169, 4,122,176, 0,120,183, 0,118,189, 0,116,193, 0, + 115,195, 0,116,193, 0,118,189, 0,120,182, 0,123,171, 3, + 123,168, 5,123,173, 1,122,178, 0,121,182, 0,120,184, 0, + 119,187, 0,118,188, 0,118,189, 0,117,191, 0,117,190, 0, + 118,189, 0,119,187, 0,121,182, 0,123,172, 2,124,162, 10, + 124,152, 20,122,132, 48,124,147, 26,124,154, 18,124,152, 20, + 124,145, 29,122,132, 48,122,130, 53,124,145, 29,124,162, 10, + 122,178, 0,118,189, 0,114,197, 0,110,202, 0,108,205, 0, + 108,206, 0,109,204, 0,109,203, 0,108,205, 0,108,205, 0, + 109,204, 0,107,206, 0,103,212, 0, 99,216, 0, 96,219, 0, + 94,221, 0, 91,223, 0, 90,225, 0, 88,226, 0, 86,228, 0, + 85,229, 0, 85,228, 0, 87,227, 0, 90,224, 0, 94,220, 0, + 99,215, 0,104,210, 0,109,203, 0,114,197, 0,118,188, 0, + 122,178, 0,124,166, 7,124,147, 26,122,130, 53,118,117, 85, + 118,117, 85,124,143, 31,124,164, 9,122,175, 0,121,180, 0, + 121,180, 0,122,178, 0,123,174, 1,123,169, 4,124,165, 8, + 124,158, 13,124,152, 20,124,147, 26,123,141, 34,123,141, 34, + 124,147, 26,124,157, 15,123,168, 5,122,177, 0,120,184, 0, + 118,189, 0,116,192, 0,115,194, 0,114,196, 0,114,196, 0, + 115,195, 0,117,191, 0,119,186, 0,122,178, 0,123,168, 5, + 124,157, 15,124,161, 11,120,182, 0,115,194, 0,111,201, 0, + 108,206, 0,106,208, 0,105,209, 0,104,210, 0,105,209, 0, + 106,208, 0,106,207, 0,107,207, 0,107,206, 0,107,207, 0, + 106,207, 0,107,207, 0,108,205, 0,111,201, 0,108,205, 0, + 106,208, 0,105,209, 0,106,208, 0,109,203, 0,114,197, 0, + 118,188, 0,122,178, 0,123,168, 5,123,171, 3,123,174, 1, + 122,175, 0,122,175, 0,123,172, 2,123,170, 3,123,168, 5, + 123,174, 1,122,178, 0,120,182, 0,119,186, 0,118,188, 0, + 117,191, 0,116,193, 0,114,196, 0,113,199, 0,110,202, 0, + 107,207, 0,104,210, 0,102,212, 0,101,213, 0,102,212, 0, + 104,210, 0,106,207, 0,110,202, 0,114,197, 0,117,192, 0, + 119,187, 0,120,184, 0,121,179, 0,123,173, 1,124,164, 9, + 124,161, 11,123,168, 5,122,175, 0,121,180, 0,121,182, 0, + 122,178, 0,123,169, 4,124,152, 20,118,114, 96, 30, 37,192, + 30, 55,222,111, 96,248,121,127, 58,124,152, 20,124,167, 6, + 122,176, 0,121,181, 0,121,182, 0,120,184, 0,118,188, 0, + 117,191, 0,117,191, 0,117,192, 0,117,192, 0,117,192, 0, + 117,192, 0,117,191, 0,118,188, 0,119,186, 0,120,184, 0, + 122,178, 0,123,174, 1,120,183, 0,118,188, 0,119,187, 0, + 120,184, 0,123,171, 3,124,152, 20,120,122, 70,107, 89,252, + 30, 55,222, 87, 65,237,118,114, 96,124,143, 31,124,158, 13, + 124,166, 7,123,171, 3,123,173, 1,123,174, 1,123,174, 1, + 123,174, 1,122,176, 0,122,178, 0,121,180, 0,121,180, 0, + 121,179, 0,122,176, 0,123,170, 3,124,166, 7,123,168, 5, + 123,168, 5,124,167, 6,124,165, 8,124,158, 13,123,141, 34, + 115,107,129,105, 84,252,102, 80,250,123,141, 34,124,167, 6, + 121,180, 0,119,186, 0,117,191, 0,116,192, 0,115,194, 0, + 115,195, 0,115,194, 0,117,191, 0,120,184, 0,121,180, 0, + 119,187, 0,117,192, 0,115,194, 0,115,195, 0,116,193, 0, + 116,193, 0,112,199, 0,110,202, 0,109,204, 0,110,202, 0, + 112,199, 0,115,194, 0,118,189, 0,120,184, 0,120,183, 0, + 121,181, 0,122,177, 0,121,182, 0,118,188, 0,115,194, 0, + 113,199, 0,112,200, 0,113,199, 0,115,194, 0,118,188, 0, + 120,182, 0,120,184, 0,121,181, 0,121,179, 0,121,182, 0, + 120,184, 0,119,187, 0,118,189, 0,117,192, 0,115,194, 0, + 115,194, 0,115,194, 0,116,193, 0,118,189, 0,120,183, 0, + 123,172, 2,124,154, 18,121,125, 63,124,145, 29,124,150, 22, + 124,150, 22,124,152, 20,124,154, 18,124,160, 12,124,167, 6, + 122,177, 0,119,187, 0,114,196, 0,110,202, 0,108,206, 0, + 107,207, 0,107,207, 0,108,205, 0,106,208, 0,106,208, 0, + 107,207, 0,108,205, 0,103,212, 0, 98,217, 0, 93,222, 0, + 89,225, 0, 87,227, 0, 87,227, 0, 85,228, 0, 83,230, 0, + 80,232, 0, 80,233, 0, 80,232, 0, 83,230, 0, 87,227, 0, + 92,222, 0, 97,217, 0,103,212, 0,107,206, 0,112,200, 0, + 116,192, 0,120,184, 0,123,173, 1,124,155, 16,123,139, 37, + 121,125, 63,124,149, 24,124,164, 9,123,172, 2,122,176, 0, + 122,178, 0,122,178, 0,123,174, 1,123,170, 3,124,166, 7, + 124,160, 12,124,155, 16,124,150, 22,124,149, 24,124,147, 26, + 124,150, 22,124,157, 15,124,166, 7,123,174, 1,121,181, 0, + 119,187, 0,117,191, 0,116,193, 0,114,196, 0,114,197, 0, + 113,198, 0,114,197, 0,115,194, 0,117,190, 0,120,184, 0, + 122,177, 0,124,166, 7,123,172, 2,118,189, 0,112,200, 0, + 107,207, 0,104,210, 0,102,212, 0,102,212, 0,102,212, 0, + 104,210, 0,106,208, 0,107,206, 0,108,205, 0,108,205, 0, + 107,206, 0,106,207, 0,107,207, 0,108,205, 0,111,201, 0, + 111,202, 0,109,204, 0,108,205, 0,109,203, 0,113,199, 0, + 117,191, 0,121,180, 0,124,167, 6,123,168, 5,123,173, 1, + 122,178, 0,120,182, 0,120,184, 0,119,185, 0,120,184, 0, + 120,182, 0,120,184, 0,118,188, 0,117,191, 0,116,193, 0, + 115,194, 0,114,196, 0,112,200, 0,109,203, 0,108,205, 0, + 109,204, 0,105,209, 0,102,213, 0, 99,215, 0, 98,216, 0, + 100,215, 0,102,212, 0,105,209, 0,109,204, 0,112,199, 0, + 115,194, 0,117,191, 0,118,189, 0,119,186, 0,121,181, 0, + 123,173, 1,124,162, 10,123,169, 4,122,178, 0,120,182, 0, + 120,184, 0,121,180, 0,123,172, 2,124,157, 15,121,127, 58, + 99, 75,247, 30, 15,143, 87, 65,237,113,100,242,121,125, 63, + 124,150, 22,124,165, 8,123,171, 3,123,174, 1,121,180, 0, + 119,185, 0,119,187, 0,119,187, 0,119,187, 0,119,187, 0, + 118,188, 0,118,188, 0,118,188, 0,119,186, 0,120,184, 0, + 120,183, 0,122,178, 0,122,175, 0,120,184, 0,118,189, 0, + 118,189, 0,120,183, 0,123,170, 3,124,149, 24,122,132, 48, + 118,114, 96, 99, 75,247, 30, 2,128, 99, 75,247,121,127, 58, + 124,152, 20,124,166, 7,123,172, 2,122,177, 0,122,178, 0, + 122,178, 0,122,178, 0,121,179, 0,121,181, 0,121,182, 0, + 121,181, 0,122,178, 0,123,173, 1,124,167, 6,124,158, 13, + 124,161, 11,124,162, 10,124,164, 9,124,162, 10,124,157, 15, + 123,141, 34,120,122, 70,117,110,109,109, 92,251,123,139, 37, + 124,165, 8,122,177, 0,120,184, 0,119,187, 0,118,189, 0, + 117,190, 0,116,192, 0,116,192, 0,118,189, 0,119,185, 0, + 120,184, 0,117,190, 0,116,193, 0,114,196, 0,114,196, 0, + 115,194, 0,112,199, 0,109,204, 0,107,207, 0,106,207, 0, + 108,206, 0,110,202, 0,113,198, 0,116,193, 0,118,189, 0, + 117,190, 0,118,189, 0,119,187, 0,120,184, 0,118,189, 0, + 114,196, 0,111,201, 0,110,202, 0,110,202, 0,112,199, 0, + 115,194, 0,116,192, 0,116,193, 0,117,190, 0,120,184, 0, + 121,179, 0,121,182, 0,120,184, 0,118,188, 0,117,191, 0, + 116,193, 0,115,195, 0,114,196, 0,114,196, 0,116,193, 0, + 118,189, 0,120,183, 0,123,174, 1,124,161, 11,123,169, 4, + 123,172, 2,123,173, 1,123,172, 2,123,173, 1,122,178, 0, + 120,184, 0,118,189, 0,115,194, 0,111,201, 0,107,206, 0, + 106,208, 0,105,209, 0,106,208, 0,105,209, 0,103,211, 0, + 104,210, 0,106,207, 0,105,209, 0, 99,215, 0, 93,221, 0, + 88,226, 0, 84,229, 0, 82,231, 0, 81,232, 0, 82,231, 0, + 78,234, 0, 75,236, 0, 75,237, 0, 76,236, 0, 79,233, 0, + 84,229, 0, 91,224, 0, 94,220, 0, 99,215, 0,106,208, 0, + 111,202, 0,115,195, 0,119,187, 0,122,176, 0,124,158, 13, + 124,145, 29,124,150, 22,124,167, 6,122,176, 0,121,180, 0, + 121,181, 0,122,178, 0,123,174, 1,124,167, 6,124,161, 11, + 124,155, 16,124,149, 24,124,145, 29,124,143, 31,124,143, 31, + 124,145, 29,124,150, 22,124,158, 13,124,167, 6,123,174, 1, + 121,180, 0,119,185, 0,118,189, 0,117,191, 0,116,193, 0, + 114,196, 0,114,197, 0,114,197, 0,114,196, 0,117,192, 0, + 119,186, 0,121,180, 0,123,171, 3,120,183, 0,114,196, 0, + 108,205, 0,103,211, 0,101,214, 0,100,214, 0,101,214, 0, + 102,213, 0,105,209, 0,102,212, 0,102,212, 0,103,211, 0, + 106,207, 0,109,204, 0,108,206, 0,108,206, 0,109,203, 0, + 112,200, 0,114,197, 0,113,198, 0,113,198, 0,114,196, 0, + 117,190, 0,121,180, 0,124,165, 8,124,167, 6,122,177, 0, + 121,181, 0,121,182, 0,119,186, 0,117,190, 0,117,192, 0, + 117,192, 0,117,191, 0,117,191, 0,115,194, 0,114,197, 0, + 113,198, 0,113,198, 0,110,203, 0,106,207, 0,103,211, 0, + 102,212, 0,103,212, 0,105,209, 0,101,213, 0, 98,216, 0, + 98,217, 0, 99,215, 0,102,212, 0,106,208, 0,109,204, 0, + 113,199, 0,115,194, 0,116,192, 0,117,191, 0,118,189, 0, + 119,186, 0,121,180, 0,123,171, 3,124,166, 7,123,174, 1, + 121,180, 0,121,181, 0,122,178, 0,123,169, 4,124,155, 16, + 122,130, 53,109, 92,251, 30, 37,192, 30, 43,203,105, 84,252, + 113,100,242,121,125, 63,124,147, 26,124,158, 13,124,164, 9, + 123,173, 1,122,178, 0,121,179, 0,120,183, 0,118,188, 0, + 118,189, 0,119,185, 0,121,181, 0,120,182, 0,121,180, 0, + 122,176, 0,122,178, 0,122,176, 0,123,174, 1,120,183, 0, + 118,188, 0,118,188, 0,122,178, 0,124,164, 9,124,162, 10, + 124,158, 13,124,147, 26,121,125, 63, 99, 75,247, 30, 37,192, + 111, 96,248,123,139, 37,124,160, 12,123,170, 3,122,175, 0, + 122,178, 0,122,178, 0,122,178, 0,121,179, 0,121,180, 0, + 121,179, 0,122,177, 0,123,172, 2,124,166, 7,124,157, 15, + 124,147, 26,124,150, 22,124,154, 18,124,157, 15,124,158, 13, + 124,152, 20,123,137, 41,122,135, 44,121,127, 58,117,110,109, + 123,137, 41,124,161, 11,123,172, 2,122,178, 0,121,182, 0, + 120,182, 0,120,184, 0,119,186, 0,118,188, 0,119,186, 0, + 120,183, 0,118,188, 0,117,191, 0,115,194, 0,114,196, 0, + 114,196, 0,113,198, 0,109,204, 0,106,208, 0,104,210, 0, + 105,209, 0,107,207, 0,110,203, 0,112,199, 0,115,194, 0, + 114,196, 0,114,197, 0,114,197, 0,115,195, 0,116,193, 0, + 117,191, 0,114,197, 0,111,202, 0,109,204, 0,108,205, 0, + 110,203, 0,112,200, 0,112,200, 0,113,199, 0,114,196, 0, + 118,189, 0,119,185, 0,120,184, 0,119,187, 0,118,188, 0, + 118,188, 0,117,191, 0,116,193, 0,115,195, 0,114,197, 0, + 114,197, 0,115,195, 0,117,192, 0,119,186, 0,121,180, 0, + 120,182, 0,120,184, 0,119,185, 0,120,184, 0,119,186, 0, + 117,191, 0,114,196, 0,113,198, 0,112,199, 0,108,206, 0, + 105,209, 0,103,211, 0,103,212, 0,103,211, 0,102,212, 0, + 102,213, 0,103,211, 0,106,208, 0,102,212, 0, 97,218, 0, + 91,224, 0, 85,229, 0, 81,232, 0, 78,234, 0, 78,235, 0, + 78,234, 0, 74,237, 0, 72,239, 0, 71,240, 0, 72,239, 0, + 76,236, 0, 82,231, 0, 88,226, 0, 91,223, 0, 96,219, 0, + 103,212, 0,110,202, 0,114,196, 0,118,188, 0,122,177, 0, + 124,158, 13,124,155, 16,123,169, 4,122,178, 0,120,184, 0, + 119,185, 0,120,184, 0,121,179, 0,123,174, 1,124,166, 7, + 124,154, 18,124,149, 24,123,141, 34,122,135, 44,121,125, 63, + 122,130, 53,122,135, 44,123,141, 34,124,152, 20,124,161, 11, + 123,169, 4,122,175, 0,121,180, 0,120,183, 0,119,186, 0, + 118,189, 0,116,192, 0,115,195, 0,114,196, 0,115,195, 0, + 116,192, 0,118,188, 0,120,182, 0,122,178, 0,117,192, 0, + 110,202, 0,104,210, 0,101,214, 0, 99,216, 0, 99,216, 0, + 100,215, 0,101,214, 0, 97,217, 0, 95,219, 0, 95,219, 0, + 98,217, 0,102,213, 0,107,207, 0,109,203, 0,109,203, 0, + 111,202, 0,113,198, 0,117,192, 0,117,191, 0,118,189, 0, + 119,185, 0,122,178, 0,124,164, 9,124,162, 10,122,175, 0, + 120,184, 0,118,188, 0,118,189, 0,118,188, 0,116,193, 0, + 114,196, 0,114,196, 0,114,196, 0,114,197, 0,112,200, 0, + 111,202, 0,110,202, 0,110,203, 0,106,208, 0,102,212, 0, + 99,216, 0, 97,217, 0, 98,217, 0,100,215, 0,102,212, 0, + 100,215, 0, 99,215, 0,100,214, 0,103,211, 0,107,207, 0, + 110,202, 0,112,200, 0,115,195, 0,117,192, 0,117,191, 0, + 117,190, 0,118,188, 0,120,184, 0,122,177, 0,124,167, 6, + 124,166, 7,123,173, 1,122,175, 0,123,171, 3,124,164, 9, + 124,149, 24,121,127, 58,111, 96,248, 69, 60,230, 30, 49,213, + 69, 60,230,102, 80,250,111, 96,248,121,125, 63,123,137, 41, + 124,149, 24,124,161, 11,124,167, 6,122,175, 0,119,186, 0, + 117,192, 0,116,193, 0,117,191, 0,120,184, 0,123,172, 2, + 123,171, 3,124,162, 10,124,167, 6,123,169, 4,123,171, 3, + 121,179, 0,120,184, 0,120,184, 0,123,170, 3,123,171, 3, + 123,172, 2,123,170, 3,124,166, 7,124,150, 22,118,117, 85, + 94, 70,242, 94, 70,242,119,119, 77,124,149, 24,124,162, 10, + 123,169, 4,123,172, 2,123,172, 2,123,172, 2,123,172, 2, + 123,172, 2,123,171, 3,124,167, 6,124,160, 12,124,150, 22, + 122,135, 44,122,132, 48,123,137, 41,123,141, 34,124,147, 26, + 124,149, 24,124,145, 29,123,137, 41,123,139, 37,122,135, 44, + 119,119, 77,123,137, 41,124,158, 13,123,168, 5,123,173, 1, + 123,174, 1,122,175, 0,122,178, 0,122,178, 0,121,181, 0, + 120,182, 0,119,186, 0,118,189, 0,116,192, 0,115,194, 0, + 115,194, 0,115,195, 0,110,202, 0,106,207, 0,104,210, 0, + 103,211, 0,104,210, 0,107,207, 0,110,202, 0,113,198, 0, + 113,199, 0,111,201, 0,111,202, 0,110,202, 0,111,201, 0, + 112,199, 0,114,197, 0,114,197, 0,110,202, 0,107,206, 0, + 106,207, 0,106,207, 0,107,206, 0,109,204, 0,110,203, 0, + 112,200, 0,113,199, 0,114,197, 0,115,194, 0,116,192, 0, + 115,194, 0,115,194, 0,116,193, 0,117,191, 0,115,194, 0, + 114,197, 0,112,199, 0,112,200, 0,113,199, 0,114,196, 0, + 116,193, 0,118,189, 0,117,192, 0,116,192, 0,116,193, 0, + 114,197, 0,112,200, 0,110,202, 0,109,204, 0,109,204, 0, + 104,210, 0,101,213, 0,100,215, 0,100,215, 0,101,214, 0, + 101,213, 0,101,213, 0,103,211, 0,104,210, 0, 98,216, 0, + 94,221, 0, 89,225, 0, 84,229, 0, 80,233, 0, 78,235, 0, + 77,235, 0, 74,237, 0, 71,240, 0, 68,242, 0, 68,242, 0, + 69,241, 0, 74,238, 0, 80,233, 0, 87,227, 0, 90,225, 0, + 95,220, 0,101,213, 0,108,205, 0,114,197, 0,118,188, 0, + 122,177, 0,124,158, 13,123,170, 3,121,180, 0,119,186, 0, + 118,189, 0,118,189, 0,119,186, 0,121,181, 0,123,174, 1, + 123,174, 1,123,173, 1,123,169, 4,124,161, 11,124,147, 26, + 122,135, 44,119,119, 77,117,110,109,120,122, 70,122,135, 44, + 124,147, 26,124,158, 13,124,166, 7,123,171, 3,122,176, 0, + 121,180, 0,120,184, 0,118,189, 0,116,192, 0,115,194, 0, + 115,194, 0,116,193, 0,118,189, 0,120,184, 0,119,187, 0, + 113,199, 0,106,208, 0,101,214, 0, 98,217, 0, 97,218, 0, + 97,217, 0, 99,216, 0, 95,220, 0, 91,223, 0, 90,225, 0, + 90,224, 0, 94,221, 0, 99,216, 0,105,209, 0,110,202, 0, + 111,202, 0,113,199, 0,115,194, 0,118,188, 0,121,181, 0, + 122,177, 0,123,170, 3,124,155, 16,124,155, 16,124,160, 12, + 122,178, 0,118,188, 0,116,192, 0,115,194, 0,115,194, 0, + 116,193, 0,114,197, 0,113,198, 0,112,199, 0,110,202, 0, + 109,204, 0,107,206, 0,107,206, 0,108,205, 0,104,210, 0, + 100,215, 0, 97,218, 0, 95,220, 0, 95,220, 0, 97,218, 0, + 99,215, 0,102,213, 0,101,214, 0,102,212, 0,105,209, 0, + 106,208, 0,107,206, 0,110,203, 0,113,198, 0,117,190, 0, + 117,190, 0,118,189, 0,118,188, 0,119,185, 0,121,180, 0, + 123,173, 1,124,166, 7,124,162, 10,124,166, 7,124,162, 10, + 124,160, 12,124,154, 18,124,143, 31,121,125, 63,109, 92,251, + 30, 49,213, 30, 43,203, 30, 43,203, 99, 75,247,114,104,197, + 119,119, 77,122,132, 48,124,145, 29,124,154, 18,122,175, 0, + 118,188, 0,116,193, 0,114,196, 0,116,193, 0,118,188, 0, + 122,178, 0,124,161, 11,124,150, 22,124,150, 22,124,160, 12, + 124,166, 7,123,173, 1,122,178, 0,122,177, 0,123,172, 2, + 123,174, 1,122,176, 0,122,178, 0,122,175, 0,124,165, 8, + 123,141, 34,113,100,242,114,104,197,117,110,109,122,132, 48, + 124,147, 26,124,157, 15,124,162, 10,123,171, 3,122,175, 0, + 122,176, 0,123,171, 3,124,164, 9,124,152, 20,123,139, 37, + 121,125, 63,119,119, 77,123,139, 37,124,150, 22,124,150, 22, + 123,141, 34,123,141, 34,123,139, 37,122,135, 44,123,139, 37, + 123,139, 37,122,130, 53,124,143, 31,124,152, 20,124,162, 10, + 124,166, 7,124,167, 6,123,174, 1,122,178, 0,121,180, 0, + 121,181, 0,120,183, 0,119,186, 0,118,189, 0,117,192, 0, + 116,193, 0,115,194, 0,114,196, 0,110,203, 0,106,208, 0, + 104,210, 0,104,210, 0,106,208, 0,109,203, 0,111,202, 0, + 110,202, 0,109,203, 0,108,205, 0,107,206, 0,107,206, 0, + 108,206, 0,109,204, 0,110,202, 0,110,202, 0,110,202, 0, + 107,207, 0,104,210, 0,103,212, 0,103,212, 0,103,211, 0, + 105,209, 0,106,208, 0,107,207, 0,108,206, 0,109,203, 0, + 112,200, 0,113,198, 0,113,199, 0,113,199, 0,113,198, 0, + 115,195, 0,114,197, 0,111,201, 0,109,204, 0,108,205, 0, + 108,205, 0,110,203, 0,111,201, 0,112,200, 0,111,201, 0, + 110,202, 0,109,204, 0,107,206, 0,106,208, 0,106,208, 0, + 106,208, 0,102,212, 0, 99,215, 0, 98,217, 0, 97,217, 0, + 98,217, 0, 99,215, 0,100,214, 0,101,214, 0,100,214, 0, + 95,220, 0, 91,224, 0, 87,227, 0, 83,230, 0, 80,233, 0, + 77,235, 0, 75,237, 0, 72,239, 0, 68,242, 0, 66,244, 0, + 65,245, 0, 67,243, 0, 71,240, 0, 77,235, 0, 84,229, 0, + 91,224, 0, 95,220, 0,101,214, 0,107,207, 0,113,198, 0, + 118,188, 0,122,178, 0,123,171, 3,121,180, 0,119,187, 0, + 117,191, 0,116,193, 0,116,192, 0,118,189, 0,120,184, 0, + 119,185, 0,119,185, 0,120,184, 0,121,179, 0,123,172, 2, + 124,160, 12,123,141, 34,121,127, 58,118,117, 85,118,117, 85, + 121,125, 63,123,137, 41,124,150, 22,124,162, 10,123,172, 2, + 121,179, 0,120,184, 0,119,187, 0,118,188, 0,117,191, 0, + 115,194, 0,115,194, 0,116,193, 0,117,190, 0,117,192, 0, + 115,194, 0,109,204, 0,102,212, 0, 97,217, 0, 95,220, 0, + 94,220, 0, 95,219, 0, 94,221, 0, 91,224, 0, 87,227, 0, + 86,228, 0, 87,227, 0, 91,223, 0, 97,218, 0,104,210, 0, + 110,203, 0,113,198, 0,115,194, 0,118,189, 0,120,182, 0, + 123,172, 2,124,162, 10,124,149, 24,123,137, 41,122,135, 44, + 124,154, 18,122,176, 0,118,188, 0,115,194, 0,114,197, 0, + 113,198, 0,113,199, 0,112,200, 0,111,202, 0,109,204, 0, + 107,207, 0,105,209, 0,104,210, 0,104,210, 0,105,209, 0, + 105,209, 0,101,214, 0, 98,217, 0, 95,219, 0, 95,219, 0, + 97,218, 0, 99,216, 0,101,214, 0,102,213, 0,102,212, 0, + 103,211, 0,104,210, 0,106,208, 0,109,204, 0,112,199, 0, + 117,191, 0,119,187, 0,118,188, 0,119,186, 0,120,184, 0, + 121,181, 0,122,175, 0,123,170, 3,123,168, 5,123,169, 4, + 123,168, 5,124,166, 7,124,161, 11,124,154, 18,123,139, 37, + 118,117, 85,105, 84,252, 87, 65,237, 30, 49,213, 87, 65,237, + 109, 92,251,114,104,197,118,114, 96,122,132, 48,124,143, 31, + 123,170, 3,120,184, 0,117,191, 0,115,194, 0,116,193, 0, + 118,189, 0,121,181, 0,123,168, 5,124,150, 22,122,130, 53, + 124,147, 26,124,158, 13,124,166, 7,123,170, 3,123,169, 4, + 123,169, 4,123,172, 2,121,179, 0,120,182, 0,121,181, 0, + 123,173, 1,124,158, 13,121,127, 58,122,130, 53,122,135, 44, + 123,139, 37,124,145, 29,124,152, 20,124,164, 9,123,172, 2, + 122,177, 0,122,177, 0,123,172, 2,124,164, 9,124,150, 22, + 122,135, 44,118,117, 85,122,135, 44,124,149, 24,124,157, 15, + 124,157, 15,124,149, 24,123,141, 34,123,137, 41,122,130, 53, + 123,141, 34,124,145, 29,123,141, 34,123,141, 34,123,141, 34, + 124,147, 26,124,154, 18,124,165, 8,123,172, 2,122,176, 0, + 122,177, 0,122,178, 0,121,180, 0,120,184, 0,119,187, 0, + 118,189, 0,117,191, 0,116,192, 0,114,196, 0,110,202, 0, + 107,207, 0,106,208, 0,107,207, 0,109,204, 0,110,202, 0, + 108,206, 0,107,207, 0,107,207, 0,106,208, 0,105,209, 0, + 105,209, 0,105,209, 0,106,208, 0,107,207, 0,107,207, 0, + 106,207, 0,106,208, 0,103,212, 0,100,214, 0, 99,216, 0, + 99,216, 0,100,215, 0,101,214, 0,102,213, 0,102,212, 0, + 104,210, 0,107,207, 0,110,202, 0,111,202, 0,110,202, 0, + 110,202, 0,112,200, 0,114,197, 0,110,203, 0,106,207, 0, + 104,210, 0,103,212, 0,103,211, 0,104,210, 0,105,209, 0, + 105,209, 0,105,209, 0,104,210, 0,103,212, 0,102,212, 0, + 102,212, 0,102,212, 0,102,213, 0, 99,215, 0, 98,217, 0, + 97,217, 0, 98,217, 0, 98,216, 0,100,215, 0,100,215, 0, + 99,216, 0, 94,220, 0, 90,224, 0, 87,227, 0, 83,230, 0, + 80,232, 0, 78,235, 0, 75,237, 0, 72,239, 0, 68,242, 0, + 65,245, 0, 64,245, 0, 65,244, 0, 69,241, 0, 75,237, 0, + 82,231, 0, 89,226, 0, 95,219, 0,100,214, 0,106,208, 0, + 111,201, 0,116,192, 0,120,183, 0,121,179, 0,119,185, 0, + 117,191, 0,115,195, 0,114,197, 0,114,196, 0,116,193, 0, + 118,189, 0,117,191, 0,117,191, 0,118,189, 0,119,186, 0, + 122,178, 0,124,166, 7,124,143, 31,120,122, 70,117,110,109, + 117,110,109,119,119, 77,122,135, 44,124,150, 22,124,164, 9, + 123,174, 1,121,182, 0,119,187, 0,118,189, 0,117,190, 0, + 117,191, 0,115,194, 0,115,195, 0,115,195, 0,114,197, 0, + 112,199, 0,111,202, 0,104,210, 0, 98,216, 0, 94,220, 0, + 92,222, 0, 92,222, 0, 93,221, 0, 90,225, 0, 87,227, 0, + 85,228, 0, 85,229, 0, 87,227, 0, 91,224, 0, 96,219, 0, + 102,212, 0,108,205, 0,114,197, 0,118,189, 0,121,182, 0, + 123,171, 3,124,158, 13,124,143, 31,118,117, 85,111, 96,248, + 107, 89,252,122,135, 44,123,168, 5,120,184, 0,116,193, 0, + 113,198, 0,111,201, 0,110,202, 0,110,203, 0,108,205, 0, + 107,207, 0,105,209, 0,103,211, 0,102,212, 0,102,213, 0, + 103,212, 0,105,209, 0,104,210, 0,101,214, 0, 99,216, 0, + 98,217, 0, 99,216, 0,100,214, 0,102,213, 0,103,212, 0, + 103,211, 0,104,210, 0,105,209, 0,106,207, 0,109,204, 0, + 112,199, 0,117,191, 0,120,183, 0,120,184, 0,120,184, 0, + 120,184, 0,121,181, 0,122,177, 0,123,172, 2,123,171, 3, + 123,171, 3,123,170, 3,124,167, 6,124,162, 10,124,154, 18, + 123,141, 34,119,119, 77,114,104,197,107, 89,252,102, 80,250, + 94, 70,242, 99, 75,247,107, 89,252,111, 96,248,118,117, 85, + 122,132, 48,124,157, 15,122,175, 0,119,185, 0,118,189, 0, + 117,191, 0,118,188, 0,120,183, 0,123,174, 1,124,162, 10, + 124,143, 31,123,139, 37,124,152, 20,124,160, 12,124,161, 11, + 124,164, 9,124,160, 12,123,172, 2,121,180, 0,120,184, 0, + 120,184, 0,122,178, 0,124,167, 6,124,145, 29,124,147, 26, + 124,150, 22,124,152, 20,124,155, 16,124,160, 12,124,164, 9, + 123,168, 5,123,173, 1,123,174, 1,123,169, 4,124,160, 12, + 124,143, 31,119,119, 77,122,135, 44,124,145, 29,124,152, 20, + 124,160, 12,124,161, 11,124,155, 16,124,143, 31,123,139, 37, + 121,127, 58,123,141, 34,124,150, 22,124,150, 22,124,149, 24, + 124,145, 29,124,143, 31,124,154, 18,124,162, 10,124,167, 6, + 123,169, 4,123,169, 4,123,170, 3,123,173, 1,122,178, 0, + 120,182, 0,119,185, 0,118,188, 0,118,189, 0,116,193, 0, + 111,201, 0,109,204, 0,108,205, 0,110,203, 0,111,201, 0, + 108,205, 0,106,208, 0,105,209, 0,105,209, 0,105,209, 0, + 104,210, 0,104,210, 0,104,210, 0,104,210, 0,104,210, 0, + 104,210, 0,103,211, 0,103,211, 0,102,213, 0, 98,217, 0, + 96,219, 0, 95,220, 0, 95,219, 0, 97,218, 0, 98,217, 0, + 99,215, 0,101,214, 0,103,212, 0,106,208, 0,109,203, 0, + 109,204, 0,108,205, 0,110,203, 0,112,199, 0,109,204, 0, + 104,210, 0,100,214, 0, 98,216, 0, 98,217, 0, 99,216, 0, + 100,215, 0,100,215, 0,100,214, 0,100,215, 0,100,215, 0, + 100,215, 0,100,215, 0,100,215, 0,100,215, 0, 99,215, 0, + 98,216, 0, 98,217, 0, 97,217, 0, 97,217, 0, 98,217, 0, + 100,215, 0, 99,215, 0, 95,220, 0, 92,223, 0, 89,226, 0, + 85,229, 0, 83,230, 0, 80,232, 0, 78,235, 0, 74,237, 0, + 70,241, 0, 66,243, 0, 65,245, 0, 66,244, 0, 69,242, 0, + 74,237, 0, 81,232, 0, 88,226, 0, 95,220, 0,100,215, 0, + 105,209, 0,109,203, 0,114,196, 0,119,187, 0,120,184, 0, + 118,188, 0,116,193, 0,114,197, 0,113,199, 0,113,199, 0, + 114,197, 0,116,193, 0,115,194, 0,115,194, 0,116,193, 0, + 118,189, 0,120,182, 0,123,169, 4,124,145, 29,117,110,109, + 109, 92,251,107, 89,252,114,104,197,121,125, 63,124,145, 29, + 124,162, 10,123,173, 1,121,182, 0,119,186, 0,118,189, 0, + 117,191, 0,116,192, 0,114,196, 0,114,196, 0,112,199, 0, + 110,202, 0,109,204, 0,107,207, 0,101,214, 0, 96,219, 0, + 92,222, 0, 91,224, 0, 91,224, 0, 90,224, 0, 87,227, 0, + 86,228, 0, 85,229, 0, 85,229, 0, 87,227, 0, 91,223, 0, + 96,219, 0,102,213, 0,108,206, 0,113,198, 0,118,189, 0, + 120,184, 0,122,178, 0,123,172, 2,124,162, 10,124,145, 29, + 117,110,109,102, 80,250,118,114, 96,124,152, 20,122,177, 0, + 118,189, 0,114,197, 0,111,201, 0,110,203, 0,109,204, 0, + 108,206, 0,106,207, 0,105,209, 0,103,211, 0,102,213, 0, + 101,214, 0,101,213, 0,103,211, 0,106,207, 0,106,208, 0, + 103,211, 0,102,212, 0,102,212, 0,103,211, 0,105,209, 0, + 105,209, 0,106,208, 0,106,208, 0,106,207, 0,108,206, 0, + 110,202, 0,113,198, 0,118,189, 0,122,178, 0,121,180, 0, + 121,182, 0,120,184, 0,120,184, 0,121,182, 0,122,175, 0, + 123,170, 3,123,170, 3,123,168, 5,124,164, 9,124,157, 15, + 124,147, 26,122,132, 48,118,117, 85,115,107,129,113,100,242, + 111, 96,248,109, 92,251,105, 84,252, 94, 70,242, 99, 75,247, + 113,100,242,120,122, 70,122,135, 44,124,160, 12,123,174, 1, + 121,182, 0,119,185, 0,119,185, 0,120,182, 0,122,177, 0, + 123,169, 4,124,158, 13,123,137, 41,124,143, 31,124,150, 22, + 124,150, 22,124,149, 24,124,150, 22,123,168, 5,122,178, 0, + 120,184, 0,120,184, 0,121,180, 0,123,170, 3,124,155, 16, + 124,157, 15,124,158, 13,124,158, 13,124,158, 13,124,160, 12, + 124,161, 11,124,164, 9,124,164, 9,124,165, 8,124,160, 12, + 124,147, 26,120,122, 70,122,135, 44,124,143, 31,124,150, 22, + 124,155, 16,124,161, 11,124,164, 9,124,160, 12,124,149, 24, + 124,143, 31,122,132, 48,123,137, 41,124,150, 22,124,155, 16, + 124,157, 15,124,157, 15,124,155, 16,124,150, 22,124,155, 16, + 124,158, 13,124,158, 13,124,155, 16,124,155, 16,124,161, 11, + 123,168, 5,123,174, 1,121,179, 0,120,183, 0,119,186, 0, + 118,189, 0,114,197, 0,111,201, 0,111,201, 0,113,199, 0, + 111,202, 0,108,205, 0,106,208, 0,106,208, 0,106,207, 0, + 106,208, 0,105,209, 0,105,209, 0,104,210, 0,104,210, 0, + 103,211, 0,103,212, 0,102,212, 0,102,212, 0,101,214, 0, + 96,219, 0, 93,221, 0, 92,222, 0, 92,222, 0, 94,220, 0, + 96,219, 0, 97,217, 0, 99,216, 0,101,214, 0,103,211, 0, + 107,207, 0,109,204, 0,108,205, 0,110,203, 0,112,200, 0, + 108,205, 0,102,212, 0, 98,217, 0, 95,219, 0, 95,220, 0, + 95,219, 0, 97,218, 0, 97,217, 0, 98,217, 0, 98,216, 0, + 99,216, 0, 99,216, 0, 98,216, 0, 98,216, 0, 98,217, 0, + 97,217, 0, 97,218, 0, 96,219, 0, 96,219, 0, 96,219, 0, + 97,218, 0, 98,217, 0, 98,216, 0, 95,220, 0, 91,223, 0, + 88,226, 0, 85,228, 0, 84,229, 0, 84,229, 0, 82,231, 0, + 78,234, 0, 74,238, 0, 70,241, 0, 68,242, 0, 68,242, 0, + 71,240, 0, 75,236, 0, 82,231, 0, 88,226, 0, 96,219, 0, + 100,215, 0,103,211, 0,108,206, 0,113,198, 0,118,189, 0, + 119,186, 0,118,189, 0,115,194, 0,113,198, 0,112,200, 0, + 111,201, 0,111,201, 0,113,198, 0,114,197, 0,114,197, 0, + 115,195, 0,117,191, 0,120,184, 0,123,170, 3,124,143, 31, + 107, 89,252, 69, 60,230, 99, 75,247,115,107,129,121,127, 58, + 123,141, 34,124,157, 15,123,170, 3,121,179, 0,120,184, 0, + 119,187, 0,118,189, 0,115,195, 0,113,198, 0,113,198, 0, + 111,202, 0,108,205, 0,107,207, 0,104,210, 0, 98,216, 0, + 94,221, 0, 91,223, 0, 89,225, 0, 89,225, 0, 87,227, 0, + 85,228, 0, 85,229, 0, 86,228, 0, 87,227, 0, 89,225, 0, + 93,222, 0, 97,217, 0,102,212, 0,108,206, 0,113,198, 0, + 116,193, 0,117,191, 0,119,187, 0,120,182, 0,122,175, 0, + 124,165, 8,124,145, 29,114,104,197,122,135, 44,124,157, 15, + 123,168, 5,120,183, 0,116,193, 0,112,199, 0,110,202, 0, + 110,203, 0,109,204, 0,108,205, 0,107,207, 0,105,209, 0, + 103,211, 0,102,212, 0,102,212, 0,103,211, 0,106,208, 0, + 109,204, 0,108,205, 0,107,206, 0,107,207, 0,108,206, 0, + 108,205, 0,107,207, 0,108,205, 0,109,204, 0,109,204, 0, + 110,202, 0,112,200, 0,115,194, 0,119,186, 0,123,172, 2, + 122,175, 0,121,181, 0,119,186, 0,119,186, 0,120,184, 0, + 122,178, 0,124,167, 6,124,165, 8,124,161, 11,124,154, 18, + 124,147, 26,124,143, 31,122,132, 48,121,125, 63,121,125, 63, + 120,122, 70,117,110,109,114,104,197,113,100,242,109, 92,251, + 94, 70,242, 94, 70,242,115,107,129,122,130, 53,123,141, 34, + 124,154, 18,124,167, 6,122,175, 0,122,178, 0,121,179, 0, + 122,178, 0,123,173, 1,124,167, 6,124,154, 18,122,130, 53, + 123,137, 41,122,135, 44,121,127, 58,123,137, 41,124,161, 11, + 123,174, 1,121,180, 0,121,182, 0,122,178, 0,123,169, 4, + 124,158, 13,124,160, 12,124,160, 12,124,157, 15,124,154, 18, + 124,152, 20,124,152, 20,124,154, 18,124,154, 18,124,149, 24, + 123,137, 41,118,117, 85,122,132, 48,124,143, 31,124,150, 22, + 124,154, 18,124,165, 8,124,167, 6,124,166, 7,124,165, 8, + 124,158, 13,124,147, 26,122,135, 44,121,125, 63,124,147, 26, + 124,155, 16,124,160, 12,124,162, 10,124,161, 11,124,157, 15, + 124,145, 29,123,141, 34,123,137, 41,122,132, 48,124,143, 31, + 124,145, 29,124,152, 20,124,162, 10,123,170, 3,122,177, 0, + 121,180, 0,120,184, 0,116,192, 0,114,196, 0,114,197, 0, + 115,195, 0,112,199, 0,110,203, 0,108,205, 0,108,205, 0, + 109,204, 0,108,206, 0,105,209, 0,103,212, 0,102,212, 0, + 102,212, 0,104,210, 0,103,211, 0,103,212, 0,102,212, 0, + 99,215, 0, 94,220, 0, 91,223, 0, 90,225, 0, 91,223, 0, + 93,222, 0, 95,220, 0, 97,218, 0, 98,217, 0,100,215, 0, + 102,212, 0,106,208, 0,109,203, 0,110,202, 0,111,201, 0, + 113,198, 0,107,206, 0,102,213, 0, 98,217, 0, 95,220, 0, + 94,220, 0, 95,220, 0, 96,219, 0, 97,217, 0, 98,216, 0, + 99,215, 0,100,215, 0,100,215, 0, 99,215, 0, 99,216, 0, + 98,217, 0, 97,218, 0, 96,219, 0, 95,219, 0, 95,220, 0, + 95,220, 0, 95,219, 0, 96,219, 0, 97,218, 0, 94,220, 0, + 91,223, 0, 89,226, 0, 87,227, 0, 86,228, 0, 87,227, 0, + 87,227, 0, 84,229, 0, 79,234, 0, 74,237, 0, 72,239, 0, + 72,239, 0, 74,237, 0, 78,234, 0, 84,229, 0, 91,224, 0, + 97,218, 0, 98,216, 0,102,213, 0,106,208, 0,111,201, 0, + 117,191, 0,119,186, 0,118,189, 0,116,193, 0,113,198, 0, + 111,201, 0,110,203, 0,109,203, 0,110,202, 0,113,198, 0, + 113,198, 0,114,197, 0,116,193, 0,119,185, 0,123,171, 3, + 123,141, 34, 99, 75,247, 30, 26,168,109, 92,251,118,117, 85, + 122,132, 48,124,150, 22,124,160, 12,124,166, 7,122,176, 0, + 121,182, 0,120,184, 0,116,193, 0,113,199, 0,111,202, 0, + 111,202, 0,111,202, 0,108,205, 0,106,207, 0,103,212, 0, + 98,217, 0, 94,221, 0, 91,223, 0, 89,225, 0, 88,226, 0, + 85,229, 0, 84,229, 0, 85,228, 0, 87,227, 0, 90,225, 0, + 93,222, 0, 96,219, 0,100,215, 0,104,210, 0,109,204, 0, + 112,199, 0,113,198, 0,114,196, 0,116,192, 0,118,189, 0, + 120,184, 0,122,177, 0,124,166, 7,123,141, 34,124,147, 26, + 124,164, 9,123,173, 1,122,178, 0,119,186, 0,115,194, 0, + 113,198, 0,112,199, 0,112,200, 0,111,201, 0,110,202, 0, + 109,204, 0,107,207, 0,105,209, 0,105,209, 0,105,209, 0, + 107,206, 0,110,203, 0,112,200, 0,112,199, 0,112,200, 0, + 112,200, 0,110,202, 0,109,204, 0,110,202, 0,112,200, 0, + 112,200, 0,113,198, 0,115,195, 0,118,189, 0,121,180, 0, + 124,166, 7,123,173, 1,120,182, 0,119,186, 0,119,187, 0, + 120,184, 0,122,176, 0,124,165, 8,124,155, 16,124,147, 26, + 124,155, 16,124,158, 13,124,155, 16,124,145, 29,123,141, 34, + 123,139, 37,123,137, 41,122,130, 53,118,117, 85,118,117, 85, + 117,110,109,111, 96,248, 94, 70,242,109, 92,251,118,117, 85, + 123,141, 34,124,154, 18,124,160, 12,124,161, 11,123,168, 5, + 123,173, 1,122,175, 0,123,174, 1,123,172, 2,124,165, 8, + 124,147, 26,118,117, 85,118,114, 96,119,119, 77,119,119, 77, + 124,150, 22,124,167, 6,122,175, 0,122,177, 0,123,173, 1, + 124,164, 9,124,158, 13,124,158, 13,124,155, 16,124,150, 22, + 124,147, 26,124,150, 22,124,145, 29,122,135, 44,122,135, 44, + 122,130, 53,115,107,129,118,114, 96,122,135, 44,124,147, 26, + 124,154, 18,124,164, 9,123,172, 2,123,174, 1,123,171, 3, + 123,170, 3,124,167, 6,124,158, 13,123,141, 34,121,125, 63, + 122,135, 44,124,150, 22,124,158, 13,124,162, 10,124,162, 10, + 124,158, 13,124,145, 29,120,122, 70,114,104,197,117,110,109, + 121,125, 63,121,127, 58,121,127, 58,124,143, 31,124,158, 13, + 124,167, 6,123,173, 1,122,177, 0,119,186, 0,117,191, 0, + 117,192, 0,117,191, 0,115,195, 0,113,199, 0,112,200, 0, + 112,200, 0,110,203, 0,107,206, 0,105,209, 0,103,211, 0, + 102,213, 0,102,213, 0,103,211, 0,105,209, 0,105,209, 0, + 103,211, 0, 97,217, 0, 92,222, 0, 89,225, 0, 89,226, 0, + 90,225, 0, 92,222, 0, 93,221, 0, 96,219, 0, 99,216, 0, + 101,214, 0,103,211, 0,107,207, 0,110,202, 0,113,198, 0, + 114,197, 0,113,199, 0,107,207, 0,102,213, 0, 98,217, 0, + 97,218, 0, 96,219, 0, 97,218, 0, 98,216, 0,100,215, 0, + 101,213, 0,102,212, 0,103,212, 0,102,212, 0,102,212, 0, + 101,214, 0,100,215, 0, 99,216, 0, 97,217, 0, 97,218, 0, + 96,219, 0, 95,219, 0, 95,220, 0, 95,219, 0, 95,220, 0, + 94,220, 0, 92,222, 0, 90,224, 0, 89,225, 0, 89,225, 0, + 90,224, 0, 92,222, 0, 89,225, 0, 85,229, 0, 80,232, 0, + 78,235, 0, 77,235, 0, 79,233, 0, 83,230, 0, 88,226, 0, + 94,221, 0, 96,219, 0, 97,217, 0,100,215, 0,105,209, 0, + 110,202, 0,116,192, 0,119,186, 0,118,189, 0,115,194, 0, + 114,197, 0,111,201, 0,109,204, 0,108,206, 0,108,205, 0, + 111,202, 0,112,200, 0,113,198, 0,115,194, 0,119,187, 0, + 123,173, 1,123,141, 34, 87, 65,237, 30, 43,203,115,107,129, + 122,132, 48,123,141, 34,124,154, 18,124,164, 9,123,169, 4, + 123,172, 2,122,178, 0,118,188, 0,114,197, 0,110,202, 0, + 108,205, 0,108,205, 0,110,203, 0,109,204, 0,107,206, 0, + 103,212, 0, 98,216, 0, 94,220, 0, 91,223, 0, 89,225, 0, + 86,228, 0, 83,230, 0, 84,229, 0, 83,230, 0, 83,230, 0, + 87,227, 0, 92,222, 0, 99,215, 0,103,211, 0,107,207, 0, + 111,202, 0,112,200, 0,112,199, 0,113,198, 0,114,196, 0, + 116,192, 0,118,189, 0,120,184, 0,122,177, 0,124,162, 10, + 124,162, 10,123,169, 4,122,177, 0,121,181, 0,121,182, 0, + 118,188, 0,116,192, 0,116,193, 0,113,198, 0,110,202, 0, + 109,204, 0,110,203, 0,111,201, 0,109,203, 0,108,205, 0, + 109,204, 0,110,202, 0,112,200, 0,114,197, 0,115,194, 0, + 116,193, 0,115,194, 0,113,198, 0,112,200, 0,113,198, 0, + 115,194, 0,115,194, 0,116,193, 0,118,189, 0,120,183, 0, + 123,171, 3,124,161, 11,123,174, 1,120,182, 0,119,186, 0, + 119,186, 0,121,181, 0,123,171, 3,124,157, 15,123,139, 37, + 124,155, 16,124,165, 8,124,167, 6,124,164, 9,124,155, 16, + 124,152, 20,124,149, 24,124,147, 26,124,143, 31,123,137, 41, + 121,127, 58,120,122, 70,118,114, 96,111, 96,248,113,100,242, + 121,127, 58,124,147, 26,124,158, 13,124,165, 8,124,167, 6, + 124,164, 9,124,164, 9,123,170, 3,123,173, 1,123,174, 1, + 123,170, 3,124,160, 12,123,141, 34,118,114, 96,118,114, 96, + 114,104,197,123,139, 37,124,160, 12,123,168, 5,123,169, 4, + 124,165, 8,124,157, 15,124,154, 18,124,152, 20,124,149, 24, + 124,154, 18,124,162, 10,124,165, 8,124,160, 12,124,145, 29, + 117,110,109,113,100,242,115,107,129,121,127, 58,123,137, 41, + 124,147, 26,124,157, 15,123,168, 5,122,176, 0,122,178, 0, + 123,174, 1,122,177, 0,122,176, 0,123,170, 3,124,160, 12, + 123,139, 37,118,117, 85,123,137, 41,124,150, 22,124,158, 13, + 124,161, 11,124,157, 15,124,143, 31,118,117, 85, 87, 65,237, + 94, 70,242,109, 92,251,111, 96,248,111, 96,248,121,125, 63, + 124,145, 29,124,155, 16,124,162, 10,124,167, 6,121,180, 0, + 119,186, 0,118,188, 0,118,188, 0,118,189, 0,116,192, 0, + 114,196, 0,112,200, 0,111,202, 0,109,204, 0,107,207, 0, + 105,209, 0,103,211, 0,103,211, 0,105,209, 0,108,206, 0, + 107,207, 0,101,214, 0, 95,220, 0, 90,224, 0, 87,227, 0, + 87,227, 0, 89,225, 0, 91,223, 0, 91,223, 0, 94,221, 0, + 98,216, 0,103,212, 0,106,208, 0,109,204, 0,113,199, 0, + 117,191, 0,116,192, 0,111,201, 0,106,207, 0,102,212, 0, + 100,215, 0, 99,215, 0,100,215, 0,101,214, 0,102,212, 0, + 104,210, 0,104,210, 0,104,210, 0,106,208, 0,107,207, 0, + 106,208, 0,105,209, 0,103,211, 0,102,212, 0,100,214, 0, + 99,215, 0, 98,217, 0, 97,217, 0, 97,218, 0, 96,219, 0, + 95,220, 0, 94,221, 0, 92,222, 0, 92,222, 0, 93,222, 0, + 94,221, 0, 95,220, 0, 97,218, 0, 95,220, 0, 90,224, 0, + 86,228, 0, 83,230, 0, 83,230, 0, 85,229, 0, 89,226, 0, + 93,222, 0, 96,219, 0, 95,219, 0, 96,219, 0, 98,216, 0, + 103,211, 0,109,203, 0,116,193, 0,119,185, 0,116,192, 0, + 114,197, 0,112,200, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,109,204, 0,111,202, 0,111,201, 0,114,197, 0, + 118,189, 0,122,177, 0,124,149, 24, 99, 75,247, 30, 37,192, + 115,107,129,122,132, 48,124,145, 29,124,155, 16,124,166, 7, + 123,171, 3,123,173, 1,122,177, 0,117,190, 0,112,199, 0, + 108,206, 0,105,209, 0,105,209, 0,107,207, 0,110,202, 0, + 109,203, 0,105,209, 0,100,215, 0, 96,219, 0, 93,222, 0, + 88,226, 0, 84,229, 0, 82,231, 0, 83,230, 0, 81,232, 0, + 81,232, 0, 83,230, 0, 89,226, 0, 96,219, 0,104,210, 0, + 110,203, 0,113,198, 0,113,198, 0,113,199, 0,113,198, 0, + 114,196, 0,115,194, 0,117,191, 0,119,187, 0,120,182, 0, + 123,172, 2,123,172, 2,123,173, 1,121,179, 0,120,182, 0, + 120,182, 0,121,180, 0,120,184, 0,117,190, 0,113,199, 0, + 109,203, 0,108,205, 0,109,204, 0,111,201, 0,114,196, 0, + 113,199, 0,112,200, 0,111,201, 0,112,200, 0,113,199, 0, + 114,196, 0,116,193, 0,118,189, 0,117,192, 0,115,194, 0, + 116,193, 0,118,189, 0,118,188, 0,119,186, 0,120,183, 0, + 122,175, 0,124,161, 11,124,161, 11,123,174, 1,121,181, 0, + 120,184, 0,120,182, 0,122,175, 0,124,162, 10,123,141, 34, + 124,145, 29,124,164, 9,123,170, 3,123,172, 2,123,168, 5, + 124,158, 13,124,154, 18,124,149, 24,124,149, 24,124,149, 24, + 124,147, 26,123,141, 34,122,132, 48,120,122, 70,115,107,129, + 113,100,242,122,132, 48,124,157, 15,123,170, 3,122,177, 0, + 122,178, 0,122,176, 0,123,169, 4,123,168, 5,123,174, 1, + 122,178, 0,121,179, 0,122,175, 0,124,165, 8,123,141, 34, + 115,107,129,107, 89,252,122,132, 48,124,154, 18,124,162, 10, + 124,164, 9,124,160, 12,124,157, 15,124,150, 22,124,147, 26, + 124,160, 12,123,168, 5,123,174, 1,123,174, 1,123,170, 3, + 124,158, 13,122,130, 53, 99, 75,247,118,114, 96,123,137, 41, + 124,149, 24,124,155, 16,124,160, 12,124,166, 7,123,174, 1, + 122,176, 0,121,181, 0,120,184, 0,120,184, 0,121,180, 0, + 123,173, 1,124,161, 11,123,139, 37,117,110,109,122,132, 48, + 124,147, 26,124,154, 18,124,152, 20,124,143, 31,119,119, 77, + 94, 70,242, 30, 2,128, 30, 55,222,105, 84,252,115,107,129, + 122,135, 44,124,152, 20,124,160, 12,124,161, 11,124,154, 18, + 123,173, 1,121,182, 0,119,185, 0,119,186, 0,119,185, 0, + 118,189, 0,115,194, 0,113,198, 0,110,202, 0,108,206, 0, + 107,207, 0,106,207, 0,106,208, 0,106,208, 0,107,207, 0, + 110,202, 0,105,209, 0, 98,216, 0, 92,222, 0, 88,226, 0, + 86,228, 0, 86,228, 0, 89,226, 0, 92,222, 0, 92,222, 0, + 94,220, 0, 98,216, 0,103,211, 0,108,206, 0,111,201, 0, + 115,194, 0,119,186, 0,115,194, 0,111,202, 0,106,207, 0, + 103,211, 0,102,212, 0,102,212, 0,104,210, 0,105,209, 0, + 104,210, 0,103,212, 0,103,212, 0,103,211, 0,105,209, 0, + 107,206, 0,109,204, 0,110,203, 0,107,207, 0,105,209, 0, + 103,211, 0,102,212, 0,102,212, 0,101,214, 0,100,215, 0, + 99,215, 0, 97,217, 0, 95,219, 0, 94,221, 0, 93,222, 0, + 93,221, 0, 95,220, 0, 98,217, 0,102,212, 0, 99,216, 0, + 94,220, 0, 91,224, 0, 88,226, 0, 88,226, 0, 90,224, 0, + 94,221, 0, 97,217, 0, 96,219, 0, 95,220, 0, 95,220, 0, + 98,217, 0,102,212, 0,108,205, 0,115,195, 0,120,184, 0, + 116,192, 0,113,198, 0,111,202, 0,109,204, 0,108,205, 0, + 107,207, 0,106,207, 0,109,204, 0,108,205, 0,109,204, 0, + 111,201, 0,115,194, 0,120,183, 0,124,161, 11,115,107,129, + 30, 2,128,105, 84,252,119,119, 77,123,137, 41,124,157, 15, + 124,167, 6,123,172, 2,123,174, 1,122,178, 0,117,191, 0, + 111,201, 0,106,207, 0,103,211, 0,103,212, 0,104,210, 0, + 107,206, 0,111,201, 0,106,207, 0,102,213, 0, 98,217, 0, + 92,223, 0, 87,227, 0, 83,230, 0, 82,231, 0, 83,230, 0, + 81,232, 0, 80,232, 0, 82,231, 0, 87,227, 0, 93,221, 0, + 101,213, 0,109,203, 0,116,193, 0,115,195, 0,114,196, 0, + 114,197, 0,113,198, 0,113,198, 0,114,196, 0,117,191, 0, + 120,182, 0,123,174, 1,123,173, 1,122,178, 0,120,182, 0, + 120,184, 0,120,183, 0,121,181, 0,122,175, 0,118,189, 0, + 113,198, 0,109,203, 0,108,206, 0,108,205, 0,111,202, 0, + 114,197, 0,115,195, 0,113,198, 0,112,200, 0,112,199, 0, + 113,198, 0,113,198, 0,114,196, 0,117,192, 0,120,184, 0, + 119,187, 0,119,186, 0,120,184, 0,120,184, 0,121,182, 0, + 122,176, 0,124,167, 6,124,149, 24,124,158, 13,123,170, 3, + 122,178, 0,121,179, 0,122,176, 0,124,167, 6,124,149, 24, + 118,117, 85,124,152, 20,124,167, 6,123,173, 1,123,174, 1, + 123,168, 5,124,157, 15,124,145, 29,123,141, 34,124,147, 26, + 124,150, 22,124,150, 22,124,152, 20,124,150, 22,124,143, 31, + 121,127, 58,109, 92,251,122,130, 53,124,158, 13,123,174, 1, + 120,182, 0,119,185, 0,120,184, 0,121,181, 0,123,173, 1, + 121,180, 0,120,184, 0,119,186, 0,120,183, 0,122,175, 0, + 124,160, 12,122,130, 53,119,119, 77,123,137, 41,124,150, 22, + 124,160, 12,124,162, 10,124,161, 11,124,157, 15,124,154, 18, + 124,164, 9,123,170, 3,122,177, 0,121,180, 0,121,180, 0, + 122,176, 0,124,166, 7,124,143, 31,120,122, 70,120,122, 70, + 124,145, 29,124,158, 13,124,165, 8,123,168, 5,123,170, 3, + 122,176, 0,121,182, 0,119,186, 0,118,189, 0,118,189, 0, + 118,188, 0,120,184, 0,122,176, 0,124,165, 8,124,150, 22, + 122,130, 53,119,119, 77,122,135, 44,123,139, 37,122,135, 44, + 121,125, 63,115,107,129,102, 80,250, 69, 60,230,111, 96,248, + 121,127, 58,124,147, 26,124,158, 13,124,165, 8,124,166, 7, + 124,161, 11,123,169, 4,122,176, 0,121,182, 0,119,186, 0, + 118,189, 0,116,192, 0,114,196, 0,111,201, 0,108,205, 0, + 106,208, 0,105,209, 0,105,209, 0,105,209, 0,106,208, 0, + 107,207, 0,106,207, 0,101,213, 0, 95,219, 0, 90,224, 0, + 87,227, 0, 85,229, 0, 86,228, 0, 89,226, 0, 92,222, 0, + 95,219, 0, 97,217, 0,101,214, 0,105,209, 0,110,202, 0, + 114,196, 0,117,190, 0,118,189, 0,114,196, 0,110,202, 0, + 107,207, 0,105,209, 0,105,209, 0,106,208, 0,107,206, 0, + 105,209, 0,104,210, 0,103,211, 0,103,211, 0,104,210, 0, + 106,208, 0,108,206, 0,108,206, 0,106,208, 0,105,209, 0, + 104,210, 0,103,211, 0,102,212, 0,102,212, 0,102,213, 0, + 100,215, 0, 98,217, 0, 97,218, 0, 97,217, 0, 95,220, 0, + 94,221, 0, 94,221, 0, 95,219, 0, 98,216, 0,100,215, 0, + 100,215, 0, 98,216, 0, 95,220, 0, 93,221, 0, 93,221, 0, + 95,220, 0, 98,217, 0,100,215, 0, 98,217, 0, 96,219, 0, + 96,219, 0, 98,217, 0,102,212, 0,108,205, 0,114,196, 0, + 119,186, 0,117,191, 0,113,198, 0,110,202, 0,108,205, 0, + 107,207, 0,106,208, 0,106,208, 0,106,208, 0,105,209, 0, + 106,208, 0,108,205, 0,113,199, 0,118,189, 0,123,172, 2, + 123,137, 41, 69, 60,230, 94, 70,242,118,114, 96,123,141, 34, + 124,158, 13,124,167, 6,123,172, 2,122,175, 0,121,180, 0, + 117,191, 0,111,201, 0,106,208, 0,102,212, 0,101,214, 0, + 102,212, 0,105,209, 0,109,204, 0,106,208, 0,101,213, 0, + 96,219, 0, 90,224, 0, 86,228, 0, 83,230, 0, 82,231, 0, + 83,230, 0, 82,231, 0, 81,232, 0, 83,230, 0, 87,227, 0, + 93,222, 0,100,214, 0,108,206, 0,114,197, 0,114,197, 0, + 113,199, 0,112,200, 0,111,202, 0,111,201, 0,112,199, 0, + 115,195, 0,118,188, 0,122,178, 0,122,176, 0,121,181, 0, + 120,184, 0,120,184, 0,120,183, 0,121,180, 0,120,182, 0, + 118,188, 0,114,196, 0,111,202, 0,109,204, 0,109,203, 0, + 110,203, 0,111,201, 0,112,200, 0,112,200, 0,112,200, 0, + 111,201, 0,111,201, 0,111,201, 0,113,199, 0,115,194, 0, + 119,187, 0,121,180, 0,121,180, 0,121,181, 0,121,179, 0, + 122,177, 0,123,171, 3,124,160, 12,123,139, 37,124,147, 26, + 124,161, 11,123,168, 5,123,169, 4,124,165, 8,124,150, 22, + 119,119, 77,122,132, 48,124,160, 12,123,172, 2,122,177, 0, + 122,175, 0,123,169, 4,124,157, 15,122,135, 44,124,149, 24, + 124,155, 16,124,157, 15,124,154, 18,124,161, 11,124,164, 9, + 124,162, 10,124,154, 18,122,135, 44,122,132, 48,124,157, 15, + 122,175, 0,120,184, 0,118,189, 0,117,190, 0,118,188, 0, + 120,182, 0,120,183, 0,118,188, 0,118,189, 0,119,187, 0, + 121,181, 0,123,169, 4,124,147, 26,124,145, 29,124,145, 29, + 124,154, 18,124,160, 12,124,161, 11,124,158, 13,124,157, 15, + 124,167, 6,123,172, 2,122,176, 0,121,180, 0,120,182, 0, + 120,182, 0,122,178, 0,123,169, 4,124,160, 12,124,155, 16, + 124,154, 18,124,155, 16,124,165, 8,123,172, 2,122,175, 0, + 122,176, 0,122,178, 0,120,183, 0,118,188, 0,117,191, 0, + 116,193, 0,116,192, 0,117,191, 0,119,186, 0,121,181, 0, + 123,174, 1,124,166, 7,124,150, 22,122,132, 48,123,141, 34, + 124,145, 29,124,143, 31,123,137, 41,121,127, 58,118,117, 85, + 118,117, 85,123,141, 34,124,157, 15,124,167, 6,123,171, 3, + 123,172, 2,123,169, 4,123,169, 4,122,178, 0,120,184, 0, + 118,189, 0,116,192, 0,114,196, 0,113,199, 0,110,202, 0, + 108,206, 0,106,208, 0,105,209, 0,104,210, 0,104,210, 0, + 105,209, 0,106,208, 0,102,212, 0, 98,217, 0, 94,221, 0, + 90,225, 0, 87,227, 0, 87,227, 0, 88,226, 0, 90,224, 0, + 94,221, 0, 97,217, 0,101,213, 0,105,209, 0,108,205, 0, + 112,200, 0,115,194, 0,118,189, 0,116,192, 0,113,198, 0, + 110,202, 0,108,206, 0,107,207, 0,107,207, 0,108,205, 0, + 108,205, 0,106,208, 0,105,209, 0,105,209, 0,106,208, 0, + 107,207, 0,108,205, 0,109,203, 0,107,207, 0,105,209, 0, + 104,210, 0,103,211, 0,103,212, 0,103,212, 0,103,212, 0, + 102,212, 0,100,215, 0, 98,217, 0, 97,218, 0, 97,218, 0, + 95,220, 0, 93,221, 0, 93,221, 0, 95,220, 0, 98,217, 0, + 98,217, 0, 98,217, 0, 99,216, 0,101,214, 0, 99,215, 0, + 99,215, 0,101,214, 0,103,211, 0,105,209, 0,103,212, 0, + 101,214, 0,100,215, 0,101,214, 0,104,210, 0,109,204, 0, + 114,196, 0,118,188, 0,118,189, 0,114,197, 0,110,203, 0, + 107,207, 0,105,209, 0,103,211, 0,103,211, 0,103,211, 0, + 103,211, 0,104,210, 0,107,207, 0,111,201, 0,117,192, 0, + 122,178, 0,124,152, 20,114,104,197,109, 92,251,121,125, 63, + 124,145, 29,124,157, 15,124,164, 9,124,167, 6,123,174, 1, + 121,181, 0,116,192, 0,111,201, 0,106,208, 0,102,212, 0, + 100,214, 0,101,214, 0,103,211, 0,106,208, 0,103,212, 0, + 99,216, 0, 94,221, 0, 89,225, 0, 86,228, 0, 84,229, 0, + 84,229, 0, 85,228, 0, 83,230, 0, 83,230, 0, 84,229, 0, + 88,226, 0, 94,221, 0,101,214, 0,108,206, 0,112,199, 0, + 112,200, 0,111,202, 0,110,202, 0,110,203, 0,110,202, 0, + 112,200, 0,114,196, 0,117,190, 0,121,182, 0,122,176, 0, + 121,180, 0,120,182, 0,120,182, 0,120,184, 0,119,186, 0, + 118,189, 0,115,194, 0,113,199, 0,110,202, 0,108,205, 0, + 107,206, 0,107,206, 0,108,205, 0,109,204, 0,109,204, 0, + 109,204, 0,109,204, 0,109,203, 0,110,202, 0,112,199, 0, + 115,195, 0,118,188, 0,121,181, 0,122,175, 0,122,175, 0, + 123,174, 1,123,171, 3,124,166, 7,124,154, 18,122,132, 48, + 121,127, 58,124,143, 31,124,150, 22,124,149, 24,123,137, 41, + 121,125, 63,117,110,109,124,150, 22,123,169, 4,122,178, 0, + 121,181, 0,121,179, 0,123,173, 1,124,161, 11,124,145, 29, + 124,147, 26,124,154, 18,124,154, 18,124,161, 11,123,169, 4, + 123,174, 1,123,174, 1,123,170, 3,124,160, 12,124,155, 16, + 124,155, 16,122,176, 0,119,187, 0,116,193, 0,115,194, 0, + 116,193, 0,118,189, 0,120,184, 0,118,189, 0,117,191, 0, + 117,190, 0,119,186, 0,122,177, 0,124,164, 9,124,162, 10, + 124,154, 18,124,154, 18,124,157, 15,124,157, 15,124,162, 10, + 123,169, 4,123,174, 1,122,177, 0,122,178, 0,121,180, 0, + 121,181, 0,121,181, 0,122,177, 0,122,177, 0,122,175, 0, + 123,173, 1,123,171, 3,123,171, 3,123,172, 2,122,178, 0, + 121,180, 0,121,181, 0,121,181, 0,120,182, 0,119,187, 0, + 117,191, 0,116,193, 0,115,194, 0,115,194, 0,116,193, 0, + 117,190, 0,119,187, 0,120,182, 0,122,176, 0,124,166, 7, + 124,154, 18,124,149, 24,124,152, 20,124,152, 20,124,149, 24, + 124,145, 29,124,145, 29,124,152, 20,124,165, 8,123,173, 1, + 122,177, 0,122,178, 0,122,177, 0,123,174, 1,122,178, 0, + 119,185, 0,117,191, 0,115,194, 0,113,198, 0,112,200, 0, + 110,202, 0,109,204, 0,107,207, 0,106,208, 0,105,209, 0, + 105,209, 0,105,209, 0,102,212, 0,100,215, 0, 96,219, 0, + 94,221, 0, 91,223, 0, 90,224, 0, 90,224, 0, 92,223, 0, + 94,221, 0, 97,218, 0,100,214, 0,105,209, 0,110,203, 0, + 113,199, 0,115,194, 0,117,190, 0,118,189, 0,115,194, 0, + 113,199, 0,111,202, 0,110,203, 0,110,203, 0,110,202, 0, + 111,201, 0,109,204, 0,108,205, 0,108,205, 0,108,205, 0, + 109,204, 0,110,202, 0,111,201, 0,111,201, 0,108,205, 0, + 106,207, 0,105,209, 0,105,209, 0,104,210, 0,105,209, 0, + 106,208, 0,104,210, 0,101,213, 0, 99,216, 0, 97,217, 0, + 96,219, 0, 94,221, 0, 92,222, 0, 92,222, 0, 94,221, 0, + 97,218, 0, 96,219, 0, 96,219, 0, 98,217, 0,101,214, 0, + 105,209, 0,106,208, 0,107,207, 0,108,205, 0,110,203, 0, + 109,204, 0,107,207, 0,106,208, 0,106,208, 0,107,206, 0, + 111,202, 0,115,195, 0,119,187, 0,118,188, 0,114,197, 0, + 110,203, 0,106,208, 0,103,211, 0,102,212, 0,102,213, 0, + 102,212, 0,103,212, 0,105,209, 0,108,206, 0,112,200, 0, + 117,191, 0,122,178, 0,124,158, 13,121,125, 63,114,104,197, + 122,130, 53,124,145, 29,124,152, 20,124,157, 15,124,164, 9, + 123,171, 3,120,184, 0,116,193, 0,111,201, 0,106,207, 0, + 103,212, 0,101,214, 0,101,214, 0,102,212, 0,104,210, 0, + 101,214, 0, 97,218, 0, 93,222, 0, 89,225, 0, 87,227, 0, + 87,227, 0, 87,227, 0, 87,227, 0, 85,228, 0, 85,228, 0, + 87,227, 0, 92,223, 0, 97,218, 0,103,211, 0,109,203, 0, + 111,201, 0,111,202, 0,110,203, 0,109,203, 0,109,203, 0, + 110,202, 0,112,200, 0,114,196, 0,117,191, 0,120,183, 0, + 123,174, 1,122,178, 0,120,183, 0,119,186, 0,118,189, 0, + 116,192, 0,115,195, 0,113,198, 0,111,202, 0,109,204, 0, + 107,206, 0,107,207, 0,106,207, 0,106,207, 0,106,207, 0, + 106,207, 0,107,207, 0,107,206, 0,109,204, 0,110,202, 0, + 113,198, 0,115,194, 0,118,188, 0,121,182, 0,122,178, 0, + 122,175, 0,123,173, 1,123,169, 4,124,162, 10,124,150, 22, + 122,130, 53,111, 96,248,117,110,109,120,122, 70,121,125, 63, + 119,119, 77,115,107,129,122,135, 44,124,164, 9,122,177, 0, + 120,184, 0,119,186, 0,120,184, 0,122,178, 0,123,168, 5, + 124,154, 18,124,150, 22,124,145, 29,124,155, 16,123,168, 5, + 122,176, 0,121,181, 0,120,182, 0,121,179, 0,123,170, 3, + 123,168, 5,124,165, 8,122,178, 0,117,190, 0,114,196, 0, + 113,198, 0,113,198, 0,115,194, 0,118,188, 0,118,189, 0, + 117,192, 0,117,192, 0,118,189, 0,120,182, 0,123,173, 1, + 123,172, 2,123,168, 5,124,167, 6,123,168, 5,123,171, 3, + 123,174, 1,122,177, 0,122,178, 0,122,177, 0,122,175, 0, + 122,176, 0,122,177, 0,122,178, 0,120,182, 0,120,184, 0, + 120,184, 0,120,183, 0,121,181, 0,121,180, 0,121,181, 0, + 120,182, 0,120,184, 0,119,186, 0,119,187, 0,119,185, 0, + 119,185, 0,118,189, 0,116,192, 0,115,194, 0,114,196, 0, + 114,196, 0,115,194, 0,116,193, 0,117,191, 0,118,188, 0, + 120,183, 0,122,176, 0,124,167, 6,124,157, 15,124,158, 13, + 124,160, 12,124,160, 12,124,161, 11,124,162, 10,123,168, 5, + 122,176, 0,121,180, 0,121,182, 0,121,182, 0,121,180, 0, + 122,178, 0,119,186, 0,117,192, 0,114,196, 0,113,198, 0, + 112,199, 0,112,200, 0,111,202, 0,110,202, 0,109,204, 0, + 108,205, 0,107,206, 0,104,210, 0,101,214, 0, 98,216, 0, + 97,218, 0, 95,219, 0, 95,220, 0, 95,220, 0, 96,219, 0, + 97,217, 0,100,215, 0,101,213, 0,105,209, 0,109,204, 0, + 114,197, 0,117,190, 0,119,187, 0,120,183, 0,117,190, 0, + 115,195, 0,114,197, 0,113,198, 0,113,198, 0,113,198, 0, + 114,197, 0,112,199, 0,111,201, 0,111,201, 0,112,200, 0, + 112,199, 0,113,198, 0,114,196, 0,115,195, 0,114,197, 0, + 111,201, 0,109,204, 0,107,207, 0,106,208, 0,105,209, 0, + 106,208, 0,106,208, 0,104,210, 0,102,212, 0,100,214, 0, + 98,217, 0, 95,220, 0, 92,222, 0, 91,223, 0, 91,223, 0, + 92,222, 0, 95,219, 0, 96,219, 0, 96,219, 0, 97,217, 0, + 100,214, 0,104,210, 0,109,203, 0,113,198, 0,114,197, 0, + 114,196, 0,115,194, 0,114,197, 0,112,200, 0,111,201, 0, + 112,200, 0,114,197, 0,117,192, 0,119,186, 0,119,186, 0, + 114,196, 0,110,203, 0,106,208, 0,103,211, 0,102,212, 0, + 102,212, 0,103,211, 0,105,209, 0,107,206, 0,111,202, 0, + 115,195, 0,119,186, 0,123,174, 1,124,157, 15,122,132, 48, + 117,110,109,122,130, 53,123,141, 34,124,145, 29,124,150, 22, + 124,160, 12,122,175, 0,119,186, 0,116,193, 0,112,200, 0, + 108,205, 0,105,209, 0,103,211, 0,103,212, 0,103,211, 0, + 104,210, 0,101,214, 0, 97,218, 0, 93,221, 0, 91,224, 0, + 89,225, 0, 90,225, 0, 91,223, 0, 89,225, 0, 88,226, 0, + 89,225, 0, 92,223, 0, 96,219, 0,101,213, 0,107,207, 0, + 112,200, 0,111,201, 0,110,202, 0,109,203, 0,109,203, 0, + 110,202, 0,111,201, 0,113,198, 0,115,194, 0,118,189, 0, + 120,184, 0,122,176, 0,121,179, 0,119,185, 0,118,189, 0, + 116,193, 0,114,196, 0,113,198, 0,112,200, 0,110,202, 0, + 109,203, 0,108,205, 0,107,206, 0,107,207, 0,106,208, 0, + 105,209, 0,105,209, 0,106,208, 0,107,207, 0,109,204, 0, + 112,200, 0,114,196, 0,117,191, 0,119,186, 0,121,182, 0, + 121,179, 0,122,178, 0,122,176, 0,123,172, 2,124,165, 8, + 124,152, 20,122,130, 53,109, 92,251, 94, 70,242,107, 89,252, + 113,100,242,113,100,242,118,114, 96,124,149, 24,123,170, 3, + 121,182, 0,119,187, 0,118,188, 0,119,186, 0,120,182, 0, + 123,174, 1,124,164, 9,124,154, 18,124,149, 24,124,160, 12, + 123,172, 2,121,181, 0,119,185, 0,119,186, 0,120,183, 0, + 122,175, 0,122,175, 0,123,170, 3,120,182, 0,116,193, 0, + 113,199, 0,111,201, 0,112,200, 0,114,197, 0,117,191, 0, + 118,189, 0,116,192, 0,116,192, 0,117,191, 0,119,186, 0, + 121,179, 0,122,177, 0,122,175, 0,122,176, 0,122,178, 0, + 121,179, 0,121,180, 0,121,181, 0,122,178, 0,123,174, 1, + 123,170, 3,123,168, 5,123,173, 1,121,180, 0,119,185, 0, + 118,188, 0,118,189, 0,118,188, 0,119,186, 0,119,185, 0, + 119,185, 0,119,187, 0,118,189, 0,117,191, 0,117,192, 0, + 117,191, 0,119,187, 0,119,186, 0,118,189, 0,116,192, 0, + 115,194, 0,114,196, 0,114,196, 0,114,196, 0,115,195, 0, + 115,194, 0,117,191, 0,119,187, 0,121,182, 0,123,173, 1, + 124,164, 9,124,164, 9,124,167, 6,123,169, 4,123,171, 3, + 123,170, 3,122,176, 0,121,180, 0,121,182, 0,120,183, 0, + 120,182, 0,121,181, 0,119,186, 0,117,192, 0,114,196, 0, + 114,197, 0,113,198, 0,114,197, 0,114,197, 0,114,197, 0, + 113,198, 0,112,200, 0,108,205, 0,104,210, 0,101,213, 0, + 100,215, 0,100,215, 0,100,215, 0,101,214, 0,102,212, 0, + 104,210, 0,105,209, 0,106,207, 0,108,206, 0,110,202, 0, + 114,197, 0,118,188, 0,122,178, 0,123,174, 1,121,182, 0, + 118,189, 0,116,192, 0,116,193, 0,117,192, 0,117,191, 0, + 117,192, 0,115,195, 0,114,197, 0,114,197, 0,115,195, 0, + 115,194, 0,115,194, 0,115,194, 0,116,193, 0,117,192, 0, + 116,193, 0,113,198, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,107,206, 0,105,209, 0,103,211, 0,101,213, 0, + 100,215, 0, 97,217, 0, 94,220, 0, 92,223, 0, 90,224, 0, + 90,224, 0, 92,223, 0, 95,220, 0, 97,218, 0, 97,218, 0, + 98,217, 0,101,214, 0,105,209, 0,109,203, 0,114,197, 0, + 118,189, 0,119,187, 0,119,186, 0,119,185, 0,118,189, 0, + 117,192, 0,116,192, 0,117,191, 0,119,187, 0,119,185, 0, + 119,186, 0,115,195, 0,110,202, 0,107,207, 0,105,209, 0, + 104,210, 0,105,209, 0,106,207, 0,109,204, 0,112,200, 0, + 115,194, 0,119,186, 0,122,177, 0,124,165, 8,124,149, 24, + 121,127, 58,118,117, 85,120,122, 70,122,130, 53,122,135, 44, + 124,145, 29,124,161, 11,122,178, 0,119,187, 0,116,192, 0, + 114,197, 0,111,201, 0,109,204, 0,107,206, 0,106,207, 0, + 107,207, 0,106,208, 0,102,212, 0, 98,217, 0, 95,220, 0, + 93,222, 0, 92,222, 0, 93,221, 0, 95,219, 0, 93,222, 0, + 92,222, 0, 94,221, 0, 97,218, 0,102,213, 0,107,207, 0, + 112,200, 0,113,198, 0,112,200, 0,110,202, 0,110,203, 0, + 110,202, 0,111,201, 0,113,199, 0,115,195, 0,115,194, 0, + 117,191, 0,119,185, 0,121,179, 0,122,177, 0,120,184, 0, + 117,190, 0,115,194, 0,114,197, 0,112,199, 0,112,200, 0, + 112,200, 0,111,201, 0,111,202, 0,110,202, 0,109,204, 0, + 107,206, 0,106,208, 0,106,208, 0,106,207, 0,108,205, 0, + 111,202, 0,114,197, 0,116,192, 0,118,189, 0,120,184, 0, + 121,181, 0,121,180, 0,121,180, 0,122,178, 0,123,173, 1, + 124,165, 8,124,150, 22,121,125, 63,105, 84,252, 30, 26,168, + 30, 55,222, 94, 70,242,102, 80,250,120,122, 70,124,154, 18, + 123,172, 2,120,182, 0,118,188, 0,118,189, 0,118,188, 0, + 120,184, 0,122,178, 0,123,171, 3,124,164, 9,124,160, 12, + 124,164, 9,122,176, 0,120,183, 0,119,186, 0,119,187, 0, + 120,184, 0,122,178, 0,122,176, 0,123,172, 2,119,187, 0, + 114,196, 0,111,201, 0,110,202, 0,111,201, 0,114,197, 0, + 117,192, 0,118,189, 0,116,192, 0,116,192, 0,117,191, 0, + 119,187, 0,121,182, 0,122,178, 0,122,178, 0,121,179, 0, + 121,182, 0,120,183, 0,120,183, 0,121,182, 0,122,178, 0, + 123,170, 3,124,162, 10,124,164, 9,123,172, 2,121,180, 0, + 119,186, 0,117,190, 0,117,192, 0,117,190, 0,118,188, 0, + 119,186, 0,118,189, 0,117,192, 0,117,192, 0,116,193, 0, + 115,194, 0,115,194, 0,117,191, 0,118,188, 0,118,188, 0, + 118,188, 0,117,191, 0,116,192, 0,115,194, 0,115,195, 0, + 114,196, 0,114,196, 0,114,196, 0,116,193, 0,118,189, 0, + 120,182, 0,123,173, 1,124,166, 7,123,169, 4,123,173, 1, + 122,175, 0,122,175, 0,123,172, 2,122,177, 0,122,178, 0, + 121,181, 0,121,182, 0,121,180, 0,119,187, 0,116,192, 0, + 115,195, 0,114,196, 0,115,194, 0,116,192, 0,116,192, 0, + 116,192, 0,117,191, 0,114,196, 0,110,202, 0,107,207, 0, + 104,210, 0,103,211, 0,104,210, 0,106,207, 0,108,205, 0, + 109,204, 0,110,203, 0,112,200, 0,114,197, 0,114,196, 0, + 116,192, 0,119,186, 0,122,175, 0,124,155, 16,124,167, 6, + 122,178, 0,120,184, 0,119,186, 0,119,185, 0,120,182, 0, + 118,188, 0,117,192, 0,116,193, 0,116,193, 0,117,191, 0, + 118,189, 0,117,192, 0,116,193, 0,116,193, 0,116,192, 0, + 117,191, 0,117,191, 0,115,195, 0,113,199, 0,111,202, 0, + 109,203, 0,109,203, 0,107,206, 0,105,209, 0,103,211, 0, + 102,213, 0,100,215, 0, 98,217, 0, 95,220, 0, 92,222, 0, + 91,224, 0, 91,224, 0, 92,222, 0, 95,220, 0, 99,216, 0, + 99,216, 0,100,215, 0,102,212, 0,106,208, 0,110,202, 0, + 114,196, 0,117,190, 0,119,185, 0,121,180, 0,123,172, 2, + 122,175, 0,121,180, 0,120,182, 0,120,182, 0,120,183, 0, + 120,183, 0,119,185, 0,115,194, 0,111,201, 0,108,205, 0, + 107,206, 0,108,206, 0,109,203, 0,111,201, 0,114,196, 0, + 117,190, 0,121,182, 0,123,171, 3,124,158, 13,124,145, 29, + 122,130, 53,121,125, 63,118,117, 85,114,104,197,117,110,109, + 121,125, 63,123,139, 37,124,162, 10,122,178, 0,119,186, 0, + 117,190, 0,116,193, 0,114,196, 0,113,198, 0,112,200, 0, + 111,201, 0,111,201, 0,110,203, 0,106,208, 0,102,213, 0, + 98,217, 0, 96,219, 0, 96,219, 0, 97,218, 0,100,215, 0, + 97,218, 0, 97,218, 0, 99,215, 0,103,211, 0,108,206, 0, + 112,200, 0,115,194, 0,114,196, 0,113,199, 0,111,201, 0, + 111,202, 0,111,201, 0,113,199, 0,114,196, 0,114,196, 0, + 115,194, 0,117,191, 0,119,186, 0,121,179, 0,123,173, 1, + 121,180, 0,119,187, 0,117,192, 0,114,196, 0,114,197, 0, + 114,197, 0,114,197, 0,114,196, 0,113,199, 0,112,199, 0, + 112,200, 0,110,202, 0,108,205, 0,108,206, 0,109,204, 0, + 110,202, 0,113,198, 0,116,193, 0,117,192, 0,118,189, 0, + 120,184, 0,121,179, 0,121,180, 0,121,181, 0,122,178, 0, + 123,174, 1,124,164, 9,124,147, 26,118,117, 85, 94, 70,242, + 30, 5,128, 30, 43,203,102, 80,250,111, 96,248,121,127, 58, + 124,150, 22,123,170, 3,121,180, 0,119,186, 0,118,188, 0, + 119,187, 0,120,184, 0,121,181, 0,122,175, 0,123,169, 4, + 124,167, 6,124,167, 6,122,176, 0,120,183, 0,119,186, 0, + 119,185, 0,121,181, 0,122,176, 0,122,175, 0,121,180, 0, + 117,191, 0,113,198, 0,111,202, 0,111,202, 0,112,200, 0, + 114,196, 0,117,190, 0,117,190, 0,117,192, 0,117,191, 0, + 118,189, 0,119,186, 0,121,181, 0,123,174, 1,122,176, 0, + 121,179, 0,120,182, 0,120,184, 0,120,184, 0,121,181, 0, + 122,175, 0,124,165, 8,124,165, 8,123,171, 3,122,175, 0, + 121,179, 0,119,186, 0,117,191, 0,116,192, 0,117,191, 0, + 118,188, 0,116,192, 0,115,195, 0,114,196, 0,114,196, 0, + 116,193, 0,115,195, 0,115,194, 0,116,193, 0,117,190, 0, + 118,189, 0,117,190, 0,118,189, 0,119,187, 0,118,189, 0, + 117,191, 0,116,193, 0,115,195, 0,114,196, 0,115,195, 0, + 117,192, 0,119,186, 0,122,178, 0,124,166, 7,123,170, 3, + 122,175, 0,122,177, 0,122,176, 0,123,171, 3,123,168, 5, + 123,171, 3,122,175, 0,122,177, 0,121,181, 0,118,188, 0, + 116,192, 0,115,194, 0,115,194, 0,117,191, 0,118,188, 0, + 117,190, 0,117,191, 0,116,192, 0,115,195, 0,114,197, 0, + 110,202, 0,108,205, 0,108,206, 0,110,202, 0,110,202, 0, + 109,204, 0,109,203, 0,111,201, 0,114,196, 0,118,188, 0, + 121,182, 0,122,178, 0,123,169, 4,124,152, 20,124,150, 22, + 124,160, 12,123,169, 4,122,175, 0,122,176, 0,123,172, 2, + 121,182, 0,118,188, 0,117,191, 0,117,191, 0,118,189, 0, + 119,185, 0,120,183, 0,119,187, 0,118,189, 0,117,190, 0, + 118,189, 0,118,189, 0,118,188, 0,118,189, 0,116,193, 0, + 114,196, 0,113,198, 0,111,201, 0,109,204, 0,107,207, 0, + 105,209, 0,103,211, 0,102,213, 0,100,215, 0, 97,218, 0, + 94,220, 0, 92,222, 0, 92,222, 0, 94,221, 0, 97,218, 0, + 101,214, 0,101,214, 0,102,212, 0,105,209, 0,108,205, 0, + 112,200, 0,115,194, 0,118,188, 0,120,184, 0,121,181, 0, + 123,174, 1,123,171, 3,123,174, 1,122,178, 0,121,179, 0, + 121,180, 0,121,179, 0,119,185, 0,116,193, 0,113,198, 0, + 111,201, 0,111,201, 0,113,199, 0,115,195, 0,117,191, 0, + 120,184, 0,121,180, 0,122,178, 0,123,174, 1,124,165, 8, + 124,145, 29,118,117, 85,118,117, 85,117,110,109,102, 80,250, + 107, 89,252,118,117, 85,122,135, 44,124,160, 12,122,176, 0, + 120,183, 0,119,186, 0,117,191, 0,115,194, 0,114,196, 0, + 114,196, 0,116,192, 0,117,192, 0,114,196, 0,110,202, 0, + 106,208, 0,102,212, 0,100,215, 0,100,215, 0,101,214, 0, + 103,211, 0,102,212, 0,103,212, 0,105,209, 0,109,204, 0, + 112,199, 0,114,196, 0,113,198, 0,112,199, 0,114,197, 0, + 112,200, 0,112,200, 0,112,199, 0,114,197, 0,116,193, 0, + 115,194, 0,116,193, 0,118,189, 0,120,183, 0,122,177, 0, + 123,171, 3,123,171, 3,121,181, 0,119,187, 0,117,191, 0, + 116,193, 0,116,192, 0,114,197, 0,111,202, 0,109,204, 0, + 109,204, 0,110,202, 0,113,199, 0,112,200, 0,111,201, 0, + 112,200, 0,113,198, 0,116,193, 0,117,191, 0,117,190, 0, + 119,187, 0,121,182, 0,122,178, 0,121,180, 0,121,180, 0, + 122,178, 0,123,172, 2,124,160, 12,123,139, 37,113,100,242, + 94, 70,242, 69, 60,230, 99, 75,247,117,110,109,121,125, 63, + 122,132, 48,124,150, 22,124,162, 10,122,175, 0,121,181, 0, + 120,184, 0,120,184, 0,120,182, 0,121,179, 0,122,175, 0, + 123,171, 3,123,168, 5,123,169, 4,123,174, 1,121,181, 0, + 120,183, 0,121,182, 0,122,176, 0,123,172, 2,123,174, 1, + 119,187, 0,115,195, 0,112,200, 0,111,202, 0,112,200, 0, + 114,197, 0,117,192, 0,118,188, 0,117,190, 0,117,191, 0, + 118,189, 0,119,186, 0,121,182, 0,122,177, 0,124,165, 8, + 123,169, 4,123,174, 1,121,179, 0,120,182, 0,120,182, 0, + 121,180, 0,123,173, 1,124,162, 10,123,170, 3,122,178, 0, + 120,182, 0,120,184, 0,119,186, 0,117,191, 0,116,192, 0, + 117,190, 0,115,194, 0,114,197, 0,112,199, 0,112,200, 0, + 113,199, 0,114,197, 0,115,194, 0,117,192, 0,117,191, 0, + 118,189, 0,119,187, 0,118,189, 0,117,191, 0,117,191, 0, + 118,189, 0,119,186, 0,118,189, 0,116,192, 0,115,194, 0, + 116,193, 0,117,191, 0,119,185, 0,122,176, 0,124,165, 8, + 123,170, 3,123,174, 1,122,175, 0,123,174, 1,123,169, 4, + 124,160, 12,124,157, 15,124,162, 10,123,173, 1,120,182, 0, + 118,189, 0,116,192, 0,116,193, 0,117,192, 0,118,188, 0, + 120,182, 0,119,186, 0,119,186, 0,116,192, 0,114,196, 0, + 114,197, 0,113,198, 0,112,199, 0,113,198, 0,112,200, 0, + 111,202, 0,110,202, 0,112,200, 0,114,196, 0,118,189, 0, + 122,177, 0,124,157, 15,124,150, 22,124,147, 26,124,150, 22, + 124,150, 22,124,149, 24,124,157, 15,124,161, 11,124,160, 12, + 123,170, 3,121,179, 0,119,185, 0,119,187, 0,119,187, 0, + 120,184, 0,122,178, 0,123,171, 3,122,178, 0,121,181, 0, + 120,182, 0,120,183, 0,121,182, 0,121,181, 0,121,181, 0, + 120,183, 0,118,188, 0,116,192, 0,114,197, 0,112,200, 0, + 110,203, 0,108,205, 0,107,207, 0,106,208, 0,104,210, 0, + 101,213, 0, 99,216, 0, 97,218, 0, 97,218, 0, 98,217, 0, + 100,214, 0,104,210, 0,104,210, 0,105,209, 0,108,206, 0, + 111,202, 0,114,196, 0,117,190, 0,119,185, 0,121,180, 0, + 122,177, 0,123,172, 2,123,173, 1,123,174, 1,122,176, 0, + 122,176, 0,122,176, 0,123,173, 1,120,184, 0,117,191, 0, + 115,194, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 122,177, 0,122,178, 0,121,179, 0,122,178, 0,122,178, 0, + 123,173, 1,124,164, 9,124,145, 29,118,114, 96,115,107,129, + 99, 75,247, 87, 65,237,117,110,109,123,137, 41,124,154, 18, + 123,169, 4,122,178, 0,119,186, 0,116,193, 0,114,197, 0, + 113,199, 0,114,197, 0,116,193, 0,118,188, 0,119,187, 0, + 115,194, 0,111,202, 0,107,207, 0,104,210, 0,103,211, 0, + 104,210, 0,106,208, 0,109,204, 0,109,204, 0,111,201, 0, + 113,198, 0,114,196, 0,114,196, 0,112,199, 0,112,200, 0, + 114,197, 0,113,199, 0,112,199, 0,113,198, 0,114,196, 0, + 117,192, 0,118,189, 0,118,188, 0,120,183, 0,121,182, 0, + 122,177, 0,123,170, 3,124,160, 12,123,168, 5,122,178, 0, + 120,184, 0,119,186, 0,115,194, 0,112,200, 0,109,204, 0, + 107,207, 0,107,207, 0,109,204, 0,112,200, 0,115,194, 0, + 114,196, 0,115,195, 0,116,192, 0,118,189, 0,119,186, 0, + 119,185, 0,121,182, 0,122,175, 0,122,178, 0,121,180, 0, + 121,180, 0,122,177, 0,123,169, 4,124,154, 18,122,130, 53, + 117,110,109,118,117, 85,118,114, 96,115,107,129,122,135, 44, + 124,147, 26,124,152, 20,124,154, 18,124,154, 18,124,165, 8, + 123,173, 1,122,177, 0,122,178, 0,122,178, 0,123,174, 1, + 123,170, 3,124,166, 7,124,165, 8,124,166, 7,123,169, 4, + 122,177, 0,121,179, 0,122,178, 0,123,171, 3,123,169, 4, + 120,182, 0,117,192, 0,113,198, 0,111,201, 0,111,201, 0, + 113,198, 0,116,193, 0,119,187, 0,118,189, 0,118,189, 0, + 118,189, 0,119,186, 0,121,181, 0,123,174, 1,123,168, 5, + 124,167, 6,123,169, 4,123,168, 5,122,176, 0,121,180, 0, + 121,181, 0,121,179, 0,123,174, 1,124,162, 10,123,171, 3, + 121,180, 0,119,185, 0,118,188, 0,117,190, 0,117,192, 0, + 116,193, 0,114,196, 0,113,198, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,112,199, 0,114,197, 0,115,195, 0, + 116,192, 0,118,188, 0,120,184, 0,120,184, 0,119,187, 0, + 118,189, 0,118,189, 0,119,187, 0,120,184, 0,118,188, 0, + 117,190, 0,118,189, 0,119,186, 0,121,181, 0,123,171, 3, + 124,161, 11,124,167, 6,123,170, 3,123,171, 3,123,169, 4, + 124,164, 9,124,154, 18,124,145, 29,124,161, 11,123,172, 2, + 121,182, 0,118,188, 0,117,191, 0,117,191, 0,118,188, 0, + 120,183, 0,122,176, 0,121,179, 0,120,184, 0,117,191, 0, + 115,194, 0,114,197, 0,114,197, 0,114,197, 0,114,197, 0, + 114,197, 0,113,198, 0,114,197, 0,116,193, 0,118,188, 0, + 122,177, 0,124,158, 13,122,130, 53,122,132, 48,123,141, 34, + 124,143, 31,124,143, 31,123,141, 34,123,141, 34,124,143, 31, + 124,150, 22,124,161, 11,123,171, 3,122,177, 0,121,179, 0, + 122,178, 0,123,173, 1,124,166, 7,124,158, 13,124,161, 11, + 124,167, 6,123,169, 4,123,170, 3,123,169, 4,122,176, 0, + 121,182, 0,120,184, 0,119,186, 0,118,188, 0,117,191, 0, + 115,194, 0,113,198, 0,110,202, 0,108,205, 0,106,208, 0, + 105,209, 0,106,208, 0,105,209, 0,102,212, 0,102,213, 0, + 102,212, 0,105,209, 0,108,205, 0,108,205, 0,109,203, 0, + 111,201, 0,114,196, 0,117,191, 0,117,190, 0,118,188, 0, + 119,185, 0,121,181, 0,122,178, 0,122,178, 0,122,177, 0, + 122,176, 0,122,175, 0,123,172, 2,123,169, 4,121,179, 0, + 119,185, 0,118,188, 0,119,186, 0,120,183, 0,122,176, 0, + 122,176, 0,121,181, 0,120,184, 0,119,186, 0,118,188, 0, + 119,186, 0,120,182, 0,123,173, 1,124,158, 13,122,132, 48, + 117,110,109,102, 80,250, 69, 60,230,115,107,129,123,139, 37, + 124,158, 13,123,171, 3,121,182, 0,118,189, 0,115,195, 0, + 113,198, 0,113,199, 0,114,197, 0,116,192, 0,119,186, 0, + 119,186, 0,119,187, 0,115,194, 0,111,201, 0,108,205, 0, + 107,207, 0,107,207, 0,108,205, 0,110,202, 0,113,199, 0, + 114,196, 0,115,194, 0,116,193, 0,114,197, 0,112,199, 0, + 113,199, 0,114,196, 0,113,198, 0,113,198, 0,114,197, 0, + 115,194, 0,117,191, 0,118,188, 0,119,185, 0,120,184, 0, + 120,183, 0,121,179, 0,123,174, 1,124,166, 7,124,161, 11, + 123,170, 3,121,179, 0,119,187, 0,115,195, 0,111,201, 0, + 108,205, 0,107,207, 0,108,206, 0,110,202, 0,114,197, 0, + 117,192, 0,117,191, 0,117,191, 0,118,188, 0,119,185, 0, + 121,181, 0,122,177, 0,122,175, 0,122,176, 0,122,178, 0, + 121,180, 0,121,179, 0,122,175, 0,124,165, 8,124,149, 24, + 123,141, 34,124,150, 22,124,150, 22,124,149, 24,123,139, 37, + 124,149, 24,124,161, 11,124,166, 7,123,168, 5,123,168, 5, + 123,168, 5,123,173, 1,122,177, 0,122,178, 0,122,178, 0, + 122,178, 0,122,178, 0,122,176, 0,123,171, 3,124,165, 8, + 124,160, 12,123,170, 3,122,175, 0,123,174, 1,123,168, 5, + 122,176, 0,119,186, 0,115,194, 0,112,199, 0,111,201, 0, + 112,199, 0,115,195, 0,118,188, 0,119,187, 0,118,188, 0, + 118,188, 0,119,186, 0,121,182, 0,122,175, 0,124,166, 7, + 124,161, 11,124,162, 10,124,166, 7,124,165, 8,123,173, 1, + 122,178, 0,121,181, 0,121,179, 0,123,174, 1,124,164, 9, + 123,170, 3,121,181, 0,119,187, 0,117,191, 0,115,194, 0, + 114,197, 0,113,198, 0,112,200, 0,111,202, 0,110,203, 0, + 109,203, 0,110,203, 0,111,201, 0,112,199, 0,114,197, 0, + 115,195, 0,116,193, 0,118,189, 0,120,183, 0,122,175, 0, + 122,178, 0,120,183, 0,120,184, 0,120,184, 0,121,180, 0, + 120,182, 0,120,184, 0,120,183, 0,121,179, 0,123,173, 1, + 124,164, 9,124,152, 20,124,157, 15,124,161, 11,124,162, 10, + 124,161, 11,124,155, 16,124,147, 26,124,149, 24,124,152, 20, + 124,166, 7,122,177, 0,120,183, 0,119,185, 0,119,185, 0, + 121,181, 0,123,174, 1,123,170, 3,123,174, 1,121,182, 0, + 118,189, 0,116,193, 0,115,194, 0,115,195, 0,116,193, 0, + 117,192, 0,117,191, 0,117,190, 0,118,188, 0,120,183, 0, + 123,174, 1,124,160, 12,124,149, 24,122,132, 48,120,122, 70, + 122,130, 53,122,132, 48,122,130, 53,123,137, 41,123,141, 34, + 124,147, 26,124,154, 18,124,162, 10,124,167, 6,123,169, 4, + 124,166, 7,124,157, 15,124,150, 22,123,139, 37,121,127, 58, + 123,137, 41,124,145, 29,124,149, 24,124,154, 18,124,166, 7, + 123,174, 1,121,179, 0,120,182, 0,120,184, 0,119,187, 0, + 117,190, 0,115,194, 0,113,198, 0,110,202, 0,108,206, 0, + 106,207, 0,106,207, 0,108,206, 0,110,202, 0,108,205, 0, + 107,207, 0,107,206, 0,109,203, 0,112,199, 0,114,197, 0, + 115,195, 0,117,192, 0,118,189, 0,118,189, 0,117,191, 0, + 117,191, 0,118,188, 0,119,185, 0,120,183, 0,121,182, 0, + 121,180, 0,122,178, 0,122,175, 0,123,171, 3,124,167, 6, + 123,169, 4,123,174, 1,123,174, 1,123,174, 1,123,174, 1, + 121,181, 0,118,188, 0,117,191, 0,116,193, 0,115,194, 0, + 116,193, 0,117,191, 0,119,185, 0,122,177, 0,124,164, 9, + 123,141, 34,118,117, 85,102, 80,250,102, 80,250,113,100,242, + 123,139, 37,124,161, 11,122,175, 0,120,184, 0,117,191, 0, + 114,196, 0,113,198, 0,114,197, 0,115,194, 0,118,189, 0, + 121,181, 0,121,181, 0,120,182, 0,119,187, 0,115,194, 0, + 112,199, 0,110,202, 0,110,203, 0,110,202, 0,112,200, 0, + 114,197, 0,115,194, 0,117,191, 0,115,194, 0,114,197, 0, + 113,198, 0,113,198, 0,115,194, 0,115,195, 0,115,194, 0, + 116,193, 0,117,191, 0,118,188, 0,119,186, 0,120,184, 0, + 120,184, 0,120,184, 0,121,181, 0,122,176, 0,123,169, 4, + 124,161, 11,124,166, 7,122,177, 0,119,186, 0,115,194, 0, + 112,200, 0,109,204, 0,109,204, 0,110,202, 0,113,198, 0, + 116,192, 0,119,186, 0,119,187, 0,119,186, 0,120,184, 0, + 121,181, 0,122,178, 0,122,175, 0,123,174, 1,122,177, 0, + 121,179, 0,121,180, 0,122,178, 0,123,170, 3,124,161, 11, + 124,154, 18,124,166, 7,123,170, 3,123,169, 4,124,166, 7, + 124,160, 12,124,154, 18,124,166, 7,123,172, 2,122,176, 0, + 122,177, 0,123,174, 1,121,179, 0,121,182, 0,120,183, 0, + 120,184, 0,120,184, 0,120,184, 0,120,183, 0,121,180, 0, + 123,174, 1,123,168, 5,123,168, 5,123,171, 3,123,171, 3, + 123,168, 5,121,179, 0,118,189, 0,114,196, 0,112,200, 0, + 111,201, 0,113,198, 0,116,192, 0,120,184, 0,119,185, 0, + 119,186, 0,119,185, 0,120,182, 0,122,177, 0,123,169, 4, + 124,158, 13,124,152, 20,124,150, 22,124,157, 15,124,162, 10, + 123,171, 3,122,178, 0,121,180, 0,122,178, 0,123,174, 1, + 124,164, 9,123,168, 5,121,180, 0,118,188, 0,116,193, 0, + 114,197, 0,112,199, 0,111,201, 0,110,202, 0,110,203, 0, + 109,203, 0,110,202, 0,111,201, 0,113,199, 0,114,196, 0, + 115,194, 0,116,193, 0,116,192, 0,118,189, 0,120,184, 0, + 122,177, 0,123,168, 5,123,170, 3,122,175, 0,122,176, 0, + 123,173, 1,122,175, 0,123,174, 1,123,172, 2,123,168, 5, + 124,161, 11,124,152, 20,123,141, 34,123,137, 41,123,141, 34, + 124,145, 29,124,147, 26,124,145, 29,123,141, 34,124,143, 31, + 124,149, 24,124,152, 20,124,166, 7,123,174, 1,122,177, 0, + 122,175, 0,123,170, 3,124,160, 12,124,165, 8,123,168, 5, + 121,179, 0,119,186, 0,117,190, 0,117,191, 0,117,191, 0, + 118,188, 0,120,184, 0,121,181, 0,122,178, 0,123,173, 1, + 124,165, 8,124,160, 12,124,157, 15,124,149, 24,122,132, 48, + 118,117, 85,123,139, 37,124,150, 22,124,157, 15,124,158, 13, + 124,162, 10,124,164, 9,124,162, 10,123,168, 5,123,170, 3, + 123,170, 3,124,165, 8,124,154, 18,122,132, 48,111, 96,248, + 99, 75,247,111, 96,248,115,107,129,120,122, 70,124,143, 31, + 124,158, 13,123,168, 5,122,175, 0,122,178, 0,121,179, 0, + 120,183, 0,118,188, 0,116,192, 0,114,197, 0,111,201, 0, + 109,203, 0,108,205, 0,109,204, 0,111,201, 0,114,196, 0, + 113,198, 0,112,200, 0,112,199, 0,114,197, 0,117,192, 0, + 119,187, 0,120,184, 0,120,183, 0,120,184, 0,119,186, 0, + 118,189, 0,117,190, 0,118,189, 0,119,186, 0,119,185, 0, + 120,183, 0,121,181, 0,122,178, 0,123,173, 1,123,169, 4, + 124,165, 8,124,161, 11,124,157, 15,124,160, 12,123,172, 2, + 120,182, 0,117,190, 0,114,196, 0,113,198, 0,113,199, 0, + 113,198, 0,114,196, 0,117,191, 0,119,185, 0,122,176, 0, + 124,164, 9,124,145, 29,120,122, 70,107, 89,252,111, 96,248, + 114,104,197,122,130, 53,124,160, 12,122,175, 0,119,185, 0, + 117,192, 0,115,195, 0,114,196, 0,115,194, 0,118,189, 0, + 120,182, 0,123,172, 2,123,172, 2,122,176, 0,122,178, 0, + 119,186, 0,117,191, 0,115,195, 0,114,197, 0,113,198, 0, + 114,196, 0,115,194, 0,117,191, 0,117,191, 0,115,194, 0, + 114,196, 0,114,197, 0,114,196, 0,116,193, 0,117,191, 0, + 118,189, 0,119,186, 0,120,184, 0,120,182, 0,121,182, 0, + 121,182, 0,120,182, 0,121,182, 0,121,179, 0,122,175, 0, + 123,168, 5,124,162, 10,124,155, 16,123,170, 3,120,182, 0, + 117,192, 0,113,198, 0,111,201, 0,111,201, 0,113,198, 0, + 117,192, 0,120,184, 0,120,182, 0,120,183, 0,121,182, 0, + 121,179, 0,122,176, 0,123,173, 1,123,171, 3,123,172, 2, + 122,175, 0,122,178, 0,122,178, 0,123,174, 1,124,167, 6, + 124,162, 10,123,170, 3,121,179, 0,121,182, 0,121,179, 0, + 122,175, 0,123,168, 5,124,157, 15,124,164, 9,123,172, 2, + 122,178, 0,121,180, 0,122,178, 0,121,181, 0,120,184, 0, + 119,185, 0,119,186, 0,119,186, 0,119,186, 0,119,186, 0, + 120,184, 0,121,179, 0,122,176, 0,123,174, 1,123,173, 1, + 123,171, 3,123,170, 3,121,181, 0,117,190, 0,114,197, 0, + 112,200, 0,112,200, 0,114,197, 0,118,189, 0,121,179, 0, + 121,182, 0,121,182, 0,121,181, 0,122,178, 0,123,171, 3, + 124,162, 10,124,149, 24,123,137, 41,121,127, 58,124,143, 31, + 124,158, 13,123,169, 4,122,176, 0,122,178, 0,122,178, 0, + 123,173, 1,124,164, 9,124,165, 8,122,178, 0,119,187, 0, + 116,193, 0,114,197, 0,112,199, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,112,199, 0,114,197, 0,116,193, 0, + 117,191, 0,118,189, 0,118,189, 0,118,189, 0,118,188, 0, + 120,184, 0,122,178, 0,123,168, 5,124,162, 10,124,162, 10, + 124,164, 9,124,165, 8,124,162, 10,124,158, 13,124,154, 18, + 124,147, 26,124,155, 16,124,161, 11,124,162, 10,124,158, 13, + 124,149, 24,123,141, 34,121,125, 63,122,130, 53,122,135, 44, + 122,135, 44,123,139, 37,124,143, 31,124,149, 24,124,160, 12, + 124,164, 9,124,161, 11,124,154, 18,124,150, 22,124,157, 15, + 124,161, 11,122,175, 0,120,183, 0,119,186, 0,119,186, 0, + 120,184, 0,122,178, 0,123,171, 3,124,164, 9,124,155, 16, + 124,143, 31,124,147, 26,124,152, 20,124,152, 20,124,149, 24, + 124,152, 20,124,155, 16,124,164, 9,123,169, 4,123,171, 3, + 123,173, 1,122,175, 0,122,175, 0,123,170, 3,123,170, 3, + 123,170, 3,123,168, 5,124,161, 11,124,147, 26,121,125, 63, + 114,104,197,109, 92,251, 69, 60,230, 30, 31,180,107, 89,252, + 120,122, 70,124,145, 29,124,158, 13,124,166, 7,123,168, 5, + 123,170, 3,122,176, 0,121,182, 0,119,187, 0,116,192, 0, + 114,197, 0,112,199, 0,112,200, 0,114,197, 0,117,192, 0, + 119,185, 0,118,189, 0,117,191, 0,117,191, 0,118,188, 0, + 121,182, 0,122,176, 0,123,172, 2,123,172, 2,123,174, 1, + 122,178, 0,120,183, 0,119,186, 0,119,186, 0,120,184, 0, + 120,183, 0,121,181, 0,122,178, 0,123,174, 1,123,170, 3, + 124,165, 8,124,160, 12,124,157, 15,124,154, 18,123,168, 5, + 121,181, 0,118,189, 0,114,196, 0,112,200, 0,110,202, 0, + 111,201, 0,113,199, 0,115,195, 0,118,189, 0,120,182, 0, + 123,173, 1,124,161, 11,124,149, 24,121,125, 63,115,107,129, + 119,119, 77,121,127, 58,121,127, 58,124,152, 20,123,172, 2, + 120,184, 0,117,190, 0,116,193, 0,116,192, 0,118,189, 0, + 120,182, 0,123,172, 2,124,158, 13,124,157, 15,124,165, 8, + 123,168, 5,123,173, 1,121,180, 0,119,186, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,119,187, 0,117,190, 0, + 116,192, 0,116,193, 0,115,194, 0,116,193, 0,117,191, 0, + 118,189, 0,119,185, 0,121,180, 0,122,176, 0,121,180, 0, + 120,183, 0,119,185, 0,119,186, 0,120,182, 0,123,174, 1, + 123,168, 5,124,162, 10,124,155, 16,124,149, 24,124,155, 16, + 123,174, 1,119,186, 0,116,193, 0,114,196, 0,115,195, 0, + 117,191, 0,120,184, 0,123,173, 1,122,177, 0,122,177, 0, + 123,174, 1,123,171, 3,123,168, 5,124,165, 8,124,164, 9, + 124,166, 7,123,170, 3,123,174, 1,123,173, 1,123,168, 5, + 124,167, 6,124,165, 8,122,178, 0,119,186, 0,119,186, 0, + 120,183, 0,122,178, 0,123,170, 3,124,160, 12,124,150, 22, + 124,165, 8,123,173, 1,122,178, 0,122,176, 0,121,179, 0, + 120,182, 0,120,184, 0,120,184, 0,119,185, 0,119,185, 0, + 119,186, 0,119,185, 0,121,181, 0,121,180, 0,122,178, 0, + 122,175, 0,123,172, 2,123,170, 3,121,180, 0,118,189, 0, + 114,196, 0,112,199, 0,113,199, 0,115,195, 0,119,187, 0, + 122,175, 0,122,175, 0,122,175, 0,123,174, 1,123,171, 3, + 124,165, 8,124,155, 16,123,141, 34,121,127, 58,118,117, 85, + 123,137, 41,124,154, 18,124,166, 7,123,173, 1,122,176, 0, + 122,175, 0,123,170, 3,124,161, 11,124,160, 12,122,175, 0, + 120,184, 0,117,190, 0,115,194, 0,114,197, 0,113,198, 0, + 113,199, 0,113,198, 0,114,196, 0,116,193, 0,118,189, 0, + 119,186, 0,120,183, 0,121,181, 0,121,182, 0,120,184, 0, + 120,184, 0,120,182, 0,122,178, 0,123,169, 4,124,154, 18, + 124,152, 20,124,152, 20,124,149, 24,123,141, 34,122,135, 44, + 123,139, 37,124,154, 18,124,162, 10,123,170, 3,123,174, 1, + 123,174, 1,123,170, 3,124,161, 11,124,143, 31,121,127, 58, + 121,125, 63,122,132, 48,122,135, 44,122,135, 44,123,139, 37, + 123,137, 41,124,145, 29,123,141, 34,122,130, 53,123,137, 41, + 124,143, 31,124,152, 20,123,168, 5,122,177, 0,121,180, 0, + 121,179, 0,123,174, 1,124,165, 8,124,150, 22,122,130, 53, + 114,104,197,118,114, 96,122,130, 53,124,143, 31,124,162, 10, + 123,170, 3,123,172, 2,123,173, 1,122,178, 0,121,181, 0, + 121,181, 0,120,182, 0,120,183, 0,120,182, 0,122,178, 0, + 123,171, 3,123,169, 4,124,165, 8,124,155, 16,123,139, 37, + 122,132, 48,121,127, 58,119,119, 77,107, 89,252, 30, 49,213, + 30, 55,222,109, 92,251,121,125, 63,123,141, 34,124,149, 24, + 124,150, 22,124,154, 18,124,162, 10,123,170, 3,122,178, 0, + 120,184, 0,118,189, 0,117,191, 0,117,190, 0,119,186, 0, + 122,178, 0,123,174, 1,122,178, 0,121,180, 0,122,178, 0, + 123,173, 1,124,165, 8,124,155, 16,124,149, 24,124,149, 24, + 124,155, 16,124,166, 7,122,175, 0,121,181, 0,120,182, 0, + 121,182, 0,121,179, 0,122,175, 0,123,170, 3,124,166, 7, + 124,161, 11,124,155, 16,124,150, 22,124,147, 26,124,155, 16, + 123,173, 1,119,185, 0,116,193, 0,113,199, 0,111,202, 0, + 110,202, 0,111,201, 0,114,197, 0,117,192, 0,119,187, 0, + 120,184, 0,122,178, 0,123,169, 4,124,155, 16,121,127, 58, + 122,135, 44,124,149, 24,124,157, 15,124,158, 13,124,152, 20, + 124,166, 7,121,180, 0,119,186, 0,118,188, 0,119,187, 0, + 121,182, 0,123,172, 2,124,157, 15,122,135, 44,122,130, 53, + 124,149, 24,124,158, 13,124,161, 11,124,162, 10,123,170, 3, + 122,177, 0,121,181, 0,120,183, 0,120,182, 0,120,183, 0, + 119,186, 0,118,188, 0,118,189, 0,118,189, 0,118,189, 0, + 118,188, 0,119,187, 0,120,184, 0,119,186, 0,119,186, 0, + 120,184, 0,119,187, 0,117,191, 0,116,192, 0,117,190, 0, + 120,184, 0,123,172, 2,124,150, 22,123,137, 41,122,130, 53, + 122,130, 53,124,158, 13,122,177, 0,119,185, 0,118,188, 0, + 118,188, 0,120,183, 0,123,173, 1,124,165, 8,123,169, 4, + 123,168, 5,124,164, 9,124,160, 12,124,154, 18,124,149, 24, + 124,149, 24,124,152, 20,124,160, 12,124,165, 8,124,165, 8, + 124,162, 10,124,164, 9,123,171, 3,120,183, 0,118,188, 0, + 118,188, 0,120,184, 0,122,177, 0,123,168, 5,124,155, 16, + 123,141, 34,124,143, 31,124,161, 11,123,168, 5,124,167, 6, + 123,172, 2,122,177, 0,122,178, 0,121,179, 0,121,179, 0, + 121,180, 0,121,182, 0,121,182, 0,120,182, 0,121,182, 0, + 121,179, 0,122,175, 0,122,175, 0,123,171, 3,122,178, 0, + 118,188, 0,115,194, 0,113,198, 0,113,198, 0,116,193, 0, + 120,184, 0,123,170, 3,124,165, 8,124,165, 8,124,165, 8, + 124,167, 6,124,166, 7,124,160, 12,124,147, 26,121,125, 63, + 113,100,242,121,125, 63,124,145, 29,124,160, 12,123,168, 5, + 123,172, 2,123,171, 3,124,167, 6,124,158, 13,124,154, 18, + 123,169, 4,121,179, 0,119,186, 0,118,189, 0,117,191, 0, + 117,192, 0,116,192, 0,117,191, 0,118,188, 0,120,184, 0, + 122,178, 0,123,174, 1,123,174, 1,123,172, 2,123,172, 2, + 122,177, 0,121,179, 0,121,179, 0,122,176, 0,123,168, 5, + 124,152, 20,122,132, 48,122,130, 53,120,122, 70,118,117, 85, + 123,139, 37,124,161, 11,123,171, 3,122,175, 0,122,176, 0, + 121,182, 0,120,184, 0,121,182, 0,122,175, 0,124,164, 9, + 124,147, 26,122,132, 48,121,127, 58,122,132, 48,121,125, 63, + 122,132, 48,122,130, 53,119,119, 77,119,119, 77,114,104,197, + 118,114, 96,120,122, 70,123,137, 41,124,157, 15,124,167, 6, + 123,171, 3,123,169, 4,124,161, 11,124,143, 31,121,127, 58, + 111, 96,248, 69, 60,230,105, 84,252,122,132, 48,124,161, 11, + 122,175, 0,121,181, 0,120,182, 0,120,184, 0,118,188, 0, + 118,188, 0,119,187, 0,119,187, 0,119,187, 0,119,186, 0, + 121,182, 0,122,175, 0,123,169, 4,124,162, 10,124,161, 11, + 124,164, 9,124,165, 8,124,162, 10,124,154, 18,122,135, 44, + 120,122, 70,119,119, 77,114,104,197,109, 92,251,117,110,109, + 119,119, 77,119,119, 77,123,139, 37,124,147, 26,124,150, 22, + 124,162, 10,123,170, 3,122,176, 0,122,177, 0,123,174, 1, + 124,165, 8,124,152, 20,124,161, 11,124,165, 8,124,164, 9, + 124,157, 15,124,145, 29,121,127, 58,120,122, 70,121,127, 58, + 123,137, 41,124,149, 24,124,161, 11,123,171, 3,122,178, 0, + 121,180, 0,121,181, 0,121,179, 0,122,176, 0,123,169, 4, + 124,157, 15,123,141, 34,122,135, 44,122,130, 53,121,127, 58, + 124,155, 16,122,175, 0,119,186, 0,115,194, 0,113,199, 0, + 111,201, 0,111,201, 0,113,198, 0,116,193, 0,115,195, 0, + 115,194, 0,117,191, 0,120,184, 0,123,173, 1,124,157, 15, + 122,132, 48,124,154, 18,123,168, 5,122,175, 0,122,177, 0, + 123,173, 1,124,166, 7,123,173, 1,121,181, 0,120,182, 0, + 121,179, 0,123,171, 3,124,158, 13,123,139, 37,120,122, 70, + 114,104,197,121,125, 63,124,145, 29,124,154, 18,124,157, 15, + 124,152, 20,124,155, 16,124,165, 8,123,170, 3,123,170, 3, + 123,174, 1,122,178, 0,121,180, 0,121,182, 0,120,182, 0, + 120,183, 0,120,184, 0,120,184, 0,118,188, 0,116,193, 0, + 115,195, 0,115,194, 0,117,190, 0,116,193, 0,114,196, 0, + 115,194, 0,118,188, 0,122,178, 0,124,161, 11,122,130, 53, + 109, 92,251,111, 96,248,121,127, 58,124,160, 12,123,174, 1, + 121,179, 0,122,178, 0,123,173, 1,124,161, 11,124,152, 20, + 124,157, 15,124,154, 18,124,154, 18,124,150, 22,124,145, 29, + 123,137, 41,121,125, 63,121,125, 63,123,139, 37,124,149, 24, + 124,150, 22,124,152, 20,124,158, 13,123,174, 1,120,184, 0, + 118,188, 0,119,187, 0,121,182, 0,122,175, 0,123,172, 2, + 124,162, 10,123,137, 41,115,107,129,122,130, 53,124,145, 29, + 124,145, 29,124,160, 12,124,165, 8,124,167, 6,123,168, 5, + 123,168, 5,123,170, 3,123,173, 1,122,178, 0,121,182, 0, + 121,181, 0,122,178, 0,122,178, 0,122,178, 0,123,174, 1, + 123,174, 1,119,185, 0,116,192, 0,114,196, 0,114,196, 0, + 117,191, 0,120,182, 0,124,166, 7,124,149, 24,124,152, 20, + 124,162, 10,124,166, 7,124,166, 7,124,161, 11,124,147, 26, + 121,125, 63, 94, 70,242,113,100,242,122,130, 53,124,149, 24, + 124,160, 12,124,165, 8,124,166, 7,124,161, 11,124,152, 20, + 124,155, 16,124,161, 11,123,172, 2,122,178, 0,121,182, 0, + 120,182, 0,120,182, 0,120,182, 0,121,181, 0,122,177, 0, + 123,171, 3,123,172, 2,123,173, 1,123,173, 1,123,170, 3, + 124,162, 10,123,168, 5,123,173, 1,122,175, 0,123,172, 2, + 124,165, 8,124,145, 29,117,110,109,105, 84,252, 87, 65,237, + 120,122, 70,124,158, 13,123,174, 1,121,182, 0,120,184, 0, + 120,184, 0,119,186, 0,118,188, 0,118,188, 0,120,183, 0, + 123,174, 1,124,160, 12,124,147, 26,121,127, 58,120,122, 70, + 118,117, 85,122,130, 53,122,132, 48,121,125, 63,115,107,129, + 105, 84,252, 94, 70,242,107, 89,252,115,107,129,123,137, 41, + 124,152, 20,124,157, 15,124,154, 18,123,141, 34,117,110,109, + 102, 80,250, 30, 55,222,102, 80,250,118,114, 96,124,154, 18, + 123,173, 1,120,183, 0,118,188, 0,118,188, 0,117,192, 0, + 115,194, 0,115,194, 0,117,192, 0,118,189, 0,118,189, 0, + 119,187, 0,120,182, 0,122,176, 0,123,173, 1,122,176, 0, + 122,178, 0,121,181, 0,121,181, 0,122,178, 0,123,171, 3, + 124,160, 12,124,160, 12,124,157, 15,124,147, 26,121,125, 63, + 105, 84,252, 94, 70,242,102, 80,250,118,114, 96,121,127, 58, + 122,130, 53,122,130, 53,124,143, 31,124,150, 22,124,150, 22, + 123,141, 34,119,119, 77,122,135, 44,123,141, 34,123,141, 34, + 122,132, 48,121,127, 58,118,117, 85,115,107,129,114,104,197, + 119,119, 77,123,139, 37,124,149, 24,124,152, 20,124,161, 11, + 123,169, 4,123,173, 1,122,175, 0,123,174, 1,123,171, 3, + 124,165, 8,124,152, 20,122,130, 53,109, 92,251,105, 84,252, + 115,107,129,124,149, 24,123,172, 2,119,185, 0,116,192, 0, + 114,196, 0,113,198, 0,114,197, 0,115,194, 0,113,199, 0, + 112,200, 0,113,199, 0,115,194, 0,119,186, 0,123,173, 1, + 124,154, 18,124,149, 24,123,169, 4,121,181, 0,119,187, 0, + 118,189, 0,119,186, 0,121,180, 0,122,177, 0,122,178, 0, + 122,175, 0,123,170, 3,124,160, 12,123,141, 34,122,130, 53, + 115,107,129, 87, 65,237,111, 96,248,122,130, 53,124,147, 26, + 124,154, 18,124,152, 20,124,145, 29,122,135, 44,124,149, 24, + 124,152, 20,124,158, 13,124,165, 8,123,168, 5,123,174, 1, + 122,178, 0,122,178, 0,122,177, 0,121,182, 0,117,191, 0, + 113,198, 0,111,202, 0,111,202, 0,112,199, 0,115,194, 0, + 114,197, 0,115,195, 0,117,190, 0,121,181, 0,124,166, 7, + 123,141, 34,115,107,129, 30, 43,203, 87, 65,237,122,132, 48, + 124,157, 15,124,167, 6,123,168, 5,124,162, 10,124,149, 24, + 122,130, 53,124,147, 26,124,154, 18,124,154, 18,124,150, 22, + 124,143, 31,122,132, 48,118,117, 85,111, 96,248,114,104,197, + 120,122, 70,121,127, 58,123,137, 41,124,158, 13,123,174, 1, + 120,182, 0,119,186, 0,120,184, 0,120,182, 0,119,185, 0, + 120,184, 0,122,178, 0,124,164, 9,122,130, 53, 69, 60,230, + 109, 92,251,118,114, 96,123,137, 41,124,145, 29,124,149, 24, + 124,150, 22,124,150, 22,124,154, 18,124,167, 6,122,176, 0, + 121,179, 0,121,179, 0,121,179, 0,120,182, 0,120,182, 0, + 122,178, 0,123,170, 3,120,182, 0,117,190, 0,115,194, 0, + 116,193, 0,118,189, 0,122,178, 0,124,160, 12,121,127, 58, + 124,145, 29,124,157, 15,124,161, 11,124,161, 11,124,154, 18, + 123,137, 41,117,110,109, 30, 2,128, 30, 43,203,111, 96,248, + 121,127, 58,124,147, 26,124,155, 16,124,157, 15,124,154, 18, + 124,145, 29,124,157, 15,124,162, 10,124,164, 9,124,167, 6, + 123,169, 4,123,169, 4,123,170, 3,123,168, 5,123,174, 1, + 122,177, 0,121,181, 0,120,183, 0,120,182, 0,121,179, 0, + 123,172, 2,124,158, 13,124,164, 9,123,169, 4,123,171, 3, + 123,168, 5,124,157, 15,122,132, 48,102, 80,250, 30, 2,128, + 107, 89,252,124,143, 31,123,169, 4,121,182, 0,118,188, 0, + 117,190, 0,118,189, 0,119,186, 0,118,189, 0,118,189, 0, + 119,186, 0,121,180, 0,123,169, 4,124,150, 22,122,132, 48, + 111, 96,248,120,122, 70,123,137, 41,124,143, 31,123,139, 37, + 121,127, 58,113,100,242, 69, 60,230, 30, 26,168, 30, 55,222, + 114,104,197,121,127, 58,123,137, 41,122,135, 44,119,119, 77, + 99, 75,247, 30, 2,128, 99, 75,247,118,117, 85,124,145, 29, + 123,168, 5,121,181, 0,118,188, 0,117,192, 0,117,192, 0, + 114,197, 0,113,199, 0,113,198, 0,115,195, 0,117,191, 0, + 119,187, 0,120,184, 0,121,179, 0,119,185, 0,118,188, 0, + 118,188, 0,118,188, 0,118,188, 0,118,188, 0,119,186, 0, + 121,180, 0,121,180, 0,121,180, 0,122,178, 0,123,172, 2, + 124,160, 12,122,132, 48,102, 80,250, 30, 2,128, 30, 55,222, + 102, 80,250,107, 89,252,105, 84,252,109, 92,251,113,100,242, + 111, 96,248, 94, 70,242,109, 92,251,115,107,129,118,117, 85, + 121,125, 63,120,122, 70,117,110,109,109, 92,251, 99, 75,247, + 115,107,129,122,132, 48,124,149, 24,124,158, 13,124,164, 9, + 124,165, 8,124,165, 8,124,164, 9,124,166, 7,124,166, 7, + 124,162, 10,124,155, 16,123,141, 34,118,114, 96, 87, 65,237, + 30, 2,128, 99, 75,247,122,135, 44,124,165, 8,121,180, 0, + 118,188, 0,116,192, 0,116,193, 0,116,192, 0,114,197, 0, + 112,200, 0,111,201, 0,112,199, 0,116,193, 0,120,184, 0, + 124,167, 6,123,141, 34,124,164, 9,121,180, 0,118,189, 0, + 115,194, 0,114,196, 0,115,194, 0,118,189, 0,121,182, 0, + 120,183, 0,121,179, 0,123,170, 3,124,155, 16,121,127, 58, + 115,107,129, 94, 70,242, 30, 2,128, 99, 75,247,119,119, 77, + 123,141, 34,124,152, 20,124,152, 20,124,145, 29,122,132, 48, + 119,119, 77,121,127, 58,122,130, 53,124,145, 29,124,161, 11, + 123,170, 3,123,174, 1,123,173, 1,124,165, 8,121,182, 0, + 116,193, 0,111,201, 0,108,205, 0,107,207, 0,108,205, 0, + 111,201, 0,114,196, 0,115,195, 0,117,191, 0,120,182, 0, + 122,176, 0,124,164, 9,124,143, 31,111, 96,248, 30, 2,128, + 111, 96,248,123,139, 37,124,155, 16,124,160, 12,124,157, 15, + 124,150, 22,124,147, 26,124,145, 29,124,152, 20,124,152, 20, + 124,154, 18,124,154, 18,124,149, 24,123,139, 37,118,114, 96, + 69, 60,230,107, 89,252,114,104,197,121,125, 63,124,157, 15, + 123,171, 3,121,179, 0,121,182, 0,121,179, 0,119,186, 0, + 118,189, 0,118,189, 0,119,185, 0,122,175, 0,124,152, 20, + 111, 96,248, 30, 2,128, 99, 75,247,117,110,109,120,122, 70, + 121,127, 58,123,141, 34,124,149, 24,124,161, 11,124,166, 7, + 122,175, 0,122,178, 0,121,181, 0,120,184, 0,119,186, 0, + 120,184, 0,122,178, 0,124,166, 7,121,179, 0,119,187, 0, + 117,191, 0,117,191, 0,119,185, 0,123,174, 1,124,150, 22, + 118,114, 96,122,132, 48,124,145, 29,124,150, 22,124,147, 26, + 122,132, 48,115,107,129, 30, 55,222,109, 92,251,117,110,109, + 115,107,129,115,107,129,118,117, 85,123,137, 41,124,143, 31, + 124,143, 31,124,143, 31,124,157, 15,124,164, 9,124,165, 8, + 124,164, 9,124,160, 12,124,166, 7,123,174, 1,121,179, 0, + 120,182, 0,119,186, 0,118,188, 0,118,189, 0,118,188, 0, + 120,184, 0,122,177, 0,123,173, 1,122,175, 0,123,174, 1, + 123,171, 3,124,164, 9,124,147, 26,118,117, 85, 94, 70,242, + 105, 84,252,118,114, 96,124,155, 16,122,176, 0,119,186, 0, + 117,192, 0,115,194, 0,116,193, 0,117,191, 0,118,188, 0, + 118,188, 0,119,186, 0,121,182, 0,123,174, 1,124,162, 10, + 124,147, 26,122,130, 53,123,137, 41,124,149, 24,124,154, 18, + 124,154, 18,124,145, 29,122,130, 53,114,104,197, 87, 65,237, + 30, 15,143, 30, 37,192,105, 84,252,114,104,197,115,107,129, + 109, 92,251, 94, 70,242,105, 84,252,118,114, 96,124,143, 31, + 124,165, 8,122,178, 0,119,187, 0,116,192, 0,115,195, 0, + 114,196, 0,112,200, 0,111,201, 0,112,200, 0,114,197, 0, + 116,193, 0,118,189, 0,119,185, 0,117,190, 0,115,194, 0, + 114,197, 0,113,198, 0,113,198, 0,114,197, 0,114,196, 0, + 115,194, 0,116,193, 0,116,193, 0,116,193, 0,117,191, 0, + 119,186, 0,122,178, 0,124,161, 11,122,130, 53,102, 80,250, + 30, 55,222, 30, 37,192, 30, 31,180, 30, 20,155, 30, 15,143, + 30, 26,168, 30, 15,143, 30, 20,155, 87, 65,237,113,100,242, + 119,119, 77,122,130, 53,122,130, 53,119,119, 77,120,122, 70, + 124,143, 31,124,154, 18,124,160, 12,124,161, 11,124,167, 6, + 123,170, 3,123,172, 2,123,172, 2,123,172, 2,123,170, 3, + 124,167, 6,124,162, 10,124,152, 20,123,139, 37,119,119, 77, + 105, 84,252,105, 84,252,107, 89,252,120,122, 70,124,158, 13, + 122,175, 0,120,184, 0,118,188, 0,118,189, 0,116,193, 0, + 114,197, 0,112,199, 0,112,199, 0,114,196, 0,118,188, 0, + 122,175, 0,124,152, 20,124,162, 10,122,176, 0,118,188, 0, + 115,195, 0,112,200, 0,111,201, 0,112,199, 0,115,194, 0, + 118,189, 0,119,185, 0,120,182, 0,122,177, 0,124,166, 7, + 124,147, 26,120,122, 70,109, 92,251, 99, 75,247,121,125, 63, + 124,150, 22,124,161, 11,124,161, 11,124,154, 18,124,149, 24, + 123,137, 41,121,125, 63,120,122, 70,121,127, 58,123,137, 41, + 124,150, 22,124,161, 11,124,166, 7,124,166, 7,123,169, 4, + 120,183, 0,115,194, 0,110,202, 0,107,207, 0,105,209, 0, + 106,208, 0,108,205, 0,111,202, 0,113,198, 0,116,193, 0, + 118,189, 0,120,182, 0,123,172, 2,124,154, 18,118,117, 85, + 30, 31,180, 99, 75,247,120,122, 70,124,147, 26,124,157, 15, + 124,162, 10,124,164, 9,124,164, 9,124,162, 10,124,164, 9, + 124,166, 7,124,167, 6,124,166, 7,124,162, 10,124,154, 18, + 122,135, 44,111, 96,248,107, 89,252,118,114, 96,122,135, 44, + 124,152, 20,124,167, 6,123,174, 1,122,177, 0,121,180, 0, + 119,187, 0,117,191, 0,117,191, 0,118,188, 0,121,179, 0, + 124,161, 11,119,119, 77,107, 89,252,105, 84,252,118,114, 96, + 122,132, 48,124,147, 26,124,160, 12,123,170, 3,122,177, 0, + 121,179, 0,121,182, 0,119,185, 0,118,188, 0,118,189, 0, + 118,189, 0,119,186, 0,121,180, 0,123,168, 5,122,175, 0, + 120,184, 0,118,188, 0,119,187, 0,121,181, 0,124,167, 6, + 123,139, 37,111, 96,248,115,107,129,119,119, 77,120,122, 70, + 118,117, 85,117,110,109,107, 89,252,107, 89,252,124,143, 31, + 124,152, 20,124,150, 22,123,139, 37,121,127, 58,122,130, 53, + 119,119, 77,121,125, 63,123,137, 41,124,155, 16,124,164, 9, + 124,166, 7,124,166, 7,124,162, 10,123,172, 2,121,181, 0, + 119,186, 0,117,190, 0,116,193, 0,115,194, 0,116,193, 0, + 117,191, 0,119,186, 0,121,182, 0,120,183, 0,120,184, 0, + 120,183, 0,121,179, 0,123,171, 3,124,157, 15,122,132, 48, + 120,122, 70,121,127, 58,123,137, 41,124,162, 10,121,179, 0, + 118,189, 0,115,194, 0,114,197, 0,114,196, 0,115,194, 0, + 117,190, 0,119,186, 0,120,184, 0,121,180, 0,123,174, 1, + 124,166, 7,124,155, 16,124,145, 29,124,147, 26,124,157, 15, + 124,161, 11,124,161, 11,124,155, 16,124,143, 31,121,127, 58, + 117,110,109,105, 84,252, 30, 31,180, 30, 55,222,105, 84,252, + 114,104,197,118,114, 96,120,122, 70,122,132, 48,124,147, 26, + 124,162, 10,122,175, 0,120,184, 0,117,191, 0,114,196, 0, + 113,199, 0,113,199, 0,111,201, 0,110,202, 0,111,201, 0, + 113,198, 0,115,194, 0,117,191, 0,117,191, 0,115,195, 0, + 113,199, 0,111,201, 0,110,203, 0,109,204, 0,109,204, 0, + 109,204, 0,109,203, 0,110,203, 0,110,202, 0,111,201, 0, + 113,199, 0,115,194, 0,119,186, 0,123,173, 1,124,150, 22, + 118,117, 85,113,100,242,109, 92,251, 99, 75,247, 30, 49,213, + 30, 37,192, 30, 49,213, 30, 49,213, 69, 60,230,113,100,242, + 121,125, 63,123,139, 37,124,143, 31,123,139, 37,123,141, 34, + 124,162, 10,123,173, 1,122,178, 0,121,181, 0,121,182, 0, + 121,180, 0,122,176, 0,122,178, 0,122,178, 0,122,178, 0, + 122,178, 0,123,174, 1,123,169, 4,124,160, 12,124,145, 29, + 122,132, 48,122,132, 48,122,132, 48,122,132, 48,121,127, 58, + 124,154, 18,123,171, 3,121,180, 0,120,184, 0,119,187, 0, + 116,192, 0,114,196, 0,114,197, 0,115,194, 0,118,189, 0, + 122,178, 0,124,160, 12,124,161, 11,122,175, 0,120,184, 0, + 116,193, 0,112,199, 0,110,203, 0,109,204, 0,110,202, 0, + 112,199, 0,115,194, 0,118,188, 0,121,182, 0,122,176, 0, + 123,168, 5,124,157, 15,124,143, 31,121,127, 58,122,130, 53, + 124,157, 15,123,170, 3,122,177, 0,122,177, 0,123,171, 3, + 124,157, 15,124,143, 31,122,135, 44,122,132, 48,122,135, 44, + 123,139, 37,124,147, 26,124,154, 18,124,161, 11,124,166, 7, + 123,173, 1,119,186, 0,114,197, 0,109,204, 0,106,208, 0, + 104,210, 0,104,210, 0,106,208, 0,108,205, 0,110,202, 0, + 113,198, 0,116,193, 0,119,186, 0,122,175, 0,124,155, 16, + 119,119, 77, 87, 65,237,107, 89,252,121,125, 63,124,147, 26, + 124,158, 13,124,166, 7,123,169, 4,123,170, 3,123,171, 3, + 123,172, 2,123,173, 1,123,173, 1,123,171, 3,124,166, 7, + 124,155, 16,123,137, 41,114,104,197,114,104,197,121,125, 63, + 124,143, 31,124,154, 18,124,162, 10,123,168, 5,123,173, 1, + 121,182, 0,118,188, 0,117,191, 0,117,192, 0,118,189, 0, + 121,181, 0,124,167, 6,124,154, 18,123,141, 34,123,141, 34, + 124,149, 24,124,160, 12,123,170, 3,122,178, 0,120,184, 0, + 118,188, 0,117,190, 0,117,191, 0,116,193, 0,115,194, 0, + 115,194, 0,116,193, 0,118,189, 0,121,182, 0,123,169, 4, + 123,169, 4,122,178, 0,120,183, 0,121,182, 0,122,175, 0, + 124,161, 11,122,130, 53,102, 80,250, 30, 43,203, 87, 65,237, + 114,104,197,118,117, 85,118,114, 96,121,125, 63,123,137, 41, + 124,167, 6,123,172, 2,123,170, 3,124,165, 8,124,154, 18, + 124,145, 29,123,141, 34,121,125, 63,121,125, 63,124,150, 22, + 124,162, 10,123,168, 5,123,169, 4,124,167, 6,123,174, 1, + 120,184, 0,117,191, 0,115,194, 0,113,198, 0,113,198, 0, + 114,197, 0,115,194, 0,118,188, 0,119,187, 0,118,189, 0, + 118,189, 0,118,188, 0,120,184, 0,122,176, 0,124,165, 8, + 124,145, 29,124,145, 29,124,149, 24,124,152, 20,124,164, 9, + 121,180, 0,118,189, 0,115,195, 0,113,198, 0,113,198, 0, + 114,196, 0,117,192, 0,119,186, 0,121,179, 0,122,177, 0, + 123,171, 3,124,165, 8,124,157, 15,124,150, 22,124,152, 20, + 124,161, 11,124,164, 9,124,162, 10,124,157, 15,124,149, 24, + 123,137, 41,122,130, 53,118,117, 85,107, 89,252, 69, 60,230, + 113,100,242,120,122, 70,123,137, 41,124,147, 26,124,155, 16, + 124,162, 10,123,173, 1,120,183, 0,118,189, 0,115,194, 0, + 113,199, 0,111,202, 0,110,202, 0,110,202, 0,110,202, 0, + 111,201, 0,113,198, 0,114,196, 0,116,192, 0,115,194, 0, + 113,198, 0,111,201, 0,110,203, 0,108,205, 0,107,207, 0, + 106,208, 0,105,209, 0,105,209, 0,105,209, 0,106,208, 0, + 107,206, 0,110,203, 0,113,198, 0,117,190, 0,122,178, 0, + 124,160, 12,122,132, 48,121,127, 58,119,119, 77,115,107,129, + 107, 89,252,105, 84,252,109, 92,251,113,100,242,111, 96,248, + 118,117, 85,123,137, 41,124,147, 26,124,150, 22,124,147, 26, + 124,167, 6,121,180, 0,119,187, 0,117,191, 0,116,192, 0, + 116,192, 0,117,191, 0,118,189, 0,119,186, 0,120,183, 0, + 121,181, 0,121,181, 0,122,178, 0,123,172, 2,123,168, 5, + 124,164, 9,124,160, 12,124,157, 15,124,157, 15,124,155, 16, + 124,152, 20,124,158, 13,123,169, 4,122,177, 0,121,179, 0, + 120,184, 0,118,189, 0,116,192, 0,117,192, 0,118,188, 0, + 121,180, 0,124,165, 8,124,150, 22,123,173, 1,120,183, 0, + 118,189, 0,115,195, 0,112,200, 0,109,203, 0,108,205, 0, + 109,204, 0,111,201, 0,114,197, 0,117,192, 0,119,185, 0, + 122,175, 0,124,167, 6,124,160, 12,124,150, 22,124,143, 31, + 124,157, 15,123,172, 2,121,182, 0,119,186, 0,119,185, 0, + 121,181, 0,123,170, 3,124,152, 20,123,139, 37,122,135, 44, + 122,135, 44,123,137, 41,124,143, 31,124,150, 22,124,158, 13, + 123,169, 4,121,180, 0,117,190, 0,113,198, 0,109,204, 0, + 105,209, 0,103,211, 0,103,211, 0,104,210, 0,106,208, 0, + 108,205, 0,111,201, 0,115,195, 0,119,187, 0,122,175, 0, + 124,154, 18,119,119, 77,109, 92,251,107, 89,252,119,119, 77, + 123,139, 37,124,154, 18,124,162, 10,124,167, 6,123,171, 3, + 123,174, 1,122,176, 0,122,176, 0,123,174, 1,123,170, 3, + 124,164, 9,124,150, 22,122,130, 53,111, 96,248,117,110,109, + 122,132, 48,124,150, 22,124,160, 12,124,165, 8,124,165, 8, + 123,173, 1,121,182, 0,119,187, 0,117,190, 0,117,191, 0, + 118,188, 0,120,184, 0,122,178, 0,123,170, 3,124,165, 8, + 124,165, 8,123,170, 3,122,177, 0,120,183, 0,118,189, 0, + 116,193, 0,114,196, 0,114,197, 0,113,198, 0,112,200, 0, + 112,200, 0,112,199, 0,114,197, 0,117,192, 0,120,184, 0, + 123,169, 4,124,167, 6,123,171, 3,122,176, 0,122,175, 0, + 123,168, 5,124,152, 20,119,119, 77, 69, 60,230, 30, 55,222, + 99, 75,247,114,104,197,120,122, 70,123,141, 34,124,154, 18, + 124,162, 10,122,178, 0,120,182, 0,121,181, 0,122,177, 0, + 123,169, 4,124,161, 11,124,157, 15,124,149, 24,124,147, 26, + 124,150, 22,124,160, 12,123,169, 4,123,172, 2,123,171, 3, + 123,172, 2,120,184, 0,116,192, 0,114,197, 0,112,200, 0, + 111,201, 0,112,200, 0,114,196, 0,118,189, 0,118,189, 0, + 117,192, 0,117,192, 0,117,190, 0,119,186, 0,122,178, 0, + 123,168, 5,124,154, 18,124,157, 15,124,157, 15,124,158, 13, + 124,165, 8,122,178, 0,118,188, 0,115,194, 0,114,197, 0, + 114,197, 0,115,195, 0,117,191, 0,119,185, 0,122,178, 0, + 123,169, 4,124,164, 9,124,157, 15,124,150, 22,124,147, 26, + 124,154, 18,124,160, 12,124,161, 11,124,158, 13,124,152, 20, + 124,145, 29,123,137, 41,122,135, 44,122,130, 53,118,117, 85, + 111, 96,248,115,107,129,122,135, 44,124,150, 22,124,161, 11, + 124,166, 7,123,171, 3,120,184, 0,116,192, 0,113,198, 0, + 111,201, 0,110,202, 0,109,204, 0,107,206, 0,107,207, 0, + 107,206, 0,108,205, 0,110,203, 0,111,201, 0,113,198, 0, + 113,199, 0,111,201, 0,111,201, 0,109,203, 0,108,206, 0, + 106,208, 0,104,210, 0,103,211, 0,103,212, 0,103,211, 0, + 104,210, 0,106,208, 0,108,205, 0,112,199, 0,117,191, 0, + 121,180, 0,124,164, 9,124,145, 29,124,143, 31,123,137, 41, + 121,125, 63,117,110,109,115,107,129,118,117, 85,121,125, 63, + 121,125, 63,121,125, 63,124,143, 31,124,152, 20,124,155, 16, + 124,164, 9,122,178, 0,118,188, 0,115,194, 0,114,197, 0, + 113,199, 0,113,199, 0,113,198, 0,114,196, 0,115,194, 0, + 116,192, 0,117,192, 0,117,191, 0,118,189, 0,119,187, 0, + 120,183, 0,121,179, 0,122,175, 0,123,171, 3,123,170, 3, + 123,171, 3,123,173, 1,123,174, 1,123,173, 1,123,174, 1, + 122,176, 0,121,179, 0,120,184, 0,119,187, 0,119,185, 0, + 121,179, 0,124,167, 6,124,143, 31,124,165, 8,121,181, 0, + 119,187, 0,117,191, 0,115,195, 0,113,199, 0,111,202, 0, + 110,203, 0,110,202, 0,111,201, 0,114,197, 0,116,192, 0, + 119,186, 0,122,176, 0,124,161, 11,124,155, 16,124,150, 22, + 124,152, 20,123,170, 3,121,181, 0,118,188, 0,117,191, 0, + 117,190, 0,119,186, 0,122,177, 0,124,161, 11,123,139, 37, + 122,132, 48,122,130, 53,122,130, 53,124,147, 26,124,160, 12, + 123,169, 4,122,178, 0,119,186, 0,116,193, 0,112,199, 0, + 109,204, 0,106,207, 0,105,209, 0,104,210, 0,105,209, 0, + 106,208, 0,107,206, 0,110,202, 0,114,196, 0,119,186, 0, + 123,172, 2,124,150, 22,122,132, 48,115,107,129,102, 80,250, + 114,104,197,120,122, 70,123,137, 41,124,149, 24,124,158, 13, + 124,166, 7,123,171, 3,123,174, 1,123,174, 1,123,171, 3, + 124,166, 7,124,154, 18,123,137, 41,117,110,109,109, 92,251, + 118,117, 85,123,141, 34,124,158, 13,124,166, 7,123,169, 4, + 123,168, 5,122,177, 0,120,184, 0,118,188, 0,117,191, 0, + 116,192, 0,117,191, 0,118,188, 0,120,183, 0,122,178, 0, + 123,174, 1,122,176, 0,121,180, 0,119,185, 0,117,190, 0, + 115,194, 0,113,198, 0,112,200, 0,111,202, 0,110,202, 0, + 109,203, 0,109,204, 0,110,202, 0,112,200, 0,115,194, 0, + 119,186, 0,123,170, 3,124,165, 8,124,162, 10,124,167, 6, + 124,165, 8,124,157, 15,123,139, 37,114,104,197,115,107,129, + 117,110,109,119,119, 77,122,135, 44,124,147, 26,124,160, 12, + 123,168, 5,123,174, 1,120,184, 0,119,187, 0,119,186, 0, + 120,183, 0,122,178, 0,123,171, 3,123,170, 3,124,166, 7, + 124,164, 9,123,168, 5,123,170, 3,123,168, 5,123,174, 1, + 122,176, 0,123,174, 1,121,182, 0,117,191, 0,114,197, 0, + 111,201, 0,110,202, 0,111,201, 0,114,197, 0,117,190, 0, + 118,189, 0,117,191, 0,117,191, 0,118,189, 0,120,184, 0, + 122,178, 0,123,169, 4,124,161, 11,124,162, 10,124,161, 11, + 124,160, 12,124,165, 8,123,174, 1,120,184, 0,117,191, 0, + 115,194, 0,115,194, 0,117,192, 0,119,187, 0,121,181, 0, + 123,173, 1,124,161, 11,124,150, 22,123,141, 34,122,132, 48, + 122,132, 48,124,147, 26,124,152, 20,124,152, 20,124,147, 26, + 123,139, 37,122,130, 53,121,125, 63,122,130, 53,122,132, 48, + 121,127, 58,119,119, 77,118,114, 96,123,141, 34,124,157, 15, + 124,167, 6,123,172, 2,121,181, 0,116,192, 0,112,200, 0, + 108,205, 0,107,207, 0,106,207, 0,107,207, 0,105,209, 0, + 104,210, 0,104,210, 0,105,209, 0,106,208, 0,108,205, 0, + 110,202, 0,111,202, 0,109,203, 0,109,204, 0,109,204, 0, + 108,205, 0,107,207, 0,105,209, 0,103,211, 0,103,211, 0, + 104,210, 0,105,209, 0,107,207, 0,109,203, 0,113,198, 0, + 117,191, 0,121,180, 0,124,164, 9,124,154, 18,124,152, 20, + 124,147, 26,123,137, 41,120,122, 70,119,119, 77,122,130, 53, + 123,137, 41,123,139, 37,122,135, 44,124,147, 26,124,157, 15, + 124,162, 10,123,170, 3,121,182, 0,117,191, 0,114,197, 0, + 112,200, 0,111,202, 0,111,202, 0,111,201, 0,112,199, 0, + 113,198, 0,114,197, 0,113,198, 0,113,198, 0,114,197, 0, + 115,195, 0,117,192, 0,118,188, 0,120,184, 0,120,184, 0, + 119,185, 0,119,185, 0,119,186, 0,119,185, 0,120,183, 0, + 122,178, 0,123,171, 3,123,170, 3,122,177, 0,122,178, 0, + 122,176, 0,124,167, 6,124,149, 24,124,145, 29,123,171, 3, + 120,184, 0,118,188, 0,118,189, 0,116,192, 0,115,194, 0, + 114,197, 0,113,198, 0,113,198, 0,114,197, 0,115,194, 0, + 117,190, 0,120,184, 0,123,174, 1,124,158, 13,124,143, 31, + 123,141, 34,124,162, 10,122,175, 0,120,184, 0,118,189, 0, + 117,192, 0,117,191, 0,119,187, 0,122,178, 0,124,162, 10, + 123,139, 37,120,122, 70,118,117, 85,123,141, 34,124,158, 13, + 123,168, 5,122,176, 0,120,182, 0,118,189, 0,115,195, 0, + 112,199, 0,110,202, 0,109,204, 0,107,206, 0,107,207, 0, + 106,207, 0,107,207, 0,108,205, 0,111,201, 0,115,194, 0, + 120,184, 0,123,168, 5,124,152, 20,123,139, 37,118,117, 85, + 102, 80,250, 87, 65,237,105, 84,252,114,104,197,120,122, 70, + 123,139, 37,124,154, 18,124,164, 9,123,168, 5,123,168, 5, + 124,165, 8,124,155, 16,123,139, 37,118,114, 96,109, 92,251, + 114,104,197,121,125, 63,124,149, 24,124,165, 8,123,172, 2, + 122,175, 0,123,174, 1,121,179, 0,119,186, 0,118,189, 0, + 116,192, 0,116,193, 0,117,191, 0,118,188, 0,120,184, 0, + 121,179, 0,122,178, 0,121,179, 0,120,184, 0,118,188, 0, + 116,192, 0,114,196, 0,112,199, 0,111,201, 0,110,202, 0, + 109,204, 0,108,205, 0,108,205, 0,109,204, 0,111,201, 0, + 114,196, 0,119,187, 0,123,171, 3,124,162, 10,124,155, 16, + 124,154, 18,124,152, 20,123,141, 34,119,119, 77,122,130, 53, + 123,139, 37,124,147, 26,124,150, 22,124,152, 20,124,161, 11, + 123,169, 4,122,175, 0,121,180, 0,119,186, 0,118,189, 0, + 118,189, 0,119,186, 0,120,182, 0,122,178, 0,121,179, 0, + 122,177, 0,123,173, 1,122,178, 0,120,182, 0,120,183, 0, + 121,181, 0,122,178, 0,122,178, 0,122,176, 0,119,186, 0, + 115,194, 0,113,199, 0,111,201, 0,112,200, 0,114,197, 0, + 117,190, 0,120,184, 0,118,188, 0,118,188, 0,119,185, 0, + 121,181, 0,122,175, 0,124,167, 6,124,164, 9,124,165, 8, + 124,161, 11,124,165, 8,124,164, 9,124,167, 6,122,178, 0, + 119,185, 0,118,189, 0,118,189, 0,119,186, 0,121,179, 0, + 123,172, 2,124,165, 8,124,154, 18,123,137, 41,117,110,109, + 113,100,242,119,119, 77,123,137, 41,123,139, 37,123,137, 41, + 121,127, 58,118,114, 96,114,104,197,113,100,242,118,114, 96, + 121,125, 63,121,127, 58,121,125, 63,121,125, 63,124,143, 31, + 124,161, 11,123,170, 3,122,175, 0,118,188, 0,113,198, 0, + 108,206, 0,104,210, 0,102,212, 0,103,212, 0,104,210, 0, + 103,211, 0,102,213, 0,101,214, 0,101,213, 0,103,211, 0, + 105,209, 0,108,206, 0,110,203, 0,108,205, 0,107,206, 0, + 107,206, 0,108,205, 0,108,205, 0,107,207, 0,106,208, 0, + 106,208, 0,107,207, 0,108,205, 0,110,203, 0,112,200, 0, + 114,196, 0,118,189, 0,122,178, 0,124,164, 9,124,157, 15, + 124,157, 15,124,150, 22,123,141, 34,121,127, 58,120,122, 70, + 122,135, 44,123,141, 34,124,143, 31,123,141, 34,124,150, 22, + 124,162, 10,123,168, 5,123,171, 3,121,181, 0,118,189, 0, + 114,196, 0,112,200, 0,111,201, 0,111,201, 0,111,201, 0, + 112,200, 0,112,199, 0,112,199, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,113,198, 0,115,194, 0,117,191, 0, + 116,193, 0,115,194, 0,116,193, 0,116,193, 0,116,193, 0, + 117,190, 0,120,184, 0,123,174, 1,124,161, 11,124,164, 9, + 124,166, 7,124,161, 11,124,149, 24,121,125, 63,124,152, 20, + 123,173, 1,120,184, 0,119,186, 0,119,186, 0,119,186, 0, + 118,188, 0,118,189, 0,117,190, 0,117,191, 0,117,191, 0, + 118,188, 0,120,184, 0,122,178, 0,123,168, 5,124,154, 18, + 121,127, 58,124,147, 26,124,165, 8,122,176, 0,120,184, 0, + 118,189, 0,117,191, 0,118,189, 0,119,185, 0,122,176, 0, + 124,160, 12,122,132, 48,113,100,242,121,127, 58,124,152, 20, + 124,166, 7,123,174, 1,121,180, 0,120,184, 0,117,190, 0, + 115,194, 0,114,197, 0,112,199, 0,111,202, 0,110,203, 0, + 110,202, 0,109,203, 0,109,204, 0,110,202, 0,113,199, 0, + 117,192, 0,121,182, 0,124,165, 8,124,150, 22,123,137, 41, + 118,114, 96, 94, 70,242, 30, 10,132, 30, 49,213,107, 89,252, + 114,104,197,115,107,129,121,127, 58,124,147, 26,124,155, 16, + 124,157, 15,124,150, 22,123,137, 41,118,114, 96,111, 96,248, + 115,107,129,119,119, 77,122,130, 53,124,157, 15,123,171, 3, + 122,178, 0,121,182, 0,121,180, 0,121,179, 0,119,186, 0, + 118,189, 0,117,192, 0,117,192, 0,118,189, 0,119,186, 0, + 121,182, 0,122,177, 0,122,176, 0,121,179, 0,120,184, 0, + 118,188, 0,116,192, 0,114,196, 0,113,198, 0,112,200, 0, + 111,201, 0,110,203, 0,109,204, 0,108,205, 0,109,204, 0, + 111,202, 0,114,196, 0,119,187, 0,123,171, 3,124,158, 13, + 124,147, 26,123,139, 37,122,132, 48,118,117, 85,122,132, 48, + 124,145, 29,124,158, 13,124,166, 7,123,168, 5,123,169, 4, + 123,170, 3,123,173, 1,122,178, 0,120,184, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,119,185, 0,119,186, 0, + 119,186, 0,120,184, 0,121,180, 0,119,186, 0,117,190, 0, + 116,192, 0,116,192, 0,118,188, 0,121,180, 0,122,177, 0, + 121,180, 0,118,189, 0,114,196, 0,113,199, 0,113,198, 0, + 115,195, 0,118,189, 0,121,180, 0,121,180, 0,121,180, 0, + 122,178, 0,123,173, 1,123,168, 5,124,164, 9,124,166, 7, + 124,165, 8,124,165, 8,123,170, 3,123,170, 3,123,168, 5, + 123,171, 3,122,178, 0,121,182, 0,121,182, 0,122,176, 0, + 124,167, 6,124,160, 12,124,150, 22,123,139, 37,121,125, 63, + 113,100,242,105, 84,252,113,100,242,119,119, 77,119,119, 77, + 117,110,109,109, 92,251, 94, 70,242, 99, 75,247, 94, 70,242, + 105, 84,252,115,107,129,119,119, 77,120,122, 70,121,127, 58, + 124,145, 29,124,162, 10,123,172, 2,122,178, 0,117,191, 0, + 110,202, 0,105,209, 0,101,214, 0, 99,215, 0,100,215, 0, + 102,213, 0,102,212, 0,100,214, 0, 99,215, 0,100,215, 0, + 101,214, 0,103,211, 0,106,207, 0,108,205, 0,109,204, 0, + 108,206, 0,107,206, 0,108,206, 0,109,204, 0,110,202, 0, + 110,203, 0,110,203, 0,111,201, 0,112,200, 0,113,198, 0, + 114,196, 0,117,192, 0,119,186, 0,122,178, 0,124,165, 8, + 124,157, 15,124,155, 16,124,152, 20,124,143, 31,122,132, 48, + 121,127, 58,123,137, 41,124,143, 31,123,141, 34,123,137, 41, + 124,155, 16,124,167, 6,122,175, 0,121,179, 0,121,181, 0, + 119,186, 0,116,193, 0,114,197, 0,112,199, 0,112,200, 0, + 112,199, 0,112,199, 0,112,199, 0,112,200, 0,110,202, 0, + 109,204, 0,108,205, 0,109,204, 0,110,202, 0,112,200, 0, + 113,198, 0,112,199, 0,112,199, 0,113,199, 0,113,198, 0, + 114,197, 0,115,194, 0,118,189, 0,121,179, 0,124,161, 11, + 124,143, 31,124,147, 26,123,139, 37,120,122, 70,121,127, 58, + 124,155, 16,123,173, 1,121,181, 0,120,182, 0,121,179, 0, + 120,183, 0,119,185, 0,120,184, 0,121,181, 0,121,180, 0, + 121,180, 0,122,178, 0,123,173, 1,124,167, 6,124,158, 13, + 124,147, 26,124,152, 20,124,154, 18,124,164, 9,123,172, 2, + 121,180, 0,119,185, 0,119,187, 0,119,186, 0,121,181, 0, + 123,170, 3,124,150, 22,118,117, 85,113,100,242,123,137, 41, + 124,157, 15,123,168, 5,122,175, 0,121,180, 0,120,184, 0, + 118,188, 0,116,192, 0,114,196, 0,111,202, 0,108,205, 0, + 107,207, 0,108,205, 0,111,202, 0,112,200, 0,113,199, 0, + 115,195, 0,118,189, 0,122,178, 0,124,161, 11,124,143, 31, + 121,127, 58,114,104,197, 69, 60,230, 30, 55,222, 94, 70,242, + 114,104,197,119,119, 77,119,119, 77,117,110,109,119,119, 77, + 122,135, 44,123,137, 41,122,130, 53,118,114, 96,105, 84,252, + 109, 92,251,118,117, 85,121,125, 63,123,139, 37,124,164, 9, + 122,177, 0,120,184, 0,119,186, 0,119,186, 0,120,182, 0, + 120,184, 0,118,188, 0,118,189, 0,118,189, 0,119,186, 0, + 120,182, 0,122,177, 0,123,172, 2,123,171, 3,122,178, 0, + 119,185, 0,118,188, 0,118,189, 0,116,193, 0,115,195, 0, + 114,197, 0,113,198, 0,112,200, 0,110,202, 0,109,203, 0, + 109,203, 0,111,201, 0,114,196, 0,119,186, 0,123,169, 4, + 124,155, 16,123,137, 41,120,122, 70,117,110,109,120,122, 70, + 123,141, 34,124,157, 15,123,168, 5,122,176, 0,122,178, 0, + 122,178, 0,121,182, 0,120,184, 0,119,185, 0,119,186, 0, + 116,192, 0,115,194, 0,116,192, 0,117,191, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,119,185, 0,117,191, 0, + 114,196, 0,113,199, 0,112,199, 0,114,196, 0,117,190, 0, + 121,180, 0,123,170, 3,120,183, 0,117,191, 0,115,194, 0, + 115,195, 0,116,192, 0,119,186, 0,122,177, 0,124,166, 7, + 124,167, 6,124,165, 8,124,161, 11,124,160, 12,124,164, 9, + 124,166, 7,124,166, 7,124,162, 10,123,169, 4,123,171, 3, + 123,169, 4,124,166, 7,123,170, 3,123,173, 1,123,171, 3, + 124,162, 10,124,161, 11,124,150, 22,122,130, 53,118,114, 96, + 113,100,242,105, 84,252,111, 96,248,105, 84,252,114,104,197, + 113,100,242,102, 80,250, 30, 55,222, 87, 65,237, 99, 75,247, + 99, 75,247, 30, 49,213,102, 80,250,113,100,242,118,114, 96, + 120,122, 70,124,145, 29,124,164, 9,123,174, 1,122,178, 0, + 116,192, 0,110,203, 0,103,211, 0, 99,215, 0, 97,217, 0, + 98,217, 0,100,215, 0,102,212, 0,100,215, 0, 99,216, 0, + 99,215, 0,101,214, 0,103,211, 0,106,208, 0,108,206, 0, + 108,205, 0,108,206, 0,108,206, 0,108,205, 0,109,204, 0, + 110,202, 0,111,201, 0,113,198, 0,115,195, 0,116,193, 0, + 117,192, 0,117,191, 0,118,188, 0,120,184, 0,122,177, 0, + 124,166, 7,124,150, 22,124,154, 18,124,154, 18,124,152, 20, + 124,149, 24,124,145, 29,124,145, 29,124,149, 24,124,147, 26, + 124,155, 16,124,164, 9,123,172, 2,121,180, 0,120,184, 0, + 119,186, 0,118,188, 0,118,189, 0,115,194, 0,114,197, 0, + 113,199, 0,111,202, 0,109,204, 0,108,205, 0,108,205, 0, + 110,203, 0,108,205, 0,107,207, 0,106,207, 0,107,207, 0, + 108,205, 0,109,203, 0,111,202, 0,111,201, 0,112,200, 0, + 112,200, 0,113,199, 0,114,196, 0,117,191, 0,121,181, 0, + 124,164, 9,121,125, 63,119,119, 77,118,114, 96,115,107,129, + 122,135, 44,124,154, 18,123,170, 3,122,177, 0,122,177, 0, + 120,182, 0,119,186, 0,119,187, 0,119,186, 0,121,182, 0, + 122,175, 0,124,167, 6,124,158, 13,124,152, 20,124,147, 26, + 124,155, 16,124,162, 10,124,167, 6,123,169, 4,123,169, 4, + 123,168, 5,122,175, 0,121,181, 0,120,182, 0,121,181, 0, + 122,175, 0,124,162, 10,123,137, 41,109, 92,251,113,100,242, + 123,137, 41,124,155, 16,124,166, 7,123,171, 3,122,175, 0, + 122,178, 0,120,184, 0,117,192, 0,112,199, 0,109,204, 0, + 106,207, 0,106,208, 0,107,206, 0,110,202, 0,115,195, 0, + 115,194, 0,117,192, 0,119,186, 0,122,176, 0,124,160, 12, + 122,132, 48,118,117, 85,114,104,197,114,104,197,117,110,109, + 117,110,109,118,117, 85,120,122, 70,119,119, 77,115,107,129, + 105, 84,252,117,110,109,118,114, 96,115,107,129,107, 89,252, + 30, 55,222,105, 84,252,117,110,109,118,117, 85,124,149, 24, + 123,169, 4,121,182, 0,118,188, 0,117,191, 0,117,190, 0, + 119,187, 0,120,182, 0,119,186, 0,118,188, 0,119,186, 0, + 120,183, 0,122,178, 0,123,171, 3,124,166, 7,124,167, 6, + 122,178, 0,119,185, 0,118,188, 0,118,189, 0,118,189, 0, + 117,192, 0,116,193, 0,115,195, 0,114,197, 0,112,200, 0, + 111,201, 0,111,201, 0,112,199, 0,115,194, 0,120,184, 0, + 124,166, 7,124,152, 20,121,125, 63,115,107,129,113,100,242, + 122,132, 48,124,150, 22,124,166, 7,122,175, 0,121,182, 0, + 119,186, 0,117,190, 0,116,193, 0,115,195, 0,115,195, 0, + 115,194, 0,112,200, 0,113,198, 0,114,197, 0,115,195, 0, + 115,194, 0,115,194, 0,115,194, 0,116,193, 0,117,190, 0, + 115,195, 0,112,200, 0,110,202, 0,110,203, 0,112,200, 0, + 115,194, 0,119,186, 0,122,175, 0,122,175, 0,120,182, 0, + 119,187, 0,118,188, 0,119,186, 0,121,179, 0,123,170, 3, + 124,158, 13,124,150, 22,124,149, 24,124,152, 20,124,157, 15, + 124,161, 11,124,164, 9,124,165, 8,124,161, 11,124,165, 8, + 123,168, 5,124,167, 6,124,164, 9,124,162, 10,124,161, 11, + 124,158, 13,124,158, 13,124,155, 16,123,141, 34,118,114, 96, + 94, 70,242, 69, 60,230,109, 92,251,114,104,197,107, 89,252, + 102, 80,250, 94, 70,242, 30, 43,203, 30, 31,180, 87, 65,237, + 105, 84,252,107, 89,252, 87, 65,237,105, 84,252,102, 80,250, + 109, 92,251,117,110,109,124,143, 31,124,164, 9,123,174, 1, + 121,180, 0,117,191, 0,110,202, 0,104,210, 0,100,215, 0, + 97,217, 0, 97,217, 0, 99,216, 0,101,214, 0,101,214, 0, + 100,214, 0,101,214, 0,102,212, 0,104,210, 0,104,210, 0, + 103,211, 0,104,210, 0,105,209, 0,106,207, 0,106,208, 0, + 105,209, 0,106,208, 0,107,207, 0,108,205, 0,110,202, 0, + 113,198, 0,117,192, 0,117,191, 0,117,190, 0,118,188, 0, + 119,185, 0,122,178, 0,123,169, 4,124,157, 15,124,155, 16, + 124,157, 15,124,158, 13,124,160, 12,124,161, 11,124,164, 9, + 123,168, 5,123,174, 1,122,178, 0,121,181, 0,120,182, 0, + 119,186, 0,118,189, 0,117,190, 0,116,192, 0,115,195, 0, + 112,199, 0,110,202, 0,108,206, 0,106,208, 0,105,209, 0, + 105,209, 0,106,207, 0,106,207, 0,105,209, 0,104,210, 0, + 103,211, 0,104,210, 0,105,209, 0,106,208, 0,106,207, 0, + 108,206, 0,111,201, 0,114,197, 0,115,194, 0,117,190, 0, + 121,182, 0,124,165, 8,121,127, 58, 99, 75,247, 87, 65,237, + 115,107,129,123,137, 41,124,154, 18,124,166, 7,123,172, 2, + 122,178, 0,120,183, 0,119,186, 0,119,186, 0,120,184, 0, + 121,179, 0,123,171, 3,124,162, 10,124,155, 16,124,158, 13, + 124,157, 15,124,165, 8,123,171, 3,122,176, 0,122,178, 0, + 121,179, 0,122,178, 0,123,172, 2,123,172, 2,122,175, 0, + 123,173, 1,124,167, 6,124,152, 20,121,125, 63, 99, 75,247, + 105, 84,252,119,119, 77,123,141, 34,124,154, 18,124,160, 12, + 124,165, 8,122,178, 0,118,188, 0,115,194, 0,111,201, 0, + 108,205, 0,107,207, 0,107,207, 0,109,204, 0,112,199, 0, + 116,192, 0,117,192, 0,118,188, 0,120,183, 0,123,174, 1, + 124,160, 12,124,145, 29,124,143, 31,123,139, 37,123,139, 37, + 123,139, 37,123,137, 41,122,132, 48,121,127, 58,117,110,109, + 105, 84,252, 30, 55,222,107, 89,252,111, 96,248,107, 89,252, + 94, 70,242, 87, 65,237,105, 84,252,115,107,129,122,132, 48, + 124,158, 13,122,175, 0,119,185, 0,117,191, 0,116,192, 0, + 117,192, 0,118,189, 0,119,185, 0,119,185, 0,119,186, 0, + 120,184, 0,121,181, 0,122,175, 0,122,175, 0,122,176, 0, + 121,181, 0,119,186, 0,118,188, 0,117,190, 0,117,190, 0, + 118,188, 0,117,191, 0,114,196, 0,113,199, 0,112,199, 0, + 113,198, 0,112,200, 0,112,200, 0,113,198, 0,116,193, 0, + 120,183, 0,124,162, 10,124,145, 29,114,104,197,107, 89,252, + 117,110,109,124,145, 29,123,170, 3,120,184, 0,118,189, 0, + 116,193, 0,114,197, 0,112,200, 0,110,202, 0,110,203, 0, + 110,202, 0,111,201, 0,107,206, 0,108,205, 0,108,205, 0, + 109,204, 0,111,202, 0,113,198, 0,114,197, 0,115,194, 0, + 115,194, 0,113,198, 0,110,202, 0,108,205, 0,108,205, 0, + 110,202, 0,114,197, 0,118,189, 0,121,180, 0,122,177, 0, + 122,175, 0,123,172, 2,123,173, 1,123,171, 3,124,165, 8, + 124,155, 16,124,143, 31,123,137, 41,123,137, 41,124,143, 31, + 124,149, 24,124,154, 18,124,157, 15,124,157, 15,124,154, 18, + 124,160, 12,124,165, 8,124,165, 8,124,164, 9,124,160, 12, + 124,157, 15,124,155, 16,124,154, 18,124,149, 24,122,130, 53, + 109, 92,251, 30, 15,143, 69, 60,230,113,100,242,117,110,109, + 109, 92,251, 30, 55,222, 30, 26,168, 30, 15,143, 30, 37,192, + 99, 75,247,109, 92,251,111, 96,248,102, 80,250,111, 96,248, + 109, 92,251, 99, 75,247,111, 96,248,122,135, 44,124,160, 12, + 123,172, 2,122,178, 0,118,188, 0,112,200, 0,106,208, 0, + 101,214, 0, 98,216, 0, 98,217, 0, 99,216, 0,100,215, 0, + 100,214, 0,101,214, 0,101,214, 0,101,214, 0,100,214, 0, + 100,215, 0,100,215, 0,101,214, 0,102,213, 0,104,210, 0, + 102,212, 0,101,214, 0,100,214, 0,101,214, 0,102,213, 0, + 103,211, 0,106,207, 0,111,201, 0,116,193, 0,116,193, 0, + 116,193, 0,117,191, 0,118,188, 0,121,181, 0,123,173, 1, + 124,166, 7,124,160, 12,124,162, 10,123,168, 5,123,172, 2, + 122,177, 0,121,182, 0,119,185, 0,118,188, 0,118,189, 0, + 118,188, 0,119,185, 0,119,187, 0,118,189, 0,117,192, 0, + 114,196, 0,112,200, 0,109,204, 0,106,207, 0,105,209, 0, + 103,211, 0,103,211, 0,105,209, 0,104,210, 0,102,212, 0, + 101,213, 0,101,214, 0,101,214, 0,101,213, 0,102,212, 0, + 102,212, 0,103,211, 0,108,206, 0,113,198, 0,116,192, 0, + 118,189, 0,121,182, 0,124,167, 6,122,135, 44, 87, 65,237, + 30, 26,168,111, 96,248,122,132, 48,124,152, 20,124,165, 8, + 123,172, 2,122,178, 0,120,182, 0,120,184, 0,120,183, 0, + 121,179, 0,123,172, 2,123,168, 5,123,169, 4,123,172, 2, + 123,174, 1,123,171, 3,123,172, 2,122,178, 0,120,183, 0, + 119,185, 0,119,186, 0,119,185, 0,121,179, 0,124,167, 6, + 124,161, 11,124,160, 12,124,152, 20,123,137, 41,115,107,129, + 30, 55,222, 30, 31,180,102, 80,250,115,107,129,121,125, 63, + 124,145, 29,123,168, 5,120,182, 0,117,190, 0,114,196, 0, + 112,200, 0,110,202, 0,109,203, 0,110,202, 0,112,199, 0, + 116,193, 0,117,191, 0,118,189, 0,119,186, 0,121,180, 0, + 123,171, 3,124,160, 12,124,161, 11,124,161, 11,124,160, 12, + 124,158, 13,124,157, 15,124,152, 20,123,141, 34,122,135, 44, + 118,117, 85,105, 84,252, 99, 75,247,107, 89,252,105, 84,252, + 87, 65,237, 30, 43,203, 69, 60,230,107, 89,252,118,117, 85, + 123,141, 34,124,162, 10,122,177, 0,119,186, 0,117,190, 0, + 117,192, 0,117,191, 0,118,188, 0,119,185, 0,119,185, 0, + 119,185, 0,120,183, 0,121,181, 0,121,181, 0,121,182, 0, + 121,181, 0,118,188, 0,116,192, 0,115,194, 0,115,194, 0, + 116,193, 0,116,192, 0,116,193, 0,114,197, 0,112,200, 0, + 112,200, 0,113,198, 0,112,200, 0,112,200, 0,113,198, 0, + 116,193, 0,120,183, 0,124,164, 9,121,125, 63, 99, 75,247, + 30, 55,222,122,135, 44,123,169, 4,119,186, 0,115,195, 0, + 112,200, 0,110,202, 0,108,205, 0,107,207, 0,106,208, 0, + 106,208, 0,106,207, 0,108,206, 0,103,211, 0,103,211, 0, + 103,212, 0,103,211, 0,105,209, 0,108,205, 0,112,199, 0, + 115,195, 0,114,197, 0,111,201, 0,109,203, 0,108,205, 0, + 109,204, 0,111,202, 0,114,197, 0,117,190, 0,120,182, 0, + 122,176, 0,123,170, 3,124,162, 10,124,149, 24,122,135, 44, + 122,130, 53,119,119, 77,117,110,109,120,122, 70,122,130, 53, + 122,135, 44,123,137, 41,124,143, 31,124,145, 29,124,143, 31, + 123,139, 37,124,154, 18,124,161, 11,124,162, 10,124,161, 11, + 124,158, 13,124,154, 18,124,150, 22,124,150, 22,124,145, 29, + 121,125, 63,102, 80,250, 30, 31,180,107, 89,252,120,122, 70, + 122,132, 48,119,119, 77,109, 92,251, 69, 60,230, 30, 49,213, + 105, 84,252,114,104,197,117,110,109,115,107,129,107, 89,252, + 117,110,109,115,107,129,109, 92,251, 94, 70,242,119,119, 77, + 124,150, 22,124,167, 6,123,174, 1,120,184, 0,114,197, 0, + 108,205, 0,104,210, 0,101,214, 0,100,215, 0,100,215, 0, + 100,215, 0, 99,215, 0, 98,216, 0, 98,217, 0, 97,217, 0, + 97,218, 0, 97,217, 0, 98,217, 0, 99,216, 0,100,215, 0, + 101,213, 0, 99,216, 0, 97,218, 0, 95,219, 0, 95,219, 0, + 96,219, 0, 98,217, 0,101,214, 0,106,208, 0,112,200, 0, + 115,194, 0,114,196, 0,115,195, 0,116,193, 0,118,188, 0, + 120,182, 0,122,177, 0,123,171, 3,124,166, 7,123,171, 3, + 122,178, 0,120,184, 0,118,189, 0,116,192, 0,115,194, 0, + 115,194, 0,116,192, 0,118,188, 0,120,183, 0,119,185, 0, + 118,189, 0,115,194, 0,112,200, 0,109,204, 0,106,208, 0, + 104,210, 0,103,211, 0,103,211, 0,105,209, 0,102,212, 0, + 101,214, 0,100,215, 0, 99,215, 0, 99,215, 0,100,215, 0, + 100,215, 0,100,215, 0,101,213, 0,106,208, 0,112,200, 0, + 118,189, 0,119,186, 0,121,180, 0,123,168, 5,123,141, 34, + 102, 80,250, 94, 70,242,118,117, 85,123,141, 34,124,154, 18, + 124,161, 11,123,170, 3,122,177, 0,121,179, 0,121,179, 0, + 122,177, 0,123,172, 2,122,175, 0,122,177, 0,122,178, 0, + 121,181, 0,121,182, 0,121,179, 0,122,177, 0,120,182, 0, + 119,186, 0,118,189, 0,118,189, 0,118,188, 0,120,183, 0, + 123,171, 3,124,152, 20,122,135, 44,121,127, 58,117,110,109, + 107, 89,252, 69, 60,230, 30, 37,192, 30, 55,222,105, 84,252, + 121,125, 63,124,154, 18,123,171, 3,120,183, 0,118,189, 0, + 115,194, 0,114,197, 0,113,198, 0,113,198, 0,114,196, 0, + 117,192, 0,118,189, 0,118,189, 0,119,187, 0,120,182, 0, + 122,176, 0,124,167, 6,124,166, 7,123,168, 5,123,169, 4, + 123,169, 4,123,168, 5,124,165, 8,124,158, 13,124,147, 26, + 123,137, 41,119,119, 77,109, 92,251,118,114, 96,118,114, 96, + 115,107,129,107, 89,252, 69, 60,230, 94, 70,242,107, 89,252, + 118,114, 96,123,141, 34,124,164, 9,122,176, 0,120,184, 0, + 119,187, 0,118,188, 0,119,187, 0,119,185, 0,120,184, 0, + 119,185, 0,120,184, 0,119,185, 0,119,185, 0,119,186, 0, + 119,185, 0,119,186, 0,116,192, 0,114,196, 0,114,197, 0, + 114,197, 0,114,197, 0,114,196, 0,116,193, 0,114,197, 0, + 112,199, 0,113,199, 0,112,199, 0,111,201, 0,111,201, 0, + 113,198, 0,116,193, 0,120,183, 0,124,164, 9,120,122, 70, + 105, 84,252,117,110,109,124,161, 11,120,183, 0,115,195, 0, + 110,203, 0,107,207, 0,105,209, 0,104,210, 0,103,212, 0, + 102,212, 0,103,212, 0,103,211, 0,105,209, 0,101,214, 0, + 99,215, 0, 98,217, 0, 98,217, 0, 99,215, 0,103,212, 0, + 108,206, 0,113,198, 0,113,198, 0,111,202, 0,109,203, 0, + 109,204, 0,110,203, 0,112,200, 0,115,195, 0,118,189, 0, + 120,183, 0,122,175, 0,124,162, 10,124,143, 31,119,119, 77, + 105, 84,252, 30, 55,222,109, 92,251,119,119, 77,123,137, 41, + 124,147, 26,124,150, 22,124,145, 29,121,127, 58,120,122, 70, + 119,119, 77,121,127, 58,124,147, 26,124,155, 16,124,157, 15, + 124,155, 16,124,152, 20,124,149, 24,124,147, 26,124,147, 26, + 124,143, 31,121,125, 63,105, 84,252, 99, 75,247,118,117, 85, + 123,141, 34,124,147, 26,123,137, 41,118,114, 96,105, 84,252, + 111, 96,248,119,119, 77,122,132, 48,123,137, 41,122,135, 44, + 118,117, 85,120,122, 70,120,122, 70,115,107,129,105, 84,252, + 107, 89,252,122,135, 44,124,157, 15,124,166, 7,121,180, 0, + 117,192, 0,112,200, 0,108,206, 0,105,209, 0,103,212, 0, + 102,213, 0,101,214, 0, 99,216, 0, 97,218, 0, 95,219, 0, + 95,220, 0, 95,220, 0, 96,219, 0, 97,218, 0, 98,216, 0, + 100,215, 0, 99,216, 0, 96,219, 0, 94,221, 0, 92,222, 0, + 92,222, 0, 93,222, 0, 94,220, 0, 98,217, 0,103,212, 0, + 109,204, 0,115,195, 0,114,197, 0,114,197, 0,114,196, 0, + 117,191, 0,119,186, 0,121,182, 0,122,178, 0,123,172, 2, + 123,171, 3,121,180, 0,119,187, 0,116,192, 0,114,196, 0, + 114,197, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 121,179, 0,119,186, 0,116,192, 0,113,199, 0,109,203, 0, + 106,207, 0,105,209, 0,104,210, 0,105,209, 0,103,211, 0, + 101,213, 0,100,214, 0,100,215, 0,100,215, 0,100,215, 0, + 100,214, 0,100,214, 0,100,214, 0,101,213, 0,106,208, 0, + 112,200, 0,118,188, 0,121,182, 0,122,177, 0,124,166, 7, + 123,141, 34,120,122, 70,118,114, 96,122,132, 48,124,149, 24, + 124,157, 15,124,158, 13,124,165, 8,123,171, 3,123,174, 1, + 123,172, 2,123,171, 3,122,177, 0,121,180, 0,121,181, 0, + 120,182, 0,119,185, 0,119,186, 0,120,184, 0,121,180, 0, + 120,183, 0,119,186, 0,118,188, 0,118,189, 0,119,187, 0, + 121,182, 0,123,170, 3,124,150, 22,118,114, 96,109, 92,251, + 117,110,109,117,110,109,113,100,242,114,104,197,119,119, 77, + 122,132, 48,123,139, 37,124,157, 15,123,171, 3,121,180, 0, + 119,186, 0,118,189, 0,117,191, 0,117,191, 0,118,189, 0, + 119,187, 0,118,188, 0,118,188, 0,119,187, 0,120,184, 0, + 122,178, 0,123,169, 4,124,164, 9,124,167, 6,123,170, 3, + 123,172, 2,123,173, 1,123,172, 2,123,168, 5,124,158, 13, + 124,150, 22,123,139, 37,120,122, 70,121,127, 58,123,139, 37, + 122,135, 44,121,125, 63,114,104,197, 99, 75,247, 99, 75,247, + 111, 96,248,120,122, 70,123,141, 34,124,157, 15,123,170, 3, + 122,178, 0,121,182, 0,120,182, 0,121,182, 0,121,180, 0, + 120,184, 0,119,185, 0,119,186, 0,118,188, 0,118,189, 0, + 118,189, 0,118,188, 0,118,189, 0,115,194, 0,114,197, 0, + 112,199, 0,111,202, 0,111,202, 0,112,200, 0,114,196, 0, + 114,196, 0,113,198, 0,113,199, 0,111,201, 0,111,202, 0, + 111,201, 0,113,198, 0,116,192, 0,120,182, 0,124,164, 9, + 120,122, 70,115,107,129,124,143, 31,122,176, 0,117,192, 0, + 111,202, 0,106,208, 0,102,212, 0,101,213, 0,101,214, 0, + 101,214, 0,101,214, 0,101,213, 0,102,212, 0,103,212, 0, + 100,215, 0, 97,218, 0, 94,221, 0, 93,222, 0, 94,220, 0, + 98,217, 0,103,211, 0,109,203, 0,111,201, 0,111,201, 0, + 110,202, 0,110,202, 0,112,200, 0,114,197, 0,116,192, 0, + 119,187, 0,120,182, 0,122,177, 0,123,168, 5,124,154, 18, + 121,125, 63,118,114, 96,114,104,197,119,119, 77,122,130, 53, + 124,145, 29,124,157, 15,124,160, 12,124,155, 16,123,139, 37, + 113,100,242,105, 84,252,117,110,109,122,135, 44,124,145, 29, + 124,149, 24,124,147, 26,124,143, 31,123,139, 37,123,137, 41, + 123,141, 34,123,139, 37,120,122, 70,117,110,109,114,104,197, + 122,132, 48,124,150, 22,124,155, 16,124,145, 29,120,122, 70, + 114,104,197,121,125, 63,123,141, 34,124,152, 20,124,157, 15, + 124,154, 18,124,145, 29,124,147, 26,123,137, 41,120,122, 70, + 113,100,242,113,100,242,117,110,109,123,141, 34,124,154, 18, + 123,173, 1,119,186, 0,115,194, 0,112,200, 0,109,204, 0, + 107,207, 0,105,209, 0,103,212, 0,100,215, 0, 97,218, 0, + 95,220, 0, 94,221, 0, 94,221, 0, 95,219, 0, 98,217, 0, + 99,215, 0,100,215, 0, 97,218, 0, 94,221, 0, 92,222, 0, + 91,223, 0, 92,223, 0, 93,222, 0, 94,220, 0, 97,218, 0, + 102,212, 0,108,205, 0,114,197, 0,114,196, 0,114,196, 0, + 115,194, 0,117,191, 0,119,186, 0,120,183, 0,121,180, 0, + 122,177, 0,123,172, 2,122,178, 0,119,186, 0,116,192, 0, + 114,196, 0,114,197, 0,114,196, 0,116,193, 0,118,189, 0, + 120,184, 0,121,181, 0,119,185, 0,117,190, 0,114,197, 0, + 110,202, 0,107,207, 0,105,209, 0,105,209, 0,105,209, 0, + 103,212, 0,101,213, 0,101,214, 0,101,214, 0,101,213, 0, + 102,212, 0,103,212, 0,103,211, 0,103,212, 0,104,210, 0, + 108,206, 0,113,198, 0,119,186, 0,122,175, 0,123,169, 4, + 124,158, 13,124,147, 26,123,141, 34,122,135, 44,123,141, 34, + 124,154, 18,124,158, 13,124,158, 13,124,155, 16,124,164, 9, + 124,166, 7,124,166, 7,122,175, 0,121,179, 0,120,182, 0, + 120,183, 0,120,184, 0,119,187, 0,118,188, 0,119,186, 0, + 121,181, 0,121,181, 0,120,182, 0,120,184, 0,120,183, 0, + 121,182, 0,122,175, 0,124,162, 10,123,139, 37,117,110,109, + 111, 96,248,118,117, 85,121,125, 63,121,125, 63,123,139, 37, + 124,152, 20,124,160, 12,124,164, 9,124,162, 10,124,166, 7, + 123,174, 1,122,178, 0,121,181, 0,121,182, 0,121,180, 0, + 120,184, 0,119,186, 0,119,187, 0,119,186, 0,120,184, 0, + 121,179, 0,123,171, 3,124,160, 12,124,160, 12,124,162, 10, + 124,166, 7,123,169, 4,123,171, 3,123,170, 3,124,166, 7, + 124,157, 15,124,152, 20,124,145, 29,122,135, 44,124,150, 22, + 124,155, 16,124,150, 22,123,137, 41,118,114, 96,113,100,242, + 102, 80,250,109, 92,251,119,119, 77,123,141, 34,124,154, 18, + 124,160, 12,123,168, 5,123,172, 2,123,174, 1,123,174, 1, + 121,181, 0,120,184, 0,120,184, 0,119,187, 0,117,190, 0, + 117,192, 0,117,192, 0,117,190, 0,117,190, 0,115,194, 0, + 113,198, 0,110,203, 0,108,205, 0,108,205, 0,110,203, 0, + 112,199, 0,113,199, 0,112,199, 0,111,201, 0,110,202, 0, + 110,202, 0,111,201, 0,114,197, 0,117,191, 0,121,180, 0, + 124,160, 12,120,122, 70,120,122, 70,124,160, 12,120,184, 0, + 114,197, 0,108,206, 0,103,212, 0,100,214, 0,100,215, 0, + 100,215, 0, 98,217, 0, 97,217, 0, 99,216, 0,102,213, 0, + 102,212, 0,100,215, 0, 95,220, 0, 91,223, 0, 89,225, 0, + 90,224, 0, 93,221, 0, 99,216, 0,105,209, 0,108,206, 0, + 109,203, 0,112,200, 0,112,199, 0,114,197, 0,116,193, 0, + 117,190, 0,119,187, 0,121,181, 0,122,178, 0,123,172, 2, + 124,165, 8,123,169, 4,123,168, 5,124,161, 11,124,149, 24, + 124,155, 16,124,161, 11,124,165, 8,124,166, 7,124,162, 10, + 124,149, 24,118,114, 96, 30, 37,192,105, 84,252,118,117, 85, + 122,130, 53,122,135, 44,122,132, 48,121,127, 58,120,122, 70, + 120,122, 70,121,127, 58,122,130, 53,119,119, 77,120,122, 70, + 119,119, 77,123,139, 37,124,154, 18,124,157, 15,124,145, 29, + 120,122, 70,121,125, 63,124,143, 31,124,158, 13,124,166, 7, + 123,170, 3,123,168, 5,124,164, 9,124,165, 8,124,157, 15, + 123,137, 41,118,114, 96,117,110,109,115,107,129,118,117, 85, + 122,135, 44,124,165, 8,121,179, 0,118,188, 0,116,193, 0, + 114,197, 0,111,201, 0,109,204, 0,106,208, 0,102,212, 0, + 98,217, 0, 95,220, 0, 94,221, 0, 94,220, 0, 97,218, 0, + 99,215, 0,101,214, 0, 99,216, 0, 96,219, 0, 94,221, 0, + 93,222, 0, 93,222, 0, 94,221, 0, 95,220, 0, 97,218, 0, + 99,215, 0,103,211, 0,108,205, 0,114,197, 0,116,193, 0, + 117,192, 0,118,189, 0,119,186, 0,121,182, 0,121,180, 0, + 122,178, 0,122,178, 0,122,176, 0,123,172, 2,121,182, 0, + 118,189, 0,116,193, 0,115,194, 0,115,194, 0,117,191, 0, + 119,187, 0,120,184, 0,119,185, 0,120,184, 0,118,189, 0, + 114,196, 0,110,202, 0,108,206, 0,106,208, 0,106,208, 0, + 106,208, 0,103,211, 0,102,212, 0,102,212, 0,103,211, 0, + 105,209, 0,106,207, 0,107,206, 0,108,205, 0,107,206, 0, + 108,205, 0,111,202, 0,115,194, 0,120,183, 0,124,166, 7, + 124,158, 13,124,157, 15,124,152, 20,124,145, 29,123,141, 34, + 124,143, 31,124,155, 16,124,161, 11,124,158, 13,124,149, 24, + 124,152, 20,124,154, 18,123,168, 5,122,177, 0,121,181, 0, + 120,183, 0,119,186, 0,118,189, 0,118,188, 0,118,189, 0, + 118,188, 0,120,183, 0,122,175, 0,123,174, 1,123,173, 1, + 123,171, 3,123,168, 5,124,160, 12,124,143, 31,122,130, 53, + 121,127, 58,120,122, 70,120,122, 70,122,132, 48,124,145, 29, + 124,158, 13,123,168, 5,123,173, 1,122,175, 0,123,172, 2, + 124,164, 9,124,162, 10,124,166, 7,124,167, 6,123,171, 3, + 121,180, 0,120,184, 0,119,186, 0,120,184, 0,121,182, 0, + 122,178, 0,123,172, 2,124,161, 11,124,147, 26,124,157, 15, + 124,161, 11,124,158, 13,124,161, 11,124,165, 8,124,165, 8, + 124,160, 12,124,158, 13,124,157, 15,124,152, 20,124,155, 16, + 124,165, 8,124,166, 7,124,160, 12,124,145, 29,124,147, 26, + 124,143, 31,121,127, 58,117,110,109,115,107,129,122,132, 48, + 124,145, 29,124,150, 22,124,150, 22,124,158, 13,124,162, 10, + 123,171, 3,121,180, 0,120,184, 0,120,182, 0,118,189, 0, + 116,193, 0,115,194, 0,115,194, 0,117,192, 0,118,189, 0, + 116,193, 0,111,201, 0,108,205, 0,107,207, 0,107,207, 0, + 108,205, 0,111,201, 0,112,200, 0,111,201, 0,111,202, 0, + 110,202, 0,111,202, 0,112,200, 0,115,195, 0,118,188, 0, + 122,176, 0,124,152, 20,121,125, 63,121,127, 58,124,167, 6, + 118,188, 0,112,200, 0,106,207, 0,102,213, 0, 99,215, 0, + 99,216, 0, 95,220, 0, 91,223, 0, 91,224, 0, 92,222, 0, + 97,218, 0,102,212, 0,101,214, 0, 95,219, 0, 91,224, 0, + 88,226, 0, 87,227, 0, 90,224, 0, 95,220, 0,101,213, 0, + 105,209, 0,107,207, 0,110,202, 0,114,196, 0,116,193, 0, + 117,192, 0,117,191, 0,119,187, 0,122,178, 0,122,177, 0, + 121,180, 0,119,186, 0,118,189, 0,118,188, 0,120,184, 0, + 122,176, 0,123,169, 4,123,174, 1,122,178, 0,122,177, 0, + 123,171, 3,124,155, 16,121,125, 63, 30, 55,222, 30, 37,192, + 107, 89,252,115,107,129,118,114, 96,117,110,109,115,107,129, + 117,110,109,114,104,197,118,117, 85,120,122, 70,119,119, 77, + 121,127, 58,121,127, 58,124,143, 31,124,154, 18,124,154, 18, + 123,141, 34,121,125, 63,123,141, 34,124,158, 13,123,169, 4, + 122,177, 0,121,180, 0,121,179, 0,122,175, 0,122,176, 0, + 123,170, 3,124,155, 16,121,125, 63,119,119, 77,118,117, 85, + 113,100,242,118,114, 96,124,152, 20,123,170, 3,121,179, 0, + 119,186, 0,118,189, 0,115,195, 0,112,199, 0,110,203, 0, + 105,209, 0,100,214, 0, 97,218, 0, 95,219, 0, 96,219, 0, + 98,217, 0,101,214, 0,103,212, 0, 99,215, 0, 97,218, 0, + 95,220, 0, 95,220, 0, 96,219, 0, 98,217, 0,100,215, 0, + 102,213, 0,104,210, 0,107,207, 0,110,202, 0,114,196, 0, + 118,188, 0,120,184, 0,121,181, 0,122,175, 0,123,171, 3, + 123,171, 3,123,173, 1,122,175, 0,122,176, 0,122,175, 0, + 123,173, 1,121,182, 0,119,187, 0,118,189, 0,117,190, 0, + 118,188, 0,119,185, 0,119,185, 0,119,186, 0,120,183, 0, + 119,187, 0,114,196, 0,110,202, 0,107,206, 0,106,208, 0, + 106,208, 0,107,207, 0,105,209, 0,105,209, 0,106,208, 0, + 107,207, 0,109,204, 0,111,201, 0,113,199, 0,113,198, 0, + 113,198, 0,113,199, 0,114,196, 0,118,189, 0,122,178, 0, + 124,160, 12,124,155, 16,124,155, 16,124,150, 22,124,143, 31, + 123,137, 41,123,141, 34,124,155, 16,124,160, 12,124,158, 13, + 124,149, 24,123,137, 41,124,155, 16,123,170, 3,122,178, 0, + 121,182, 0,119,185, 0,117,190, 0,117,192, 0,118,189, 0, + 118,189, 0,118,188, 0,120,184, 0,122,177, 0,124,164, 9, + 124,152, 20,124,145, 29,123,137, 41,120,122, 70,120,122, 70, + 122,132, 48,123,139, 37,123,139, 37,123,137, 41,123,139, 37, + 124,157, 15,123,168, 5,122,176, 0,121,181, 0,121,181, 0, + 122,177, 0,124,167, 6,124,149, 24,124,145, 29,124,155, 16, + 123,171, 3,121,179, 0,120,183, 0,120,183, 0,121,180, 0, + 122,176, 0,123,170, 3,124,162, 10,124,154, 18,124,155, 16, + 124,164, 9,124,167, 6,124,165, 8,124,155, 16,124,152, 20, + 124,154, 18,124,158, 13,124,164, 9,124,165, 8,124,164, 9, + 124,167, 6,123,173, 1,123,173, 1,124,167, 6,123,170, 3, + 123,172, 2,123,169, 4,124,161, 11,124,143, 31,118,117, 85, + 119,119, 77,122,135, 44,123,139, 37,123,139, 37,123,137, 41, + 124,147, 26,124,167, 6,122,177, 0,121,181, 0,120,184, 0, + 117,191, 0,114,196, 0,113,198, 0,114,197, 0,115,194, 0, + 117,190, 0,116,193, 0,111,201, 0,108,206, 0,106,207, 0, + 107,207, 0,109,204, 0,111,201, 0,111,201, 0,111,201, 0, + 111,202, 0,111,202, 0,112,200, 0,114,197, 0,117,192, 0, + 120,183, 0,123,168, 5,123,137, 41,120,122, 70,121,125, 63, + 123,168, 5,118,189, 0,112,200, 0,106,208, 0,102,213, 0, + 100,215, 0, 95,219, 0, 89,225, 0, 85,228, 0, 85,229, 0, + 87,227, 0, 92,222, 0, 98,216, 0,102,212, 0, 97,218, 0, + 91,223, 0, 87,227, 0, 86,228, 0, 88,226, 0, 92,222, 0, + 98,216, 0,104,210, 0,105,209, 0,108,205, 0,112,200, 0, + 115,194, 0,116,192, 0,117,191, 0,119,186, 0,122,177, 0, + 120,184, 0,117,191, 0,114,196, 0,113,199, 0,113,198, 0, + 115,194, 0,118,188, 0,122,178, 0,120,183, 0,119,185, 0, + 119,185, 0,121,180, 0,123,169, 4,124,145, 29,109, 92,251, + 30, 5,128, 69, 60,230,102, 80,250,107, 89,252,113,100,242, + 119,119, 77,121,125, 63,121,125, 63,117,110,109,120,122, 70, + 120,122, 70,121,127, 58,122,132, 48,124,143, 31,124,150, 22, + 124,149, 24,122,132, 48,124,154, 18,124,165, 8,123,170, 3, + 122,178, 0,120,184, 0,118,188, 0,118,188, 0,120,182, 0, + 120,183, 0,122,178, 0,124,167, 6,124,145, 29,121,127, 58, + 120,122, 70,117,110,109,102, 80,250,123,137, 41,124,160, 12, + 123,170, 3,122,178, 0,119,186, 0,116,193, 0,114,197, 0, + 113,198, 0,108,205, 0,103,211, 0,100,215, 0, 98,217, 0, + 98,217, 0,100,214, 0,102,212, 0,105,209, 0,102,212, 0, + 100,215, 0, 99,216, 0, 99,215, 0,101,214, 0,103,211, 0, + 106,208, 0,107,206, 0,109,204, 0,111,201, 0,114,197, 0, + 117,192, 0,119,186, 0,122,177, 0,124,167, 6,124,155, 16, + 124,150, 22,124,152, 20,124,160, 12,124,166, 7,123,170, 3, + 123,173, 1,123,172, 2,123,171, 3,121,179, 0,120,184, 0, + 119,185, 0,120,184, 0,120,183, 0,121,181, 0,121,181, 0, + 122,178, 0,118,189, 0,113,198, 0,109,204, 0,106,208, 0, + 105,209, 0,105,209, 0,106,207, 0,108,205, 0,108,205, 0, + 109,204, 0,110,202, 0,111,201, 0,112,200, 0,112,200, 0, + 114,197, 0,117,191, 0,117,191, 0,118,189, 0,120,184, 0, + 122,178, 0,123,171, 3,124,162, 10,124,150, 22,123,141, 34, + 122,130, 53,121,125, 63,122,135, 44,124,150, 22,124,158, 13, + 124,157, 15,124,147, 26,121,125, 63,124,158, 13,123,172, 2, + 121,180, 0,120,184, 0,119,186, 0,117,190, 0,117,191, 0, + 118,188, 0,118,189, 0,118,188, 0,120,184, 0,122,177, 0, + 124,164, 9,123,141, 34,115,107,129, 94, 70,242,107, 89,252, + 117,110,109,122,132, 48,124,143, 31,124,150, 22,124,155, 16, + 124,160, 12,124,161, 11,123,171, 3,122,178, 0,120,183, 0, + 120,183, 0,122,178, 0,123,169, 4,124,150, 22,119,119, 77, + 124,152, 20,123,169, 4,122,177, 0,121,179, 0,122,178, 0, + 123,173, 1,124,166, 7,124,160, 12,124,161, 11,124,164, 9, + 124,166, 7,124,166, 7,124,167, 6,124,165, 8,124,154, 18, + 122,135, 44,124,154, 18,124,166, 7,123,172, 2,122,175, 0, + 122,177, 0,122,177, 0,122,178, 0,122,178, 0,121,182, 0, + 119,185, 0,119,186, 0,119,185, 0,121,179, 0,123,168, 5, + 124,145, 29,114,104,197,120,122, 70,122,130, 53,122,132, 48, + 121,125, 63,122,135, 44,124,157, 15,123,169, 4,122,175, 0, + 119,186, 0,115,194, 0,112,199, 0,111,202, 0,111,201, 0, + 113,199, 0,115,194, 0,117,190, 0,113,198, 0,110,203, 0, + 109,204, 0,109,204, 0,111,202, 0,112,200, 0,112,199, 0, + 112,199, 0,112,200, 0,112,199, 0,114,197, 0,116,193, 0, + 119,187, 0,122,176, 0,124,155, 16,118,114, 96,115,107,129, + 118,114, 96,124,166, 7,118,188, 0,112,200, 0,106,208, 0, + 102,212, 0,100,215, 0, 92,222, 0, 86,228, 0, 82,231, 0, + 82,231, 0, 84,229, 0, 89,225, 0, 96,219, 0,101,213, 0, + 98,216, 0, 93,222, 0, 89,226, 0, 87,227, 0, 88,226, 0, + 91,223, 0, 96,219, 0,102,213, 0,104,210, 0,107,207, 0, + 110,202, 0,112,200, 0,114,197, 0,115,194, 0,117,191, 0, + 119,186, 0,117,191, 0,113,198, 0,110,202, 0,109,204, 0, + 110,203, 0,112,199, 0,116,193, 0,119,185, 0,118,188, 0, + 117,191, 0,117,191, 0,119,187, 0,122,178, 0,124,161, 11, + 121,127, 58, 87, 65,237, 69, 60,230, 94, 70,242,107, 89,252, + 118,114, 96,121,127, 58,122,135, 44,122,135, 44,121,125, 63, + 122,132, 48,123,137, 41,123,137, 41,123,139, 37,123,141, 34, + 124,147, 26,124,145, 29,124,158, 13,123,170, 3,122,177, 0, + 121,181, 0,120,184, 0,117,190, 0,116,193, 0,116,192, 0, + 118,189, 0,118,189, 0,120,184, 0,122,175, 0,124,158, 13, + 123,141, 34,120,122, 70,117,110,109,107, 89,252,120,122, 70, + 124,149, 24,124,164, 9,123,171, 3,120,182, 0,118,189, 0, + 116,193, 0,115,194, 0,111,201, 0,107,207, 0,103,211, 0, + 100,215, 0, 97,217, 0, 97,217, 0,100,215, 0,102,212, 0, + 101,214, 0,101,214, 0,102,212, 0,104,210, 0,106,208, 0, + 106,207, 0,106,207, 0,108,205, 0,111,201, 0,115,195, 0, + 117,191, 0,119,186, 0,120,184, 0,121,181, 0,122,178, 0, + 123,173, 1,124,164, 9,124,147, 26,123,141, 34,124,147, 26, + 124,157, 15,124,162, 10,124,165, 8,124,165, 8,123,171, 3, + 122,178, 0,121,181, 0,121,182, 0,121,181, 0,121,179, 0, + 122,178, 0,121,180, 0,117,191, 0,112,199, 0,108,205, 0, + 106,208, 0,104,210, 0,105,209, 0,106,208, 0,108,206, 0, + 109,204, 0,110,203, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,109,204, 0,113,198, 0,117,191, 0,119,187, 0, + 119,186, 0,120,182, 0,122,178, 0,123,172, 2,124,165, 8, + 124,157, 15,124,143, 31,118,117, 85,121,125, 63,124,145, 29, + 124,154, 18,124,152, 20,124,143, 31,122,135, 44,124,162, 10, + 122,175, 0,121,182, 0,120,184, 0,119,186, 0,119,186, 0, + 119,186, 0,118,188, 0,118,188, 0,119,186, 0,121,182, 0, + 123,173, 1,124,157, 15,121,127, 58,117,110,109,118,114, 96, + 118,117, 85,118,117, 85,122,135, 44,124,150, 22,124,165, 8, + 123,173, 1,122,178, 0,122,178, 0,122,175, 0,122,178, 0, + 121,182, 0,121,182, 0,122,177, 0,124,167, 6,124,149, 24, + 123,139, 37,124,149, 24,124,165, 8,123,172, 2,123,174, 1, + 123,171, 3,124,166, 7,124,161, 11,124,165, 8,124,167, 6, + 123,170, 3,123,172, 2,123,173, 1,123,171, 3,124,166, 7, + 124,160, 12,124,154, 18,124,165, 8,123,174, 1,121,181, 0, + 120,184, 0,119,186, 0,118,188, 0,118,188, 0,118,189, 0, + 116,192, 0,115,194, 0,114,196, 0,115,194, 0,118,189, 0, + 121,182, 0,124,167, 6,124,143, 31,121,125, 63,121,125, 63, + 122,130, 53,122,132, 48,121,127, 58,124,149, 24,124,165, 8, + 122,178, 0,118,188, 0,114,196, 0,111,202, 0,109,204, 0, + 109,204, 0,110,202, 0,112,199, 0,114,196, 0,116,193, 0, + 113,198, 0,112,200, 0,112,199, 0,112,199, 0,111,201, 0, + 110,202, 0,110,202, 0,112,200, 0,114,197, 0,116,193, 0, + 118,189, 0,121,182, 0,123,171, 3,124,155, 16,122,132, 48, + 120,122, 70,115,107,129,124,162, 10,119,186, 0,113,199, 0, + 107,207, 0,103,211, 0, 98,217, 0, 91,223, 0, 85,229, 0, + 81,232, 0, 81,232, 0, 83,230, 0, 89,226, 0, 95,220, 0, + 101,213, 0,100,214, 0, 95,220, 0, 91,224, 0, 88,226, 0, + 89,226, 0, 91,223, 0, 95,220, 0,100,215, 0,103,211, 0, + 105,209, 0,108,205, 0,110,202, 0,111,201, 0,113,198, 0, + 115,194, 0,117,192, 0,114,197, 0,110,202, 0,108,206, 0, + 107,207, 0,108,206, 0,111,202, 0,115,195, 0,118,189, 0, + 117,192, 0,115,194, 0,115,194, 0,117,191, 0,120,184, 0, + 123,172, 2,124,150, 22,118,114, 96,113,100,242,113,100,242, + 114,104,197,119,119, 77,121,127, 58,122,132, 48,122,132, 48, + 121,127, 58,124,143, 31,124,149, 24,124,150, 22,124,150, 22, + 124,154, 18,124,154, 18,124,158, 13,123,168, 5,122,178, 0, + 120,183, 0,119,187, 0,118,189, 0,116,193, 0,115,194, 0, + 115,194, 0,115,195, 0,115,194, 0,117,190, 0,121,182, 0, + 123,168, 5,124,149, 24,119,119, 77,111, 96,248,113,100,242, + 118,117, 85,124,143, 31,124,158, 13,124,166, 7,122,175, 0, + 120,184, 0,118,188, 0,118,189, 0,114,197, 0,108,205, 0, + 102,212, 0, 98,217, 0, 95,219, 0, 95,219, 0, 97,217, 0, + 98,216, 0, 97,218, 0, 97,217, 0, 99,215, 0,102,212, 0, + 106,208, 0,106,208, 0,106,208, 0,108,206, 0,110,202, 0, + 114,197, 0,117,190, 0,118,189, 0,118,189, 0,118,189, 0, + 119,187, 0,120,184, 0,122,178, 0,124,165, 8,124,143, 31, + 122,135, 44,121,127, 58,123,141, 34,124,147, 26,124,149, 24, + 124,164, 9,123,172, 2,122,177, 0,122,178, 0,121,179, 0, + 122,178, 0,122,177, 0,122,178, 0,118,188, 0,114,196, 0, + 110,202, 0,108,205, 0,106,207, 0,106,207, 0,107,207, 0, + 108,205, 0,109,203, 0,110,202, 0,107,207, 0,104,210, 0, + 102,212, 0,102,212, 0,104,210, 0,108,206, 0,113,199, 0, + 117,191, 0,119,187, 0,119,186, 0,120,184, 0,121,179, 0, + 123,173, 1,124,166, 7,124,154, 18,122,130, 53,121,127, 58, + 124,143, 31,124,150, 22,124,149, 24,123,139, 37,124,145, 29, + 124,166, 7,122,176, 0,121,181, 0,120,184, 0,120,184, 0, + 119,185, 0,119,185, 0,119,186, 0,119,185, 0,120,182, 0, + 122,175, 0,124,162, 10,123,137, 41,122,135, 44,122,135, 44, + 123,137, 41,123,139, 37,123,137, 41,124,143, 31,124,165, 8, + 122,178, 0,120,184, 0,118,189, 0,117,190, 0,118,188, 0, + 120,183, 0,122,178, 0,122,178, 0,123,173, 1,124,162, 10, + 124,157, 15,124,157, 15,124,158, 13,124,162, 10,123,169, 4, + 123,171, 3,123,168, 5,124,162, 10,124,160, 12,124,166, 7, + 123,168, 5,123,171, 3,123,174, 1,122,175, 0,123,174, 1, + 123,172, 2,123,169, 4,124,167, 6,123,173, 1,121,182, 0, + 119,186, 0,117,190, 0,116,193, 0,115,194, 0,115,195, 0, + 114,196, 0,113,198, 0,112,200, 0,111,201, 0,112,200, 0, + 114,197, 0,117,190, 0,121,180, 0,124,166, 7,124,145, 29, + 122,132, 48,122,135, 44,123,139, 37,123,141, 34,124,147, 26, + 124,165, 8,122,178, 0,118,189, 0,114,196, 0,111,202, 0, + 109,204, 0,108,205, 0,109,203, 0,111,202, 0,112,199, 0, + 113,198, 0,114,197, 0,113,198, 0,112,199, 0,111,201, 0, + 110,202, 0,110,203, 0,110,202, 0,111,201, 0,113,198, 0, + 116,193, 0,118,188, 0,121,182, 0,123,171, 3,124,152, 20, + 124,143, 31,122,135, 44,119,119, 77,124,164, 9,119,185, 0, + 114,197, 0,109,204, 0,103,211, 0, 97,218, 0, 90,224, 0, + 85,229, 0, 82,231, 0, 82,231, 0, 85,229, 0, 90,224, 0, + 96,219, 0,101,214, 0,101,214, 0, 97,217, 0, 93,221, 0, + 91,224, 0, 90,224, 0, 92,223, 0, 95,220, 0, 99,215, 0, + 102,212, 0,104,210, 0,107,207, 0,109,204, 0,110,202, 0, + 112,200, 0,114,196, 0,114,196, 0,112,200, 0,108,205, 0, + 106,208, 0,105,209, 0,107,207, 0,110,202, 0,115,195, 0, + 117,191, 0,116,193, 0,115,195, 0,115,195, 0,116,192, 0, + 119,187, 0,122,177, 0,124,161, 11,123,139, 37,121,125, 63, + 119,119, 77,117,110,109,119,119, 77,120,122, 70,120,122, 70, + 120,122, 70,123,139, 37,124,150, 22,124,157, 15,124,161, 11, + 124,164, 9,124,165, 8,124,164, 9,124,166, 7,123,173, 1, + 121,180, 0,119,186, 0,118,189, 0,116,193, 0,114,196, 0, + 113,198, 0,112,199, 0,112,200, 0,113,198, 0,116,193, 0, + 119,186, 0,122,175, 0,124,157, 15,122,130, 53,113,100,242, + 117,110,109,120,122, 70,123,137, 41,124,154, 18,124,162, 10, + 123,168, 5,122,176, 0,121,181, 0,119,187, 0,113,199, 0, + 106,208, 0,100,214, 0, 96,219, 0, 94,221, 0, 94,221, 0, + 95,219, 0, 93,221, 0, 92,222, 0, 93,221, 0, 95,219, 0, + 99,216, 0,103,212, 0,103,212, 0,103,211, 0,105,209, 0, + 107,206, 0,111,201, 0,116,193, 0,116,192, 0,116,193, 0, + 115,194, 0,116,193, 0,117,191, 0,120,184, 0,123,174, 1, + 124,158, 13,122,135, 44,120,122, 70,115,107,129,118,114, 96, + 123,137, 41,124,157, 15,124,167, 6,123,173, 1,122,176, 0, + 122,176, 0,122,175, 0,123,173, 1,123,172, 2,120,182, 0, + 117,190, 0,114,196, 0,112,200, 0,110,202, 0,110,203, 0, + 110,203, 0,110,202, 0,111,202, 0,108,205, 0,104,210, 0, + 101,214, 0, 99,216, 0, 98,217, 0,100,215, 0,103,211, 0, + 108,205, 0,113,198, 0,118,189, 0,118,189, 0,119,187, 0, + 120,184, 0,122,178, 0,123,170, 3,124,158, 13,123,141, 34, + 124,143, 31,124,147, 26,124,150, 22,124,152, 20,124,155, 16, + 124,157, 15,124,167, 6,123,174, 1,122,178, 0,121,180, 0, + 121,180, 0,121,180, 0,121,180, 0,121,181, 0,121,179, 0, + 123,174, 1,124,164, 9,124,149, 24,124,149, 24,124,149, 24, + 124,147, 26,124,149, 24,124,150, 22,124,158, 13,124,164, 9, + 122,175, 0,119,185, 0,117,192, 0,114,196, 0,113,198, 0, + 114,197, 0,116,193, 0,118,188, 0,120,183, 0,122,178, 0, + 123,173, 1,123,170, 3,123,168, 5,124,167, 6,124,165, 8, + 124,166, 7,123,168, 5,124,166, 7,124,161, 11,124,154, 18, + 124,162, 10,124,166, 7,123,170, 3,123,173, 1,123,174, 1, + 122,175, 0,122,175, 0,122,175, 0,122,176, 0,121,181, 0, + 119,186, 0,117,191, 0,115,194, 0,114,197, 0,113,198, 0, + 112,199, 0,112,200, 0,111,201, 0,110,203, 0,109,204, 0, + 109,203, 0,111,201, 0,114,196, 0,118,188, 0,122,178, 0, + 124,161, 11,124,155, 16,124,145, 29,124,147, 26,124,150, 22, + 124,149, 24,124,162, 10,122,176, 0,119,186, 0,116,193, 0, + 113,198, 0,111,201, 0,110,202, 0,110,202, 0,111,201, 0, + 112,200, 0,112,200, 0,112,200, 0,112,200, 0,111,201, 0, + 111,201, 0,111,202, 0,111,202, 0,111,201, 0,112,199, 0, + 114,197, 0,116,193, 0,118,188, 0,121,180, 0,124,167, 6, + 124,154, 18,124,149, 24,123,139, 37,122,130, 53,124,166, 7, + 119,185, 0,114,196, 0,110,202, 0,103,211, 0, 96,219, 0, + 91,224, 0, 87,227, 0, 85,229, 0, 85,228, 0, 89,226, 0, + 94,221, 0, 99,215, 0,101,213, 0,100,214, 0,100,215, 0, + 96,219, 0, 94,221, 0, 92,222, 0, 93,222, 0, 95,219, 0, + 99,215, 0,102,212, 0,103,211, 0,106,208, 0,108,205, 0, + 110,202, 0,111,201, 0,114,197, 0,113,198, 0,111,202, 0, + 108,206, 0,106,208, 0,105,209, 0,107,207, 0,111,201, 0, + 115,194, 0,117,191, 0,116,193, 0,115,194, 0,115,194, 0, + 117,192, 0,119,186, 0,122,178, 0,124,165, 8,124,147, 26, + 122,135, 44,121,125, 63,117,110,109,118,114, 96,118,114, 96, + 117,110,109,121,127, 58,124,145, 29,124,155, 16,124,166, 7, + 123,171, 3,122,175, 0,122,175, 0,123,173, 1,123,171, 3, + 122,175, 0,121,181, 0,119,186, 0,117,191, 0,115,195, 0, + 113,199, 0,111,202, 0,110,203, 0,110,203, 0,111,201, 0, + 114,196, 0,118,189, 0,122,178, 0,124,165, 8,124,145, 29, + 119,119, 77,118,114, 96,121,125, 63,122,132, 48,124,149, 24, + 124,157, 15,124,164, 9,123,169, 4,122,178, 0,117,191, 0, + 111,202, 0,104,210, 0, 99,216, 0, 95,220, 0, 93,221, 0, + 93,221, 0, 91,223, 0, 89,226, 0, 89,226, 0, 89,225, 0, + 92,223, 0, 95,219, 0,100,214, 0,102,213, 0,102,212, 0, + 103,212, 0,106,208, 0,110,203, 0,114,196, 0,116,193, 0, + 115,195, 0,114,196, 0,114,196, 0,116,193, 0,118,188, 0, + 122,178, 0,124,164, 9,124,145, 29,120,122, 70,114,104,197, + 107, 89,252,121,125, 63,124,147, 26,124,161, 11,123,168, 5, + 123,172, 2,123,172, 2,123,171, 3,123,169, 4,124,166, 7, + 123,171, 3,121,181, 0,119,186, 0,117,191, 0,115,194, 0, + 114,197, 0,113,198, 0,113,198, 0,112,200, 0,107,206, 0, + 103,211, 0, 99,215, 0, 97,218, 0, 96,219, 0, 97,217, 0, + 101,214, 0,105,209, 0,111,202, 0,116,193, 0,117,191, 0, + 117,190, 0,119,187, 0,121,181, 0,123,171, 3,124,157, 15, + 124,152, 20,124,155, 16,124,158, 13,124,162, 10,124,166, 7, + 123,168, 5,123,168, 5,124,165, 8,123,170, 3,123,171, 3, + 123,172, 2,123,172, 2,123,172, 2,123,172, 2,123,172, 2, + 123,169, 4,124,160, 12,124,158, 13,124,160, 12,124,157, 15, + 124,155, 16,124,154, 18,124,157, 15,124,167, 6,123,174, 1, + 122,178, 0,121,182, 0,117,190, 0,114,196, 0,111,201, 0, + 110,202, 0,110,202, 0,112,200, 0,114,196, 0,117,191, 0, + 119,186, 0,121,182, 0,122,177, 0,123,174, 1,123,172, 2, + 124,167, 6,124,161, 11,124,164, 9,124,164, 9,124,160, 12, + 124,157, 15,124,157, 15,124,162, 10,124,166, 7,123,169, 4, + 123,171, 3,123,173, 1,122,176, 0,122,178, 0,120,182, 0, + 119,186, 0,117,191, 0,115,194, 0,114,197, 0,112,199, 0, + 111,201, 0,111,202, 0,110,202, 0,110,202, 0,109,204, 0, + 108,205, 0,108,205, 0,110,203, 0,112,199, 0,116,193, 0, + 119,185, 0,123,173, 1,123,170, 3,124,166, 7,124,158, 13, + 124,157, 15,124,154, 18,124,150, 22,124,167, 6,122,178, 0, + 119,186, 0,117,190, 0,116,193, 0,114,196, 0,114,197, 0, + 113,198, 0,112,199, 0,112,200, 0,112,200, 0,112,200, 0, + 112,200, 0,112,200, 0,112,199, 0,113,199, 0,113,198, 0, + 114,197, 0,115,194, 0,117,191, 0,119,186, 0,122,177, 0, + 124,164, 9,124,157, 15,124,150, 22,124,145, 29,124,147, 26, + 123,169, 4,119,186, 0,115,194, 0,111,202, 0,104,210, 0, + 98,217, 0, 93,222, 0, 90,224, 0, 89,225, 0, 91,223, 0, + 94,220, 0, 99,216, 0,103,211, 0,102,212, 0,100,214, 0, + 100,214, 0, 99,215, 0, 97,218, 0, 95,220, 0, 95,220, 0, + 97,218, 0,100,214, 0,102,213, 0,103,211, 0,105,209, 0, + 108,205, 0,110,202, 0,111,201, 0,113,198, 0,113,198, 0, + 110,202, 0,108,206, 0,106,208, 0,106,208, 0,108,205, 0, + 112,200, 0,117,192, 0,118,189, 0,117,191, 0,117,192, 0, + 117,191, 0,118,188, 0,120,182, 0,123,173, 1,124,161, 11, + 124,147, 26,122,135, 44,120,122, 70,113,100,242,113,100,242, + 111, 96,248,113,100,242,122,130, 53,124,154, 18,124,167, 6, + 122,175, 0,121,181, 0,120,183, 0,120,183, 0,121,182, 0, + 122,178, 0,121,179, 0,119,186, 0,117,190, 0,116,193, 0, + 114,196, 0,111,201, 0,109,203, 0,108,205, 0,108,205, 0, + 110,202, 0,114,197, 0,118,189, 0,121,180, 0,124,167, 6, + 124,150, 22,122,130, 53,117,110,109,119,119, 77,121,125, 63, + 123,141, 34,124,150, 22,124,158, 13,123,168, 5,121,181, 0, + 116,193, 0,109,203, 0,103,211, 0, 98,217, 0, 94,220, 0, + 92,222, 0, 92,222, 0, 87,227, 0, 85,229, 0, 85,228, 0, + 87,227, 0, 89,225, 0, 92,222, 0, 97,217, 0,102,212, 0, + 102,212, 0,103,211, 0,105,209, 0,109,204, 0,114,197, 0, + 116,193, 0,115,195, 0,114,196, 0,114,196, 0,116,192, 0, + 119,186, 0,122,177, 0,124,164, 9,124,147, 26,122,130, 53, + 118,114, 96,113,100,242,115,107,129,122,135, 44,124,152, 20, + 124,162, 10,124,167, 6,123,168, 5,124,167, 6,124,165, 8, + 124,167, 6,123,169, 4,121,181, 0,118,188, 0,117,192, 0, + 116,193, 0,116,192, 0,117,191, 0,116,193, 0,113,198, 0, + 108,205, 0,103,211, 0,100,215, 0, 97,218, 0, 96,219, 0, + 97,217, 0,100,214, 0,105,209, 0,110,202, 0,115,194, 0, + 116,193, 0,117,192, 0,118,188, 0,121,181, 0,123,169, 4, + 124,155, 16,124,160, 12,124,164, 9,124,167, 6,123,171, 3, + 123,174, 1,122,176, 0,122,176, 0,123,170, 3,124,162, 10, + 124,161, 11,124,164, 9,124,164, 9,124,157, 15,124,157, 15, + 124,157, 15,124,161, 11,124,165, 8,124,166, 7,124,165, 8, + 124,162, 10,124,158, 13,124,157, 15,124,167, 6,122,177, 0, + 120,184, 0,118,189, 0,117,191, 0,117,192, 0,113,198, 0, + 110,202, 0,108,205, 0,108,205, 0,109,203, 0,112,200, 0, + 114,196, 0,117,191, 0,119,185, 0,121,179, 0,122,175, 0, + 123,171, 3,124,166, 7,124,157, 15,124,157, 15,124,158, 13, + 124,158, 13,124,158, 13,124,160, 12,124,164, 9,123,168, 5, + 123,173, 1,122,177, 0,121,180, 0,120,183, 0,119,185, 0, + 118,188, 0,117,192, 0,114,196, 0,112,200, 0,110,202, 0, + 109,204, 0,108,205, 0,108,206, 0,108,206, 0,109,204, 0, + 109,204, 0,108,205, 0,108,205, 0,109,204, 0,111,201, 0, + 114,196, 0,117,190, 0,121,179, 0,121,180, 0,122,178, 0, + 123,174, 1,124,165, 8,124,154, 18,124,143, 31,124,145, 29, + 124,161, 11,123,169, 4,122,176, 0,121,181, 0,119,185, 0, + 118,189, 0,116,192, 0,115,195, 0,114,197, 0,113,199, 0, + 113,199, 0,113,198, 0,113,198, 0,114,197, 0,113,198, 0, + 114,197, 0,115,194, 0,116,192, 0,118,189, 0,120,184, 0, + 123,174, 1,124,167, 6,124,160, 12,124,158, 13,124,160, 12, + 124,162, 10,123,173, 1,119,186, 0,115,194, 0,113,199, 0, + 106,207, 0,101,214, 0, 97,218, 0, 95,219, 0, 96,219, 0, + 98,217, 0,101,213, 0,105,209, 0,106,208, 0,104,210, 0, + 102,213, 0,101,214, 0,101,214, 0,100,215, 0, 98,216, 0, + 98,217, 0, 99,215, 0,102,212, 0,102,212, 0,103,211, 0, + 106,208, 0,108,205, 0,110,202, 0,112,200, 0,114,197, 0, + 114,197, 0,111,201, 0,109,204, 0,107,207, 0,107,206, 0, + 110,203, 0,114,197, 0,118,189, 0,119,186, 0,119,187, 0, + 119,187, 0,119,185, 0,121,182, 0,123,174, 1,124,164, 9, + 124,150, 22,122,135, 44,120,122, 70,115,107,129,102, 80,250, + 102, 80,250,107, 89,252,118,117, 85,124,147, 26,124,164, 9, + 123,174, 1,120,182, 0,119,187, 0,118,189, 0,117,190, 0, + 118,188, 0,120,184, 0,120,184, 0,118,188, 0,116,193, 0, + 114,196, 0,114,197, 0,111,201, 0,109,204, 0,108,206, 0, + 108,205, 0,111,201, 0,114,196, 0,117,190, 0,120,184, 0, + 123,172, 2,124,150, 22,122,132, 48,117,110,109,114,104,197, + 117,110,109,122,130, 53,124,143, 31,124,152, 20,124,167, 6, + 121,182, 0,116,193, 0,109,204, 0,103,212, 0, 98,217, 0, + 94,221, 0, 92,223, 0, 89,225, 0, 85,229, 0, 83,230, 0, + 84,229, 0, 85,229, 0, 87,227, 0, 91,224, 0, 95,220, 0, + 100,215, 0,104,210, 0,105,209, 0,107,207, 0,110,202, 0, + 114,197, 0,117,191, 0,116,192, 0,116,193, 0,117,192, 0, + 118,188, 0,121,181, 0,123,171, 3,124,157, 15,124,145, 29, + 121,125, 63,117,110,109,111, 96,248,105, 84,252,118,114, 96, + 123,137, 41,124,152, 20,124,160, 12,124,164, 9,124,162, 10, + 124,162, 10,124,166, 7,123,169, 4,121,179, 0,118,188, 0, + 116,192, 0,115,194, 0,116,193, 0,118,189, 0,118,188, 0, + 115,195, 0,110,202, 0,105,209, 0,102,213, 0,100,215, 0, + 99,215, 0,100,214, 0,103,212, 0,107,207, 0,111,201, 0, + 115,195, 0,115,195, 0,116,192, 0,118,188, 0,121,179, 0, + 124,165, 8,124,165, 8,123,172, 2,122,175, 0,123,174, 1, + 122,176, 0,121,179, 0,121,181, 0,121,180, 0,123,174, 1, + 124,165, 8,124,166, 7,124,167, 6,124,165, 8,124,158, 13, + 124,149, 24,124,161, 11,123,168, 5,123,170, 3,123,170, 3, + 123,168, 5,124,164, 9,124,164, 9,123,173, 1,122,178, 0, + 120,183, 0,117,191, 0,114,196, 0,113,199, 0,112,199, 0, + 113,198, 0,111,201, 0,109,204, 0,108,205, 0,109,204, 0, + 111,202, 0,114,197, 0,117,192, 0,119,185, 0,122,178, 0, + 123,171, 3,124,166, 7,124,158, 13,124,147, 26,124,145, 29, + 124,150, 22,124,154, 18,124,157, 15,124,160, 12,124,166, 7, + 123,173, 1,122,178, 0,120,183, 0,119,186, 0,118,188, 0, + 118,189, 0,117,191, 0,114,196, 0,112,200, 0,109,204, 0, + 107,206, 0,107,207, 0,106,207, 0,106,208, 0,106,208, 0, + 107,207, 0,109,204, 0,109,204, 0,109,204, 0,109,204, 0, + 111,202, 0,113,198, 0,116,192, 0,119,186, 0,118,188, 0, + 118,188, 0,120,184, 0,122,177, 0,124,162, 10,123,137, 41, + 113,100,242,118,114, 96,122,130, 53,124,143, 31,124,155, 16, + 124,166, 7,122,175, 0,120,183, 0,118,188, 0,116,193, 0, + 115,195, 0,114,196, 0,114,196, 0,115,194, 0,114,196, 0, + 114,197, 0,114,197, 0,116,193, 0,117,191, 0,118,188, 0, + 120,182, 0,123,174, 1,123,169, 4,124,165, 8,124,167, 6, + 123,169, 4,123,171, 3,122,175, 0,119,187, 0,116,193, 0, + 114,197, 0,110,202, 0,105,209, 0,102,212, 0,102,213, 0, + 103,211, 0,106,208, 0,109,204, 0,112,200, 0,108,205, 0, + 107,207, 0,104,210, 0,102,212, 0,102,212, 0,103,211, 0, + 101,213, 0,101,214, 0,101,213, 0,104,210, 0,103,211, 0, + 105,209, 0,107,207, 0,109,203, 0,111,201, 0,112,199, 0, + 114,197, 0,115,195, 0,113,199, 0,110,202, 0,109,204, 0, + 109,203, 0,112,200, 0,115,194, 0,119,185, 0,122,178, 0, + 121,180, 0,121,179, 0,122,177, 0,123,170, 3,124,161, 11, + 124,147, 26,121,127, 58,117,110,109,113,100,242, 99, 75,247, + 69, 60,230,107, 89,252,117,110,109,122,132, 48,124,157, 15, + 123,170, 3,121,179, 0,119,186, 0,117,191, 0,115,194, 0, + 115,195, 0,115,194, 0,117,191, 0,117,190, 0,117,190, 0, + 115,194, 0,114,197, 0,113,199, 0,112,200, 0,109,203, 0, + 108,205, 0,109,203, 0,112,199, 0,114,197, 0,115,194, 0, + 118,189, 0,122,178, 0,124,160, 12,121,127, 58,113,100,242, + 94, 70,242,105, 84,252,118,114, 96,122,130, 53,124,145, 29, + 124,167, 6,121,179, 0,116,192, 0,110,203, 0,103,211, 0, + 98,217, 0, 94,221, 0, 91,223, 0, 89,226, 0, 85,229, 0, + 84,229, 0, 85,229, 0, 86,228, 0, 87,227, 0, 90,225, 0, + 94,221, 0, 98,216, 0,104,210, 0,108,206, 0,109,204, 0, + 112,200, 0,115,195, 0,118,189, 0,118,188, 0,119,187, 0, + 119,186, 0,121,181, 0,123,172, 2,123,169, 4,124,164, 9, + 124,152, 20,122,130, 53,111, 96,248,102, 80,250, 30, 55,222, + 99, 75,247,118,114, 96,123,137, 41,124,150, 22,124,157, 15, + 124,158, 13,124,155, 16,124,161, 11,124,167, 6,123,174, 1, + 119,186, 0,117,191, 0,116,193, 0,116,192, 0,118,188, 0, + 120,182, 0,117,191, 0,113,199, 0,108,205, 0,105,209, 0, + 103,211, 0,103,211, 0,105,209, 0,107,207, 0,110,202, 0, + 113,198, 0,113,198, 0,114,196, 0,116,193, 0,119,186, 0, + 122,176, 0,124,167, 6,122,177, 0,120,182, 0,120,184, 0, + 120,183, 0,121,180, 0,120,182, 0,120,183, 0,121,182, 0, + 122,176, 0,122,178, 0,121,182, 0,120,182, 0,121,181, 0, + 122,176, 0,123,168, 5,124,167, 6,123,172, 2,123,174, 1, + 123,173, 1,123,170, 3,124,166, 7,123,171, 3,121,181, 0, + 120,184, 0,118,188, 0,114,196, 0,111,201, 0,108,205, 0, + 108,205, 0,109,204, 0,111,201, 0,110,202, 0,109,203, 0, + 110,203, 0,112,200, 0,114,196, 0,117,190, 0,121,182, 0, + 123,172, 2,124,162, 10,124,154, 18,124,143, 31,121,127, 58, + 121,125, 63,123,137, 41,124,145, 29,124,150, 22,124,155, 16, + 124,164, 9,123,172, 2,121,179, 0,119,185, 0,118,188, 0, + 117,190, 0,117,191, 0,117,192, 0,113,198, 0,110,202, 0, + 108,206, 0,106,207, 0,106,208, 0,106,208, 0,106,207, 0, + 106,208, 0,106,207, 0,108,205, 0,109,203, 0,109,204, 0, + 109,203, 0,111,202, 0,113,198, 0,116,193, 0,116,192, 0, + 115,194, 0,116,193, 0,117,191, 0,120,184, 0,123,172, 2, + 124,150, 22,118,114, 96, 30, 55,222,105, 84,252,122,130, 53, + 124,152, 20,124,164, 9,123,169, 4,123,171, 3,121,182, 0, + 118,189, 0,117,191, 0,117,191, 0,117,192, 0,117,191, 0, + 116,193, 0,114,196, 0,115,194, 0,117,191, 0,117,190, 0, + 118,188, 0,120,182, 0,122,178, 0,123,173, 1,124,167, 6, + 123,170, 3,123,173, 1,122,176, 0,122,176, 0,119,186, 0, + 116,193, 0,114,196, 0,114,196, 0,111,202, 0,108,205, 0, + 109,204, 0,110,202, 0,113,199, 0,114,197, 0,112,199, 0, + 109,204, 0,110,203, 0,107,207, 0,105,209, 0,105,209, 0, + 106,208, 0,104,210, 0,103,211, 0,104,210, 0,106,207, 0, + 106,208, 0,107,207, 0,109,204, 0,111,201, 0,112,199, 0, + 113,198, 0,114,196, 0,117,191, 0,115,195, 0,113,199, 0, + 111,201, 0,112,200, 0,114,197, 0,117,191, 0,121,182, 0, + 123,170, 3,123,171, 3,123,170, 3,124,166, 7,124,158, 13, + 124,145, 29,121,125, 63,113,100,242,102, 80,250, 87, 65,237, + 30, 49,213, 69, 60,230,114,104,197,122,130, 53,124,149, 24, + 124,162, 10,123,172, 2,121,181, 0,118,189, 0,115,194, 0, + 113,198, 0,112,200, 0,112,200, 0,114,197, 0,116,193, 0, + 115,194, 0,115,194, 0,113,198, 0,112,200, 0,111,201, 0, + 111,202, 0,110,202, 0,111,201, 0,111,201, 0,111,201, 0, + 113,198, 0,116,193, 0,120,184, 0,123,169, 4,123,139, 37, + 102, 80,250, 30, 20,155, 30, 55,222,113,100,242,122,132, 48, + 124,152, 20,123,169, 4,121,182, 0,118,189, 0,111,201, 0, + 105,209, 0, 98,216, 0, 94,221, 0, 90,224, 0, 88,226, 0, + 87,227, 0, 87,227, 0, 87,227, 0, 88,226, 0, 89,225, 0, + 91,223, 0, 95,220, 0, 99,215, 0,105,209, 0,110,202, 0, + 112,200, 0,114,197, 0,116,192, 0,119,186, 0,121,182, 0, + 121,180, 0,122,178, 0,123,171, 3,123,173, 1,123,172, 2, + 123,168, 5,124,165, 8,124,155, 16,122,135, 44,113,100,242, + 30, 49,213, 30, 31,180, 94, 70,242,118,114, 96,123,139, 37, + 124,152, 20,124,157, 15,124,155, 16,124,149, 24,124,157, 15, + 124,166, 7,122,178, 0,119,186, 0,118,189, 0,118,189, 0, + 119,185, 0,122,178, 0,119,186, 0,115,194, 0,112,200, 0, + 109,203, 0,108,205, 0,109,204, 0,110,202, 0,112,200, 0, + 112,199, 0,112,199, 0,113,199, 0,114,197, 0,116,192, 0, + 119,185, 0,122,176, 0,123,174, 1,120,183, 0,118,188, 0, + 118,189, 0,118,189, 0,119,187, 0,120,184, 0,120,184, 0, + 120,182, 0,119,185, 0,118,189, 0,117,191, 0,117,192, 0, + 117,191, 0,119,186, 0,121,180, 0,123,172, 2,122,177, 0, + 122,178, 0,122,177, 0,123,173, 1,123,169, 4,123,174, 1, + 121,182, 0,120,184, 0,117,192, 0,112,199, 0,108,205, 0, + 106,208, 0,105,209, 0,106,207, 0,109,204, 0,112,200, 0, + 111,201, 0,112,200, 0,114,197, 0,116,192, 0,119,185, 0, + 122,175, 0,124,161, 11,124,147, 26,122,132, 48,118,117, 85, + 107, 89,252,109, 92,251,117,110,109,121,125, 63,122,132, 48, + 123,141, 34,124,152, 20,124,165, 8,122,175, 0,121,182, 0, + 119,186, 0,118,189, 0,117,190, 0,117,191, 0,113,198, 0, + 110,203, 0,108,206, 0,107,207, 0,107,207, 0,108,206, 0, + 108,206, 0,108,205, 0,108,205, 0,109,204, 0,109,204, 0, + 109,204, 0,110,203, 0,111,201, 0,113,198, 0,115,194, 0, + 114,197, 0,113,199, 0,113,198, 0,114,196, 0,118,189, 0, + 122,178, 0,124,160, 12,122,130, 53,117,110,109,121,125, 63, + 124,152, 20,124,166, 7,123,173, 1,122,175, 0,122,176, 0, + 122,175, 0,120,184, 0,119,187, 0,118,188, 0,118,188, 0, + 118,189, 0,118,189, 0,117,191, 0,117,190, 0,117,191, 0, + 117,192, 0,118,189, 0,119,185, 0,121,182, 0,122,178, 0, + 123,174, 1,123,169, 4,123,173, 1,122,177, 0,122,178, 0, + 120,184, 0,117,190, 0,116,193, 0,115,194, 0,116,193, 0, + 114,196, 0,115,195, 0,116,193, 0,116,193, 0,117,192, 0, + 113,199, 0,110,203, 0,112,199, 0,110,202, 0,109,204, 0, + 109,204, 0,108,205, 0,107,207, 0,106,208, 0,106,208, 0, + 108,205, 0,109,204, 0,110,203, 0,112,200, 0,113,198, 0, + 113,198, 0,114,197, 0,115,194, 0,117,192, 0,115,194, 0, + 112,200, 0,110,203, 0,109,204, 0,110,202, 0,113,198, 0, + 117,191, 0,120,183, 0,122,178, 0,122,177, 0,123,171, 3, + 124,162, 10,124,145, 29,123,139, 37,122,132, 48,118,117, 85, + 102, 80,250, 30, 49,213, 30, 55,222,118,117, 85,124,149, 24, + 124,167, 6,122,178, 0,119,186, 0,118,189, 0,117,191, 0, + 114,196, 0,112,200, 0,110,202, 0,110,203, 0,111,202, 0, + 113,199, 0,114,197, 0,115,195, 0,113,198, 0,111,201, 0, + 110,202, 0,110,202, 0,110,202, 0,110,202, 0,110,203, 0, + 110,202, 0,112,200, 0,114,196, 0,118,188, 0,122,175, 0, + 124,152, 20,117,110,109, 30, 43,203,102, 80,250,120,122, 70, + 124,145, 29,124,158, 13,123,170, 3,121,182, 0,118,189, 0, + 113,198, 0,106,208, 0,100,215, 0, 94,221, 0, 89,225, 0, + 86,228, 0, 84,229, 0, 84,229, 0, 86,228, 0, 90,225, 0, + 94,221, 0, 95,219, 0, 98,216, 0,102,212, 0,107,206, 0, + 112,199, 0,114,197, 0,116,193, 0,118,188, 0,120,182, 0, + 122,176, 0,123,172, 2,123,169, 4,124,167, 6,123,170, 3, + 123,169, 4,123,174, 1,123,173, 1,123,168, 5,124,155, 16, + 123,137, 41,117,110,109,102, 80,250,102, 80,250,111, 96,248, + 121,125, 63,124,147, 26,124,157, 15,124,160, 12,124,154, 18, + 123,141, 34,124,149, 24,124,165, 8,122,177, 0,120,182, 0, + 120,182, 0,122,178, 0,121,179, 0,120,183, 0,118,189, 0, + 115,194, 0,114,197, 0,114,197, 0,114,196, 0,112,199, 0, + 112,200, 0,112,200, 0,112,200, 0,112,199, 0,114,197, 0, + 117,192, 0,120,184, 0,120,184, 0,120,182, 0,119,186, 0, + 117,191, 0,116,193, 0,115,194, 0,115,194, 0,116,193, 0, + 117,192, 0,117,192, 0,115,194, 0,114,197, 0,113,199, 0, + 113,199, 0,114,197, 0,116,193, 0,119,187, 0,121,181, 0, + 121,179, 0,121,180, 0,121,179, 0,122,177, 0,123,174, 1, + 122,177, 0,120,183, 0,118,189, 0,114,196, 0,110,202, 0, + 107,207, 0,105,209, 0,104,210, 0,106,208, 0,108,205, 0, + 111,201, 0,114,197, 0,114,196, 0,116,193, 0,118,188, 0, + 121,179, 0,123,172, 2,124,164, 9,124,150, 22,121,125, 63, + 107, 89,252, 30, 31,180, 30, 43,203,102, 80,250,115,107,129, + 122,130, 53,124,145, 29,124,152, 20,124,152, 20,124,166, 7, + 122,175, 0,121,182, 0,119,185, 0,119,187, 0,118,188, 0, + 114,196, 0,112,200, 0,110,202, 0,110,202, 0,109,203, 0, + 107,206, 0,106,208, 0,106,207, 0,107,206, 0,108,206, 0, + 108,205, 0,108,205, 0,109,203, 0,111,202, 0,113,198, 0, + 114,196, 0,112,200, 0,110,202, 0,110,202, 0,112,199, 0, + 116,193, 0,120,184, 0,123,170, 3,124,164, 9,124,155, 16, + 124,145, 29,124,160, 12,123,171, 3,122,177, 0,122,178, 0, + 122,178, 0,122,177, 0,122,177, 0,120,182, 0,120,184, 0, + 119,186, 0,118,189, 0,117,191, 0,116,192, 0,116,193, 0, + 115,195, 0,115,195, 0,116,193, 0,118,189, 0,119,186, 0, + 120,184, 0,121,181, 0,122,178, 0,123,173, 1,123,173, 1, + 122,177, 0,122,178, 0,119,186, 0,117,191, 0,117,192, 0, + 117,191, 0,118,189, 0,118,189, 0,118,189, 0,118,188, 0, + 118,189, 0,114,196, 0,112,200, 0,115,194, 0,113,198, 0, + 112,199, 0,112,199, 0,112,200, 0,110,203, 0,108,205, 0, + 108,205, 0,110,202, 0,112,199, 0,113,198, 0,114,197, 0, + 114,196, 0,114,196, 0,115,194, 0,115,194, 0,116,193, 0, + 114,196, 0,111,201, 0,108,205, 0,107,206, 0,108,206, 0, + 110,202, 0,113,198, 0,116,192, 0,119,187, 0,121,181, 0, + 122,175, 0,123,169, 4,124,165, 8,124,158, 13,124,150, 22, + 123,137, 41,117,110,109,107, 89,252,111, 96,248,120,122, 70, + 124,158, 13,122,177, 0,118,188, 0,115,194, 0,113,199, 0, + 112,200, 0,112,199, 0,112,200, 0,109,203, 0,109,204, 0, + 109,203, 0,111,201, 0,111,201, 0,112,200, 0,113,199, 0, + 111,201, 0,110,202, 0,110,202, 0,110,202, 0,110,202, 0, + 110,202, 0,111,202, 0,112,200, 0,114,196, 0,118,189, 0, + 122,178, 0,124,161, 11,122,130, 53,111, 96,248,117,110,109, + 123,139, 37,124,154, 18,124,164, 9,124,167, 6,122,178, 0, + 119,187, 0,114,196, 0,107,206, 0,101,214, 0, 94,220, 0, + 89,225, 0, 85,229, 0, 83,230, 0, 82,231, 0, 84,229, 0, + 89,226, 0, 94,220, 0,101,214, 0,103,211, 0,107,207, 0, + 111,201, 0,114,196, 0,116,193, 0,118,189, 0,120,184, 0, + 122,176, 0,124,167, 6,124,164, 9,124,161, 11,124,155, 16, + 124,158, 13,123,168, 5,123,174, 1,122,175, 0,123,172, 2, + 124,166, 7,124,155, 16,123,139, 37,119,119, 77,118,117, 85, + 114,104,197,122,130, 53,123,141, 34,124,157, 15,124,161, 11, + 124,158, 13,124,145, 29,120,122, 70,123,141, 34,124,162, 10, + 123,170, 3,123,172, 2,122,176, 0,120,183, 0,119,186, 0, + 120,184, 0,119,187, 0,118,188, 0,119,187, 0,117,192, 0, + 113,198, 0,112,200, 0,112,200, 0,112,200, 0,112,199, 0, + 114,196, 0,117,191, 0,117,190, 0,118,189, 0,118,188, 0, + 118,188, 0,116,193, 0,114,197, 0,112,199, 0,112,200, 0, + 112,200, 0,112,200, 0,112,200, 0,111,202, 0,109,203, 0, + 109,204, 0,110,203, 0,111,201, 0,114,197, 0,117,192, 0, + 119,186, 0,121,182, 0,121,180, 0,121,179, 0,122,178, 0, + 122,177, 0,121,181, 0,119,187, 0,116,192, 0,113,198, 0, + 110,203, 0,107,207, 0,105,209, 0,105,209, 0,107,207, 0, + 109,204, 0,111,201, 0,114,197, 0,116,193, 0,118,188, 0, + 120,183, 0,122,177, 0,123,169, 4,124,160, 12,124,145, 29, + 119,119, 77,105, 84,252, 30, 31,180, 99, 75,247,115,107,129, + 122,130, 53,124,145, 29,124,154, 18,124,155, 16,124,150, 22, + 124,155, 16,123,168, 5,122,176, 0,121,181, 0,120,184, 0, + 119,186, 0,118,189, 0,115,194, 0,114,197, 0,111,201, 0, + 108,205, 0,106,208, 0,105,209, 0,105,209, 0,106,208, 0, + 106,207, 0,107,207, 0,108,206, 0,109,204, 0,111,202, 0, + 113,199, 0,113,198, 0,111,202, 0,109,204, 0,108,205, 0, + 110,203, 0,113,198, 0,117,191, 0,121,182, 0,122,178, 0, + 123,172, 2,124,167, 6,124,160, 12,123,171, 3,122,177, 0, + 121,179, 0,122,178, 0,122,177, 0,123,174, 1,122,176, 0, + 121,180, 0,119,186, 0,117,191, 0,115,194, 0,114,196, 0, + 113,198, 0,113,199, 0,113,198, 0,114,196, 0,117,192, 0, + 119,187, 0,119,186, 0,119,186, 0,120,184, 0,121,181, 0, + 123,173, 1,123,174, 1,122,177, 0,121,181, 0,118,188, 0, + 118,189, 0,118,189, 0,119,187, 0,119,186, 0,120,184, 0, + 121,181, 0,119,186, 0,117,192, 0,115,194, 0,118,189, 0, + 116,192, 0,116,193, 0,116,192, 0,115,194, 0,113,198, 0, + 111,201, 0,111,202, 0,112,200, 0,114,197, 0,115,195, 0, + 115,194, 0,116,193, 0,116,193, 0,116,192, 0,116,193, 0, + 116,192, 0,114,196, 0,111,201, 0,108,205, 0,107,207, 0, + 107,207, 0,108,205, 0,111,202, 0,113,198, 0,115,194, 0, + 117,190, 0,119,185, 0,121,180, 0,123,174, 1,124,167, 6, + 124,158, 13,124,145, 29,121,125, 63,118,114, 96,122,130, 53, + 124,145, 29,124,162, 10,120,182, 0,116,193, 0,111,201, 0, + 108,205, 0,107,206, 0,107,206, 0,108,205, 0,109,204, 0, + 109,204, 0,109,204, 0,109,204, 0,108,205, 0,110,203, 0, + 112,200, 0,111,202, 0,110,202, 0,110,202, 0,111,202, 0, + 111,201, 0,111,201, 0,112,199, 0,114,197, 0,116,193, 0, + 119,187, 0,122,178, 0,124,162, 10,123,139, 37,121,125, 63, + 122,132, 48,124,150, 22,124,161, 11,124,166, 7,123,168, 5, + 123,171, 3,120,182, 0,115,194, 0,109,204, 0,102,212, 0, + 96,219, 0, 91,224, 0, 86,228, 0, 83,230, 0, 82,231, 0, + 84,229, 0, 89,226, 0, 95,220, 0,101,213, 0,107,206, 0, + 112,200, 0,115,194, 0,117,192, 0,117,191, 0,119,186, 0, + 122,177, 0,124,167, 6,124,154, 18,124,152, 20,124,150, 22, + 124,145, 29,124,143, 31,124,162, 10,123,170, 3,123,173, 1, + 123,172, 2,123,168, 5,124,162, 10,124,154, 18,123,139, 37, + 123,137, 41,121,125, 63,123,139, 37,124,152, 20,124,157, 15, + 124,160, 12,124,157, 15,124,143, 31,120,122, 70,120,122, 70, + 124,143, 31,124,157, 15,123,170, 3,121,180, 0,119,186, 0, + 118,188, 0,119,186, 0,121,179, 0,122,176, 0,123,174, 1, + 119,186, 0,115,194, 0,114,197, 0,113,198, 0,113,198, 0, + 114,197, 0,115,194, 0,116,193, 0,115,194, 0,116,193, 0, + 117,191, 0,118,189, 0,114,196, 0,112,200, 0,110,203, 0, + 109,204, 0,108,205, 0,108,205, 0,108,206, 0,107,207, 0, + 107,207, 0,107,207, 0,108,205, 0,110,202, 0,113,199, 0, + 115,194, 0,117,190, 0,119,186, 0,121,180, 0,122,178, 0, + 122,177, 0,122,176, 0,121,181, 0,118,188, 0,116,193, 0, + 114,197, 0,112,200, 0,110,203, 0,109,204, 0,109,204, 0, + 109,203, 0,111,201, 0,112,199, 0,114,196, 0,116,192, 0, + 119,187, 0,121,181, 0,123,173, 1,124,162, 10,124,149, 24, + 121,127, 58,113,100,242, 99, 75,247,105, 84,252,118,117, 85, + 123,137, 41,124,149, 24,124,155, 16,124,158, 13,124,157, 15, + 124,150, 22,124,158, 13,124,164, 9,123,169, 4,122,175, 0, + 121,179, 0,120,182, 0,119,187, 0,117,191, 0,114,196, 0, + 111,201, 0,108,205, 0,106,207, 0,105,209, 0,105,209, 0, + 106,208, 0,106,208, 0,106,207, 0,107,206, 0,109,204, 0, + 110,202, 0,112,199, 0,113,198, 0,110,203, 0,108,206, 0, + 107,207, 0,108,206, 0,110,202, 0,114,197, 0,118,189, 0, + 119,186, 0,121,181, 0,122,177, 0,123,171, 3,123,168, 5, + 122,175, 0,122,178, 0,122,177, 0,122,175, 0,123,170, 3, + 123,169, 4,122,176, 0,119,185, 0,116,192, 0,114,197, 0, + 112,199, 0,112,200, 0,111,201, 0,112,200, 0,114,197, 0, + 116,193, 0,119,186, 0,119,186, 0,119,187, 0,118,188, 0, + 119,186, 0,121,179, 0,123,170, 3,122,176, 0,122,178, 0, + 120,182, 0,119,186, 0,119,186, 0,120,183, 0,121,181, 0, + 122,178, 0,123,173, 1,121,180, 0,119,185, 0,119,186, 0, + 121,181, 0,119,185, 0,119,186, 0,120,184, 0,119,186, 0, + 117,192, 0,115,195, 0,114,197, 0,115,195, 0,116,192, 0, + 117,191, 0,117,191, 0,117,190, 0,118,189, 0,118,189, 0, + 117,192, 0,117,191, 0,115,195, 0,112,200, 0,109,204, 0, + 107,207, 0,107,207, 0,108,206, 0,109,204, 0,111,202, 0, + 112,199, 0,114,196, 0,117,191, 0,119,185, 0,122,178, 0, + 123,169, 4,124,158, 13,124,143, 31,122,130, 53,122,130, 53, + 124,152, 20,124,164, 9,123,169, 4,120,184, 0,114,197, 0, + 109,204, 0,105,209, 0,103,211, 0,103,211, 0,104,210, 0, + 105,209, 0,106,207, 0,107,207, 0,106,207, 0,106,208, 0, + 108,206, 0,111,202, 0,110,202, 0,110,202, 0,111,201, 0, + 110,202, 0,111,201, 0,113,198, 0,114,196, 0,116,193, 0, + 118,188, 0,120,182, 0,123,173, 1,124,160, 12,123,137, 41, + 122,135, 44,124,143, 31,124,157, 15,124,164, 9,124,167, 6, + 123,169, 4,123,171, 3,121,182, 0,116,192, 0,111,201, 0, + 105,209, 0,100,215, 0, 94,221, 0, 89,225, 0, 86,228, 0, + 85,229, 0, 86,228, 0, 91,223, 0, 97,218, 0,103,211, 0, + 110,203, 0,114,197, 0,114,197, 0,114,197, 0,114,196, 0, + 117,191, 0,120,182, 0,124,165, 8,122,135, 44,122,135, 44, + 123,139, 37,122,132, 48,123,139, 37,124,150, 22,124,161, 11, + 124,166, 7,124,167, 6,124,167, 6,124,164, 9,124,160, 12, + 124,149, 24,124,147, 26,123,137, 41,124,143, 31,124,158, 13, + 124,164, 9,124,160, 12,124,149, 24,122,132, 48,115,107,129, + 119,119, 77,123,141, 34,124,160, 12,123,173, 1,120,183, 0, + 118,188, 0,118,189, 0,119,186, 0,121,179, 0,124,164, 9, + 124,158, 13,122,177, 0,119,187, 0,117,191, 0,116,192, 0, + 116,193, 0,116,192, 0,116,193, 0,114,196, 0,114,196, 0, + 115,195, 0,116,193, 0,117,191, 0,114,197, 0,110,202, 0, + 108,205, 0,106,207, 0,106,208, 0,106,208, 0,106,208, 0, + 105,209, 0,106,208, 0,107,207, 0,109,204, 0,111,202, 0, + 113,198, 0,115,194, 0,117,192, 0,118,188, 0,120,182, 0, + 123,173, 1,123,173, 1,123,172, 2,122,176, 0,120,184, 0, + 118,189, 0,117,192, 0,115,194, 0,114,196, 0,114,196, 0, + 114,197, 0,114,196, 0,114,196, 0,115,195, 0,116,193, 0, + 118,189, 0,120,184, 0,122,176, 0,124,165, 8,124,149, 24, + 121,125, 63,111, 96,248,107, 89,252,105, 84,252,118,117, 85, + 123,141, 34,124,152, 20,124,158, 13,124,160, 12,124,160, 12, + 124,155, 16,124,155, 16,124,161, 11,124,164, 9,124,164, 9, + 124,165, 8,123,170, 3,122,176, 0,120,182, 0,118,188, 0, + 115,194, 0,113,199, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,107,207, 0,107,207, 0,107,207, 0,106,208, 0, + 106,208, 0,108,205, 0,112,200, 0,113,198, 0,109,203, 0, + 107,207, 0,105,209, 0,106,208, 0,107,206, 0,110,202, 0, + 114,196, 0,117,191, 0,119,186, 0,120,182, 0,122,178, 0, + 123,170, 3,123,168, 5,123,172, 2,123,172, 2,123,169, 4, + 124,161, 11,124,161, 11,123,170, 3,120,184, 0,116,192, 0, + 114,197, 0,112,200, 0,111,201, 0,111,201, 0,112,199, 0, + 114,196, 0,117,191, 0,119,185, 0,120,182, 0,119,185, 0, + 118,188, 0,118,188, 0,120,183, 0,123,173, 1,123,174, 1, + 122,178, 0,122,178, 0,121,179, 0,121,179, 0,122,176, 0, + 123,173, 1,123,168, 5,124,161, 11,123,170, 3,122,175, 0, + 122,176, 0,123,168, 5,123,174, 1,122,175, 0,123,173, 1, + 123,174, 1,120,182, 0,118,188, 0,117,190, 0,118,189, 0, + 119,186, 0,119,186, 0,119,186, 0,119,186, 0,119,185, 0, + 119,186, 0,118,188, 0,118,188, 0,116,193, 0,113,199, 0, + 110,202, 0,109,204, 0,108,205, 0,108,205, 0,109,204, 0, + 110,203, 0,111,202, 0,113,198, 0,116,193, 0,119,187, 0, + 122,178, 0,124,166, 7,124,150, 22,122,132, 48,121,125, 63, + 124,143, 31,124,166, 7,122,176, 0,121,181, 0,120,184, 0, + 114,197, 0,108,205, 0,104,210, 0,102,213, 0,101,213, 0, + 102,213, 0,103,212, 0,104,210, 0,105,209, 0,105,209, 0, + 105,209, 0,107,207, 0,110,202, 0,110,202, 0,110,202, 0, + 110,202, 0,109,203, 0,110,202, 0,112,199, 0,115,194, 0, + 119,187, 0,121,182, 0,123,174, 1,124,164, 9,124,149, 24, + 122,130, 53,122,132, 48,124,147, 26,124,160, 12,124,166, 7, + 123,169, 4,123,171, 3,121,180, 0,120,184, 0,118,189, 0, + 114,197, 0,109,204, 0,104,210, 0, 99,215, 0, 94,220, 0, + 91,223, 0, 89,225, 0, 91,223, 0, 95,220, 0,101,214, 0, + 107,206, 0,109,203, 0,110,202, 0,110,202, 0,110,202, 0, + 111,201, 0,114,196, 0,118,188, 0,123,172, 2,123,141, 34, + 117,110,109,119,119, 77,118,117, 85,123,137, 41,124,152, 20, + 124,160, 12,124,160, 12,124,158, 13,124,160, 12,124,160, 12, + 124,160, 12,124,152, 20,124,150, 22,124,143, 31,124,143, 31, + 124,158, 13,124,164, 9,124,161, 11,124,143, 31,114,104,197, + 99, 75,247,117,110,109,123,139, 37,124,160, 12,123,174, 1, + 120,183, 0,118,188, 0,118,189, 0,119,186, 0,121,179, 0, + 124,166, 7,123,139, 37,124,157, 15,123,173, 1,121,179, 0, + 121,182, 0,120,184, 0,119,187, 0,116,193, 0,114,196, 0, + 114,197, 0,114,196, 0,115,194, 0,117,192, 0,113,198, 0, + 110,203, 0,107,207, 0,106,208, 0,106,208, 0,106,208, 0, + 106,208, 0,106,208, 0,107,207, 0,109,204, 0,111,202, 0, + 113,199, 0,114,196, 0,116,193, 0,117,192, 0,118,189, 0, + 120,183, 0,123,173, 1,124,165, 8,124,165, 8,124,164, 9, + 123,174, 1,121,179, 0,121,182, 0,120,182, 0,120,182, 0, + 120,183, 0,120,184, 0,119,186, 0,119,187, 0,118,188, 0, + 118,188, 0,120,184, 0,122,178, 0,124,167, 6,124,150, 22, + 121,125, 63,113,100,242,102, 80,250,102, 80,250,115,107,129, + 123,141, 34,124,157, 15,124,164, 9,124,165, 8,124,162, 10, + 124,158, 13,124,154, 18,124,160, 12,124,162, 10,124,162, 10, + 124,158, 13,124,150, 22,124,154, 18,124,165, 8,122,175, 0, + 120,182, 0,118,189, 0,116,193, 0,114,197, 0,112,200, 0, + 111,201, 0,111,202, 0,109,203, 0,106,208, 0,103,211, 0, + 102,212, 0,103,212, 0,105,209, 0,109,204, 0,113,199, 0, + 109,204, 0,106,208, 0,104,210, 0,104,210, 0,105,209, 0, + 107,206, 0,110,202, 0,113,198, 0,117,192, 0,119,186, 0, + 121,181, 0,123,174, 1,124,164, 9,124,161, 11,124,161, 11, + 124,157, 15,124,145, 29,124,152, 20,123,169, 4,120,184, 0, + 117,192, 0,114,197, 0,112,199, 0,112,200, 0,112,199, 0, + 114,197, 0,116,192, 0,118,188, 0,121,182, 0,122,175, 0, + 121,179, 0,120,184, 0,119,186, 0,120,184, 0,122,176, 0, + 123,171, 3,122,176, 0,122,178, 0,123,174, 1,124,167, 6, + 124,164, 9,124,160, 12,124,154, 18,124,145, 29,124,154, 18, + 124,160, 12,124,160, 12,124,145, 29,124,154, 18,124,157, 15, + 124,155, 16,124,157, 15,123,169, 4,122,177, 0,121,181, 0, + 121,180, 0,122,178, 0,122,178, 0,121,179, 0,121,181, 0, + 121,181, 0,121,179, 0,120,182, 0,120,182, 0,117,190, 0, + 115,195, 0,113,199, 0,111,201, 0,111,202, 0,111,202, 0, + 111,202, 0,110,202, 0,111,202, 0,113,199, 0,116,193, 0, + 119,185, 0,123,174, 1,124,157, 15,122,132, 48,115,107,129, + 117,110,109,124,152, 20,123,174, 1,120,184, 0,118,189, 0, + 117,191, 0,114,196, 0,109,204, 0,104,210, 0,102,213, 0, + 101,214, 0,101,214, 0,102,212, 0,104,210, 0,105,209, 0, + 105,209, 0,105,209, 0,107,206, 0,109,203, 0,109,203, 0, + 110,202, 0,109,203, 0,109,204, 0,110,203, 0,112,200, 0, + 114,196, 0,117,191, 0,121,182, 0,124,166, 7,124,145, 29, + 121,125, 63,117,110,109,118,117, 85,124,145, 29,124,160, 12, + 124,167, 6,123,171, 3,122,178, 0,119,186, 0,118,189, 0, + 117,190, 0,116,192, 0,113,199, 0,109,204, 0,105,209, 0, + 101,214, 0, 97,217, 0, 96,219, 0, 97,218, 0,101,214, 0, + 106,208, 0,106,208, 0,106,207, 0,107,207, 0,107,206, 0, + 108,206, 0,109,204, 0,112,200, 0,117,192, 0,121,179, 0, + 124,154, 18,121,125, 63,107, 89,252,109, 92,251,122,130, 53, + 124,152, 20,124,162, 10,124,166, 7,124,165, 8,124,158, 13, + 124,152, 20,124,155, 16,124,152, 20,124,150, 22,124,145, 29, + 123,141, 34,124,154, 18,124,158, 13,124,154, 18,122,132, 48, + 99, 75,247, 30, 43,203,107, 89,252,121,127, 58,124,155, 16, + 123,171, 3,121,181, 0,119,186, 0,119,186, 0,120,184, 0, + 121,179, 0,123,168, 5,124,143, 31,122,130, 53,124,143, 31, + 124,155, 16,124,161, 11,123,170, 3,120,184, 0,117,191, 0, + 115,195, 0,114,196, 0,115,194, 0,116,193, 0,116,193, 0, + 112,199, 0,109,203, 0,107,206, 0,107,207, 0,107,207, 0, + 108,206, 0,108,205, 0,108,205, 0,110,202, 0,112,200, 0, + 114,197, 0,115,195, 0,116,193, 0,117,191, 0,117,190, 0, + 118,188, 0,120,182, 0,123,172, 2,124,155, 16,124,149, 24, + 124,145, 29,124,152, 20,124,160, 12,124,160, 12,124,158, 13, + 124,157, 15,124,165, 8,123,170, 3,123,171, 3,123,172, 2, + 122,178, 0,122,178, 0,122,176, 0,123,168, 5,124,154, 18, + 121,127, 58,118,117, 85,117,110,109,107, 89,252, 99, 75,247, + 122,130, 53,124,157, 15,124,167, 6,123,171, 3,123,170, 3, + 124,164, 9,124,155, 16,124,161, 11,124,164, 9,124,162, 10, + 124,158, 13,124,150, 22,123,137, 41,120,122, 70,123,141, 34, + 124,160, 12,123,172, 2,121,180, 0,119,185, 0,118,189, 0, + 117,191, 0,116,193, 0,112,199, 0,109,204, 0,105,209, 0, + 102,212, 0,101,214, 0,101,214, 0,103,211, 0,107,207, 0, + 112,200, 0,109,204, 0,105,209, 0,103,211, 0,102,212, 0, + 103,211, 0,105,209, 0,108,206, 0,110,202, 0,113,198, 0, + 118,188, 0,120,183, 0,122,176, 0,124,165, 8,124,145, 29, + 123,139, 37,122,132, 48,118,114, 96,123,139, 37,123,168, 5, + 120,183, 0,117,191, 0,115,194, 0,114,196, 0,114,197, 0, + 115,195, 0,117,192, 0,119,187, 0,121,182, 0,122,175, 0, + 123,169, 4,123,168, 5,122,178, 0,120,182, 0,121,182, 0, + 122,178, 0,123,169, 4,123,173, 1,122,175, 0,123,172, 2, + 124,164, 9,124,145, 29,123,137, 41,122,130, 53,119,119, 77, + 120,122, 70,122,132, 48,122,132, 48,111, 96,248,118,114, 96, + 120,122, 70,121,125, 63,121,125, 63,124,147, 26,124,160, 12, + 124,166, 7,124,167, 6,124,166, 7,123,168, 5,123,172, 2, + 122,175, 0,122,177, 0,122,176, 0,123,174, 1,122,176, 0, + 119,185, 0,117,190, 0,116,193, 0,114,196, 0,114,197, 0, + 112,200, 0,112,200, 0,112,199, 0,112,200, 0,114,197, 0, + 117,190, 0,121,181, 0,124,165, 8,124,143, 31,113,100,242, + 30, 55,222,114,104,197,124,158, 13,121,180, 0,117,191, 0, + 114,196, 0,113,198, 0,113,198, 0,110,202, 0,106,208, 0, + 103,211, 0,102,212, 0,102,212, 0,103,211, 0,105,209, 0, + 107,207, 0,107,206, 0,108,206, 0,109,203, 0,109,204, 0, + 108,205, 0,109,204, 0,109,204, 0,109,204, 0,110,202, 0, + 112,200, 0,114,197, 0,116,192, 0,120,184, 0,123,171, 3, + 124,145, 29,107, 89,252, 87, 65,237,114,104,197,123,141, 34, + 124,160, 12,123,169, 4,123,174, 1,120,184, 0,117,190, 0, + 116,193, 0,116,193, 0,114,197, 0,112,200, 0,108,205, 0, + 106,208, 0,106,208, 0,104,210, 0,102,212, 0,103,211, 0, + 105,209, 0,104,210, 0,104,210, 0,104,210, 0,104,210, 0, + 105,209, 0,106,208, 0,107,207, 0,110,202, 0,114,196, 0, + 120,184, 0,124,165, 8,122,132, 48,107, 89,252, 30, 55,222, + 118,114, 96,124,145, 29,124,161, 11,123,168, 5,123,170, 3, + 124,166, 7,124,157, 15,124,149, 24,124,150, 22,124,147, 26, + 124,145, 29,123,137, 41,124,143, 31,124,147, 26,123,139, 37, + 117,110,109, 30, 49,213,107, 89,252,118,114, 96,124,143, 31, + 124,155, 16,124,166, 7,122,177, 0,121,182, 0,120,182, 0, + 121,182, 0,122,178, 0,123,169, 4,124,154, 18,119,119, 77, + 107, 89,252,111, 96,248,120,122, 70,124,161, 11,122,178, 0, + 118,188, 0,116,192, 0,116,193, 0,116,192, 0,117,191, 0, + 115,194, 0,112,200, 0,110,203, 0,108,205, 0,109,204, 0, + 110,203, 0,111,201, 0,111,201, 0,112,200, 0,114,197, 0, + 112,199, 0,111,201, 0,112,200, 0,114,197, 0,117,191, 0, + 119,187, 0,119,186, 0,121,181, 0,123,171, 3,124,155, 16, + 121,127, 58,118,117, 85,111, 96,248,115,107,129,114,104,197, + 120,122, 70,124,149, 24,124,162, 10,124,167, 6,124,166, 7, + 124,161, 11,124,161, 11,124,165, 8,124,162, 10,124,154, 18, + 122,135, 44,118,117, 85,119,119, 77,118,117, 85,109, 92,251, + 117,110,109,124,149, 24,123,168, 5,122,176, 0,122,178, 0, + 123,174, 1,124,167, 6,124,160, 12,124,164, 9,124,164, 9, + 124,160, 12,124,150, 22,122,135, 44,117,110,109,105, 84,252, + 109, 92,251,122,130, 53,124,150, 22,124,164, 9,123,171, 3, + 122,176, 0,120,184, 0,117,191, 0,114,197, 0,110,202, 0, + 107,207, 0,103,211, 0,101,214, 0,101,214, 0,102,212, 0, + 106,208, 0,111,202, 0,108,205, 0,105,209, 0,102,212, 0, + 101,213, 0,102,212, 0,104,210, 0,106,207, 0,108,205, 0, + 112,200, 0,116,192, 0,120,184, 0,122,177, 0,124,165, 8, + 124,143, 31,113,100,242,102, 80,250,109, 92,251,123,141, 34, + 123,168, 5,121,182, 0,118,188, 0,117,191, 0,117,192, 0, + 117,192, 0,117,190, 0,118,189, 0,117,191, 0,117,190, 0, + 119,186, 0,122,177, 0,124,160, 12,124,167, 6,122,175, 0, + 122,178, 0,122,178, 0,123,173, 1,123,169, 4,123,171, 3, + 123,169, 4,124,161, 11,124,145, 29,121,127, 58,114,104,197, + 109, 92,251,102, 80,250,105, 84,252,107, 89,252, 30, 2,128, + 30, 26,168, 30, 49,213, 87, 65,237,102, 80,250,115,107,129, + 122,132, 48,124,145, 29,124,147, 26,124,161, 11,124,166, 7, + 124,166, 7,123,169, 4,123,173, 1,123,174, 1,123,171, 3, + 124,162, 10,122,175, 0,120,182, 0,119,186, 0,118,189, 0, + 115,194, 0,114,197, 0,113,198, 0,115,194, 0,115,195, 0, + 117,192, 0,119,185, 0,123,173, 1,124,161, 11,123,139, 37, + 111, 96,248, 30, 2,128,118,114, 96,124,164, 9,120,184, 0, + 115,194, 0,112,200, 0,111,202, 0,111,202, 0,111,201, 0, + 108,206, 0,105,209, 0,104,210, 0,105,209, 0,107,207, 0, + 109,204, 0,111,201, 0,111,201, 0,110,202, 0,109,204, 0, + 108,206, 0,107,207, 0,107,207, 0,108,205, 0,109,203, 0, + 111,201, 0,113,198, 0,115,195, 0,117,191, 0,120,184, 0, + 123,173, 1,124,150, 22,113,100,242, 30, 2,128,109, 92,251, + 123,139, 37,124,161, 11,123,172, 2,121,179, 0,119,185, 0, + 117,191, 0,115,194, 0,112,199, 0,109,204, 0,106,207, 0, + 105,209, 0,105,209, 0,104,210, 0,105,209, 0,107,206, 0, + 105,209, 0,103,211, 0,102,212, 0,100,215, 0, 99,216, 0, + 99,216, 0,101,214, 0,105,209, 0,106,208, 0,109,204, 0, + 113,198, 0,118,189, 0,123,173, 1,124,145, 29,105, 84,252, + 30, 2,128, 99, 75,247,121,127, 58,124,154, 18,124,165, 8, + 123,169, 4,123,168, 5,124,162, 10,124,147, 26,124,150, 22, + 124,145, 29,123,141, 34,122,135, 44,122,130, 53,122,130, 53, + 118,117, 85, 94, 70,242,117,110,109,122,135, 44,124,149, 24, + 124,157, 15,124,167, 6,123,171, 3,123,174, 1,122,175, 0, + 122,177, 0,122,178, 0,122,176, 0,123,170, 3,124,158, 13, + 122,135, 44,105, 84,252, 30, 2,128,111, 96,248,124,149, 24, + 123,172, 2,120,184, 0,118,189, 0,117,191, 0,117,191, 0, + 118,189, 0,115,195, 0,112,200, 0,110,202, 0,110,202, 0, + 111,201, 0,113,199, 0,114,197, 0,115,195, 0,114,197, 0, + 111,201, 0,110,203, 0,109,204, 0,110,202, 0,113,199, 0, + 116,192, 0,120,184, 0,120,183, 0,122,178, 0,123,170, 3, + 124,155, 16,122,130, 53,107, 89,252, 30, 43,203, 30, 2,128, + 30, 55,222,115,107,129,123,139, 37,124,154, 18,124,160, 12, + 124,158, 13,124,149, 24,122,135, 44,124,145, 29,124,145, 29, + 123,137, 41,122,132, 48,119,119, 77,119,119, 77,117,110,109, + 105, 84,252,123,139, 37,124,165, 8,122,178, 0,120,183, 0, + 120,184, 0,121,180, 0,123,171, 3,124,160, 12,124,161, 11, + 124,160, 12,124,152, 20,123,137, 41,118,114, 96,107, 89,252, + 30, 43,203, 30, 2,128, 87, 65,237,117,110,109,122,135, 44, + 124,155, 16,123,170, 3,121,180, 0,119,187, 0,116,192, 0, + 113,198, 0,109,204, 0,105,209, 0,103,212, 0,102,212, 0, + 103,211, 0,106,207, 0,111,201, 0,106,207, 0,103,211, 0, + 101,214, 0,101,214, 0,102,213, 0,104,210, 0,106,208, 0, + 109,204, 0,112,200, 0,116,193, 0,119,187, 0,122,178, 0, + 124,164, 9,123,139, 37,107, 89,252, 30, 2,128,109, 92,251, + 124,145, 29,123,169, 4,121,181, 0,119,186, 0,118,188, 0, + 118,188, 0,118,188, 0,116,192, 0,114,196, 0,113,198, 0, + 114,197, 0,116,193, 0,119,186, 0,123,173, 1,124,155, 16, + 123,168, 5,123,174, 1,122,177, 0,122,177, 0,123,174, 1, + 123,169, 4,124,167, 6,124,158, 13,124,152, 20,123,137, 41, + 121,127, 58,118,114, 96,111, 96,248, 94, 70,242, 30, 31,180, + 107, 89,252,105, 84,252, 99, 75,247, 30, 43,203, 30, 15,143, + 30, 55,222,111, 96,248,118,117, 85,123,137, 41,124,155, 16, + 124,162, 10,124,164, 9,123,168, 5,123,174, 1,122,177, 0, + 122,175, 0,123,168, 5,124,161, 11,124,167, 6,123,174, 1, + 121,182, 0,118,188, 0,117,191, 0,117,192, 0,118,189, 0, + 118,189, 0,119,186, 0,122,178, 0,124,164, 9,124,152, 20, + 121,127, 58, 99, 75,247, 30, 55,222,121,125, 63,123,168, 5, + 119,186, 0,114,196, 0,111,201, 0,110,203, 0,110,202, 0, + 111,201, 0,109,204, 0,107,207, 0,107,207, 0,108,205, 0, + 110,202, 0,112,200, 0,108,205, 0,107,206, 0,108,205, 0, + 108,205, 0,107,207, 0,105,209, 0,105,209, 0,105,209, 0, + 106,207, 0,108,205, 0,112,200, 0,117,192, 0,118,188, 0, + 120,182, 0,123,173, 1,124,154, 18,118,114, 96, 94, 70,242, + 102, 80,250,122,135, 44,124,162, 10,122,176, 0,119,185, 0, + 117,191, 0,114,196, 0,111,201, 0,107,206, 0,104,210, 0, + 101,214, 0, 99,215, 0, 99,216, 0,100,215, 0,101,214, 0, + 101,214, 0,100,214, 0, 98,216, 0, 96,219, 0, 94,220, 0, + 93,221, 0, 94,221, 0, 96,219, 0,100,215, 0,105,209, 0, + 109,204, 0,113,199, 0,117,191, 0,122,178, 0,124,158, 13, + 119,119, 77, 30, 55,222, 99, 75,247,114,104,197,122,135, 44, + 124,154, 18,124,161, 11,124,162, 10,124,158, 13,124,149, 24, + 124,152, 20,124,150, 22,123,141, 34,122,132, 48,120,122, 70, + 117,110,109,111, 96,248,118,114, 96,123,139, 37,124,155, 16, + 124,166, 7,123,170, 3,123,174, 1,122,178, 0,121,179, 0, + 121,181, 0,121,180, 0,122,175, 0,123,173, 1,123,170, 3, + 124,162, 10,124,145, 29,118,114, 96,107, 89,252, 99, 75,247, + 122,132, 48,124,164, 9,122,178, 0,119,185, 0,118,188, 0, + 118,188, 0,118,189, 0,114,196, 0,112,199, 0,111,201, 0, + 110,202, 0,110,202, 0,111,202, 0,109,204, 0,108,206, 0, + 108,206, 0,107,206, 0,108,205, 0,109,204, 0,110,202, 0, + 113,198, 0,117,192, 0,120,183, 0,122,178, 0,123,173, 1, + 124,166, 7,124,154, 18,124,143, 31,122,130, 53,118,117, 85, + 117,110,109,118,114, 96,121,127, 58,123,137, 41,123,139, 37, + 124,147, 26,124,143, 31,122,135, 44,118,117, 85,117,110,109, + 122,130, 53,123,137, 41,123,139, 37,122,135, 44,121,127, 58, + 119,119, 77,121,127, 58,124,158, 13,122,176, 0,119,185, 0, + 118,189, 0,118,189, 0,119,185, 0,122,178, 0,123,168, 5, + 124,155, 16,124,147, 26,122,135, 44,121,125, 63,121,125, 63, + 121,127, 58,120,122, 70,111, 96,248,105, 84,252,115,107,129, + 122,130, 53,124,152, 20,123,168, 5,121,181, 0,117,190, 0, + 114,197, 0,111,201, 0,110,203, 0,108,205, 0,105,209, 0, + 105,209, 0,105,209, 0,108,206, 0,109,203, 0,105,209, 0, + 102,212, 0,101,213, 0,102,213, 0,102,212, 0,101,213, 0, + 102,212, 0,106,208, 0,110,203, 0,114,197, 0,118,189, 0, + 121,179, 0,124,162, 10,122,130, 53, 99, 75,247,102, 80,250, + 115,107,129,124,150, 22,123,170, 3,121,180, 0,120,184, 0, + 119,185, 0,119,186, 0,116,193, 0,114,197, 0,111,201, 0, + 110,202, 0,111,201, 0,114,197, 0,117,191, 0,121,180, 0, + 124,166, 7,124,161, 11,123,170, 3,122,175, 0,122,178, 0, + 122,178, 0,122,176, 0,123,174, 1,123,173, 1,123,170, 3, + 124,161, 11,124,145, 29,123,139, 37,122,135, 44,121,125, 63, + 117,110,109,122,135, 44,122,130, 53,121,125, 63,114,104,197, + 69, 60,230, 30, 26,168, 30, 49,213,109, 92,251,118,117, 85, + 124,145, 29,124,157, 15,124,160, 12,123,169, 4,122,177, 0, + 121,181, 0,121,181, 0,122,177, 0,123,169, 4,124,161, 11, + 124,165, 8,123,174, 1,121,181, 0,120,184, 0,120,183, 0, + 120,184, 0,120,184, 0,121,180, 0,123,169, 4,124,152, 20, + 123,137, 41,117,110,109, 69, 60,230,114,104,197,122,135, 44, + 123,171, 3,118,188, 0,114,196, 0,111,201, 0,110,202, 0, + 111,201, 0,111,201, 0,109,204, 0,108,205, 0,109,204, 0, + 110,202, 0,113,199, 0,110,203, 0,107,207, 0,106,208, 0, + 106,208, 0,107,207, 0,106,207, 0,105,209, 0,104,210, 0, + 103,211, 0,104,210, 0,106,208, 0,109,203, 0,115,195, 0, + 120,184, 0,121,180, 0,123,173, 1,124,160, 12,122,135, 44, + 120,122, 70,114,104,197,121,125, 63,124,161, 11,122,178, 0, + 118,189, 0,114,196, 0,111,202, 0,107,207, 0,103,211, 0, + 100,215, 0, 97,218, 0, 95,220, 0, 94,221, 0, 94,221, 0, + 94,220, 0, 95,220, 0, 94,221, 0, 92,222, 0, 90,224, 0, + 89,226, 0, 88,226, 0, 89,226, 0, 91,223, 0, 95,220, 0, + 100,214, 0,106,207, 0,113,199, 0,117,191, 0,121,181, 0, + 124,166, 7,123,141, 34,113,100,242,102, 80,250,111, 96,248, + 118,114, 96,121,127, 58,123,141, 34,124,147, 26,124,145, 29, + 124,149, 24,124,152, 20,124,149, 24,123,141, 34,122,130, 53, + 119,119, 77,118,114, 96,119,119, 77,122,135, 44,124,152, 20, + 124,165, 8,123,172, 2,122,176, 0,122,177, 0,121,179, 0, + 121,181, 0,120,183, 0,120,183, 0,122,178, 0,123,172, 2, + 123,170, 3,124,166, 7,124,157, 15,124,147, 26,123,139, 37, + 121,127, 58,120,122, 70,124,147, 26,123,169, 4,122,178, 0, + 120,182, 0,120,182, 0,117,190, 0,114,196, 0,112,200, 0, + 109,204, 0,107,206, 0,107,206, 0,106,208, 0,102,213, 0, + 100,215, 0,100,215, 0,100,215, 0,101,214, 0,103,211, 0, + 107,207, 0,110,202, 0,115,195, 0,118,188, 0,121,179, 0, + 122,176, 0,123,174, 1,123,171, 3,123,168, 5,124,165, 8, + 124,162, 10,124,161, 11,124,161, 11,124,161, 11,124,157, 15, + 124,150, 22,122,135, 44,121,125, 63,119,119, 77,118,117, 85, + 118,117, 85,118,117, 85,122,132, 48,123,141, 34,124,145, 29, + 124,145, 29,123,141, 34,124,150, 22,123,170, 3,121,182, 0, + 118,188, 0,117,192, 0,117,192, 0,118,189, 0,120,184, 0, + 122,177, 0,124,167, 6,124,155, 16,124,147, 26,124,152, 20, + 124,157, 15,124,160, 12,124,157, 15,124,143, 31,122,130, 53, + 123,141, 34,124,155, 16,124,167, 6,122,178, 0,119,186, 0, + 115,194, 0,111,201, 0,108,205, 0,107,207, 0,106,208, 0, + 106,207, 0,107,207, 0,107,207, 0,108,205, 0,109,204, 0, + 105,209, 0,103,211, 0,103,211, 0,102,212, 0,100,215, 0, + 98,216, 0, 99,215, 0,102,212, 0,107,207, 0,112,200, 0, + 117,192, 0,121,180, 0,124,160, 12,119,119, 77,118,114, 96, + 122,130, 53,123,141, 34,124,155, 16,123,170, 3,122,178, 0, + 121,181, 0,121,180, 0,118,188, 0,115,195, 0,112,200, 0, + 109,203, 0,108,205, 0,109,204, 0,112,200, 0,116,193, 0, + 120,184, 0,123,172, 2,124,162, 10,124,165, 8,123,172, 2, + 122,176, 0,122,178, 0,122,178, 0,122,178, 0,121,180, 0, + 121,179, 0,123,173, 1,124,164, 9,124,150, 22,124,152, 20, + 124,150, 22,124,145, 29,124,154, 18,124,157, 15,124,152, 20, + 123,139, 37,115,107,129, 99, 75,247, 69, 60,230, 99, 75,247, + 113,100,242,122,130, 53,124,149, 24,124,158, 13,123,171, 3, + 121,179, 0,120,184, 0,119,186, 0,120,184, 0,122,178, 0, + 123,172, 2,124,164, 9,124,166, 7,123,171, 3,123,173, 1, + 123,172, 2,122,178, 0,121,179, 0,123,173, 1,124,160, 12, + 122,135, 44,119,119, 77,105, 84,252,114,104,197,122,130, 53, + 124,145, 29,123,173, 1,119,187, 0,115,194, 0,113,199, 0, + 112,200, 0,113,199, 0,111,201, 0,110,203, 0,109,203, 0, + 110,202, 0,112,200, 0,111,201, 0,109,203, 0,106,207, 0, + 105,209, 0,106,208, 0,106,207, 0,107,207, 0,106,208, 0, + 105,209, 0,104,210, 0,104,210, 0,105,209, 0,108,205, 0, + 114,197, 0,119,185, 0,122,178, 0,123,173, 1,124,164, 9, + 124,157, 15,124,150, 22,123,137, 41,119,119, 77,124,157, 15, + 122,178, 0,117,191, 0,113,199, 0,108,205, 0,105,209, 0, + 101,214, 0, 97,218, 0, 94,221, 0, 92,222, 0, 91,224, 0, + 90,224, 0, 90,224, 0, 90,224, 0, 89,225, 0, 87,227, 0, + 86,228, 0, 85,229, 0, 84,229, 0, 85,229, 0, 87,227, 0, + 91,223, 0, 97,218, 0,102,212, 0,109,204, 0,115,195, 0, + 120,183, 0,123,169, 4,124,152, 20,121,125, 63,114,104,197, + 115,107,129,115,107,129,120,122, 70,122,130, 53,123,137, 41, + 123,141, 34,124,147, 26,124,149, 24,124,143, 31,122,135, 44, + 121,125, 63,119,119, 77,118,117, 85,121,127, 58,123,141, 34, + 124,157, 15,124,167, 6,123,173, 1,122,177, 0,122,178, 0, + 122,177, 0,121,179, 0,121,182, 0,120,183, 0,121,180, 0, + 123,171, 3,123,170, 3,123,168, 5,124,165, 8,124,165, 8, + 124,162, 10,124,158, 13,124,149, 24,124,145, 29,124,155, 16, + 123,169, 4,122,176, 0,120,182, 0,118,189, 0,113,199, 0, + 108,205, 0,105,209, 0,104,210, 0,105,209, 0,101,214, 0, + 95,219, 0, 93,221, 0, 93,222, 0, 94,221, 0, 95,219, 0, + 98,216, 0,102,212, 0,106,207, 0,110,202, 0,114,196, 0, + 117,191, 0,120,184, 0,121,181, 0,121,180, 0,121,180, 0, + 121,181, 0,121,181, 0,121,182, 0,121,181, 0,121,179, 0, + 122,176, 0,123,168, 5,124,157, 15,123,137, 41,119,119, 77, + 121,125, 63,122,130, 53,122,130, 53,121,127, 58,124,143, 31, + 124,152, 20,124,155, 16,124,155, 16,124,162, 10,122,175, 0, + 120,184, 0,118,189, 0,117,192, 0,116,193, 0,117,191, 0, + 118,188, 0,120,183, 0,122,176, 0,124,167, 6,124,164, 9, + 123,168, 5,123,171, 3,123,173, 1,123,171, 3,124,165, 8, + 124,152, 20,124,160, 12,123,168, 5,122,176, 0,120,183, 0, + 117,190, 0,114,197, 0,110,203, 0,106,208, 0,103,211, 0, + 102,212, 0,102,212, 0,103,211, 0,105,209, 0,106,207, 0, + 108,205, 0,107,207, 0,106,208, 0,104,210, 0,101,214, 0, + 98,217, 0, 97,218, 0, 97,217, 0,101,214, 0,105,209, 0, + 111,202, 0,116,192, 0,121,179, 0,124,157, 15,121,127, 58, + 123,141, 34,124,150, 22,124,157, 15,124,162, 10,123,169, 4, + 122,175, 0,122,176, 0,123,174, 1,119,187, 0,115,195, 0, + 112,200, 0,109,204, 0,108,206, 0,108,205, 0,111,201, 0, + 115,195, 0,119,186, 0,122,175, 0,123,168, 5,124,166, 7, + 124,166, 7,123,171, 3,123,174, 1,122,177, 0,122,178, 0, + 121,182, 0,120,182, 0,121,179, 0,123,173, 1,124,164, 9, + 124,160, 12,124,162, 10,124,160, 12,124,167, 6,123,171, 3, + 123,168, 5,124,158, 13,122,135, 44,117,110,109,102, 80,250, + 111, 96,248,111, 96,248,119,119, 77,123,141, 34,124,160, 12, + 123,171, 3,121,180, 0,119,186, 0,118,189, 0,118,188, 0, + 120,184, 0,121,180, 0,123,172, 2,124,161, 11,124,161, 11, + 124,161, 11,124,164, 9,123,171, 3,123,173, 1,124,166, 7, + 124,147, 26,118,114, 96,111, 96,248,107, 89,252,121,125, 63, + 124,145, 29,124,154, 18,122,176, 0,119,187, 0,116,193, 0, + 114,196, 0,114,196, 0,113,198, 0,111,201, 0,111,202, 0, + 111,201, 0,112,200, 0,113,198, 0,110,202, 0,109,204, 0, + 108,206, 0,107,207, 0,107,207, 0,107,207, 0,108,206, 0, + 108,205, 0,107,207, 0,106,208, 0,106,208, 0,106,208, 0, + 109,204, 0,114,197, 0,119,185, 0,123,174, 1,123,171, 3, + 124,165, 8,124,166, 7,124,162, 10,124,152, 20,123,137, 41, + 124,157, 15,122,178, 0,117,191, 0,112,199, 0,108,205, 0, + 104,210, 0,101,214, 0, 97,217, 0, 94,220, 0, 92,222, 0, + 90,224, 0, 89,225, 0, 88,226, 0, 88,226, 0, 87,227, 0, + 85,228, 0, 84,229, 0, 83,230, 0, 83,230, 0, 84,229, 0, + 87,227, 0, 90,224, 0, 94,220, 0,100,215, 0,105,209, 0, + 111,201, 0,117,190, 0,122,176, 0,124,158, 13,123,139, 37, + 118,117, 85,121,127, 58,122,135, 44,123,137, 41,123,137, 41, + 123,137, 41,123,139, 37,123,141, 34,123,139, 37,122,132, 48, + 119,119, 77,121,125, 63,120,122, 70,117,110,109,121,125, 63, + 123,141, 34,124,155, 16,124,165, 8,123,170, 3,123,173, 1, + 123,173, 1,123,173, 1,123,174, 1,122,178, 0,121,182, 0, + 121,180, 0,123,174, 1,123,168, 5,124,167, 6,123,169, 4, + 123,171, 3,123,172, 2,123,170, 3,124,165, 8,124,161, 11, + 124,160, 12,124,165, 8,123,173, 1,120,184, 0,116,193, 0, + 110,202, 0,106,208, 0,102,212, 0,101,214, 0,102,213, 0, + 98,217, 0, 92,223, 0, 89,226, 0, 89,226, 0, 90,225, 0, + 92,222, 0, 95,220, 0, 99,215, 0,103,211, 0,107,206, 0, + 110,202, 0,113,199, 0,115,194, 0,118,188, 0,119,185, 0, + 119,186, 0,118,188, 0,118,189, 0,117,191, 0,117,190, 0, + 118,188, 0,120,184, 0,122,178, 0,124,167, 6,124,154, 18, + 122,135, 44,121,125, 63,122,135, 44,123,137, 41,122,132, 48, + 124,145, 29,124,157, 15,124,162, 10,124,162, 10,123,168, 5, + 122,177, 0,120,182, 0,119,187, 0,117,190, 0,117,191, 0, + 117,191, 0,117,190, 0,119,186, 0,121,182, 0,123,174, 1, + 123,170, 3,123,174, 1,122,177, 0,122,178, 0,122,177, 0, + 123,172, 2,124,162, 10,123,168, 5,123,174, 1,121,180, 0, + 119,186, 0,117,192, 0,113,198, 0,109,204, 0,105,209, 0, + 101,213, 0, 99,215, 0, 99,216, 0,100,215, 0,102,212, 0, + 105,209, 0,107,207, 0,109,203, 0,108,206, 0,103,211, 0, + 100,215, 0, 98,217, 0, 97,218, 0, 98,217, 0,101,214, 0, + 106,208, 0,111,201, 0,117,192, 0,122,178, 0,124,152, 20, + 123,141, 34,124,152, 20,124,160, 12,124,164, 9,124,166, 7, + 124,165, 8,123,170, 3,123,171, 3,123,168, 5,120,182, 0, + 117,192, 0,113,198, 0,110,202, 0,109,204, 0,109,204, 0, + 111,201, 0,115,194, 0,119,186, 0,122,176, 0,123,174, 1, + 123,171, 3,124,167, 6,124,162, 10,124,167, 6,123,170, 3, + 123,174, 1,122,178, 0,121,182, 0,121,181, 0,122,177, 0, + 123,171, 3,124,165, 8,123,168, 5,123,168, 5,122,176, 0, + 121,180, 0,122,178, 0,123,169, 4,124,152, 20,122,132, 48, + 117,110,109,115,107,129,118,114, 96,118,117, 85,123,139, 37, + 124,157, 15,123,169, 4,121,179, 0,119,185, 0,118,189, 0, + 118,189, 0,118,188, 0,120,184, 0,122,178, 0,123,168, 5, + 124,150, 22,124,149, 24,124,152, 20,124,164, 9,124,165, 8, + 124,155, 16,122,130, 53,107, 89,252,102, 80,250,115,107,129, + 123,137, 41,124,152, 20,124,161, 11,122,178, 0,119,186, 0, + 117,191, 0,116,193, 0,115,195, 0,113,199, 0,112,200, 0, + 112,199, 0,113,198, 0,114,196, 0,113,198, 0,110,202, 0, + 108,205, 0,107,206, 0,108,205, 0,108,205, 0,108,205, 0, + 108,205, 0,109,203, 0,111,201, 0,110,202, 0,109,203, 0, + 109,203, 0,111,201, 0,115,194, 0,120,184, 0,123,169, 4, + 123,168, 5,124,164, 9,123,169, 4,123,168, 5,124,160, 12, + 124,150, 22,123,168, 5,121,179, 0,118,188, 0,114,197, 0, + 110,203, 0,106,207, 0,103,212, 0,100,215, 0, 97,217, 0, + 95,220, 0, 92,222, 0, 91,224, 0, 89,225, 0, 88,226, 0, + 87,227, 0, 86,228, 0, 85,228, 0, 85,229, 0, 85,229, 0, + 86,228, 0, 89,226, 0, 91,223, 0, 95,220, 0, 99,215, 0, + 103,211, 0,109,204, 0,114,196, 0,120,184, 0,123,168, 5, + 124,149, 24,122,135, 44,123,141, 34,124,152, 20,124,157, 15, + 124,157, 15,124,154, 18,124,147, 26,124,143, 31,122,135, 44, + 120,122, 70,122,132, 48,123,137, 41,122,130, 53,119,119, 77, + 117,110,109,122,132, 48,124,149, 24,124,157, 15,124,162, 10, + 124,165, 8,124,165, 8,124,165, 8,124,167, 6,123,174, 1, + 121,179, 0,121,180, 0,122,178, 0,123,169, 4,124,162, 10, + 124,167, 6,123,171, 3,123,174, 1,123,173, 1,123,170, 3, + 124,167, 6,124,167, 6,123,170, 3,122,177, 0,119,185, 0, + 115,194, 0,109,203, 0,104,210, 0,100,214, 0, 98,216, 0, + 98,216, 0, 97,218, 0, 91,223, 0, 87,227, 0, 87,227, 0, + 88,226, 0, 91,223, 0, 94,220, 0, 98,216, 0,102,212, 0, + 106,208, 0,108,206, 0,109,203, 0,112,200, 0,114,196, 0, + 118,189, 0,118,188, 0,117,190, 0,116,193, 0,115,194, 0, + 116,193, 0,117,191, 0,119,186, 0,121,180, 0,123,171, 3, + 124,160, 12,124,145, 29,122,135, 44,122,135, 44,123,139, 37, + 123,137, 41,124,147, 26,124,160, 12,124,166, 7,124,165, 8, + 123,168, 5,123,174, 1,122,178, 0,120,182, 0,119,185, 0, + 118,188, 0,118,189, 0,118,189, 0,118,188, 0,120,184, 0, + 122,177, 0,123,171, 3,123,174, 1,122,176, 0,122,177, 0, + 122,176, 0,123,173, 1,124,165, 8,123,170, 3,122,176, 0, + 121,182, 0,119,186, 0,116,192, 0,113,198, 0,109,204, 0, + 103,211, 0,100,215, 0, 97,218, 0, 96,219, 0, 97,218, 0, + 100,215, 0,103,211, 0,107,207, 0,110,202, 0,108,205, 0, + 104,210, 0,101,214, 0, 99,216, 0, 99,216, 0,101,214, 0, + 104,210, 0,108,205, 0,113,198, 0,118,189, 0,122,175, 0, + 124,150, 22,124,145, 29,124,154, 18,124,158, 13,124,162, 10, + 124,165, 8,124,164, 9,124,164, 9,124,165, 8,124,161, 11, + 123,172, 2,119,185, 0,116,192, 0,113,198, 0,111,201, 0, + 111,201, 0,113,199, 0,116,193, 0,119,185, 0,122,176, 0, + 122,178, 0,122,176, 0,123,171, 3,124,165, 8,124,155, 16, + 124,157, 15,124,162, 10,123,170, 3,122,177, 0,122,178, 0, + 122,176, 0,123,173, 1,123,170, 3,123,171, 3,123,173, 1, + 121,180, 0,120,184, 0,120,183, 0,122,176, 0,124,162, 10, + 123,141, 34,120,122, 70,117,110,109,120,122, 70,121,125, 63, + 122,132, 48,124,157, 15,123,168, 5,122,175, 0,120,182, 0, + 119,187, 0,118,188, 0,118,188, 0,119,185, 0,121,180, 0, + 123,171, 3,124,157, 15,123,139, 37,123,137, 41,124,152, 20, + 124,154, 18,123,141, 34,118,114, 96, 87, 65,237,111, 96,248, + 118,117, 85,123,141, 34,124,155, 16,124,166, 7,121,179, 0, + 119,186, 0,118,189, 0,117,191, 0,115,195, 0,113,198, 0, + 114,197, 0,115,195, 0,116,193, 0,117,192, 0,114,196, 0, + 111,201, 0,109,204, 0,108,205, 0,109,204, 0,111,202, 0, + 110,203, 0,109,203, 0,110,202, 0,112,199, 0,115,194, 0, + 115,195, 0,114,196, 0,115,194, 0,118,189, 0,121,179, 0, + 124,165, 8,124,164, 9,124,160, 12,124,167, 6,124,167, 6, + 124,160, 12,124,164, 9,122,176, 0,120,184, 0,118,188, 0, + 116,192, 0,113,198, 0,110,202, 0,107,207, 0,105,209, 0, + 102,212, 0,100,215, 0, 97,218, 0, 95,220, 0, 93,222, 0, + 92,223, 0, 91,223, 0, 90,225, 0, 89,225, 0, 89,225, 0, + 90,225, 0, 91,223, 0, 93,222, 0, 95,219, 0, 98,217, 0, + 101,214, 0,104,210, 0,108,206, 0,113,199, 0,117,190, 0, + 122,178, 0,124,158, 13,124,147, 26,124,150, 22,124,161, 11, + 124,166, 7,124,166, 7,124,162, 10,124,154, 18,124,145, 29, + 122,130, 53,123,139, 37,124,149, 24,124,152, 20,124,145, 29, + 121,127, 58,113,100,242,118,114, 96,122,132, 48,124,143, 31, + 124,149, 24,124,150, 22,124,150, 22,124,157, 15,124,166, 7, + 123,170, 3,122,176, 0,121,180, 0,121,181, 0,122,176, 0, + 124,164, 9,124,160, 12,124,166, 7,123,170, 3,123,171, 3, + 123,169, 4,124,167, 6,124,167, 6,123,169, 4,122,176, 0, + 120,184, 0,116,193, 0,110,202, 0,104,210, 0,100,215, 0, + 97,218, 0, 96,219, 0, 97,218, 0, 92,222, 0, 89,226, 0, + 88,226, 0, 89,225, 0, 92,222, 0, 95,219, 0, 99,215, 0, + 103,212, 0,105,209, 0,107,207, 0,107,206, 0,109,204, 0, + 111,201, 0,114,196, 0,118,189, 0,118,189, 0,117,192, 0, + 116,193, 0,116,193, 0,117,190, 0,119,185, 0,122,178, 0, + 123,169, 4,124,158, 13,124,147, 26,123,139, 37,122,130, 53, + 123,137, 41,123,137, 41,124,149, 24,124,161, 11,124,167, 6, + 124,165, 8,124,165, 8,123,169, 4,122,175, 0,120,183, 0, + 119,186, 0,119,185, 0,119,185, 0,118,188, 0,118,188, 0, + 120,184, 0,122,178, 0,124,167, 6,124,167, 6,123,169, 4, + 123,169, 4,123,169, 4,124,167, 6,124,161, 11,124,167, 6, + 123,174, 1,121,180, 0,119,186, 0,116,192, 0,113,198, 0, + 109,204, 0,103,211, 0, 98,216, 0, 95,220, 0, 94,221, 0, + 95,220, 0, 98,217, 0,102,212, 0,106,207, 0,109,204, 0, + 108,205, 0,105,209, 0,103,212, 0,102,212, 0,103,212, 0, + 105,209, 0,108,205, 0,112,200, 0,116,193, 0,120,184, 0, + 123,171, 3,124,150, 22,123,141, 34,124,147, 26,124,150, 22, + 124,154, 18,124,158, 13,124,158, 13,124,154, 18,124,155, 16, + 124,152, 20,124,152, 20,123,172, 2,120,183, 0,117,191, 0, + 114,196, 0,114,197, 0,115,195, 0,117,190, 0,120,182, 0, + 122,177, 0,121,179, 0,122,178, 0,123,174, 1,124,167, 6, + 124,158, 13,124,149, 24,123,141, 34,124,155, 16,124,166, 7, + 123,170, 3,123,172, 2,123,171, 3,123,171, 3,123,172, 2, + 122,175, 0,121,182, 0,119,186, 0,119,185, 0,121,180, 0, + 123,168, 5,124,147, 26,121,127, 58,114,104,197,119,119, 77, + 120,122, 70,123,141, 34,124,160, 12,123,172, 2,122,178, 0, + 121,179, 0,121,182, 0,120,184, 0,120,184, 0,120,183, 0, + 121,179, 0,123,172, 2,124,160, 12,123,137, 41,121,125, 63, + 123,137, 41,123,139, 37,121,125, 63,109, 92,251,105, 84,252, + 117,110,109,121,125, 63,123,141, 34,124,154, 18,123,169, 4, + 121,180, 0,119,186, 0,118,188, 0,118,189, 0,116,192, 0, + 115,194, 0,116,192, 0,118,189, 0,119,187, 0,119,185, 0, + 116,193, 0,113,198, 0,111,202, 0,110,202, 0,111,201, 0, + 113,198, 0,112,200, 0,111,202, 0,111,201, 0,113,198, 0, + 116,193, 0,119,186, 0,119,186, 0,120,184, 0,121,180, 0, + 123,172, 2,124,160, 12,124,155, 16,124,152, 20,124,162, 10, + 124,162, 10,124,160, 12,123,172, 2,121,181, 0,119,186, 0, + 118,188, 0,118,188, 0,117,191, 0,115,195, 0,112,200, 0, + 110,203, 0,107,207, 0,104,210, 0,102,212, 0,100,215, 0, + 98,217, 0, 97,218, 0, 96,219, 0, 95,219, 0, 95,220, 0, + 95,220, 0, 96,219, 0, 97,217, 0, 99,216, 0,101,214, 0, + 102,212, 0,104,210, 0,106,208, 0,108,205, 0,112,200, 0, + 116,192, 0,120,182, 0,124,167, 6,124,157, 15,124,157, 15, + 124,167, 6,123,172, 2,123,171, 3,124,166, 7,124,155, 16, + 123,141, 34,123,137, 41,124,152, 20,124,160, 12,124,162, 10, + 124,157, 15,123,141, 34,117,110,109,107, 89,252,117,110,109, + 120,122, 70,121,127, 58,122,130, 53,122,135, 44,124,152, 20, + 124,164, 9,123,168, 5,123,174, 1,121,181, 0,120,184, 0, + 121,182, 0,123,174, 1,124,164, 9,124,154, 18,124,161, 11, + 124,164, 9,124,162, 10,124,161, 11,124,161, 11,123,169, 4, + 122,177, 0,121,182, 0,117,190, 0,112,200, 0,106,208, 0, + 100,214, 0, 96,219, 0, 94,220, 0, 94,220, 0, 96,219, 0, + 92,222, 0, 91,223, 0, 92,222, 0, 95,220, 0, 98,217, 0, + 101,214, 0,102,212, 0,105,209, 0,107,207, 0,107,207, 0, + 108,206, 0,109,204, 0,111,201, 0,114,196, 0,118,189, 0, + 118,188, 0,117,190, 0,118,189, 0,119,186, 0,121,180, 0, + 123,172, 2,124,162, 10,124,152, 20,123,141, 34,123,137, 41, + 122,130, 53,122,130, 53,122,135, 44,124,152, 20,124,164, 9, + 124,167, 6,124,164, 9,124,160, 12,123,170, 3,120,184, 0, + 117,191, 0,115,194, 0,115,194, 0,117,191, 0,120,184, 0, + 119,186, 0,120,183, 0,122,177, 0,124,165, 8,124,154, 18, + 124,155, 16,124,155, 16,124,155, 16,124,154, 18,124,149, 24, + 124,160, 12,123,169, 4,122,178, 0,118,189, 0,114,197, 0, + 111,202, 0,109,204, 0,103,211, 0, 98,217, 0, 94,221, 0, + 92,222, 0, 94,221, 0, 97,218, 0,102,212, 0,107,207, 0, + 107,207, 0,107,207, 0,107,207, 0,106,208, 0,106,208, 0, + 108,205, 0,111,202, 0,114,197, 0,117,192, 0,119,186, 0, + 122,178, 0,124,167, 6,124,152, 20,122,132, 48,122,130, 53, + 122,130, 53,122,135, 44,124,143, 31,124,147, 26,124,145, 29, + 123,141, 34,123,141, 34,122,135, 44,124,147, 26,123,168, 5, + 121,181, 0,118,188, 0,117,191, 0,117,190, 0,119,186, 0, + 121,179, 0,122,175, 0,122,178, 0,122,178, 0,123,174, 1, + 123,168, 5,124,167, 6,124,164, 9,124,155, 16,124,143, 31, + 124,149, 24,124,158, 13,124,164, 9,124,166, 7,124,167, 6, + 123,172, 2,122,177, 0,122,178, 0,120,184, 0,120,184, 0, + 121,181, 0,123,172, 2,124,155, 16,121,125, 63,105, 84,252, + 113,100,242,122,132, 48,124,154, 18,124,165, 8,122,177, 0, + 120,182, 0,120,184, 0,121,182, 0,122,178, 0,122,178, 0, + 122,178, 0,122,176, 0,123,171, 3,124,161, 11,124,143, 31, + 122,130, 53,118,117, 85,120,122, 70,115,107,129,102, 80,250, + 113,100,242,119,119, 77,122,130, 53,123,137, 41,124,149, 24, + 123,171, 3,121,181, 0,119,185, 0,119,187, 0,119,186, 0, + 119,186, 0,118,188, 0,119,185, 0,119,186, 0,120,184, 0, + 120,182, 0,119,187, 0,116,192, 0,114,196, 0,114,197, 0, + 115,194, 0,114,196, 0,113,198, 0,113,199, 0,113,198, 0, + 115,195, 0,118,189, 0,121,182, 0,123,171, 3,123,171, 3, + 123,168, 5,124,162, 10,124,152, 20,124,143, 31,123,141, 34, + 124,154, 18,124,154, 18,123,168, 5,122,178, 0,120,184, 0, + 119,186, 0,119,186, 0,120,184, 0,121,182, 0,119,187, 0, + 114,197, 0,109,204, 0,105,209, 0,102,213, 0,100,214, 0, + 100,215, 0,101,214, 0,102,213, 0,101,213, 0,101,214, 0, + 101,214, 0,102,213, 0,102,212, 0,104,210, 0,105,209, 0, + 107,207, 0,108,205, 0,108,205, 0,109,203, 0,111,202, 0, + 113,198, 0,117,192, 0,120,183, 0,123,169, 4,124,167, 6, + 124,166, 7,123,171, 3,123,174, 1,123,172, 2,124,165, 8, + 124,152, 20,122,135, 44,124,147, 26,124,160, 12,124,167, 6, + 123,168, 5,124,162, 10,124,149, 24,120,122, 70,102, 80,250, + 107, 89,252,111, 96,248,114,104,197,117,110,109,118,117, 85, + 123,141, 34,124,155, 16,124,160, 12,123,173, 1,120,182, 0, + 119,186, 0,119,186, 0,121,181, 0,123,171, 3,124,161, 11, + 124,147, 26,124,150, 22,124,150, 22,124,152, 20,124,161, 11, + 123,170, 3,122,177, 0,121,182, 0,120,184, 0,115,195, 0, + 108,205, 0,102,212, 0, 97,217, 0, 94,220, 0, 94,221, 0, + 94,220, 0, 96,219, 0, 95,220, 0, 96,219, 0, 98,217, 0, + 98,217, 0, 99,215, 0,101,214, 0,104,210, 0,108,205, 0, + 108,205, 0,108,205, 0,109,204, 0,110,202, 0,113,198, 0, + 116,193, 0,118,188, 0,120,184, 0,120,184, 0,121,180, 0, + 122,177, 0,123,173, 1,123,168, 5,124,160, 12,124,147, 26, + 121,127, 58,119,119, 77,119,119, 77,123,141, 34,124,157, 15, + 124,165, 8,124,167, 6,124,162, 10,124,155, 16,122,176, 0, + 118,189, 0,114,197, 0,112,200, 0,111,201, 0,113,198, 0, + 117,192, 0,119,185, 0,120,182, 0,122,175, 0,124,162, 10, + 123,137, 41,124,143, 31,124,150, 22,124,149, 24,122,135, 44, + 122,130, 53,124,150, 22,123,168, 5,120,183, 0,116,192, 0, + 112,199, 0,110,203, 0,109,204, 0,103,212, 0, 97,217, 0, + 93,221, 0, 92,223, 0, 93,221, 0, 97,218, 0,102,212, 0, + 107,206, 0,106,207, 0,106,208, 0,107,207, 0,109,204, 0, + 110,202, 0,113,198, 0,116,193, 0,118,188, 0,120,184, 0, + 122,178, 0,123,171, 3,124,164, 9,124,154, 18,123,139, 37, + 120,122, 70,111, 96,248,114,104,197,118,117, 85,121,127, 58, + 121,127, 58,119,119, 77,119,119, 77,118,114, 96,114,104,197, + 124,143, 31,124,167, 6,122,178, 0,120,184, 0,119,185, 0, + 120,182, 0,122,176, 0,123,169, 4,122,175, 0,122,176, 0, + 123,173, 1,123,171, 3,123,173, 1,123,172, 2,124,166, 7, + 124,152, 20,124,150, 22,123,141, 34,124,147, 26,124,154, 18, + 124,161, 11,123,171, 3,122,178, 0,121,180, 0,122,178, 0, + 121,181, 0,121,179, 0,123,173, 1,124,161, 11,123,141, 34, + 115,107,129,120,122, 70,124,147, 26,124,164, 9,123,172, 2, + 121,179, 0,120,184, 0,119,185, 0,120,184, 0,121,180, 0, + 123,172, 2,123,169, 4,123,168, 5,124,165, 8,124,157, 15, + 124,143, 31,123,139, 37,122,130, 53,118,117, 85,114,104,197, + 107, 89,252,114,104,197,120,122, 70,122,132, 48,122,135, 44, + 124,147, 26,123,168, 5,122,178, 0,120,182, 0,120,184, 0, + 120,184, 0,120,182, 0,121,182, 0,121,182, 0,120,182, 0, + 121,181, 0,121,182, 0,119,187, 0,117,191, 0,116,192, 0, + 116,193, 0,115,194, 0,115,194, 0,114,196, 0,114,196, 0, + 116,193, 0,118,189, 0,120,182, 0,123,172, 2,124,158, 13, + 124,154, 18,124,152, 20,124,147, 26,123,139, 37,122,132, 48, + 122,130, 53,124,145, 29,124,157, 15,123,169, 4,122,178, 0, + 120,182, 0,120,184, 0,120,182, 0,121,179, 0,122,178, 0, + 118,188, 0,113,198, 0,108,205, 0,104,210, 0,101,214, 0, + 99,215, 0, 99,216, 0,100,215, 0,101,214, 0,102,213, 0, + 103,211, 0,104,210, 0,105,209, 0,106,208, 0,107,207, 0, + 107,206, 0,108,205, 0,110,202, 0,113,199, 0,113,198, 0, + 114,197, 0,115,194, 0,118,189, 0,121,180, 0,122,176, 0, + 123,174, 1,123,172, 2,123,172, 2,123,173, 1,123,169, 4, + 124,158, 13,123,141, 34,124,147, 26,124,157, 15,124,165, 8, + 123,170, 3,123,170, 3,124,166, 7,124,154, 18,122,132, 48, + 114,104,197,105, 84,252, 87, 65,237, 99, 75,247,107, 89,252, + 114,104,197,121,127, 58,124,147, 26,124,164, 9,122,177, 0, + 119,185, 0,118,189, 0,118,189, 0,119,185, 0,122,175, 0, + 124,166, 7,124,154, 18,124,147, 26,124,147, 26,124,150, 22, + 124,158, 13,124,166, 7,123,174, 1,122,177, 0,121,179, 0, + 117,190, 0,111,201, 0,105,209, 0,100,215, 0, 97,218, 0, + 95,220, 0, 95,220, 0, 96,219, 0, 97,218, 0, 97,217, 0, + 98,217, 0, 98,216, 0,100,215, 0,102,212, 0,106,208, 0, + 110,203, 0,110,203, 0,110,203, 0,110,202, 0,111,201, 0, + 113,198, 0,115,194, 0,118,189, 0,119,187, 0,119,186, 0, + 120,184, 0,121,182, 0,122,178, 0,123,173, 1,124,166, 7, + 124,155, 16,123,139, 37,120,122, 70,122,130, 53,124,150, 22, + 124,162, 10,124,167, 6,124,167, 6,124,162, 10,124,161, 11, + 121,180, 0,117,192, 0,112,200, 0,109,204, 0,108,205, 0, + 110,203, 0,113,199, 0,116,193, 0,119,187, 0,121,181, 0, + 123,172, 2,124,160, 12,124,147, 26,124,154, 18,124,155, 16, + 124,145, 29,122,132, 48,124,157, 15,123,174, 1,119,186, 0, + 115,194, 0,112,200, 0,109,203, 0,108,205, 0,102,212, 0, + 97,218, 0, 93,221, 0, 92,222, 0, 94,221, 0, 98,217, 0, + 103,212, 0,107,206, 0,107,206, 0,107,207, 0,108,205, 0, + 110,202, 0,114,196, 0,117,191, 0,119,187, 0,119,187, 0, + 120,184, 0,121,179, 0,123,170, 3,124,158, 13,124,152, 20, + 124,145, 29,122,135, 44,120,122, 70,113,100,242,118,117, 85, + 121,127, 58,121,127, 58,119,119, 77,113,100,242,102, 80,250, + 69, 60,230,111, 96,248,123,141, 34,124,164, 9,123,174, 1, + 122,178, 0,122,178, 0,123,174, 1,123,169, 4,124,167, 6, + 123,169, 4,123,172, 2,122,176, 0,122,178, 0,122,178, 0, + 123,173, 1,124,164, 9,124,157, 15,124,147, 26,121,125, 63, + 123,137, 41,124,158, 13,123,171, 3,122,178, 0,121,181, 0, + 121,179, 0,122,175, 0,123,174, 1,123,170, 3,124,162, 10, + 124,149, 24,121,127, 58,122,135, 44,124,155, 16,123,168, 5, + 122,176, 0,121,179, 0,120,183, 0,120,184, 0,120,183, 0, + 121,180, 0,123,172, 2,124,157, 15,124,154, 18,124,152, 20, + 124,147, 26,124,145, 29,124,147, 26,123,139, 37,121,127, 58, + 118,114, 96,109, 92,251,111, 96,248,118,117, 85,122,130, 53, + 123,137, 41,123,137, 41,124,160, 12,123,170, 3,122,175, 0, + 122,177, 0,122,177, 0,122,175, 0,123,174, 1,123,174, 1, + 123,174, 1,123,174, 1,122,178, 0,119,185, 0,118,189, 0, + 117,192, 0,116,193, 0,116,193, 0,116,193, 0,116,193, 0, + 116,192, 0,116,192, 0,118,189, 0,121,182, 0,123,168, 5, + 124,147, 26,122,130, 53,122,130, 53,121,127, 58,119,119, 77, + 118,117, 85,119,119, 77,123,137, 41,124,150, 22,124,165, 8, + 123,174, 1,122,178, 0,122,178, 0,122,175, 0,123,174, 1, + 120,184, 0,117,191, 0,112,199, 0,107,206, 0,103,211, 0, + 100,214, 0, 99,216, 0, 98,216, 0, 99,216, 0,100,215, 0, + 101,213, 0,103,211, 0,105,209, 0,107,207, 0,108,205, 0, + 110,203, 0,111,202, 0,112,200, 0,113,198, 0,115,194, 0, + 116,193, 0,116,192, 0,117,190, 0,119,185, 0,121,180, 0, + 121,179, 0,122,178, 0,122,175, 0,123,169, 4,123,168, 5, + 124,161, 11,124,145, 29,124,147, 26,124,155, 16,124,161, 11, + 124,167, 6,123,171, 3,123,171, 3,124,167, 6,124,158, 13, + 124,143, 31,121,125, 63,111, 96,248, 30, 55,222, 30, 26,168, + 30, 55,222,111, 96,248,122,130, 53,124,152, 20,123,168, 5, + 121,180, 0,118,188, 0,117,192, 0,117,192, 0,118,188, 0, + 121,180, 0,124,167, 6,124,157, 15,124,147, 26,124,143, 31, + 124,143, 31,124,150, 22,124,165, 8,123,172, 2,122,176, 0, + 122,178, 0,120,184, 0,115,195, 0,109,204, 0,104,210, 0, + 101,214, 0, 98,216, 0, 98,217, 0, 98,217, 0, 98,217, 0, + 98,216, 0, 99,215, 0,101,214, 0,103,212, 0,106,208, 0, + 109,204, 0,112,200, 0,112,200, 0,112,200, 0,113,199, 0, + 114,197, 0,115,194, 0,117,192, 0,118,189, 0,118,188, 0, + 119,187, 0,119,186, 0,120,183, 0,121,179, 0,123,173, 1, + 124,166, 7,124,155, 16,123,141, 34,121,127, 58,123,139, 37, + 124,155, 16,124,165, 8,123,168, 5,124,167, 6,124,165, 8, + 124,167, 6,120,184, 0,115,195, 0,110,203, 0,106,207, 0, + 105,209, 0,106,208, 0,108,205, 0,111,201, 0,114,196, 0, + 117,191, 0,119,185, 0,122,177, 0,124,165, 8,124,158, 13, + 124,160, 12,124,154, 18,124,147, 26,124,167, 6,121,180, 0, + 117,190, 0,114,197, 0,111,201, 0,109,204, 0,108,205, 0, + 103,212, 0, 98,217, 0, 94,221, 0, 93,221, 0, 95,220, 0, + 98,217, 0,103,212, 0,107,207, 0,110,203, 0,110,203, 0, + 110,202, 0,113,199, 0,116,193, 0,119,186, 0,120,184, 0, + 119,185, 0,120,183, 0,122,178, 0,123,170, 3,124,160, 12, + 124,143, 31,123,141, 34,123,137, 41,122,130, 53,118,114, 96, + 121,125, 63,122,135, 44,123,141, 34,123,141, 34,122,130, 53, + 118,117, 85,114,104,197, 87, 65,237,114,104,197,122,132, 48, + 124,157, 15,124,167, 6,123,171, 3,123,171, 3,123,168, 5, + 123,168, 5,123,170, 3,122,175, 0,122,178, 0,121,182, 0, + 120,182, 0,121,180, 0,123,173, 1,124,164, 9,124,155, 16, + 124,147, 26,123,139, 37,124,157, 15,123,171, 3,122,178, 0, + 122,178, 0,122,178, 0,123,174, 1,124,167, 6,124,166, 7, + 124,160, 12,124,150, 22,123,137, 41,123,139, 37,124,158, 13, + 123,169, 4,122,175, 0,122,178, 0,122,178, 0,121,180, 0, + 121,179, 0,122,177, 0,123,170, 3,124,154, 18,122,130, 53, + 122,130, 53,122,132, 48,124,149, 24,124,154, 18,124,147, 26, + 122,135, 44,119,119, 77,115,107,129,102, 80,250,111, 96,248, + 119,119, 77,122,132, 48,123,137, 41,124,143, 31,124,155, 16, + 124,161, 11,124,162, 10,124,164, 9,124,165, 8,124,162, 10, + 124,160, 12,124,161, 11,124,161, 11,123,172, 2,121,180, 0, + 119,186, 0,118,189, 0,117,191, 0,117,191, 0,118,189, 0, + 116,192, 0,115,194, 0,115,194, 0,117,191, 0,120,184, 0, + 123,173, 1,124,155, 16,124,149, 24,122,135, 44,113,100,242, + 109, 92,251,111, 96,248,117,110,109,122,130, 53,123,139, 37, + 124,154, 18,124,166, 7,123,170, 3,123,170, 3,124,166, 7, + 122,177, 0,119,186, 0,117,191, 0,112,200, 0,108,206, 0, + 104,210, 0,101,213, 0,100,215, 0, 99,215, 0,100,215, 0, + 101,214, 0,102,212, 0,103,211, 0,104,210, 0,106,208, 0, + 108,206, 0,110,202, 0,113,198, 0,116,193, 0,117,191, 0, + 118,188, 0,118,189, 0,118,188, 0,119,186, 0,121,181, 0, + 121,179, 0,121,179, 0,122,178, 0,123,174, 1,124,166, 7, + 124,158, 13,124,147, 26,123,137, 41,124,154, 18,124,160, 12, + 124,162, 10,124,166, 7,123,168, 5,123,168, 5,124,165, 8, + 124,160, 12,124,150, 22,123,137, 41,120,122, 70,109, 92,251, + 30, 37,192, 30, 20,155,107, 89,252,122,130, 53,124,155, 16, + 123,170, 3,121,182, 0,118,189, 0,116,193, 0,116,193, 0, + 117,190, 0,120,182, 0,124,167, 6,124,154, 18,123,141, 34, + 122,130, 53,121,125, 63,124,147, 26,124,162, 10,123,170, 3, + 122,178, 0,120,183, 0,118,188, 0,118,189, 0,114,196, 0, + 110,202, 0,106,207, 0,103,211, 0,102,213, 0,101,214, 0, + 101,214, 0,101,214, 0,102,212, 0,104,210, 0,107,207, 0, + 110,202, 0,113,198, 0,114,197, 0,115,195, 0,115,194, 0, + 117,192, 0,117,190, 0,118,188, 0,119,186, 0,120,184, 0, + 120,184, 0,119,185, 0,120,184, 0,121,181, 0,122,176, 0, + 123,169, 4,124,160, 12,124,147, 26,122,132, 48,121,125, 63, + 124,147, 26,124,161, 11,123,168, 5,123,170, 3,124,167, 6, + 123,170, 3,123,173, 1,119,186, 0,114,197, 0,108,205, 0, + 105,209, 0,103,212, 0,103,212, 0,105,209, 0,107,206, 0, + 110,203, 0,113,199, 0,115,194, 0,119,187, 0,122,178, 0, + 124,162, 10,124,161, 11,124,155, 16,124,157, 15,123,173, 1, + 120,184, 0,116,192, 0,113,198, 0,110,202, 0,109,204, 0, + 109,204, 0,103,211, 0, 99,215, 0, 96,219, 0, 95,220, 0, + 97,218, 0,100,215, 0,103,211, 0,107,207, 0,110,202, 0, + 113,199, 0,113,198, 0,115,195, 0,117,190, 0,120,183, 0, + 120,182, 0,121,181, 0,121,180, 0,122,177, 0,123,170, 3, + 124,161, 11,124,147, 26,122,130, 53,122,130, 53,121,127, 58, + 119,119, 77,122,130, 53,123,141, 34,124,149, 24,124,152, 20, + 124,150, 22,124,143, 31,122,132, 48,118,114, 96,114,104,197, + 120,122, 70,122,132, 48,124,147, 26,124,160, 12,124,165, 8, + 124,165, 8,124,166, 7,123,170, 3,122,175, 0,121,179, 0, + 120,183, 0,120,184, 0,120,184, 0,121,181, 0,122,176, 0, + 123,171, 3,124,167, 6,124,162, 10,124,152, 20,124,167, 6, + 122,176, 0,123,172, 2,123,172, 2,123,169, 4,124,164, 9, + 124,158, 13,124,154, 18,124,147, 26,123,137, 41,122,135, 44, + 124,154, 18,124,165, 8,123,169, 4,123,171, 3,123,169, 4, + 123,170, 3,123,170, 3,123,168, 5,124,162, 10,124,145, 29, + 117,110,109,111, 96,248,123,137, 41,124,154, 18,124,158, 13, + 124,154, 18,123,141, 34,124,143, 31,123,137, 41,119,119, 77, + 115,107,129,118,114, 96,121,125, 63,123,137, 41,123,139, 37, + 124,143, 31,124,150, 22,124,154, 18,124,155, 16,124,154, 18, + 124,150, 22,124,145, 29,123,139, 37,124,143, 31,124,161, 11, + 123,172, 2,121,180, 0,120,184, 0,119,186, 0,119,186, 0, + 119,187, 0,117,192, 0,115,194, 0,115,194, 0,117,192, 0, + 119,186, 0,122,177, 0,124,166, 7,124,164, 9,124,152, 20, + 121,127, 58,102, 80,250, 94, 70,242,113,100,242,118,117, 85, + 122,130, 53,123,137, 41,124,152, 20,124,160, 12,124,160, 12, + 124,164, 9,122,175, 0,120,184, 0,118,189, 0,113,198, 0, + 109,203, 0,106,207, 0,104,210, 0,103,212, 0,102,212, 0, + 102,212, 0,103,212, 0,103,211, 0,102,213, 0,102,212, 0, + 103,211, 0,106,208, 0,108,205, 0,111,201, 0,114,196, 0, + 117,191, 0,119,185, 0,120,184, 0,120,184, 0,121,182, 0, + 122,177, 0,122,175, 0,122,177, 0,122,175, 0,123,170, 3, + 124,158, 13,124,143, 31,121,127, 58,123,141, 34,124,154, 18, + 124,157, 15,124,155, 16,124,157, 15,124,158, 13,124,158, 13, + 124,157, 15,124,154, 18,124,149, 24,124,143, 31,122,132, 48, + 117,110,109, 94, 70,242, 30, 31,180, 94, 70,242,121,125, 63, + 124,154, 18,123,170, 3,121,181, 0,118,188, 0,116,193, 0, + 116,193, 0,117,190, 0,120,182, 0,124,167, 6,124,143, 31, + 121,125, 63,113,100,242,119,119, 77,124,152, 20,123,169, 4, + 122,178, 0,120,184, 0,118,188, 0,117,191, 0,117,191, 0, + 117,190, 0,117,192, 0,113,198, 0,110,202, 0,108,206, 0, + 106,208, 0,105,209, 0,105,209, 0,107,207, 0,109,204, 0, + 112,200, 0,115,194, 0,116,193, 0,117,192, 0,118,188, 0, + 119,185, 0,121,181, 0,122,177, 0,123,174, 1,123,173, 1, + 123,174, 1,122,177, 0,121,179, 0,122,178, 0,122,175, 0, + 123,168, 5,124,158, 13,124,145, 29,122,130, 53,119,119, 77, + 121,127, 58,124,152, 20,124,166, 7,123,172, 2,123,173, 1, + 123,169, 4,123,174, 1,122,177, 0,118,188, 0,113,198, 0, + 108,205, 0,104,210, 0,102,213, 0,101,213, 0,102,212, 0, + 105,209, 0,107,207, 0,109,204, 0,112,199, 0,116,193, 0, + 120,184, 0,123,170, 3,124,155, 16,124,150, 22,124,160, 12, + 122,175, 0,119,186, 0,115,194, 0,112,199, 0,110,202, 0, + 109,204, 0,109,204, 0,105,209, 0,101,214, 0, 98,216, 0, + 98,217, 0, 99,215, 0,102,212, 0,105,209, 0,108,205, 0, + 111,202, 0,114,197, 0,116,193, 0,117,191, 0,119,187, 0, + 121,181, 0,121,179, 0,122,178, 0,122,176, 0,123,173, 1, + 123,169, 4,124,162, 10,124,152, 20,123,139, 37,121,125, 63, + 119,119, 77,120,122, 70,122,132, 48,124,143, 31,124,150, 22, + 124,157, 15,124,160, 12,124,160, 12,124,155, 16,124,143, 31, + 118,114, 96,118,117, 85,121,127, 58,121,127, 58,123,137, 41, + 124,150, 22,124,155, 16,124,158, 13,124,165, 8,123,170, 3, + 122,177, 0,121,182, 0,119,185, 0,119,186, 0,119,185, 0, + 120,183, 0,121,181, 0,122,178, 0,123,174, 1,124,167, 6, + 124,160, 12,123,169, 4,124,161, 11,124,162, 10,124,161, 11, + 124,157, 15,124,150, 22,124,143, 31,123,137, 41,122,130, 53, + 120,122, 70,124,143, 31,124,154, 18,124,158, 13,124,158, 13, + 124,155, 16,124,152, 20,124,152, 20,124,152, 20,124,147, 26, + 121,125, 63,109, 92,251,115,107,129,124,143, 31,124,158, 13, + 124,164, 9,124,160, 12,124,157, 15,124,162, 10,124,161, 11, + 124,150, 22,121,127, 58,118,117, 85,118,117, 85,122,135, 44, + 123,141, 34,123,141, 34,123,141, 34,124,143, 31,124,143, 31, + 123,139, 37,122,132, 48,121,127, 58,120,122, 70,122,130, 53, + 124,143, 31,124,158, 13,123,169, 4,122,175, 0,122,177, 0, + 122,175, 0,120,182, 0,118,188, 0,117,192, 0,116,192, 0, + 117,191, 0,119,186, 0,122,178, 0,123,174, 1,123,173, 1, + 124,166, 7,124,149, 24,118,114, 96, 87, 65,237,102, 80,250, + 115,107,129,118,117, 85,119,119, 77,122,135, 44,124,145, 29, + 124,147, 26,124,154, 18,124,167, 6,122,178, 0,120,184, 0, + 116,193, 0,113,199, 0,110,202, 0,108,205, 0,107,207, 0, + 107,207, 0,107,207, 0,107,207, 0,104,210, 0,102,212, 0, + 101,213, 0,102,212, 0,104,210, 0,107,207, 0,108,205, 0, + 110,202, 0,113,198, 0,116,193, 0,119,186, 0,121,179, 0, + 122,178, 0,123,173, 1,124,167, 6,123,170, 3,123,168, 5, + 124,161, 11,124,145, 29,120,122, 70,118,114, 96,122,135, 44, + 124,147, 26,124,147, 26,123,141, 34,123,137, 41,123,137, 41, + 123,137, 41,123,139, 37,123,139, 37,123,141, 34,123,139, 37, + 122,132, 48,118,117, 85,113,100,242, 94, 70,242, 69, 60,230, + 118,114, 96,124,147, 26,124,165, 8,122,178, 0,119,186, 0, + 117,190, 0,117,191, 0,118,188, 0,121,179, 0,124,161, 11, + 121,125, 63,105, 84,252,102, 80,250,123,137, 41,124,165, 8, + 122,178, 0,119,185, 0,118,189, 0,117,192, 0,116,192, 0, + 117,191, 0,118,188, 0,120,184, 0,120,183, 0,117,190, 0, + 114,196, 0,112,200, 0,110,202, 0,110,202, 0,112,200, 0, + 114,196, 0,117,191, 0,118,188, 0,119,187, 0,120,184, 0, + 122,178, 0,123,169, 4,124,160, 12,124,150, 22,124,145, 29, + 124,145, 29,124,152, 20,124,161, 11,124,167, 6,124,167, 6, + 124,164, 9,124,154, 18,123,141, 34,120,122, 70,118,117, 85, + 115,107,129,122,132, 48,124,158, 13,123,170, 3,122,177, 0, + 122,177, 0,123,173, 1,123,174, 1,122,178, 0,119,187, 0, + 114,197, 0,109,204, 0,105,209, 0,102,212, 0,102,213, 0, + 102,212, 0,103,211, 0,105,209, 0,108,206, 0,111,201, 0, + 115,195, 0,119,186, 0,123,172, 2,124,149, 24,123,137, 41, + 124,154, 18,123,173, 1,119,186, 0,116,193, 0,113,199, 0, + 110,202, 0,110,203, 0,110,203, 0,107,207, 0,103,211, 0, + 102,213, 0,102,213, 0,103,211, 0,106,208, 0,108,205, 0, + 110,202, 0,112,199, 0,115,194, 0,118,189, 0,119,186, 0, + 120,184, 0,122,178, 0,123,172, 2,123,171, 3,123,171, 3, + 123,169, 4,124,166, 7,124,161, 11,124,154, 18,123,141, 34, + 121,127, 58,120,122, 70,121,125, 63,122,130, 53,123,139, 37, + 124,147, 26,124,155, 16,124,162, 10,124,167, 6,123,168, 5, + 124,165, 8,124,150, 22,121,125, 63,121,127, 58,121,125, 63, + 118,114, 96,120,122, 70,122,135, 44,123,141, 34,124,150, 22, + 124,160, 12,123,169, 4,122,176, 0,121,182, 0,119,185, 0, + 119,186, 0,119,186, 0,119,186, 0,120,184, 0,121,181, 0, + 123,173, 1,124,161, 11,124,155, 16,123,137, 41,124,143, 31, + 124,145, 29,124,147, 26,124,145, 29,123,137, 41,120,122, 70, + 118,117, 85,118,114, 96,120,122, 70,122,135, 44,123,137, 41, + 122,135, 44,121,127, 58,118,117, 85,118,117, 85,119,119, 77, + 118,117, 85,111, 96,248,118,117, 85,121,127, 58,124,150, 22, + 124,164, 9,123,168, 5,124,165, 8,123,170, 3,122,175, 0, + 122,175, 0,123,170, 3,124,155, 16,120,122, 70,118,114, 96, + 121,125, 63,123,139, 37,124,143, 31,123,141, 34,121,127, 58, + 119,119, 77,117,110,109,113,100,242,111, 96,248,118,114, 96, + 121,125, 63,121,127, 58,123,137, 41,124,149, 24,124,157, 15, + 124,160, 12,124,160, 12,123,171, 3,121,180, 0,119,186, 0, + 118,189, 0,118,189, 0,119,186, 0,121,180, 0,121,179, 0, + 122,178, 0,123,173, 1,124,161, 11,122,135, 44,113,100,242, + 99, 75,247,109, 92,251,114,104,197,109, 92,251,117,110,109, + 122,130, 53,123,137, 41,122,135, 44,124,149, 24,124,164, 9, + 122,175, 0,119,186, 0,117,192, 0,114,196, 0,113,198, 0, + 112,199, 0,112,200, 0,112,200, 0,111,201, 0,107,207, 0, + 104,210, 0,102,212, 0,102,212, 0,104,210, 0,106,208, 0, + 106,208, 0,107,206, 0,110,202, 0,113,199, 0,116,193, 0, + 119,186, 0,123,174, 1,123,168, 5,124,158, 13,124,158, 13, + 124,157, 15,124,147, 26,121,127, 58,107, 89,252,111, 96,248, + 119,119, 77,121,127, 58,120,122, 70,115,107,129,111, 96,248, + 107, 89,252,109, 92,251,113,100,242,117,110,109,118,117, 85, + 120,122, 70,119,119, 77,118,114, 96,117,110,109,109, 92,251, + 87, 65,237,111, 96,248,122,135, 44,124,157, 15,123,170, 3, + 121,180, 0,119,186, 0,119,186, 0,120,183, 0,123,173, 1, + 124,152, 20,114,104,197, 30, 20,155,114,104,197,124,154, 18, + 123,173, 1,120,184, 0,118,189, 0,117,192, 0,116,193, 0, + 116,193, 0,117,191, 0,119,186, 0,121,182, 0,122,176, 0, + 123,170, 3,121,182, 0,118,189, 0,116,192, 0,116,193, 0, + 117,191, 0,119,187, 0,121,180, 0,122,178, 0,122,176, 0, + 123,170, 3,124,158, 13,123,137, 41,122,130, 53,120,122, 70, + 115,107,129,107, 89,252,113,100,242,121,127, 58,124,143, 31, + 124,147, 26,123,141, 34,122,130, 53,117,110,109,115,107,129, + 117,110,109,121,125, 63,123,141, 34,124,164, 9,122,176, 0, + 121,181, 0,121,182, 0,122,178, 0,123,172, 2,122,175, 0, + 119,185, 0,115,195, 0,110,202, 0,107,207, 0,104,210, 0, + 103,211, 0,104,210, 0,105,209, 0,106,208, 0,108,205, 0, + 111,201, 0,115,194, 0,120,184, 0,123,168, 5,123,139, 37, + 114,104,197,123,141, 34,124,167, 6,120,182, 0,117,192, 0, + 113,198, 0,111,201, 0,110,202, 0,111,201, 0,109,204, 0, + 106,207, 0,106,208, 0,106,208, 0,108,206, 0,110,202, 0, + 113,199, 0,114,196, 0,115,194, 0,118,189, 0,120,184, 0, + 121,182, 0,121,180, 0,122,176, 0,123,168, 5,124,162, 10, + 124,164, 9,124,165, 8,124,164, 9,124,157, 15,124,149, 24, + 123,137, 41,121,125, 63,118,117, 85,118,117, 85,122,130, 53, + 124,149, 24,124,158, 13,124,162, 10,124,162, 10,123,168, 5, + 123,174, 1,123,174, 1,123,168, 5,124,155, 16,123,139, 37, + 122,135, 44,121,127, 58,115,107,129,120,122, 70,121,127, 58, + 123,137, 41,124,149, 24,124,160, 12,123,168, 5,122,175, 0, + 121,181, 0,120,184, 0,119,186, 0,119,187, 0,119,186, 0, + 120,182, 0,122,175, 0,124,161, 11,122,135, 44,111, 96,248, + 115,107,129,120,122, 70,122,132, 48,123,137, 41,123,137, 41, + 122,130, 53,118,114, 96,113,100,242,111, 96,248,115,107,129, + 115,107,129,113,100,242,105, 84,252,105, 84,252, 87, 65,237, + 87, 65,237, 94, 70,242,117,110,109,122,132, 48,124,143, 31, + 124,158, 13,123,169, 4,123,172, 2,123,171, 3,122,178, 0, + 120,184, 0,119,185, 0,121,182, 0,123,172, 2,124,152, 20, + 117,110,109,118,114, 96,122,135, 44,123,141, 34,123,139, 37, + 121,127, 58,114,104,197, 87, 65,237, 30, 31,180, 94, 70,242, + 111, 96,248,118,114, 96,119,119, 77,119,119, 77,118,117, 85, + 121,125, 63,121,125, 63,121,125, 63,124,147, 26,124,165, 8, + 122,176, 0,120,182, 0,119,185, 0,119,185, 0,121,182, 0, + 121,180, 0,121,181, 0,122,178, 0,123,168, 5,124,150, 22, + 120,122, 70,113,100,242, 94, 70,242,102, 80,250, 94, 70,242, + 107, 89,252,118,114, 96,121,127, 58,122,130, 53,121,125, 63, + 122,135, 44,124,162, 10,122,175, 0,120,182, 0,119,186, 0, + 118,189, 0,117,190, 0,117,191, 0,117,192, 0,115,194, 0, + 111,202, 0,107,207, 0,105,209, 0,103,211, 0,104,210, 0, + 105,209, 0,105,209, 0,106,208, 0,108,205, 0,110,202, 0, + 113,198, 0,117,192, 0,120,183, 0,124,167, 6,124,152, 20, + 123,139, 37,123,137, 41,121,125, 63,113,100,242, 30, 49,213, + 87, 65,237,107, 89,252,109, 92,251,107, 89,252, 99, 75,247, + 111, 96,248,114,104,197,111, 96,248, 94, 70,242, 69, 60,230, + 87, 65,237,105, 84,252,105, 84,252,113,100,242,115,107,129, + 114,104,197,109, 92,251,107, 89,252,118,117, 85,124,143, 31, + 124,161, 11,123,173, 1,121,179, 0,121,181, 0,122,177, 0, + 124,166, 7,123,139, 37, 99, 75,247, 30, 49,213,121,127, 58, + 124,162, 10,122,178, 0,119,186, 0,117,190, 0,117,192, 0, + 116,193, 0,117,192, 0,117,190, 0,117,191, 0,118,189, 0, + 119,185, 0,122,177, 0,124,166, 7,123,172, 2,121,180, 0, + 120,182, 0,121,180, 0,122,175, 0,124,166, 7,124,160, 12, + 124,157, 15,124,145, 29,118,117, 85,118,114, 96,118,114, 96, + 118,114, 96,114,104,197, 94, 70,242, 30, 31,180, 87, 65,237, + 114,104,197,117,110,109,115,107,129,109, 92,251, 99, 75,247, + 109, 92,251,118,117, 85,122,132, 48,124,149, 24,123,169, 4, + 121,181, 0,119,185, 0,119,186, 0,120,182, 0,123,173, 1, + 123,171, 3,120,183, 0,116,193, 0,112,200, 0,109,204, 0, + 107,207, 0,106,207, 0,106,207, 0,107,207, 0,108,205, 0, + 110,202, 0,114,197, 0,117,190, 0,122,178, 0,124,158, 13, + 118,117, 85, 69, 60,230,120,122, 70,124,158, 13,122,178, 0, + 118,189, 0,115,195, 0,113,199, 0,112,200, 0,113,199, 0, + 111,202, 0,109,204, 0,109,204, 0,110,202, 0,113,199, 0, + 115,194, 0,117,190, 0,118,188, 0,119,186, 0,121,181, 0, + 122,176, 0,122,177, 0,122,176, 0,123,172, 2,124,165, 8, + 124,150, 22,124,155, 16,124,160, 12,124,161, 11,124,155, 16, + 124,143, 31,120,122, 70,114,104,197,111, 96,248,109, 92,251, + 122,132, 48,124,155, 16,124,167, 6,123,172, 2,123,173, 1, + 123,170, 3,122,175, 0,122,178, 0,122,176, 0,123,170, 3, + 124,157, 15,123,141, 34,122,132, 48,118,117, 85,115,107,129, + 118,117, 85,123,137, 41,124,160, 12,123,172, 2,122,177, 0, + 122,178, 0,121,181, 0,120,184, 0,120,184, 0,120,184, 0, + 120,184, 0,121,181, 0,123,172, 2,124,155, 16,121,125, 63, + 30, 37,192,111, 96,248,118,117, 85,119,119, 77,121,127, 58, + 122,135, 44,122,135, 44,120,122, 70,109, 92,251,109, 92,251, + 102, 80,250, 99, 75,247, 87, 65,237,102, 80,250,102, 80,250, + 30, 55,222, 30, 2,128,102, 80,250,119,119, 77,123,139, 37, + 124,150, 22,124,165, 8,123,174, 1,122,178, 0,122,176, 0, + 120,183, 0,118,188, 0,117,191, 0,118,188, 0,121,182, 0, + 123,168, 5,123,139, 37,109, 92,251,120,122, 70,122,130, 53, + 122,130, 53,118,117, 85,107, 89,252, 87, 65,237, 30, 55,222, + 69, 60,230,102, 80,250,107, 89,252,114,104,197,117,110,109, + 115,107,129,111, 96,248, 99, 75,247, 30, 43,203,111, 96,248, + 123,139, 37,124,162, 10,123,174, 1,121,181, 0,120,183, 0, + 121,182, 0,122,177, 0,121,179, 0,122,178, 0,123,172, 2, + 124,161, 11,123,141, 34,118,114, 96,105, 84,252, 30, 37,192, + 102, 80,250,113,100,242,114,104,197,119,119, 77,120,122, 70, + 121,125, 63,122,135, 44,124,145, 29,124,164, 9,123,172, 2, + 122,178, 0,119,186, 0,118,189, 0,117,191, 0,117,190, 0, + 119,186, 0,115,195, 0,110,202, 0,107,207, 0,105,209, 0, + 104,210, 0,105,209, 0,106,208, 0,107,207, 0,109,204, 0, + 110,202, 0,112,199, 0,115,194, 0,118,188, 0,122,176, 0, + 124,155, 16,118,114, 96,118,114, 96,113,100,242, 87, 65,237, + 30, 15,143, 30, 26,168, 94, 70,242,109, 92,251,107, 89,252, + 119,119, 77,122,135, 44,123,139, 37,123,137, 41,120,122, 70, + 111, 96,248, 30, 55,222, 30, 5,128, 30, 26,168, 87, 65,237, + 107, 89,252,111, 96,248,115,107,129,119,119, 77,121,127, 58, + 122,130, 53,124,152, 20,124,167, 6,123,174, 1,122,177, 0, + 123,172, 2,124,160, 12,121,127, 58, 30, 49,213,113,100,242, + 122,135, 44,124,165, 8,122,178, 0,119,185, 0,118,188, 0, + 118,189, 0,118,189, 0,116,192, 0,115,194, 0,114,196, 0, + 115,194, 0,118,189, 0,121,182, 0,123,169, 4,124,149, 24, + 124,162, 10,124,167, 6,124,167, 6,124,161, 11,124,150, 22, + 122,132, 48,118,117, 85,109, 92,251,115,107,129,121,125, 63, + 123,137, 41,123,139, 37,122,130, 53,118,114, 96,102, 80,250, + 30, 15,143, 87, 65,237,102, 80,250, 87, 65,237, 69, 60,230, + 30, 43,203,105, 84,252,118,117, 85,122,130, 53,124,158, 13, + 122,176, 0,119,185, 0,118,189, 0,118,189, 0,119,186, 0, + 122,178, 0,124,165, 8,121,181, 0,117,191, 0,114,197, 0, + 111,202, 0,109,204, 0,109,204, 0,109,204, 0,110,203, 0, + 111,201, 0,113,198, 0,116,192, 0,120,184, 0,123,169, 4, + 123,139, 37, 99, 75,247, 30, 15,143,111, 96,248,124,147, 26, + 123,172, 2,119,186, 0,116,193, 0,114,197, 0,114,197, 0, + 114,196, 0,112,200, 0,111,201, 0,112,199, 0,114,196, 0, + 117,191, 0,120,184, 0,121,179, 0,123,174, 1,123,174, 1, + 123,172, 2,123,173, 1,123,171, 3,123,168, 5,124,164, 9, + 124,155, 16,123,139, 37,124,149, 24,124,155, 16,124,158, 13, + 124,154, 18,123,141, 34,118,117, 85, 99, 75,247, 30, 26,168, + 105, 84,252,122,130, 53,124,157, 15,123,170, 3,122,176, 0, + 122,178, 0,122,177, 0,123,172, 2,122,178, 0,121,179, 0, + 122,177, 0,123,170, 3,124,158, 13,123,139, 37,120,122, 70, + 107, 89,252,122,130, 53,124,158, 13,123,174, 1,120,183, 0, + 119,187, 0,118,188, 0,119,186, 0,120,184, 0,119,185, 0, + 119,186, 0,120,183, 0,122,177, 0,124,165, 8,124,143, 31, + 114,104,197, 30, 43,203,102, 80,250,113,100,242,115,107,129, + 118,114, 96,121,125, 63,121,127, 58,119,119, 77,109, 92,251, + 113,100,242,105, 84,252, 87, 65,237, 30, 55,222, 99, 75,247, + 99, 75,247, 30, 55,222, 30, 31,180,102, 80,250,118,117, 85, + 123,137, 41,124,155, 16,123,171, 3,121,180, 0,120,183, 0, + 120,182, 0,120,184, 0,118,189, 0,117,192, 0,117,191, 0, + 119,186, 0,122,175, 0,124,157, 15,121,127, 58,120,122, 70, + 120,122, 70,119,119, 77,118,114, 96,114,104,197,118,114, 96, + 121,125, 63,122,130, 53,121,125, 63,118,117, 85,120,122, 70, + 120,122, 70,118,117, 85,114,104,197,105, 84,252, 30, 49,213, + 30, 55,222,107, 89,252,123,137, 41,124,161, 11,123,173, 1, + 122,178, 0,121,179, 0,122,177, 0,122,177, 0,122,178, 0, + 122,175, 0,123,169, 4,124,158, 13,124,145, 29,121,127, 58, + 118,117, 85,118,117, 85,119,119, 77,120,122, 70,119,119, 77, + 122,135, 44,124,143, 31,124,150, 22,124,158, 13,124,167, 6, + 122,176, 0,120,184, 0,118,189, 0,116,192, 0,115,194, 0, + 116,193, 0,118,188, 0,119,187, 0,114,196, 0,110,202, 0, + 106,207, 0,104,210, 0,103,211, 0,103,211, 0,104,210, 0, + 106,207, 0,110,202, 0,113,198, 0,115,195, 0,118,189, 0, + 121,180, 0,124,164, 9,122,132, 48,120,122, 70,115,107,129, + 105, 84,252, 30, 55,222, 87, 65,237,107, 89,252,115,107,129, + 120,122, 70,124,143, 31,124,154, 18,124,158, 13,124,157, 15, + 124,149, 24,122,132, 48,115,107,129, 94, 70,242, 30, 49,213, + 30, 37,192,102, 80,250,117,110,109,122,130, 53,124,143, 31, + 124,150, 22,124,152, 20,124,147, 26,124,158, 13,123,168, 5, + 123,170, 3,124,167, 6,124,154, 18,119,119, 77,107, 89,252, + 119,119, 77,123,141, 34,124,162, 10,122,175, 0,121,181, 0, + 120,183, 0,118,189, 0,115,194, 0,114,197, 0,113,198, 0, + 113,198, 0,114,196, 0,117,191, 0,120,183, 0,123,169, 4, + 124,149, 24,124,150, 22,124,150, 22,124,150, 22,124,145, 29, + 122,135, 44,121,127, 58,120,122, 70,120,122, 70,122,135, 44, + 124,145, 29,124,150, 22,124,154, 18,124,154, 18,124,145, 29, + 120,122, 70,102, 80,250, 94, 70,242, 87, 65,237, 30, 49,213, + 69, 60,230,102, 80,250,115,107,129,122,130, 53,124,152, 20, + 123,170, 3,120,183, 0,117,190, 0,116,193, 0,116,193, 0, + 118,189, 0,120,182, 0,123,173, 1,122,178, 0,118,188, 0, + 115,194, 0,113,199, 0,111,201, 0,108,205, 0,107,207, 0, + 108,206, 0,110,202, 0,114,197, 0,118,189, 0,121,179, 0, + 124,164, 9,122,135, 44,107, 89,252, 94, 70,242,119,119, 77, + 124,143, 31,123,168, 5,120,183, 0,117,191, 0,115,194, 0, + 114,196, 0,116,193, 0,114,196, 0,114,196, 0,115,194, 0, + 118,189, 0,121,181, 0,122,176, 0,121,179, 0,121,180, 0, + 122,178, 0,122,177, 0,122,175, 0,123,171, 3,124,165, 8, + 124,154, 18,123,139, 37,122,130, 53,124,143, 31,124,150, 22, + 124,152, 20,124,147, 26,122,135, 44,117,110,109, 99, 75,247, + 107, 89,252,109, 92,251,119,119, 77,124,150, 22,124,167, 6, + 122,175, 0,122,178, 0,122,178, 0,122,176, 0,122,176, 0, + 121,179, 0,121,179, 0,122,177, 0,123,170, 3,124,160, 12, + 124,147, 26,122,132, 48,124,152, 20,123,171, 3,120,183, 0, + 117,190, 0,115,194, 0,114,196, 0,115,194, 0,116,192, 0, + 118,188, 0,120,184, 0,120,182, 0,122,176, 0,124,164, 9, + 123,137, 41,105, 84,252, 30, 43,203, 30, 26,168, 69, 60,230, + 94, 70,242,111, 96,248,117,110,109,118,114, 96,114,104,197, + 111, 96,248,114,104,197,109, 92,251, 87, 65,237, 30, 55,222, + 99, 75,247,102, 80,250, 94, 70,242, 69, 60,230, 99, 75,247, + 115,107,129,122,132, 48,124,160, 12,123,174, 1,120,183, 0, + 119,187, 0,119,187, 0,120,182, 0,119,187, 0,117,190, 0, + 117,190, 0,119,186, 0,122,178, 0,124,165, 8,124,149, 24, + 124,149, 24,124,147, 26,123,141, 34,122,135, 44,123,141, 34, + 124,152, 20,124,157, 15,124,157, 15,124,154, 18,124,147, 26, + 124,143, 31,124,145, 29,124,143, 31,123,137, 41,121,125, 63, + 114,104,197,105, 84,252,113,100,242,118,114, 96,124,143, 31, + 124,158, 13,123,168, 5,123,173, 1,123,173, 1,122,177, 0, + 121,179, 0,121,179, 0,122,177, 0,123,173, 1,123,168, 5, + 124,162, 10,124,158, 13,124,160, 12,124,161, 11,124,161, 11, + 124,160, 12,124,155, 16,124,161, 11,124,166, 7,123,170, 3, + 122,177, 0,120,184, 0,118,189, 0,115,194, 0,114,197, 0, + 113,198, 0,114,196, 0,116,192, 0,119,185, 0,118,188, 0, + 113,198, 0,108,205, 0,105,209, 0,102,212, 0,102,213, 0, + 102,213, 0,104,210, 0,108,205, 0,114,197, 0,115,195, 0, + 117,191, 0,120,183, 0,123,169, 4,124,143, 31,122,135, 44, + 120,122, 70,114,104,197,105, 84,252,109, 92,251,117,110,109, + 121,125, 63,123,137, 41,124,155, 16,124,165, 8,123,169, 4, + 123,169, 4,124,164, 9,124,152, 20,122,135, 44,117,110,109, + 111, 96,248,105, 84,252,115,107,129,122,130, 53,124,147, 26, + 124,157, 15,124,162, 10,124,164, 9,124,161, 11,124,152, 20, + 124,154, 18,124,158, 13,124,155, 16,123,141, 34,121,127, 58, + 118,117, 85,122,132, 48,124,147, 26,124,157, 15,123,170, 3, + 122,176, 0,119,185, 0,116,193, 0,113,198, 0,112,200, 0, + 111,202, 0,111,201, 0,113,198, 0,117,192, 0,120,183, 0, + 123,168, 5,124,157, 15,124,158, 13,124,158, 13,124,157, 15, + 124,155, 16,124,152, 20,124,149, 24,124,147, 26,124,150, 22, + 124,155, 16,124,162, 10,123,170, 3,123,172, 2,123,170, 3, + 124,162, 10,124,147, 26,120,122, 70,117,110,109,114,104,197, + 111, 96,248,109, 92,251,118,117, 85,123,137, 41,124,155, 16, + 123,170, 3,121,182, 0,118,189, 0,115,195, 0,114,197, 0, + 114,197, 0,116,193, 0,119,187, 0,121,180, 0,120,184, 0, + 118,189, 0,115,194, 0,112,199, 0,109,203, 0,107,207, 0, + 106,208, 0,106,208, 0,108,205, 0,111,201, 0,115,194, 0, + 119,186, 0,123,173, 1,124,152, 20,118,114, 96,115,107,129, + 122,135, 44,124,150, 22,124,165, 8,121,182, 0,117,190, 0, + 115,194, 0,115,194, 0,116,192, 0,117,191, 0,117,190, 0, + 119,187, 0,121,180, 0,123,172, 2,122,177, 0,120,182, 0, + 120,182, 0,121,180, 0,122,178, 0,123,173, 1,124,167, 6, + 124,158, 13,124,143, 31,118,114, 96,118,117, 85,122,130, 53, + 123,139, 37,123,141, 34,123,137, 41,121,125, 63,114,104,197, + 118,117, 85,119,119, 77,119,119, 77,120,122, 70,123,137, 41, + 124,160, 12,123,171, 3,122,176, 0,122,178, 0,122,177, 0, + 123,174, 1,121,180, 0,120,182, 0,121,182, 0,122,178, 0, + 123,172, 2,124,164, 9,124,154, 18,124,167, 6,121,179, 0, + 118,188, 0,115,195, 0,112,199, 0,111,202, 0,110,202, 0, + 111,201, 0,114,197, 0,117,191, 0,121,181, 0,122,176, 0, + 124,166, 7,124,145, 29,115,107,129,109, 92,251, 30, 37,192, + 30, 10,132, 30, 49,213,102, 80,250,109, 92,251,109, 92,251, + 102, 80,250,113,100,242,117,110,109,113,100,242, 99, 75,247, + 30, 55,222,105, 84,252,109, 92,251,107, 89,252, 99, 75,247, + 99, 75,247,111, 96,248,122,130, 53,124,157, 15,123,174, 1, + 120,184, 0,118,189, 0,118,189, 0,119,186, 0,120,183, 0, + 119,187, 0,118,188, 0,119,185, 0,121,179, 0,123,170, 3, + 123,168, 5,124,166, 7,124,162, 10,124,157, 15,124,161, 11, + 124,166, 7,123,170, 3,123,172, 2,123,171, 3,124,167, 6, + 124,162, 10,124,158, 13,124,158, 13,124,157, 15,124,150, 22, + 124,143, 31,122,132, 48,115,107,129,121,125, 63,123,137, 41, + 124,147, 26,124,154, 18,124,164, 9,124,167, 6,123,170, 3, + 122,177, 0,121,180, 0,120,182, 0,120,183, 0,120,182, 0, + 121,181, 0,121,179, 0,122,178, 0,122,178, 0,121,179, 0, + 121,179, 0,122,178, 0,122,175, 0,123,171, 3,123,174, 1, + 122,178, 0,120,184, 0,118,189, 0,115,194, 0,113,198, 0, + 112,200, 0,112,200, 0,113,199, 0,115,195, 0,118,188, 0, + 121,180, 0,116,193, 0,110,202, 0,106,208, 0,102,212, 0, + 101,214, 0,101,214, 0,103,211, 0,108,206, 0,113,198, 0, + 115,195, 0,117,192, 0,120,184, 0,123,171, 3,124,149, 24, + 124,143, 31,122,130, 53,118,117, 85,111, 96,248,115,107,129, + 121,127, 58,123,141, 34,124,147, 26,124,161, 11,123,171, 3, + 122,176, 0,122,176, 0,123,172, 2,124,164, 9,124,149, 24, + 122,130, 53,119,119, 77,117,110,109,122,130, 53,124,145, 29, + 124,155, 16,124,162, 10,124,167, 6,123,169, 4,124,167, 6, + 124,161, 11,124,149, 24,122,135, 44,122,135, 44,124,143, 31, + 124,147, 26,124,145, 29,123,141, 34,124,150, 22,124,158, 13, + 124,165, 8,122,178, 0,118,188, 0,114,196, 0,112,200, 0, + 110,202, 0,109,203, 0,110,202, 0,113,199, 0,116,192, 0, + 120,182, 0,124,167, 6,124,166, 7,124,166, 7,124,166, 7, + 124,166, 7,124,165, 8,124,164, 9,124,162, 10,124,162, 10, + 124,166, 7,122,175, 0,121,181, 0,120,183, 0,120,182, 0, + 121,179, 0,123,172, 2,124,162, 10,124,147, 26,123,139, 37, + 122,132, 48,120,122, 70,118,117, 85,123,137, 41,124,157, 15, + 123,170, 3,121,181, 0,118,189, 0,115,195, 0,113,199, 0, + 112,200, 0,112,199, 0,114,196, 0,117,191, 0,119,185, 0, + 118,188, 0,116,193, 0,114,197, 0,111,201, 0,108,205, 0, + 107,207, 0,106,208, 0,106,208, 0,107,207, 0,110,203, 0, + 113,198, 0,117,191, 0,121,180, 0,124,162, 10,122,135, 44, + 121,127, 58,123,137, 41,124,150, 22,124,165, 8,121,182, 0, + 117,190, 0,115,194, 0,115,194, 0,117,192, 0,118,188, 0, + 120,183, 0,122,178, 0,123,171, 3,123,170, 3,122,178, 0, + 120,182, 0,120,182, 0,121,179, 0,122,177, 0,123,171, 3, + 124,162, 10,124,147, 26,121,127, 58,109, 92,251,111, 96,248, + 117,110,109,119,119, 77,120,122, 70,120,122, 70,122,132, 48, + 124,145, 29,124,149, 24,124,145, 29,122,135, 44,122,132, 48, + 122,130, 53,124,143, 31,124,162, 10,123,171, 3,122,175, 0, + 122,176, 0,123,173, 1,121,180, 0,119,185, 0,119,186, 0, + 120,184, 0,121,179, 0,123,172, 2,124,165, 8,122,175, 0, + 120,184, 0,117,191, 0,113,198, 0,110,202, 0,108,205, 0, + 107,207, 0,107,207, 0,108,205, 0,112,199, 0,117,192, 0, + 121,181, 0,123,169, 4,124,157, 15,122,132, 48,122,130, 53, + 114,104,197,102, 80,250, 30, 55,222, 69, 60,230, 94, 70,242, + 87, 65,237, 94, 70,242,113,100,242,117,110,109,114,104,197, + 102, 80,250, 87, 65,237,109, 92,251,113,100,242,109, 92,251, + 105, 84,252, 99, 75,247,111, 96,248,120,122, 70,124,149, 24, + 123,169, 4,121,181, 0,119,187, 0,118,189, 0,119,187, 0, + 120,184, 0,120,183, 0,120,184, 0,120,183, 0,121,179, 0, + 121,180, 0,121,180, 0,122,177, 0,123,173, 1,123,172, 2, + 122,177, 0,121,179, 0,121,181, 0,121,181, 0,122,178, 0, + 123,174, 1,123,170, 3,123,169, 4,124,167, 6,124,164, 9, + 124,158, 13,124,152, 20,124,145, 29,122,132, 48,123,141, 34, + 124,154, 18,124,162, 10,123,168, 5,123,171, 3,123,174, 1, + 122,176, 0,122,177, 0,121,180, 0,120,183, 0,119,185, 0, + 119,186, 0,118,188, 0,118,188, 0,119,187, 0,119,187, 0, + 118,188, 0,118,188, 0,119,186, 0,120,184, 0,121,181, 0, + 122,178, 0,121,182, 0,119,186, 0,117,192, 0,114,197, 0, + 112,200, 0,111,202, 0,111,202, 0,112,200, 0,114,196, 0, + 118,189, 0,122,177, 0,118,188, 0,112,199, 0,107,206, 0, + 104,210, 0,102,212, 0,102,212, 0,104,210, 0,109,204, 0, + 114,197, 0,114,196, 0,116,193, 0,119,186, 0,123,173, 1, + 124,152, 20,124,145, 29,122,135, 44,119,119, 77,115,107,129, + 119,119, 77,123,141, 34,124,152, 20,124,154, 18,124,166, 7, + 122,175, 0,121,180, 0,121,181, 0,122,178, 0,123,171, 3, + 124,158, 13,123,141, 34,122,132, 48,122,135, 44,124,143, 31, + 124,152, 20,124,158, 13,124,162, 10,124,166, 7,123,168, 5, + 123,168, 5,124,164, 9,124,157, 15,124,154, 18,124,149, 24, + 124,152, 20,124,160, 12,124,162, 10,124,162, 10,124,162, 10, + 124,166, 7,123,170, 3,121,180, 0,118,189, 0,115,195, 0, + 112,200, 0,110,203, 0,109,204, 0,110,202, 0,113,199, 0, + 117,192, 0,121,181, 0,123,171, 3,123,173, 1,123,172, 2, + 123,171, 3,123,170, 3,123,169, 4,123,168, 5,123,170, 3, + 122,176, 0,120,182, 0,118,188, 0,117,190, 0,117,190, 0, + 118,188, 0,120,184, 0,122,178, 0,123,171, 3,124,166, 7, + 124,158, 13,124,149, 24,123,139, 37,122,132, 48,124,149, 24, + 124,166, 7,122,178, 0,119,187, 0,116,193, 0,113,198, 0, + 111,201, 0,111,201, 0,112,200, 0,114,197, 0,116,193, 0, + 118,189, 0,117,190, 0,115,194, 0,113,198, 0,111,202, 0, + 109,204, 0,107,206, 0,106,207, 0,106,207, 0,107,207, 0, + 108,205, 0,111,201, 0,115,195, 0,119,185, 0,123,169, 4, + 124,147, 26,123,137, 41,122,130, 53,123,141, 34,124,166, 7, + 121,182, 0,117,190, 0,116,193, 0,116,193, 0,117,191, 0, + 119,187, 0,121,182, 0,123,174, 1,124,165, 8,123,172, 2, + 122,178, 0,121,181, 0,121,181, 0,120,182, 0,121,181, 0, + 122,177, 0,123,168, 5,124,154, 18,121,125, 63,102, 80,250, + 94, 70,242,105, 84,252,115,107,129,123,139, 37,124,155, 16, + 124,165, 8,123,168, 5,124,166, 7,124,160, 12,124,147, 26, + 123,137, 41,122,135, 44,122,130, 53,124,149, 24,124,165, 8, + 123,171, 3,123,173, 1,123,171, 3,121,182, 0,118,188, 0, + 118,189, 0,118,188, 0,120,184, 0,122,176, 0,123,173, 1, + 121,179, 0,119,186, 0,116,192, 0,113,198, 0,109,203, 0, + 106,207, 0,104,210, 0,103,211, 0,105,209, 0,108,206, 0, + 112,200, 0,117,191, 0,122,178, 0,124,165, 8,124,152, 20, + 124,152, 20,123,137, 41,120,122, 70,115,107,129,102, 80,250, + 87, 65,237, 30, 43,203, 30, 55,222,109, 92,251,115,107,129, + 114,104,197,107, 89,252, 99, 75,247,114,104,197,118,114, 96, + 114,104,197,102, 80,250, 94, 70,242,109, 92,251,118,114, 96, + 123,137, 41,124,158, 13,123,173, 1,121,182, 0,120,184, 0, + 120,184, 0,120,184, 0,120,184, 0,119,185, 0,119,186, 0, + 119,187, 0,119,187, 0,119,186, 0,120,184, 0,121,179, 0, + 120,183, 0,119,186, 0,118,188, 0,118,188, 0,119,185, 0, + 121,182, 0,122,177, 0,122,178, 0,122,176, 0,123,173, 1, + 123,168, 5,124,164, 9,124,157, 15,124,152, 20,124,147, 26, + 124,152, 20,124,164, 9,123,171, 3,122,176, 0,122,178, 0, + 121,179, 0,121,179, 0,121,179, 0,122,178, 0,121,182, 0, + 119,185, 0,118,188, 0,118,189, 0,117,191, 0,117,191, 0, + 117,191, 0,117,191, 0,117,191, 0,117,190, 0,118,188, 0, + 119,185, 0,121,182, 0,121,180, 0,119,186, 0,117,191, 0, + 114,196, 0,112,199, 0,111,201, 0,111,201, 0,112,199, 0, + 115,195, 0,118,188, 0,121,179, 0,120,183, 0,114,196, 0, + 110,203, 0,106,207, 0,105,209, 0,105,209, 0,107,206, 0, + 111,201, 0,113,198, 0,113,198, 0,115,194, 0,119,186, 0, + 123,173, 1,124,154, 18,124,145, 29,122,132, 48,118,117, 85, + 117,110,109,121,127, 58,124,149, 24,124,160, 12,124,162, 10, + 123,168, 5,122,178, 0,120,183, 0,120,184, 0,121,182, 0, + 122,175, 0,124,164, 9,124,145, 29,124,149, 24,124,150, 22, + 124,152, 20,124,154, 18,124,155, 16,124,157, 15,124,160, 12, + 124,162, 10,124,164, 9,123,170, 3,123,173, 1,123,173, 1, + 123,169, 4,124,165, 8,123,168, 5,123,172, 2,123,172, 2, + 123,173, 1,123,174, 1,122,176, 0,121,179, 0,119,186, 0, + 116,192, 0,114,197, 0,111,201, 0,110,202, 0,111,201, 0, + 113,198, 0,117,191, 0,121,180, 0,122,175, 0,122,176, 0, + 122,175, 0,123,173, 1,123,171, 3,123,172, 2,122,176, 0, + 121,180, 0,119,185, 0,118,189, 0,116,193, 0,115,194, 0, + 116,193, 0,117,191, 0,119,187, 0,120,182, 0,121,181, 0, + 122,177, 0,123,171, 3,123,168, 5,124,164, 9,124,155, 16, + 124,154, 18,123,170, 3,120,182, 0,117,190, 0,114,196, 0, + 112,199, 0,111,201, 0,111,201, 0,112,199, 0,114,197, 0, + 116,193, 0,117,191, 0,118,189, 0,115,194, 0,114,197, 0, + 112,200, 0,110,202, 0,109,204, 0,108,205, 0,108,206, 0, + 107,206, 0,108,205, 0,110,202, 0,114,197, 0,118,188, 0, + 123,171, 3,124,152, 20,123,139, 37,119,119, 77,122,135, 44, + 123,168, 5,120,183, 0,117,190, 0,116,193, 0,116,192, 0, + 118,189, 0,119,185, 0,121,179, 0,123,170, 3,124,165, 8, + 123,171, 3,122,176, 0,121,179, 0,120,183, 0,119,185, 0, + 120,184, 0,121,181, 0,123,173, 1,124,158, 13,121,127, 58, + 87, 65,237, 30, 43,203,113,100,242,124,145, 29,124,166, 7, + 122,176, 0,121,180, 0,121,180, 0,122,176, 0,124,166, 7, + 124,152, 20,122,135, 44,122,132, 48,122,130, 53,121,127, 58, + 124,154, 18,124,166, 7,123,169, 4,123,171, 3,120,184, 0, + 117,190, 0,116,192, 0,117,191, 0,119,186, 0,122,178, 0, + 122,178, 0,121,182, 0,119,186, 0,117,191, 0,114,197, 0, + 110,202, 0,106,207, 0,103,211, 0,101,213, 0,102,213, 0, + 104,210, 0,108,205, 0,113,198, 0,119,187, 0,123,172, 2, + 124,164, 9,124,164, 9,124,155, 16,124,147, 26,122,135, 44, + 118,114, 96,107, 89,252, 69, 60,230, 30, 43,203,107, 89,252, + 114,104,197,114,104,197,111, 96,248,109, 92,251,118,114, 96, + 120,122, 70,118,117, 85,107, 89,252, 94, 70,242,109, 92,251, + 113,100,242,119,119, 77,123,141, 34,124,158, 13,123,170, 3, + 122,176, 0,122,178, 0,121,180, 0,120,183, 0,119,186, 0, + 118,188, 0,117,190, 0,117,191, 0,117,190, 0,119,187, 0, + 119,187, 0,117,191, 0,116,193, 0,116,193, 0,117,192, 0, + 118,188, 0,120,183, 0,120,184, 0,120,184, 0,121,181, 0, + 122,177, 0,123,174, 1,123,172, 2,123,169, 4,124,160, 12, + 124,157, 15,124,160, 12,123,170, 3,122,178, 0,121,181, 0, + 120,182, 0,121,182, 0,121,181, 0,121,179, 0,122,177, 0, + 122,177, 0,121,181, 0,119,185, 0,118,188, 0,117,190, 0, + 117,191, 0,117,191, 0,117,191, 0,117,190, 0,118,189, 0, + 118,188, 0,119,185, 0,120,182, 0,122,178, 0,121,181, 0, + 119,187, 0,116,192, 0,114,196, 0,113,198, 0,113,198, 0, + 114,196, 0,116,192, 0,119,185, 0,120,183, 0,120,184, 0, + 117,192, 0,112,199, 0,110,203, 0,109,204, 0,109,203, 0, + 111,201, 0,113,198, 0,112,200, 0,112,200, 0,114,196, 0, + 119,187, 0,123,172, 2,124,150, 22,123,141, 34,121,127, 58, + 115,107,129,117,110,109,122,132, 48,124,154, 18,124,164, 9, + 124,167, 6,123,170, 3,121,180, 0,119,185, 0,119,187, 0, + 119,185, 0,122,178, 0,124,167, 6,124,157, 15,124,158, 13, + 124,157, 15,124,154, 18,124,149, 24,124,145, 29,124,143, 31, + 124,143, 31,124,150, 22,124,167, 6,122,178, 0,120,182, 0, + 120,184, 0,121,182, 0,122,177, 0,123,172, 2,122,177, 0, + 122,178, 0,122,178, 0,122,178, 0,122,178, 0,122,178, 0, + 121,181, 0,119,186, 0,117,192, 0,114,196, 0,113,199, 0, + 113,198, 0,115,195, 0,118,189, 0,121,180, 0,122,175, 0, + 122,176, 0,123,174, 1,122,175, 0,122,177, 0,122,178, 0, + 121,181, 0,120,184, 0,118,188, 0,116,192, 0,114,196, 0, + 114,196, 0,115,194, 0,117,192, 0,118,188, 0,118,189, 0, + 118,189, 0,120,184, 0,121,180, 0,121,180, 0,122,178, 0, + 123,172, 2,123,171, 3,123,170, 3,120,182, 0,117,191, 0, + 114,196, 0,113,198, 0,112,199, 0,113,199, 0,114,197, 0, + 115,194, 0,117,192, 0,117,190, 0,119,186, 0,117,192, 0, + 115,195, 0,113,198, 0,112,200, 0,110,202, 0,110,202, 0, + 109,203, 0,108,205, 0,108,205, 0,109,203, 0,113,198, 0, + 118,189, 0,123,171, 3,124,152, 20,122,135, 44,117,110,109, + 124,143, 31,123,171, 3,120,184, 0,118,189, 0,117,191, 0, + 117,190, 0,119,187, 0,120,183, 0,122,176, 0,124,166, 7, + 124,162, 10,123,169, 4,123,173, 1,121,179, 0,120,183, 0, + 119,186, 0,119,186, 0,120,183, 0,122,176, 0,124,161, 11, + 121,127, 58, 30, 49,213, 30, 55,222,122,130, 53,124,164, 9, + 121,179, 0,119,186, 0,118,189, 0,119,187, 0,121,182, 0, + 123,170, 3,124,160, 12,122,135, 44,121,127, 58,121,125, 63, + 120,122, 70,123,141, 34,124,160, 12,124,165, 8,123,174, 1, + 119,186, 0,116,192, 0,115,194, 0,116,193, 0,119,187, 0, + 121,182, 0,121,182, 0,119,185, 0,115,194, 0,111,201, 0, + 108,205, 0,107,207, 0,108,206, 0,104,210, 0,101,213, 0, + 101,214, 0,102,212, 0,106,208, 0,110,202, 0,116,193, 0, + 121,181, 0,123,170, 3,123,168, 5,124,162, 10,124,157, 15, + 124,147, 26,122,132, 48,114,104,197,105, 84,252, 30, 55,222, + 102, 80,250,113,100,242,114,104,197,111, 96,248,115,107,129, + 121,125, 63,122,130, 53,121,125, 63,114,104,197,109, 92,251, + 111, 96,248,113,100,242,113,100,242,118,117, 85,122,132, 48, + 124,147, 26,124,157, 15,124,164, 9,123,170, 3,122,176, 0, + 120,182, 0,119,186, 0,118,189, 0,117,191, 0,117,191, 0, + 118,188, 0,116,193, 0,114,197, 0,113,198, 0,113,198, 0, + 115,195, 0,117,190, 0,119,186, 0,118,188, 0,119,187, 0, + 120,184, 0,121,179, 0,122,178, 0,122,178, 0,122,175, 0, + 123,168, 5,124,166, 7,124,166, 7,122,175, 0,121,181, 0, + 120,183, 0,120,183, 0,121,182, 0,121,179, 0,122,177, 0, + 122,177, 0,122,176, 0,121,179, 0,121,181, 0,120,183, 0, + 119,186, 0,119,187, 0,119,187, 0,119,187, 0,119,186, 0, + 119,186, 0,120,184, 0,121,182, 0,122,178, 0,122,176, 0, + 123,174, 1,121,179, 0,119,186, 0,117,191, 0,116,193, 0, + 116,193, 0,117,191, 0,118,188, 0,120,182, 0,119,186, 0, + 119,186, 0,118,188, 0,115,194, 0,113,198, 0,113,198, 0, + 114,197, 0,115,195, 0,112,200, 0,110,202, 0,111,201, 0, + 114,196, 0,119,187, 0,123,170, 3,124,143, 31,122,132, 48, + 118,117, 85,113,100,242,117,110,109,122,132, 48,124,152, 20, + 124,164, 9,124,167, 6,123,172, 2,120,182, 0,118,188, 0, + 118,189, 0,118,188, 0,121,182, 0,123,171, 3,124,162, 10, + 124,161, 11,124,157, 15,124,149, 24,123,139, 37,122,132, 48, + 121,127, 58,118,117, 85,124,149, 24,123,169, 4,121,181, 0, + 118,188, 0,117,190, 0,118,189, 0,119,185, 0,122,178, 0, + 122,178, 0,121,179, 0,122,178, 0,122,177, 0,122,175, 0, + 122,178, 0,121,179, 0,122,178, 0,120,184, 0,117,190, 0, + 116,193, 0,115,194, 0,116,193, 0,118,188, 0,121,180, 0, + 123,172, 2,122,177, 0,122,178, 0,122,178, 0,121,179, 0, + 121,180, 0,121,182, 0,120,184, 0,118,188, 0,116,192, 0, + 115,195, 0,115,195, 0,115,194, 0,116,193, 0,115,194, 0, + 115,195, 0,116,193, 0,119,187, 0,119,186, 0,119,187, 0, + 119,186, 0,120,183, 0,120,183, 0,120,182, 0,121,181, 0, + 118,189, 0,115,194, 0,114,196, 0,114,197, 0,114,196, 0, + 115,194, 0,117,192, 0,118,189, 0,118,188, 0,119,185, 0, + 118,188, 0,117,192, 0,114,196, 0,111,202, 0,109,204, 0, + 109,204, 0,111,202, 0,109,204, 0,108,205, 0,109,203, 0, + 113,198, 0,118,189, 0,123,170, 3,124,149, 24,121,127, 58, + 122,130, 53,124,150, 22,123,173, 1,120,184, 0,118,188, 0, + 118,189, 0,119,186, 0,120,183, 0,122,178, 0,123,172, 2, + 124,161, 11,124,157, 15,124,165, 8,123,170, 3,122,176, 0, + 121,180, 0,120,183, 0,120,184, 0,120,182, 0,122,176, 0, + 124,161, 11,121,125, 63, 99, 75,247, 99, 75,247,124,143, 31, + 123,173, 1,119,186, 0,116,193, 0,115,194, 0,117,191, 0, + 120,184, 0,122,175, 0,123,168, 5,124,150, 22,122,130, 53, + 119,119, 77,119,119, 77,122,132, 48,124,154, 18,124,161, 11, + 122,177, 0,118,188, 0,115,194, 0,114,197, 0,115,194, 0, + 118,188, 0,119,185, 0,120,184, 0,116,193, 0,110,202, 0, + 105,209, 0,102,212, 0,101,214, 0,102,212, 0,105,209, 0, + 102,212, 0,102,213, 0,102,212, 0,104,210, 0,108,205, 0, + 113,198, 0,119,187, 0,123,173, 1,124,166, 7,124,161, 11, + 124,157, 15,124,150, 22,123,139, 37,119,119, 77,114,104,197, + 105, 84,252, 69, 60,230,107, 89,252,109, 92,251,113,100,242, + 120,122, 70,122,135, 44,123,139, 37,122,135, 44,118,117, 85, + 121,125, 63,119,119, 77,114,104,197,114,104,197,111, 96,248, + 111, 96,248,115,107,129,119,119, 77,124,147, 26,124,161, 11, + 123,169, 4,123,174, 1,121,180, 0,119,185, 0,118,188, 0, + 118,188, 0,116,192, 0,114,197, 0,111,201, 0,110,202, 0, + 111,201, 0,113,198, 0,116,193, 0,119,187, 0,118,188, 0, + 118,188, 0,119,185, 0,121,181, 0,121,179, 0,122,178, 0, + 122,177, 0,123,171, 3,122,175, 0,122,175, 0,122,178, 0, + 120,182, 0,120,183, 0,121,181, 0,122,178, 0,123,174, 1, + 122,178, 0,121,182, 0,121,182, 0,120,183, 0,119,186, 0, + 119,186, 0,120,184, 0,121,182, 0,120,182, 0,120,182, 0, + 121,182, 0,121,181, 0,121,179, 0,122,177, 0,123,174, 1, + 123,170, 3,123,169, 4,123,169, 4,122,178, 0,120,184, 0, + 119,187, 0,118,188, 0,119,186, 0,121,182, 0,119,185, 0, + 119,187, 0,119,186, 0,120,184, 0,117,190, 0,116,192, 0, + 117,192, 0,117,191, 0,114,197, 0,111,201, 0,110,203, 0, + 111,202, 0,114,196, 0,119,186, 0,123,168, 5,122,132, 48, + 119,119, 77,113,100,242,111, 96,248,117,110,109,120,122, 70, + 124,147, 26,124,158, 13,124,162, 10,122,176, 0,119,185, 0, + 117,191, 0,116,192, 0,117,191, 0,119,186, 0,122,176, 0, + 124,161, 11,124,155, 16,124,149, 24,123,141, 34,123,139, 37, + 123,137, 41,122,130, 53,123,139, 37,124,147, 26,123,170, 3, + 120,184, 0,117,191, 0,115,194, 0,115,194, 0,117,191, 0, + 120,184, 0,122,177, 0,122,177, 0,122,175, 0,123,173, 1, + 122,176, 0,121,179, 0,121,180, 0,121,179, 0,122,176, 0, + 120,183, 0,118,188, 0,117,191, 0,117,191, 0,118,189, 0, + 119,185, 0,121,180, 0,122,176, 0,122,178, 0,122,178, 0, + 121,182, 0,119,185, 0,118,188, 0,117,190, 0,117,191, 0, + 117,191, 0,116,193, 0,115,194, 0,115,195, 0,114,197, 0, + 113,198, 0,114,197, 0,116,193, 0,118,188, 0,118,189, 0, + 117,191, 0,117,191, 0,118,189, 0,117,190, 0,117,190, 0, + 118,189, 0,118,188, 0,116,193, 0,115,194, 0,115,195, 0, + 115,194, 0,115,194, 0,115,194, 0,117,192, 0,119,186, 0, + 120,183, 0,120,184, 0,117,191, 0,113,198, 0,110,202, 0, + 108,205, 0,108,205, 0,111,201, 0,109,203, 0,108,205, 0, + 109,203, 0,113,198, 0,118,188, 0,123,169, 4,124,149, 24, + 121,125, 63,123,141, 34,124,150, 22,123,172, 2,121,182, 0, + 119,185, 0,119,185, 0,121,182, 0,122,178, 0,122,175, 0, + 123,168, 5,124,157, 15,124,149, 24,124,161, 11,123,168, 5, + 123,172, 2,123,172, 2,122,177, 0,121,179, 0,122,178, 0, + 123,172, 2,124,157, 15,123,139, 37,114,104,197,105, 84,252, + 124,149, 24,122,177, 0,118,189, 0,114,196, 0,114,197, 0, + 115,194, 0,119,187, 0,122,176, 0,123,169, 4,124,152, 20, + 122,132, 48,120,122, 70,120,122, 70,122,130, 53,124,152, 20, + 124,160, 12,121,179, 0,117,190, 0,114,196, 0,113,198, 0, + 115,195, 0,117,190, 0,118,189, 0,118,188, 0,113,198, 0, + 107,207, 0,101,213, 0, 98,217, 0, 97,218, 0, 98,217, 0, + 101,214, 0,104,210, 0,102,212, 0,102,212, 0,103,211, 0, + 107,207, 0,111,201, 0,117,192, 0,121,180, 0,123,173, 1, + 124,158, 13,124,154, 18,124,150, 22,123,141, 34,121,125, 63, + 119,119, 77,117,110,109,113,100,242,113,100,242,117,110,109, + 120,122, 70,122,135, 44,124,143, 31,124,145, 29,123,139, 37, + 123,141, 34,124,149, 24,124,149, 24,123,139, 37,120,122, 70, + 111, 96,248,107, 89,252,107, 89,252,114,104,197,122,132, 48, + 124,152, 20,124,164, 9,123,170, 3,122,176, 0,121,180, 0, + 120,184, 0,118,189, 0,115,194, 0,112,199, 0,110,202, 0, + 110,203, 0,111,202, 0,113,199, 0,116,193, 0,118,188, 0, + 119,187, 0,119,186, 0,120,184, 0,121,182, 0,122,178, 0, + 122,177, 0,122,177, 0,121,180, 0,120,183, 0,120,183, 0, + 121,182, 0,121,182, 0,121,179, 0,122,175, 0,123,173, 1, + 121,179, 0,120,183, 0,120,184, 0,120,184, 0,120,183, 0, + 119,186, 0,119,187, 0,119,186, 0,119,185, 0,120,183, 0, + 121,182, 0,121,180, 0,121,179, 0,121,179, 0,122,178, 0, + 122,178, 0,122,177, 0,123,172, 2,124,166, 7,123,169, 4, + 122,178, 0,121,182, 0,120,183, 0,121,181, 0,121,180, 0, + 120,183, 0,120,184, 0,120,183, 0,122,178, 0,120,184, 0, + 119,186, 0,119,186, 0,117,192, 0,114,197, 0,111,201, 0, + 110,202, 0,112,200, 0,116,193, 0,120,182, 0,124,161, 11, + 119,119, 77,113,100,242, 99, 75,247,109, 92,251,117,110,109, + 121,125, 63,123,139, 37,124,152, 20,124,167, 6,121,179, 0, + 118,188, 0,116,193, 0,115,194, 0,116,193, 0,118,188, 0, + 121,179, 0,124,166, 7,124,149, 24,124,143, 31,123,141, 34, + 123,137, 41,122,130, 53,122,135, 44,124,150, 22,124,158, 13, + 123,174, 1,119,186, 0,116,193, 0,114,197, 0,114,197, 0, + 115,194, 0,118,188, 0,121,182, 0,122,176, 0,123,174, 1, + 122,175, 0,122,176, 0,122,178, 0,122,178, 0,122,175, 0, + 123,171, 3,122,178, 0,120,184, 0,118,189, 0,117,191, 0, + 117,191, 0,118,188, 0,119,186, 0,120,184, 0,120,184, 0, + 119,186, 0,118,188, 0,117,191, 0,116,193, 0,115,194, 0, + 115,195, 0,115,194, 0,115,194, 0,115,195, 0,114,196, 0, + 114,197, 0,114,197, 0,115,194, 0,117,191, 0,117,190, 0, + 117,191, 0,116,193, 0,116,193, 0,117,191, 0,116,193, 0, + 115,194, 0,116,193, 0,116,193, 0,115,194, 0,114,196, 0, + 113,198, 0,113,199, 0,112,199, 0,113,199, 0,114,196, 0, + 117,191, 0,120,184, 0,120,184, 0,117,192, 0,113,198, 0, + 110,202, 0,109,204, 0,110,203, 0,110,203, 0,108,205, 0, + 108,206, 0,109,204, 0,113,198, 0,118,188, 0,123,168, 5, + 124,145, 29,122,132, 48,124,145, 29,124,149, 24,123,168, 5, + 122,177, 0,121,180, 0,121,179, 0,121,181, 0,120,184, 0, + 121,181, 0,123,173, 1,124,158, 13,124,143, 31,124,158, 13, + 123,169, 4,123,174, 1,122,175, 0,123,169, 4,124,167, 6, + 123,168, 5,124,164, 9,124,161, 11,124,150, 22,121,125, 63, + 118,114, 96,124,145, 29,123,174, 1,118,188, 0,115,195, 0, + 114,197, 0,115,195, 0,118,189, 0,122,178, 0,124,162, 10, + 123,141, 34,121,127, 58,121,127, 58,121,127, 58,123,137, 41, + 124,155, 16,124,165, 8,121,182, 0,117,192, 0,114,197, 0, + 113,199, 0,114,196, 0,116,193, 0,116,193, 0,117,192, 0, + 112,200, 0,106,208, 0,100,214, 0, 97,218, 0, 95,220, 0, + 96,219, 0, 98,217, 0,101,213, 0,104,210, 0,103,211, 0, + 105,209, 0,107,207, 0,111,201, 0,115,194, 0,120,184, 0, + 123,173, 1,124,157, 15,124,150, 22,124,147, 26,123,139, 37, + 122,130, 53,122,132, 48,122,130, 53,121,127, 58,121,127, 58, + 122,135, 44,123,141, 34,124,147, 26,124,149, 24,124,149, 24, + 124,147, 26,124,160, 12,124,166, 7,124,166, 7,124,162, 10, + 124,152, 20,122,135, 44,117,110,109,102, 80,250,109, 92,251, + 115,107,129,122,132, 48,124,150, 22,124,161, 11,123,168, 5, + 123,174, 1,121,180, 0,119,186, 0,116,192, 0,114,197, 0, + 112,200, 0,111,201, 0,112,199, 0,114,196, 0,117,191, 0, + 119,186, 0,119,186, 0,119,185, 0,120,184, 0,121,182, 0, + 121,181, 0,121,181, 0,120,182, 0,119,186, 0,118,189, 0, + 118,189, 0,119,185, 0,121,180, 0,123,174, 1,123,170, 3, + 121,179, 0,120,184, 0,119,185, 0,119,186, 0,120,184, 0, + 121,181, 0,120,182, 0,120,184, 0,120,184, 0,120,183, 0, + 121,182, 0,121,181, 0,121,180, 0,121,180, 0,121,179, 0, + 121,179, 0,122,178, 0,122,177, 0,123,172, 2,124,165, 8, + 124,165, 8,123,171, 3,122,177, 0,122,178, 0,122,178, 0, + 123,174, 1,122,178, 0,122,178, 0,123,174, 1,124,167, 6, + 122,175, 0,122,178, 0,119,186, 0,116,193, 0,114,197, 0, + 112,200, 0,112,199, 0,114,196, 0,118,188, 0,122,175, 0, + 124,149, 24,115,107,129,102, 80,250, 30, 55,222,102, 80,250, + 113,100,242,119,119, 77,123,137, 41,124,155, 16,123,169, 4, + 121,181, 0,118,188, 0,116,193, 0,115,194, 0,116,192, 0, + 119,187, 0,122,178, 0,124,166, 7,124,149, 24,123,137, 41, + 122,132, 48,121,125, 63,118,117, 85,124,143, 31,124,155, 16, + 124,161, 11,122,178, 0,118,189, 0,115,195, 0,113,198, 0, + 113,198, 0,115,195, 0,117,190, 0,120,184, 0,121,179, 0, + 122,176, 0,122,175, 0,123,174, 1,123,172, 2,123,170, 3, + 124,166, 7,124,161, 11,123,171, 3,121,179, 0,119,185, 0, + 118,188, 0,118,189, 0,118,189, 0,118,188, 0,119,187, 0, + 118,188, 0,117,191, 0,116,192, 0,115,194, 0,115,195, 0, + 115,195, 0,115,194, 0,116,193, 0,116,193, 0,116,193, 0, + 115,194, 0,116,193, 0,116,192, 0,117,190, 0,117,191, 0, + 117,191, 0,116,192, 0,116,193, 0,116,192, 0,117,191, 0, + 115,194, 0,115,195, 0,115,195, 0,114,196, 0,114,197, 0, + 112,200, 0,111,202, 0,110,202, 0,110,202, 0,111,201, 0, + 113,198, 0,116,193, 0,119,187, 0,119,185, 0,117,192, 0, + 114,197, 0,111,201, 0,110,202, 0,109,203, 0,108,205, 0, + 107,207, 0,107,207, 0,109,203, 0,114,197, 0,119,186, 0, + 124,164, 9,123,139, 37,122,135, 44,124,143, 31,123,141, 34, + 124,160, 12,123,169, 4,123,173, 1,121,181, 0,119,186, 0, + 118,188, 0,119,185, 0,122,176, 0,124,160, 12,124,154, 18, + 124,157, 15,123,169, 4,122,176, 0,122,178, 0,123,172, 2, + 124,157, 15,124,150, 22,124,160, 12,124,162, 10,124,157, 15, + 124,143, 31,122,135, 44,124,143, 31,124,166, 7,120,182, 0, + 117,191, 0,116,193, 0,116,193, 0,118,188, 0,122,178, 0, + 124,164, 9,123,141, 34,122,132, 48,122,130, 53,121,127, 58, + 124,147, 26,124,160, 12,123,171, 3,119,185, 0,116,193, 0, + 113,198, 0,113,199, 0,114,196, 0,115,194, 0,115,194, 0, + 115,194, 0,111,201, 0,106,208, 0,101,214, 0, 97,217, 0, + 95,219, 0, 96,219, 0, 97,217, 0,100,215, 0,103,211, 0, + 106,207, 0,107,206, 0,110,203, 0,113,198, 0,117,192, 0, + 120,183, 0,124,167, 6,124,152, 20,124,143, 31,123,137, 41, + 122,130, 53,121,127, 58,123,137, 41,123,141, 34,124,149, 24, + 124,152, 20,124,152, 20,124,154, 18,124,155, 16,124,155, 16, + 124,150, 22,124,160, 12,123,171, 3,122,176, 0,122,177, 0, + 123,174, 1,123,168, 5,124,158, 13,124,145, 29,121,127, 58, + 118,117, 85,111, 96,248,111, 96,248,120,122, 70,123,141, 34, + 124,152, 20,124,162, 10,123,171, 3,121,179, 0,119,187, 0, + 117,192, 0,115,194, 0,115,195, 0,116,193, 0,117,190, 0, + 119,186, 0,120,184, 0,120,184, 0,120,183, 0,121,182, 0, + 120,182, 0,120,184, 0,119,186, 0,118,188, 0,117,191, 0, + 115,194, 0,116,193, 0,118,188, 0,122,178, 0,123,168, 5, + 122,175, 0,120,182, 0,119,186, 0,119,185, 0,120,184, 0, + 121,180, 0,122,175, 0,123,171, 3,122,175, 0,122,176, 0, + 122,177, 0,122,177, 0,122,177, 0,122,176, 0,122,176, 0, + 122,176, 0,123,174, 1,123,173, 1,123,171, 3,124,167, 6, + 124,164, 9,124,164, 9,124,166, 7,123,172, 2,122,175, 0, + 123,174, 1,123,170, 3,124,167, 6,124,165, 8,124,157, 15, + 124,152, 20,124,166, 7,121,181, 0,118,189, 0,115,194, 0, + 114,197, 0,114,197, 0,115,194, 0,118,189, 0,121,179, 0, + 124,161, 11,121,125, 63,111, 96,248, 30, 49,213, 30, 55,222, + 99, 75,247,102, 80,250,115,107,129,122,130, 53,124,152, 20, + 123,169, 4,121,180, 0,119,187, 0,117,191, 0,117,191, 0, + 118,188, 0,120,183, 0,123,174, 1,124,161, 11,124,143, 31, + 120,122, 70,118,114, 96,114,104,197,121,127, 58,124,149, 24, + 124,158, 13,123,168, 5,121,182, 0,117,190, 0,115,195, 0, + 114,197, 0,114,197, 0,116,193, 0,118,189, 0,120,183, 0, + 122,178, 0,122,175, 0,123,172, 2,123,169, 4,124,164, 9, + 124,157, 15,124,145, 29,124,147, 26,124,161, 11,123,171, 3, + 122,178, 0,120,182, 0,120,184, 0,119,185, 0,119,186, 0, + 119,187, 0,118,189, 0,117,192, 0,116,193, 0,115,194, 0, + 115,194, 0,116,193, 0,117,191, 0,118,189, 0,117,190, 0, + 117,191, 0,117,191, 0,117,190, 0,118,189, 0,118,189, 0, + 118,189, 0,117,190, 0,117,191, 0,117,191, 0,118,189, 0, + 118,189, 0,117,192, 0,116,193, 0,115,194, 0,114,196, 0, + 112,199, 0,110,202, 0,109,204, 0,108,205, 0,108,205, 0, + 110,202, 0,112,199, 0,115,194, 0,118,189, 0,118,188, 0, + 116,193, 0,113,198, 0,111,201, 0,109,203, 0,108,206, 0, + 106,207, 0,106,208, 0,107,207, 0,110,202, 0,115,194, 0, + 121,182, 0,124,157, 15,121,125, 63,122,135, 44,123,137, 41, + 121,127, 58,124,149, 24,124,165, 8,122,178, 0,119,187, 0, + 117,191, 0,117,192, 0,118,188, 0,122,178, 0,124,167, 6, + 124,165, 8,124,160, 12,123,168, 5,122,175, 0,122,178, 0, + 123,172, 2,124,158, 13,122,132, 48,124,152, 20,124,161, 11, + 124,161, 11,124,154, 18,124,147, 26,124,155, 16,124,160, 12, + 123,171, 3,120,182, 0,119,187, 0,118,188, 0,120,184, 0, + 122,176, 0,124,162, 10,123,139, 37,123,137, 41,122,135, 44, + 123,141, 34,124,158, 13,124,166, 7,122,178, 0,118,189, 0, + 114,196, 0,113,199, 0,113,199, 0,114,196, 0,115,194, 0, + 115,195, 0,114,197, 0,111,201, 0,107,206, 0,103,211, 0, + 100,214, 0, 98,216, 0, 98,217, 0, 98,217, 0,100,215, 0, + 102,212, 0,106,207, 0,110,202, 0,114,197, 0,117,192, 0, + 119,186, 0,122,178, 0,124,155, 16,124,143, 31,122,130, 53, + 118,117, 85,114,104,197,118,117, 85,123,137, 41,124,155, 16, + 124,162, 10,124,166, 7,123,168, 5,124,166, 7,124,162, 10, + 124,160, 12,124,155, 16,123,168, 5,122,178, 0,120,182, 0, + 120,183, 0,121,181, 0,122,177, 0,123,170, 3,124,162, 10, + 124,154, 18,124,147, 26,123,137, 41,118,114, 96, 87, 65,237, + 113,100,242,120,122, 70,123,139, 37,124,160, 12,124,167, 6, + 122,178, 0,120,184, 0,119,186, 0,119,187, 0,119,185, 0, + 121,182, 0,121,182, 0,120,183, 0,121,182, 0,121,179, 0, + 120,182, 0,119,185, 0,118,188, 0,118,189, 0,117,191, 0, + 116,192, 0,114,196, 0,114,196, 0,117,191, 0,121,182, 0, + 123,170, 3,122,177, 0,120,183, 0,120,184, 0,120,182, 0, + 122,178, 0,123,171, 3,124,164, 9,124,152, 20,124,157, 15, + 124,161, 11,124,165, 8,124,166, 7,124,167, 6,123,168, 5, + 123,168, 5,124,167, 6,124,166, 7,124,162, 10,124,160, 12, + 124,160, 12,124,161, 11,124,161, 11,124,160, 12,124,167, 6, + 123,170, 3,123,169, 4,124,167, 6,124,161, 11,124,154, 18, + 124,145, 29,124,158, 13,122,175, 0,119,185, 0,117,192, 0, + 115,195, 0,115,195, 0,116,193, 0,118,188, 0,121,180, 0, + 124,166, 7,124,147, 26,121,125, 63,105, 84,252, 30, 55,222, + 99, 75,247,107, 89,252,115,107,129,115,107,129,118,117, 85, + 124,145, 29,124,166, 7,122,178, 0,120,184, 0,119,186, 0, + 119,186, 0,120,182, 0,122,176, 0,124,165, 8,124,150, 22, + 122,132, 48,117,110,109,105, 84,252,115,107,129,123,137, 41, + 124,152, 20,124,158, 13,123,173, 1,120,184, 0,118,189, 0, + 116,193, 0,116,193, 0,116,192, 0,118,189, 0,120,184, 0, + 122,178, 0,123,174, 1,123,170, 3,124,166, 7,124,160, 12, + 124,149, 24,122,130, 53,115,107,129,120,122, 70,123,141, 34, + 124,155, 16,124,164, 9,123,169, 4,123,173, 1,122,176, 0, + 121,179, 0,120,182, 0,119,186, 0,118,189, 0,117,191, 0, + 117,191, 0,117,190, 0,118,188, 0,119,185, 0,119,185, 0, + 118,188, 0,118,188, 0,118,188, 0,119,186, 0,119,186, 0, + 119,185, 0,119,185, 0,119,186, 0,119,186, 0,120,184, 0, + 121,180, 0,120,184, 0,118,188, 0,117,190, 0,116,192, 0, + 114,196, 0,112,200, 0,109,204, 0,107,206, 0,107,207, 0, + 107,206, 0,109,203, 0,112,199, 0,115,194, 0,117,192, 0, + 116,192, 0,114,197, 0,111,201, 0,109,204, 0,107,206, 0, + 106,207, 0,105,209, 0,105,209, 0,107,206, 0,111,201, 0, + 117,191, 0,122,177, 0,124,147, 26,120,122, 70,122,130, 53, + 120,122, 70,118,114, 96,124,155, 16,122,175, 0,119,186, 0, + 117,192, 0,115,194, 0,116,193, 0,118,189, 0,122,178, 0, + 122,175, 0,123,174, 1,123,169, 4,124,162, 10,123,170, 3, + 123,172, 2,123,168, 5,124,154, 18,123,141, 34,124,143, 31, + 124,155, 16,124,160, 12,124,158, 13,124,150, 22,124,161, 11, + 124,166, 7,123,168, 5,123,168, 5,122,176, 0,122,178, 0, + 122,177, 0,123,169, 4,124,154, 18,122,135, 44,123,137, 41, + 123,139, 37,124,160, 12,123,169, 4,123,173, 1,120,184, 0, + 116,193, 0,113,198, 0,112,200, 0,113,199, 0,115,195, 0, + 116,192, 0,115,194, 0,114,197, 0,112,200, 0,110,203, 0, + 107,207, 0,105,209, 0,102,212, 0,101,213, 0,101,214, 0, + 101,213, 0,103,211, 0,106,208, 0,110,203, 0,114,197, 0, + 118,189, 0,121,180, 0,123,168, 5,124,149, 24,122,130, 53, + 114,104,197, 94, 70,242, 87, 65,237,118,114, 96,124,145, 29, + 124,161, 11,123,170, 3,123,174, 1,122,177, 0,122,176, 0, + 123,170, 3,124,165, 8,124,160, 12,123,171, 3,121,181, 0, + 119,185, 0,119,186, 0,120,184, 0,121,182, 0,122,177, 0, + 123,171, 3,124,166, 7,124,164, 9,124,160, 12,124,145, 29, + 118,114, 96, 30, 43,203, 30, 55,222,117,110,109,124,143, 31, + 124,155, 16,124,162, 10,123,171, 3,123,174, 1,122,175, 0, + 123,173, 1,122,177, 0,121,179, 0,121,179, 0,122,178, 0, + 121,182, 0,119,187, 0,117,190, 0,117,191, 0,117,190, 0, + 116,192, 0,116,193, 0,114,196, 0,114,196, 0,116,192, 0, + 120,184, 0,121,180, 0,121,181, 0,121,180, 0,121,180, 0, + 122,175, 0,124,167, 6,124,155, 16,124,145, 29,124,143, 31, + 123,141, 34,124,149, 24,124,149, 24,124,147, 26,124,150, 22, + 124,154, 18,124,155, 16,124,154, 18,124,150, 22,124,150, 22, + 124,152, 20,124,154, 18,124,155, 16,124,155, 16,124,152, 20, + 124,158, 13,124,162, 10,124,162, 10,124,161, 11,124,155, 16, + 124,150, 22,124,150, 22,123,170, 3,121,182, 0,118,188, 0, + 116,192, 0,115,194, 0,116,192, 0,119,187, 0,121,179, 0, + 123,174, 1,124,167, 6,124,152, 20,121,125, 63,102, 80,250, + 87, 65,237,109, 92,251,121,127, 58,123,141, 34,123,141, 34, + 121,127, 58,122,132, 48,124,158, 13,123,172, 2,122,178, 0, + 121,180, 0,122,178, 0,123,174, 1,124,165, 8,124,154, 18, + 123,139, 37,119,119, 77,109, 92,251,102, 80,250,120,122, 70, + 124,145, 29,124,155, 16,124,161, 11,122,176, 0,120,184, 0, + 118,188, 0,118,189, 0,118,188, 0,120,184, 0,121,179, 0, + 123,173, 1,124,167, 6,124,164, 9,124,160, 12,124,152, 20, + 123,141, 34,120,122, 70,111, 96,248, 30, 55,222,109, 92,251, + 118,114, 96,121,125, 63,122,135, 44,124,143, 31,124,150, 22, + 124,157, 15,124,164, 9,123,170, 3,122,178, 0,120,182, 0, + 119,185, 0,119,185, 0,120,184, 0,121,180, 0,122,175, 0, + 121,180, 0,120,182, 0,120,182, 0,121,181, 0,121,179, 0, + 122,178, 0,122,176, 0,122,176, 0,122,176, 0,122,175, 0, + 123,171, 3,123,170, 3,122,176, 0,121,180, 0,120,184, 0, + 118,188, 0,115,194, 0,112,200, 0,109,204, 0,106,207, 0, + 105,209, 0,106,207, 0,109,204, 0,112,199, 0,114,196, 0, + 115,194, 0,114,196, 0,111,201, 0,109,203, 0,107,206, 0, + 106,207, 0,105,209, 0,105,209, 0,105,209, 0,108,205, 0, + 113,198, 0,119,187, 0,123,170, 3,123,139, 37,118,117, 85, + 118,117, 85,111, 96,248,122,132, 48,124,167, 6,120,183, 0, + 117,191, 0,115,194, 0,115,195, 0,116,193, 0,118,188, 0, + 121,182, 0,120,182, 0,121,181, 0,122,175, 0,124,164, 9, + 124,158, 13,124,161, 11,124,157, 15,124,147, 26,123,139, 37, + 122,132, 48,124,149, 24,124,155, 16,124,155, 16,124,150, 22, + 124,160, 12,123,168, 5,123,171, 3,123,171, 3,123,168, 5, + 124,162, 10,124,162, 10,124,154, 18,123,139, 37,122,130, 53, + 123,137, 41,124,164, 9,123,174, 1,121,179, 0,121,181, 0, + 117,190, 0,114,197, 0,112,200, 0,112,200, 0,113,198, 0, + 115,194, 0,118,189, 0,116,193, 0,114,197, 0,111,202, 0, + 108,206, 0,105,209, 0,104,210, 0,104,210, 0,106,208, 0, + 105,209, 0,104,210, 0,105,209, 0,107,207, 0,110,203, 0, + 114,197, 0,118,189, 0,121,179, 0,124,165, 8,122,130, 53, + 113,100,242, 30, 43,203, 30, 10,132, 94, 70,242,119,119, 77, + 124,147, 26,124,165, 8,123,174, 1,121,179, 0,120,182, 0, + 120,183, 0,121,180, 0,123,173, 1,124,166, 7,123,170, 3, + 121,181, 0,119,185, 0,119,186, 0,119,185, 0,120,183, 0, + 121,180, 0,121,181, 0,122,177, 0,123,173, 1,123,171, 3, + 124,164, 9,124,143, 31,114,104,197, 30, 26,168, 69, 60,230, + 118,114, 96,122,132, 48,123,141, 34,124,150, 22,124,157, 15, + 124,162, 10,123,168, 5,123,171, 3,123,173, 1,122,176, 0, + 121,180, 0,119,186, 0,117,192, 0,114,196, 0,114,196, 0, + 116,193, 0,117,191, 0,117,192, 0,115,195, 0,114,196, 0, + 116,193, 0,118,188, 0,119,186, 0,119,186, 0,120,182, 0, + 123,173, 1,124,162, 10,124,147, 26,124,152, 20,124,154, 18, + 124,149, 24,122,135, 44,122,132, 48,121,127, 58,119,119, 77, + 121,125, 63,122,132, 48,122,132, 48,122,130, 53,121,125, 63, + 122,132, 48,123,137, 41,123,139, 37,123,141, 34,123,141, 34, + 123,137, 41,124,147, 26,124,150, 22,124,152, 20,124,149, 24, + 124,147, 26,124,143, 31,124,164, 9,122,178, 0,119,186, 0, + 117,190, 0,116,192, 0,117,192, 0,118,188, 0,120,183, 0, + 120,184, 0,121,181, 0,123,173, 1,124,157, 15,120,122, 70, + 94, 70,242, 87, 65,237,121,125, 63,124,152, 20,124,161, 11, + 124,161, 11,124,152, 20,122,132, 48,124,149, 24,124,164, 9, + 123,170, 3,123,172, 2,123,169, 4,124,162, 10,124,157, 15, + 124,152, 20,122,135, 44,114,104,197,111, 96,248,121,125, 63, + 123,139, 37,124,152, 20,124,158, 13,124,161, 11,122,175, 0, + 121,181, 0,120,183, 0,121,182, 0,122,178, 0,123,171, 3, + 124,162, 10,124,152, 20,124,143, 31,123,139, 37,122,135, 44, + 121,127, 58,120,122, 70,118,114, 96,109, 92,251, 30, 55,222, + 102, 80,250,107, 89,252,105, 84,252,105, 84,252,105, 84,252, + 109, 92,251,115,107,129,121,125, 63,124,145, 29,124,160, 12, + 123,170, 3,122,175, 0,122,176, 0,123,173, 1,123,168, 5, + 124,164, 9,123,170, 3,123,172, 2,123,171, 3,123,169, 4, + 124,165, 8,124,161, 11,124,158, 13,124,155, 16,124,155, 16, + 124,152, 20,124,143, 31,124,154, 18,124,162, 10,123,170, 3, + 121,179, 0,119,187, 0,117,192, 0,112,199, 0,108,205, 0, + 106,208, 0,105,209, 0,106,208, 0,108,205, 0,112,199, 0, + 113,198, 0,114,196, 0,112,200, 0,109,203, 0,107,206, 0, + 106,207, 0,106,208, 0,105,209, 0,105,209, 0,106,207, 0, + 110,202, 0,115,194, 0,120,182, 0,124,165, 8,124,147, 26, + 117,110,109,111, 96,248,107, 89,252,124,149, 24,122,175, 0, + 118,188, 0,115,194, 0,114,196, 0,115,195, 0,116,192, 0, + 119,186, 0,118,188, 0,118,188, 0,119,185, 0,122,178, 0, + 124,165, 8,123,139, 37,122,135, 44,122,132, 48,122,130, 53, + 122,130, 53,121,125, 63,123,137, 41,124,145, 29,124,147, 26, + 124,147, 26,124,152, 20,124,164, 9,123,170, 3,123,171, 3, + 123,168, 5,124,157, 15,122,135, 44,121,125, 63,115,107,129, + 120,122, 70,124,161, 11,122,178, 0,119,185, 0,118,188, 0, + 118,188, 0,115,194, 0,112,200, 0,111,202, 0,111,201, 0, + 113,198, 0,115,194, 0,117,190, 0,116,192, 0,115,195, 0, + 111,202, 0,107,207, 0,103,211, 0,102,213, 0,102,212, 0, + 105,209, 0,108,205, 0,108,205, 0,108,205, 0,109,203, 0, + 112,200, 0,115,195, 0,119,187, 0,122,175, 0,124,155, 16, + 109, 92,251, 30, 26,168, 30, 37,192,113,100,242,122,130, 53, + 124,145, 29,124,154, 18,124,167, 6,122,177, 0,120,183, 0, + 119,187, 0,118,189, 0,118,188, 0,120,184, 0,122,176, 0, + 123,169, 4,122,178, 0,120,183, 0,120,184, 0,120,184, 0, + 119,185, 0,118,188, 0,118,189, 0,119,186, 0,121,180, 0, + 121,180, 0,123,174, 1,124,164, 9,123,141, 34,113,100,242, + 87, 65,237, 30, 49,213,107, 89,252,115,107,129,120,122, 70, + 123,137, 41,124,147, 26,124,155, 16,124,161, 11,123,168, 5, + 122,175, 0,121,179, 0,118,189, 0,114,196, 0,112,200, 0, + 112,200, 0,114,197, 0,117,191, 0,118,188, 0,116,193, 0, + 115,195, 0,115,194, 0,117,190, 0,118,189, 0,118,188, 0, + 120,184, 0,122,175, 0,124,155, 16,124,155, 16,124,160, 12, + 124,160, 12,124,152, 20,122,135, 44,113,100,242,105, 84,252, + 94, 70,242,107, 89,252,114,104,197,117,110,109,114,104,197, + 109, 92,251,111, 96,248,115,107,129,117,110,109,118,114, 96, + 117,110,109,118,117, 85,121,125, 63,122,130, 53,122,132, 48, + 122,130, 53,121,127, 58,124,150, 22,123,170, 3,121,182, 0, + 118,188, 0,117,191, 0,117,191, 0,118,189, 0,119,186, 0, + 118,189, 0,117,190, 0,119,186, 0,122,178, 0,124,160, 12, + 118,117, 85, 30, 43,203,107, 89,252,124,143, 31,124,165, 8, + 123,173, 1,123,173, 1,124,167, 6,124,152, 20,123,141, 34, + 124,155, 16,124,162, 10,124,164, 9,124,160, 12,124,154, 18, + 124,155, 16,124,149, 24,121,125, 63,114,104,197,122,135, 44, + 124,152, 20,124,161, 11,124,161, 11,124,162, 10,124,161, 11, + 123,169, 4,122,175, 0,123,174, 1,123,169, 4,124,160, 12, + 124,149, 24,122,135, 44,115,107,129,109, 92,251,107, 89,252, + 117,110,109,123,139, 37,124,150, 22,124,152, 20,124,143, 31, + 120,122, 70,121,125, 63,122,130, 53,120,122, 70,113,100,242, + 69, 60,230, 30, 20,155, 30, 31,180, 30, 37,192,105, 84,252, + 120,122, 70,124,145, 29,124,157, 15,124,160, 12,124,158, 13, + 124,150, 22,123,139, 37,124,150, 22,124,152, 20,124,150, 22, + 124,143, 31,122,135, 44,121,125, 63,118,114, 96,115,107,129, + 114,104,197,111, 96,248,118,114, 96,122,130, 53,124,150, 22, + 123,170, 3,121,182, 0,118,188, 0,117,191, 0,113,199, 0, + 108,205, 0,105,209, 0,103,211, 0,105,209, 0,108,206, 0, + 112,200, 0,113,198, 0,112,199, 0,110,203, 0,108,206, 0, + 106,207, 0,106,208, 0,106,208, 0,106,208, 0,106,208, 0, + 108,206, 0,112,200, 0,117,191, 0,121,180, 0,123,169, 4, + 124,149, 24,115,107,129, 30, 55,222,113,100,242,124,158, 13, + 121,181, 0,117,191, 0,114,196, 0,114,197, 0,115,194, 0, + 117,191, 0,117,191, 0,116,192, 0,117,192, 0,119,187, 0, + 122,178, 0,124,161, 11,121,125, 63,107, 89,252,109, 92,251, + 113,100,242,118,114, 96,120,122, 70,120,122, 70,121,127, 58, + 122,130, 53,122,132, 48,122,135, 44,124,154, 18,124,164, 9, + 124,167, 6,124,164, 9,124,150, 22,118,117, 85, 94, 70,242, + 99, 75,247,124,147, 26,122,175, 0,118,188, 0,116,193, 0, + 114,196, 0,115,195, 0,113,198, 0,110,202, 0,109,203, 0, + 110,202, 0,112,199, 0,114,197, 0,114,196, 0,117,192, 0, + 116,192, 0,112,200, 0,107,207, 0,103,211, 0,101,213, 0, + 102,212, 0,105,209, 0,110,203, 0,113,198, 0,113,199, 0, + 113,198, 0,114,196, 0,117,190, 0,121,180, 0,124,164, 9, 122,135, 44 +} ; diff --git a/examples/src/ssg/majik/majik_demo.cxx b/examples/src/ssg/majik/majik_demo.cxx new file mode 100644 index 0000000..3ead8ca --- /dev/null +++ b/examples/src/ssg/majik/majik_demo.cxx @@ -0,0 +1,433 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: majik_demo.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#define TILE_SIZE 2000.0f /* cubits */ +#define LAMBDA (TILE_SIZE/48.0f) +#define TILE_GRID_SIZE 20 /* Even number please! */ +#define TRIANGLE_GRID_SIZE 12 /* Num vertices */ +#define ELEVATION_SCALE 4.0f +#define ONLINE_TERRAIN_RANGE ((float)(TILE_GRID_SIZE/2 )* TILE_SIZE ) /* cubits */ +#define VISUAL_RANGE ((float)(TILE_GRID_SIZE/2-1)* TILE_SIZE ) /* cubits */ + +/***** *****/ + +static ssgSimpleState *state = NULL ; +static ssgRoot *scene = NULL ; +static ssgTransform *penguin = NULL ; +static ssgTransform *terrain = NULL ; +static ssgTransform *tilegrid [ TILE_GRID_SIZE ][ TILE_GRID_SIZE ] ; + +#include "elevation_map.h" +#include "image_map.h" + +static jsJoystick *js ; +static float *ax ; +static sgCoord tuxpos = { { 0.0f,0.0f,0.0f }, { 0.0f,0.0f,0.0f } } ; + +#define ROT_SPEED 5.0f +#define TRANS_SPEED 10.0f + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + + int buttons ; + + js -> read ( &buttons, ax ) ; + + tuxpos.hpr[0] -= ax [ 0 ] * ROT_SPEED ; + tuxpos.xyz[0] -= (float)sin ( tuxpos.hpr[0] * SG_DEGREES_TO_RADIANS ) * ax[1] * TRANS_SPEED ; + tuxpos.xyz[1] += (float)cos ( tuxpos.hpr[0] * SG_DEGREES_TO_RADIANS ) * ax[1] * TRANS_SPEED ; + + sgVec3 test_vec ; + sgMat4 invmat ; + sgMakeIdentMat4 ( invmat ) ; + + invmat[3][0] = -tuxpos.xyz[0] ; + invmat[3][1] = -tuxpos.xyz[1] ; + invmat[3][2] = 0.0f ; + + test_vec [0] = 0.0f ; + test_vec [1] = 0.0f ; + test_vec [2] = 100000.0f ; + + ssgHit *results ; + int num_hits = ssgHOT ( scene, test_vec, invmat, &results ) ; + + float hot = -1000000.0f ; + + for ( int i = 0 ; i < num_hits ; i++ ) + { + ssgHit *h = &results [ i ] ; + + float hgt = - h->plane[3] / h->plane[2] ; + + if ( hgt >= hot ) + hot = hgt ; + } + + tuxpos . xyz [ 2 ] = hot + 0.1f ; + tuxpos . hpr [ 1 ] = 0.0f ; + tuxpos . hpr [ 2 ] = 0.0f ; + + sgCopyVec3 ( campos.xyz, tuxpos.xyz ) ; + + campos . hpr [ 0 ] = 0.0f ; + campos . hpr [ 1 ] = -20.0f ; + campos . hpr [ 2 ] = 0.0f ; + campos . xyz [ 0 ] += 1.0f ; + campos . xyz [ 1 ] -= 20.0f ; + campos . xyz [ 2 ] += 12.0f ; + + ssgSetCamera ( & campos ) ; + penguin -> setTransform ( & tuxpos ) ; +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard ( unsigned char k, int, int ) +{ + static int wireframe = FALSE ; + + if ( k == 'w' ) + { + wireframe = ! wireframe ; + + glPolygonMode ( GL_FRONT_AND_BACK, wireframe ? GL_LINE : GL_FILL ) ; + } + + if ( k == 0x03 || k == 'x' ) + exit ( 0 ) ; +} + + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "MajikTerrain" ; + fake_argv[1] = "Majik Terrain Demo - by Steve Baker" ; + fake_argv[2] = NULL ; + + jsInit () ; + + js = new jsJoystick ( 0 ) ; + + if ( js -> notWorking () || js -> getNumAxes () < 2 ) + { + fprintf ( stderr, + "Majik_Demo: Joystick with at least two axes required.\n" ) ; + exit ( 0 ) ; + } + + ax = new float [ js->getNumAxes () ] ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + sgVec4 skycol ; sgSetVec4 ( skycol, 0.4f, 0.7f, 1.0f, 1.0f ) ; + sgVec4 fogcol ; sgSetVec4 ( fogcol, 0.4f, 0.7f, 1.0f, 1.0f ) ; + + glClearColor ( skycol[0], skycol[1], skycol[2], skycol[3] ) ; + + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 30000.0f ) ; + + /* + Initial Position + */ + + sgCoord startpos ; + sgSetCoord ( &startpos, 2350.0f, -920.0f, 0.0f, 0.0f, 0.0f, 0.0f ) ; + + /* + Set up some fog + */ + + glFogf ( GL_FOG_DENSITY, 0.015f / 100.0f ) ; + glFogfv( GL_FOG_COLOR , fogcol ) ; + glFogf ( GL_FOG_START , 0.0 ) ; + glFogi ( GL_FOG_MODE , GL_EXP2 ) ; + glHint ( GL_FOG_HINT , GL_NICEST ) ; + glEnable ( GL_FOG ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgVec4 sunamb ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + sgSetVec4 ( sunamb , 0.4f, 0.4f, 0.4f, 1.0f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + ssgGetLight ( 0 ) -> setColour ( GL_AMBIENT, sunamb ) ; +} + + +static ssgBranch *createTileLOD ( int x, int y, ssgSimpleState *state, int ntris, float z_off ) +{ + sgVec4 *scolors = new sgVec4 [ (ntris+1) * (ntris+1) ] ; + sgVec2 *tcoords = new sgVec2 [ (ntris+1) * (ntris+1) ] ; + sgVec3 *snorms = new sgVec3 [ (ntris+1) * (ntris+1) ] ; + sgVec3 *scoords = new sgVec3 [ (ntris+1) * (ntris+1) ] ; + + for ( int j = 0 ; j < (ntris+1) ; j++ ) + for ( int i = 0 ; i < (ntris+1) ; i++ ) + { + int address = (int)floor( 12.0f * ((float)x + (float)i / (float)ntris) ) % 256 + + ((int)floor( 12.0f * ((float)y + (float)j / (float)ntris) ) % 256 ) * 256 ; + int addressN = (int)floor( 12.0f * ((float)x + (float)i / (float)ntris) ) % 256 + + ((int)floor( 12.0f * ((float)y + (float)j / (float)ntris)+1.0f ) % 256 ) * 256 ; + int addressE = (int)floor( 12.0f * ((float)x + (float)i / (float)ntris)+1.0f ) % 256 + + ((int)floor( 12.0f * ((float)y + (float)j / (float)ntris) ) % 256 ) * 256 ; + + float zz = (float) elevation_map [ address ] * ELEVATION_SCALE + z_off ; + float zzN = (float) elevation_map [ addressN ] * ELEVATION_SCALE + z_off ; + float zzE = (float) elevation_map [ addressE ] * ELEVATION_SCALE + z_off ; + + float rr = (float) image_map [ address * 3 + 0 ] / 255.0f ; + float gg = (float) image_map [ address * 3 + 1 ] / 255.0f ; + float bb = (float) image_map [ address * 3 + 2 ] / 255.0f ; + + float xx = (float) i * (TILE_SIZE/(float)ntris) ; + float yy = (float) j * (TILE_SIZE/(float)ntris) ; + + sgVec3 nrm ; + + nrm [ 0 ] = (zz - zzE) / (TILE_SIZE/12.0f) ; + nrm [ 1 ] = (zz - zzN) / (TILE_SIZE/12.0f) ; + nrm [ 2 ] = (float)sqrt ( nrm[0] * nrm[0] + nrm[1] * nrm[1] ) ; + + sgCopyVec3( snorms [i+j*(ntris+1)], nrm ) ; + sgSetVec2 ( tcoords [i+j*(ntris+1)], xx/LAMBDA, yy/LAMBDA ) ; + sgSetVec4 ( scolors [i+j*(ntris+1)], rr, gg, bb, 1.0f ) ; + sgSetVec3 ( scoords [i+j*(ntris+1)], xx, yy, zz ) ; + } + + ssgBranch *branch = new ssgBranch () ; + + for ( int i = 0 ; i < ntris ; i++ ) + { + unsigned short *vlist = new unsigned short [ 2 * (ntris+1) ] ; + + for ( int j = 0 ; j < (ntris+1) ; j++ ) + { + vlist [ j + j ] = j + (i+1) * (ntris+1) ; + vlist [ j + j + 1 ] = j + i * (ntris+1) ; + } + + ssgLeaf *gset = new ssgVTable ( GL_TRIANGLE_STRIP, + 2 * (ntris+1), vlist, scoords, + 2 * (ntris+1), vlist, snorms , + 2 * (ntris+1), vlist, tcoords, + 2 * (ntris+1), vlist, scolors ) ; + gset -> setState ( state ) ; + branch -> addKid ( gset ) ; + } + + return branch ; +} + + +static void createTile ( ssgTransform *tile, int x, int y, ssgSimpleState *state ) +{ + float rr[] = { 0.0f, 6000.0f, 7000.0f, 12000.0f } ; + ssgRangeSelector *lod = new ssgRangeSelector () ; + + lod -> addKid ( createTileLOD ( x, y, state, TRIANGLE_GRID_SIZE - 1, 0.0f ) ) ; + lod -> addKid ( createTileLOD ( x, y, state, TRIANGLE_GRID_SIZE/2 - 1, -30.0f ) ) ; + lod -> addKid ( createTileLOD ( x, y, state, TRIANGLE_GRID_SIZE/4 - 1, -250.0f ) ) ; + lod -> setRanges ( rr, 4 ) ; + + tile -> addKid ( lod ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* + Create a root node - and a transform to position + the terrain - and another to position the player. + */ + + scene = new ssgRoot ; + penguin = new ssgTransform ; + terrain = new ssgTransform ; + state = new ssgSimpleState ; + state -> setTexture ( "data/bumpnoise.rgb" ) ; + state -> enable ( GL_TEXTURE_2D ) ; + state -> enable ( GL_LIGHTING ) ; + state -> setShadeModel ( GL_SMOOTH ) ; + state -> enable ( GL_COLOR_MATERIAL ) ; + state -> enable ( GL_CULL_FACE ) ; + state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + state -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + state -> setShininess ( 0 ) ; + state -> setOpaque () ; + state -> disable ( GL_BLEND ) ; + + /* + Load the models - optimise them a bit + and then add them into the scene. + */ + + ssgEntity *tux_obj = ssgLoadAC ( "tuxedo.ac" ) ; + + penguin -> addKid ( tux_obj ) ; + ssgFlatten ( tux_obj ) ; + ssgStripify ( penguin ) ; + penguin -> clrTraversalMaskBits ( SSGTRAV_HOT ) ; + + for ( int i = 0 ; i < TILE_GRID_SIZE ; i++ ) + for ( int j = 0 ; j < TILE_GRID_SIZE ; j++ ) + { + tilegrid [ i ][ j ] = new ssgTransform ; + + terrain -> addKid ( tilegrid [ i ][ j ] ) ; + + sgVec3 tilepos ; + sgSetVec3 ( tilepos, (float)i * TILE_SIZE - ONLINE_TERRAIN_RANGE, + (float)j * TILE_SIZE - ONLINE_TERRAIN_RANGE, 0.0f ) ; + + tilegrid [ i ][ j ] -> setTransform ( tilepos ) ; + createTile ( tilegrid [ i ][ j ], i, j, state ) ; + } + + scene -> addKid ( terrain ) ; + scene -> addKid ( penguin ) ; +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + diff --git a/examples/src/ssg/majik/majik_demo.dsp b/examples/src/ssg/majik/majik_demo.dsp new file mode 100644 index 0000000..b95e4c8 --- /dev/null +++ b/examples/src/ssg/majik/majik_demo.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="majik_demo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=majik_demo - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "majik_demo.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "majik_demo.mak" CFG="majik_demo - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "majik_demo - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "majik_demo - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "majik_demo - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib js.lib winmm.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"majik_demo.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "majik_demo - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib js_d.lib winmm.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"majik_demo.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "majik_demo - Win32 Release" +# Name "majik_demo - Win32 Debug" +# Begin Source File + +SOURCE=.\elevation_map.h +# End Source File +# Begin Source File + +SOURCE=.\image_map.h +# End Source File +# Begin Source File + +SOURCE=.\majik_demo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/shapes/Makefile.am b/examples/src/ssg/shapes/Makefile.am new file mode 100644 index 0000000..3a41859 --- /dev/null +++ b/examples/src/ssg/shapes/Makefile.am @@ -0,0 +1,10 @@ +if BUILD_SSG + +noinst_PROGRAMS = shapes + +shapes_SOURCES = shapes.cxx + +shapes_LDADD = -lplibssgaux -lplibssg -lplibsg -lplibul -lplibpw $(OGL_LIBS) + +endif + diff --git a/examples/src/ssg/shapes/shapes.cxx b/examples/src/ssg/shapes/shapes.cxx new file mode 100644 index 0000000..3b0dbce --- /dev/null +++ b/examples/src/ssg/shapes/shapes.cxx @@ -0,0 +1,180 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: shapes.cxx 2015 2005-02-02 15:48:09Z sjbaker $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include +#include + +#define MAX_SHAPE 5 +static ssgRoot *scene = NULL ; +static ssgTransform *shape [ MAX_SHAPE ] = { NULL } ; + +static sgVec4 colour [ MAX_SHAPE ] = +{ + { 1, 0, 0, 1 }, + { 0, 1, 0, 1 }, + { 0, 0, 1, 1 }, + { 1, 1, 0, 1 }, + { 0, 1, 1, 1 } +} ; + +/* + Something to make some interesting motion +*/ + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + + sgSetCoord ( & campos, 0.0f, -6.0f, 0.0f, 0.0f, 0.0f, 0.0f ) ; + ssgSetCamera ( & campos ) ; + + for ( int i = 0 ; i < MAX_SHAPE ; i++ ) + { + sgCoord pos ; + sgSetCoord ( & pos, 2.0f*(float)(i % 3)-2.0f, 0.0f, + 2.0f*(float)(i / 3)-1.5f, + frameno/100.0f, frameno/20.0f, 0.0f ) ; + shape [ i ] -> setTransform ( & pos ) ; + } +} + + +static void redraw () +{ + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + pwSwapBuffers () ; +} + + + +static void init_graphics () +{ + pwInit ( 100, 100, 640, 480, false, "PLIB Shapes Demo", true, 0 ) ; + pwSetCallbacks ( NULL, NULL, NULL, NULL, NULL ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + +/* + Create a simple database +*/ + +static void load_database () +{ + scene = new ssgRoot ; + + ssgSimpleState *state = new ssgSimpleState () ; + state -> setShininess ( 8.0f ) ; + + for ( int i = 0 ; i < MAX_SHAPE ; i++ ) + { + ssgaShape *s ; + + shape [ i ] = new ssgTransform ; + scene -> addKid ( shape [ i ] ) ; + + switch ( i ) + { + case 0 : s = new ssgaCube ( 1000 ) ; break ; + case 1 : s = new ssgaCylinder ( 1000 ) ; break ; + case 2 : s = new ssgaSphere ( 1000 ) ; + ((ssgaSphere*)s) -> setLatLongStyle ( FALSE) ; break ; + case 3 : s = new ssgaSphere ( 1000 ) ; + ((ssgaSphere*)s) -> setLatLongStyle ( TRUE ) ; break ; + case 4 : + default: s = new ssgaTeapot ( 1000 ) ; break ; + } + + s -> setColour ( colour[i] ) ; + s -> setKidState ( state ) ; + shape [ i ] -> addKid ( s ) ; + } +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + + while ( 1 ) + redraw () ; + + return 0 ; +} + + + diff --git a/examples/src/ssg/shrubs/Makefile.am b/examples/src/ssg/shrubs/Makefile.am new file mode 100644 index 0000000..dfdcaca --- /dev/null +++ b/examples/src/ssg/shrubs/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SSG + +noinst_PROGRAMS = shrubs + +shrubs_SOURCES = shrubs.cxx + +shrubs_LDADD = -lplibssgaux -lplibssg -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = + diff --git a/examples/src/ssg/shrubs/data/Courier-Bold.txf b/examples/src/ssg/shrubs/data/Courier-Bold.txf new file mode 100644 index 0000000000000000000000000000000000000000..13b02050fb91ba306e5affae532c9a0e7ac16fab GIT binary patch literal 66708 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/shrubs/data/deciduous-tree.rgb b/examples/src/ssg/shrubs/data/deciduous-tree.rgb new file mode 100644 index 0000000000000000000000000000000000000000..d59441f44d7e29f2bb8bf668c4c7f111b55aaea1 GIT binary patch literal 8496 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 XmXw?4<-z)-u6RkA7nAamccc7Q-lZ4I literal 0 HcmV?d00001 diff --git a/examples/src/ssg/shrubs/shrubs.cxx b/examples/src/ssg/shrubs/shrubs.cxx new file mode 100644 index 0000000..e960a9e --- /dev/null +++ b/examples/src/ssg/shrubs/shrubs.cxx @@ -0,0 +1,718 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: shrubs.cxx 2053 2005-11-14 12:12:25Z bram $ +*/ + +// $Id: shrubs.cxx 2053 2005-11-14 12:12:25Z bram $ + +#include +#include +#include +#include + +#include +#include + +#ifdef UL_WIN32 +# include +#else +# include +# include +#endif + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef UL_MAC_OSX +# include +# else +# include +# endif +#endif + + +#define TILE_RES 12 +#define NUM_TILES (TILE_RES * TILE_RES) + +#define TILE_SIZE 300.0f +#define WORLD_SIZE (TILE_RES * TILE_SIZE) + +#define FRAC_LEVEL 7 // --> 128*128 + + +// framerate control +static double min_delta = 1.0 / 200.0; +static double cur_delta = 1.0 / 50.0; + +// terrain grid (display list) +static GLuint grid; +static bool draw_grid = true; + +// billboard config +static float near_dist = 500.0f; +static float fade_dist = 2000.0f; +static float clip_dist = 2500.0f; +static int draw_num = 1000; +static int fade_num = 100; + +// state variables (GLUT enforced) +static int winsx = 800, winsy = 600; +static int mousex, mousey, bstate; + +// motion model +static int model = 1; // 0 - trackball, 1 - fly + +// trackball +static sgQuat rot = { 0, 0, 0, 1 }; +static sgVec3 tra = { 0, 1500, 0 }; + +// fly +static sgVec2 spin; +static float thrust; +static float speed; +static sgVec3 hpr; +static sgVec3 pos = { 0, -0.25f * WORLD_SIZE, 20.0f }; +static sgVec3 dir = { 0, 1, 0 }; + +// scene +static ssgRoot *scene; +static ssgaBillboards *bb[NUM_TILES]; + +// gui +static fntRenderer *text; +static puSlider *near_dist_sl, *fade_dist_sl, *clip_dist_sl; +static puSlider *draw_num_sl, *fade_num_sl; + + +inline float clampf(float x, float lo, float hi) +{ + return x <= lo ? lo : x >= hi ? hi : x; +} + + +static double get_time() +{ +#ifdef UL_WIN32 + static LONGLONG t0, f; + LONGLONG t; + QueryPerformanceCounter((LARGE_INTEGER *) &t); + if (f == 0) { + QueryPerformanceFrequency((LARGE_INTEGER *) &f); + t0 = t; + } + return (double) (t - t0) / f; +#else + static struct timeval t0; + struct timeval t; + gettimeofday(&t, 0); + if (t0.tv_sec == 0) + t0 = t; + return (t.tv_sec - t0.tv_sec) + 1e-6 * (t.tv_usec - t0.tv_usec); +#endif +} + + +#ifdef UL_WIN32 +inline void srand48(int seed) { srand(seed); } +inline double drand48() { return (double) rand() / RAND_MAX; } +#endif + +#ifdef _MSC_VER /* UL_MSVC sometimes defined for mingw (gcc) */ +inline float hypotf(float x, float y) { return sqrtf(x*x + y*y); } +#define snprintf _snprintf +#endif + + +static void motion_fn(int x, int y) +{ + if (bstate || !puMouse(x, y)) { + + if (model == 1) { + if (bstate & 4) { + spin[0] = 0; + spin[1] = 0; + } else { + spin[0] = 2.0f * x / winsx - 1.0f; + spin[1] = 2.0f * y / winsy - 1.0f; + } + } else { + float fx = 2.0f * x / winsx - 1.0f; + float fy = 1.0f - 2.0f * y / winsy; + float dx = 2.0f * (x - mousex) / winsx; + float dy = 2.0f * (mousey - y) / winsy; + + if (bstate & 1) { + float a = 0.5f * hypotf(dx, dy); + if (a > 1e-6f) { + sgQuat q; + q[0] = dy * (1.0f - fx * fx); + q[1] = dy * fx - dx * fy; + q[2] = dx * (fy * fy - 1.0f); + sgScaleVec3(q, sinf(a) / sgLengthVec3(q)); + q[3] = cosf(a); + sgMultQuat(rot, rot, q); + } + } + + if (bstate & 4) { + tra[1] = tra[1] / (1.0f - dy); + } + } + } + mousex = x; + mousey = y; +} + + +static void mouse_fn(int button, int updown, int x, int y) +{ + if (bstate || !puMouse(button, updown, x, y)) { + int bit = 0; + switch (button) { + case GLUT_LEFT_BUTTON: bit = 1; break; + case GLUT_MIDDLE_BUTTON: bit = 2; break; + case GLUT_RIGHT_BUTTON: bit = 4; break; + } + switch (updown) { + case GLUT_DOWN: bstate |= bit; break; + case GLUT_UP: bstate &= ~bit; break; + } + thrust = (bstate & 1) - (bstate & 4) / 2; + if (bstate & 4) { + spin[0] = 0; + spin[1] = 0; + } + } + mousex = x; + mousey = y; +} + + +static void special_fn(int key, int x, int y) +{ + if (!puKeyboard(key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN)) { + switch (key) { + case GLUT_KEY_UP: min_delta *= 1.1; break; + case GLUT_KEY_DOWN: min_delta /= 1.1; break; + } + } +} + + +static void keyboard_fn(unsigned char key, int, int) +{ + if (!puKeyboard(key, PU_DOWN)) { + switch (key) { + case 0x1b: + exit(0); + case '1': + model = 0; + break; + case '2': + model = 1; + break; + case 'g': + draw_grid = !draw_grid; + break; + } + } +} + + + +static void update_bboards() +{ + for (int i = 0; i < NUM_TILES; i++) { + bb[i]->setNearRange(near_dist); + bb[i]->setFadeRange(fade_dist); + bb[i]->setClipRange(clip_dist); + bb[i]->setDrawNum(draw_num); + bb[i]->setFadeNum(fade_num); + } +} + +static void near_dist_cb(puObject *ob) +{ + near_dist = ob->getFloatValue(); + update_bboards(); +} + +static void fade_dist_cb(puObject *ob) +{ + fade_dist = ob->getFloatValue(); + update_bboards(); +} + +static void clip_dist_cb(puObject *ob) +{ + clip_dist = ob->getFloatValue(); + update_bboards(); +} + +static void draw_num_cb(puObject *ob) +{ + draw_num = ob->getIntegerValue(); + update_bboards(); +} + +static void fade_num_cb(puObject *ob) +{ + fade_num = ob->getIntegerValue(); + update_bboards(); +} + + + +static void init_scene() +{ + int i, x, y; + + scene = new ssgRoot; + + // produce a simple diamond-square fractal + const int size = 1 << FRAC_LEVEL; + const int mask = size - 1; + float *elev = new float[size * size]; // height map + memset(elev, 0, sizeof(elev)); + for (i = FRAC_LEVEL; i > 0; i--) { + int s = 1 << i, t = s >> 1; + float r = 750.0f * powf(s / size, 0.9f); + for (y = 0; y < size; y += s) { + int y1 = (y + s) & mask; + for (x = 0; x < size; x += s) { + int x1 = (x + s) & mask; + elev[(y + t) * size + (x + t)] = (0.25f * (elev[y * size + x ] + + elev[y * size + x1] + + elev[y1 * size + x ] + + elev[y1 * size + x1]) + + r * (0.5f - (float) drand48())); + } + } + r = 750.0f * powf(0.7071f * s / size, 0.9f); + for (y = 0; y < size; y += s) { + int y0 = (y - t) & mask; + int y1 = (y + s) & mask; + for (x = 0; x < size; x += s) { + int x0 = (x - t) & mask; + int x1 = (x + s) & mask; + elev[y * size + (x + t)] = (0.25f * (elev[y0 * size + (x + t)] + + elev[y * size + x ] + + elev[y * size + x1] + + elev[(y + t) * size + (x + t)]) + + r * (0.5f - (float) drand48())); + elev[(y + t) * size + x] = (0.25f * (elev[(y + t) * size + x0] + + elev[y * size + x] + + elev[y1 * size + x] + + elev[(y + t) * size + (x + t)]) + + r * (0.5f - (float) drand48())); + } + } + } + + // generate display list + grid = glGenLists(1); + glNewList(grid, GL_COMPILE); + for (y = 0; y <= size; y += 4) { + glBegin(GL_LINE_STRIP); + for (x = 0; x <= size; x++) + glVertex3f(((float) x / size - 0.5f) * WORLD_SIZE, + ((float) y / size - 0.5f) * WORLD_SIZE, + elev[(y & mask) * size + (x & mask)]); + glEnd(); + } + for (x = 0; x <= size; x += 4) { + glBegin(GL_LINE_STRIP); + for (y = 0; y <= size; y++) + glVertex3f(((float) x / size - 0.5f) * WORLD_SIZE, + ((float) y / size - 0.5f) * WORLD_SIZE, + elev[(y & mask) * size + (x & mask)]); + glEnd(); + } + glEndList(); + + + // now load the shrubs + + ssgTexture *tex = new ssgTexture("data/deciduous-tree.rgb", FALSE, FALSE); + + ssgaBillboards::initTexAlpha(tex); + + for (y = 0; y < TILE_RES; y++) { + for (x = 0; x < TILE_RES; x++) { + ssgaBillboards *b = new ssgaBillboards; + + b->setTexture(tex); + b->setWidth(3.0f); + b->setHeight(8.0f); + + float tx = (x - 0.5f * TILE_RES) * TILE_SIZE; + float ty = (y - 0.5f * TILE_RES) * TILE_SIZE; + + for (int i = 0; i < 3000; i++) { + + float x = tx + drand48() * TILE_SIZE; + float y = ty + drand48() * TILE_SIZE; + float z; + + // bilinear lookup + float xf = (0.5f + x / WORLD_SIZE) * size; + float yf = (0.5f + y / WORLD_SIZE) * size; + int x0 = (int) xf; + int y0 = (int) yf; + int x1 = x0 + 1; + int y1 = y0 + 1; + float xw1 = xf - (float) x0; + float yw1 = yf - (float) y0; + float xw0 = 1.0f - xw1; + float yw0 = 1.0f - yw1; + x0 &= mask; + y0 &= mask; + x1 &= mask; + y1 &= mask; + z = (elev[y0 * size + x0] * xw0 * yw0 + + elev[y0 * size + x1] * xw1 * yw0 + + elev[y1 * size + x0] * xw0 * yw1 + + elev[y1 * size + x1] * xw1 * yw1); + + b->add(x, y, z, 0.75f + 0.5f * drand48()); + } + + bb[y * TILE_RES + x] = b; + + scene->addKid(b); + } + } + + x = (int)((0.5f + pos[0] / WORLD_SIZE) * size); + y = (int)((0.5f + pos[1] / WORLD_SIZE) * size); + pos[2] += elev[(y & mask) * size + (x & mask)]; + + delete [] elev; + + update_bboards(); +} + + +static void init_gui() +{ + fntInit(); + + fntTexFont *font = new fntTexFont("data/Courier-Bold.txf"); + + text = new fntRenderer(); + text->setFont(font); + text->setPointSize(18); + + puInit(); + + puSetDefaultFonts(puFont(font, 14), puFont(font, 18)); + puSetDefaultStyle(PUSTYLE_SMALL_SHADED); + puSetDefaultColourScheme(0.7f, 0.7f, 0.7f, 1.0f); + + puGroup *group = new puGroup(0, 0); + puSlider *sl; + int h = winsy; + + near_dist_sl = sl = new puSlider(250, h - 30, 150); + sl->setValue(near_dist); + sl->setMinValue(100.0f); + sl->setMaxValue(5000.0f); + sl->setCallback(near_dist_cb); + + fade_dist_sl = sl = new puSlider(250, h - 55, 150); + sl->setValue(fade_dist); + sl->setMinValue(100.0f); + sl->setMaxValue(5000.0f); + sl->setCallback(fade_dist_cb); + + clip_dist_sl = sl = new puSlider(250, h - 80, 150); + sl->setValue(clip_dist); + sl->setMinValue(100.0f); + sl->setMaxValue(5000.0f); + sl->setCallback(clip_dist_cb); + + draw_num_sl = sl = new puSlider(250, h - 110, 150); + sl->setValue(draw_num); + sl->setMinValue(0); + sl->setMaxValue(3000); + sl->setCallback(draw_num_cb); + + fade_num_sl = sl = new puSlider(250, h - 135, 150); + sl->setValue(fade_num); + sl->setMinValue(0); + sl->setMaxValue(3000); + sl->setCallback(fade_num_cb); + + group->close(); +} + + +static void reshape_fn(int w, int h) +{ + winsx = w; + winsy = h; + + glViewport(0, 0, w, h); + + if (near_dist_sl) { + near_dist_sl->setPosition(250, h - 30); + fade_dist_sl->setPosition(250, h - 55); + clip_dist_sl->setPosition(250, h - 80); + draw_num_sl->setPosition(250, h - 110); + fade_num_sl->setPosition(250, h - 135); + } +} + + +static void display_fn() +{ + static float backlog1[256]; + static float backlog2[256]; + + static double last_time; + static double sync_time; + static int frame_count; + + double t0 = get_time(); + int i; + + frame_count++; + + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + sgMat4 mat; + + if (model == 0) { + // trackball + sgQuatToMatrix(mat, rot); + sgCopyVec3(mat[3], tra); + sgTransposeNegateMat4(mat); + } else { + // fly + float dt = cur_delta; + float s = powf(0.2f, dt); + hpr[2] = s * hpr[2] + (1.0f - s) * 40.0f * spin[0]; + hpr[1] = s * hpr[1] + (1.0f - s) * 40.0f * spin[1]; + hpr[0] -= dt * hpr[2]; + float a = 10.0f * (thrust - dir[2]); + speed = clampf(speed + a * dt, 1.0f, 25.0f); + sgAddScaledVec3(pos, dir, dt * speed); + sgMakeCoordMat4(mat, pos, hpr); + sgCopyVec3(dir, mat[1]); + } + + ssgaBillboards::total_drawn = 0; + + ssgSetCamera(mat); + ssgCullAndDraw(scene); + ssgLoadModelviewMatrix(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + + if (draw_grid) { + glColor3f(0, 0, 0); + glCallList(grid); + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glAlphaFunc(GL_GREATER, 0.3f); + glEnable(GL_ALPHA_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + + // timings + + const float y0 = 0.45f, y1 = 1.0f; + const float d0 = 0, d1 = 0.05f; + + glBegin(GL_LINE_STRIP); + glColor3f(0.4f, 0.7f, 0.4f); + for (i = 1; i <= 256; i++) { + float d = backlog2[(frame_count - i) & 0xff]; + glVertex2f(2.0f * i / 257 - 1.0f, y0 + (y1 - y0) * (d - d0) / (d1 - d0)); + } + glEnd(); + + glBegin(GL_LINE_STRIP); + glColor3f(0.7f, 0.7f, 0.7f); + for (i = 1; i <= 256; i++) { + float d = backlog1[(frame_count - i) & 0xff]; + glVertex2f(2.0f * i / 257 - 1.0f, y0 + (y1 - y0) * (d - d0) / (d1 - d0)); + } + glEnd(); + + glBegin(GL_LINES); + glColor3f(0.3f, 0.4f, 0.4f); + float y = y0 + (y1 - y0) * ((float) min_delta - d0) / (d1 - d0); + glVertex2f(-1.0f, y); + glVertex2f( 1.0f, y); + glEnd(); + + + // gui + + int w = winsx; + int h = winsy; + char buf[64]; + + glOrtho(0, w, 0, h, 0, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glColor3f(0, 0, 0); + + text->begin(); + + text->start2f(w - 200, h - 30); + text->puts("fps:"); + snprintf(buf, sizeof(buf), "%.0f Hz", 1.0 / cur_delta); + text->start2f(w - 100, h - 30); + text->puts(buf); + + text->start2f(w - 200, h - 55); + text->puts("shrubs:"); + snprintf(buf, sizeof(buf), "%d\n", ssgaBillboards::total_drawn); + text->start2f(w - 100, h - 55); + text->puts(buf); + + text->start2f(20, h - 30); + text->puts("NearRange:"); + snprintf(buf, sizeof(buf), "%5.0f", near_dist); + text->start2f(150, h - 30); + text->puts(buf); + + text->start2f(20, h - 55); + text->puts("FadeRange:"); + snprintf(buf, sizeof(buf), "%5.0f", fade_dist); + text->start2f(150, h - 55); + text->puts(buf); + + text->start2f(20, h - 80); + text->puts("ClipRange:"); + snprintf(buf, sizeof(buf), "%5.0f", clip_dist); + text->start2f(150, h - 80); + text->puts(buf); + + text->start2f(20, h - 110); + text->puts("DrawNum:"); + snprintf(buf, sizeof(buf), "%5d", draw_num); + text->start2f(150, h - 110); + text->puts(buf); + + text->start2f(20, h - 135); + text->puts("FadeNum:"); + snprintf(buf, sizeof(buf), "%5d", fade_num); + text->start2f(150, h - 135); + text->puts(buf); + + text->end(); + + puDisplay(); + + glFinish(); + + + // sync + + double t1 = get_time(); + + backlog1[frame_count & 0xff] = t1 - t0; + backlog2[frame_count & 0xff] = t1 - last_time; + + last_time = t1; + + sync_time += min_delta; + + if (t1 < sync_time) { + double dt = sync_time - t1 - 0.005; + if (dt > 0) { +#ifdef UL_WIN32 + Sleep((int)(1e+3 * dt)); +#else + usleep((int)(1e+6 * dt)); +#endif + } + } else if (t1 > sync_time + 0.05) + sync_time = t1; + + cur_delta = 0; + for (i = 0; i < 256; i++) + cur_delta += backlog2[i]; + cur_delta *= 1.0 / 256; + + glutSwapBuffers(); + glutPostRedisplay(); +} + + + +int main(int argc, char** argv) +{ + srand48(time(0)); + + glutInit(&argc, argv); + glutInitWindowSize(winsx, winsy); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); + glutCreateWindow("shrubs"); + + glutDisplayFunc(display_fn); + glutReshapeFunc(reshape_fn); + glutMouseFunc(mouse_fn); + glutMotionFunc(motion_fn); + glutKeyboardFunc(keyboard_fn); + glutSpecialFunc(special_fn); + + ssgInit(); + + ssgSetFOV(60.0f, 0.0f); + ssgSetNearFar(10.0f, 10000.0f); + + //ssgModelPath("."); + //ssgTexturePath("."); + + init_scene(); + + init_gui(); + + glutMainLoop(); + + return 0; +} + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/examples/src/ssg/sky/Makefile.am b/examples/src/ssg/sky/Makefile.am new file mode 100644 index 0000000..3599ca5 --- /dev/null +++ b/examples/src/ssg/sky/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SSG + +noinst_PROGRAMS = sky + +sky_SOURCES = sky.cxx + +sky_LDADD = -lplibpuaux -lplibpu -lplibfnt -lplibssgaux -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = sky.dsp README + diff --git a/examples/src/ssg/sky/README b/examples/src/ssg/sky/README new file mode 100644 index 0000000..ebdc29b --- /dev/null +++ b/examples/src/ssg/sky/README @@ -0,0 +1,39 @@ + +SkyDome. +~~~~~~~~~~~~~~~~ + +The PLIB ssgAux library contains a class for creating a sky dome. + + class ssgaSky ; + +The program in this directory demonstrates creating a sky dome, +including the sun, moon, stars and clouds. + +This program does not attempt to place celestial bodies accurately +if you wish to place sun, moon, planets & stars correctly then you +could possibly plug-in the SimGear Ephemeris code. + + +Keyboard controls: + +left/right : turn left/right +up/down arrow : look up/down +up/down arrow + shift : increase/decrease altitude +a/z : move forward/backward +f : fullscreen +w : wireframe +s : solid +space : toggle gui on and off +q/esc : quit + + +History: + +Written by Durk Talsma. Originally started October 1997, for distribution +with the FlightGear project. Version 2 was written in August and +September 1998. This code is based upon algorithms and data kindly +provided by Mr. Paul Schlyter. (pausch@saaf.se). + +Separated out rendering pieces and converted to ssg by Curt Olson, March 2000. + +Moved (from SimGear) into ssgAux by Nick McEvoy, July 2003. \ No newline at end of file diff --git a/examples/src/ssg/sky/data/candy.rgb b/examples/src/ssg/sky/data/candy.rgb new file mode 100644 index 0000000000000000000000000000000000000000..c897f2684a77264d414571a82785061079583dc0 GIT binary patch literal 1568 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/sky/data/moon.rgba b/examples/src/ssg/sky/data/moon.rgba new file mode 100644 index 0000000000000000000000000000000000000000..0d9c72146a1822d81e5788097f9bf4682127d027 GIT binary patch literal 80170 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 Lq7 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/sky/data/ocean.rgb b/examples/src/ssg/sky/data/ocean.rgb new file mode 100644 index 0000000000000000000000000000000000000000..a72795b3ef93622b9fceebc061e106b43097cbaa GIT binary patch literal 206134 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

Q2ZCj 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�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 HRV}{(#ku1) literal 0 HcmV?d00001 diff --git a/examples/src/ssg/sky/data/scattered.rgba b/examples/src/ssg/sky/data/scattered.rgba new file mode 100644 index 0000000000000000000000000000000000000000..1ed0d9b9925d34dab73dfc0870585dc548814c38 GIT binary patch literal 246992 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

X>@UJ4P+wNGQ)dPq1w`7E0C@7Xr~zJSCXD6 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 N^=HYkQ^&H&{|DYMrkDT# literal 0 HcmV?d00001 diff --git a/examples/src/ssg/sky/data/sorority.txf b/examples/src/ssg/sky/data/sorority.txf new file mode 100644 index 0000000000000000000000000000000000000000..8b457aafb72a70f703aaf9c754eb64da85709072 GIT binary patch literal 9304 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 +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#define ROT_SPEED 2.0f +#define TRANS_SPEED 5.0f + +#define MAX_BODIES 2 +#define MAX_CLOUDS 3 + +#define GUI_BASE 20 +#define FONT_COLOUR 1,1,1,1 + +static int wireframe = FALSE ; +static int displayGUI = TRUE ; + +static puaSelectBox *bodySelectBox = (puaSelectBox *) NULL ; +static puDial *bodyRADial = (puDial *) NULL ; +static puDial *bodyDeclDial = (puDial *) NULL ; +static puSlider *cloudElevationSlider = (puSlider *) NULL ; +static puaSelectBox *cloudSelectBox = (puaSelectBox *) NULL ; +static puButton *cloudEnableButton = (puButton *) NULL ; + +static ssgRoot *scene = NULL ; +static ssgaSky *sky = NULL ; +static ssgTransform *teapot = NULL ; +static ssgTransform *wave = NULL ; + +static ssgaTeapot *tpt_obj = NULL ; +static ssgaWaveSystem *wave_obj = NULL ; +static ssgaWaveTrain wave_train; + +static ssgaCelestialBody *bodies[MAX_BODIES] = {NULL} ; +static ssgaCloudLayer *clouds[MAX_CLOUDS] = {NULL} ; + +static ssgSimpleState *teapot_state = NULL ; +static ssgSimpleState *wave_state = NULL ; + +static int nstars = 1000 ; +static sgdVec3 *star_data = NULL ; +static int nplanets = 0 ; +static sgdVec3 *planet_data = NULL ; + +static char *bodyNameList[] = { "Sun", "Moon", NULL } ; +static char *cloudNameList[] = { "Cloud 0", "Cloud 1", "Cloud 2", NULL } ; + +static int curr_body = 0 ; +static int curr_cloud = 0 ; + +static sgVec4 black = { 0.0f, 0.0f, 0.0f, 1.0f } ; +static sgVec4 white = { 1.0f, 1.0f, 1.0f, 1.0f } ; +static sgVec4 translucent_white = { 1.0f, 1.0f, 1.0f, 0.8f } ; + +static sgVec4 base_sky_color = { 0.39f, 0.50f, 0.74f, 1.0f } ; +static sgVec4 base_fog_color = { 0.84f, 0.87f, 1.00f, 1.0f } ; + +static sgVec4 base_ambient = { 0.2f, 0.2f, 0.2f, 1.0f } ; +static sgVec4 base_diffuse = { 1.0f, 1.0f, 1.0f, 1.0f } ; +static sgVec4 base_specular = { 1.0f, 1.0f, 1.0f, 1.0f } ; + +static sgVec4 sky_color ; +static sgVec4 fog_color ; +static sgVec4 cloud_color ; + +static sgVec4 scene_ambient ; +static sgVec4 scene_diffuse ; +static sgVec4 scene_specular ; + +static const double m_log01 = -log( 0.01 ); +static const double sqrt_m_log01 = sqrt( m_log01 ); + +static int mods = 0 ; +unsigned char keypress = 0 ; +static int special_key = 0 ; + +static sgCoord campos = { { -300, 0, 10 }, { -90, 0, 0 } } ; + +static void bodySelectBox_cb ( puObject *ob ) +{ + curr_body = ((puaSelectBox *) ob) -> getCurrentItem () ; + + if ( curr_body < 0 ) + curr_body = 0 ; + + if ( curr_body >= MAX_BODIES ) + curr_body = MAX_BODIES - 1 ; + + bodyRADial -> setValue ( float(bodies[curr_body]->getRightAscension () * SGD_RADIANS_TO_DEGREES) ) ; + bodyDeclDial -> setValue ( float(bodies[curr_body]->getDeclination () * SGD_RADIANS_TO_DEGREES) ) ; +} + +static void bodyRADial_cb ( puObject *ob ) +{ + bodies[curr_body] -> setRightAscension ( ob -> getFloatValue () * SGD_DEGREES_TO_RADIANS ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + +static void bodyDeclDial_cb ( puObject *ob ) +{ + bodies[curr_body] -> setDeclination ( ob -> getFloatValue () * SGD_DEGREES_TO_RADIANS ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + +static void cloudSelectBox_cb ( puObject *ob ) +{ + curr_cloud = ((puaSelectBox *) ob) -> getCurrentItem () ; + + if ( curr_cloud < 0 ) + curr_cloud = 0 ; + + if ( curr_cloud >= MAX_CLOUDS ) + curr_cloud = MAX_CLOUDS - 1 ; + + cloudElevationSlider -> setValue ( clouds[curr_cloud]->getElevation() ) ; + cloudEnableButton -> setValue ( clouds[curr_cloud]->isEnabled() ) ; +} + +static void cloudElevationSlider_cb ( puObject *ob ) +{ + clouds[curr_cloud] -> setElevation ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + +static void cloudEnableButton_cb ( puObject *ob ) +{ + if ( ob -> getIntegerValue () ) + clouds[curr_cloud] -> enable() ; + else + clouds[curr_cloud] -> disable() ; +} + +static void update_motion () +{ + static int frameno = 0 ; + static ulClock ck ; + + frameno++ ; + + ck . update () ; + + double t = ck . getAbsTime () ; + double dt = ck . getDeltaTime () ; + + /* update camera + + keyboard controls: + - left/right : turn left/right + - up/down arrow : look up/down + - up/down arrow + shift : increase/decrease altitude + - a/z : move forward/backward + + todo: add mouse or joystick support + */ + + if ( special_key == GLUT_KEY_UP ) + { + if ( mods & GLUT_ACTIVE_SHIFT ) + campos.xyz[SG_Z] += TRANS_SPEED ; + else + campos.hpr[1] += ROT_SPEED ; + } + else if ( special_key == GLUT_KEY_DOWN ) + { + if ( mods & GLUT_ACTIVE_SHIFT ) + campos.xyz[SG_Z] -= TRANS_SPEED ; + else + campos.hpr[1] -= ROT_SPEED ; + } + else if ( special_key == GLUT_KEY_LEFT ) + campos.hpr[0] += ROT_SPEED ; + else if ( special_key == GLUT_KEY_RIGHT ) + campos.hpr[0] -= ROT_SPEED ; + + if ( keypress == 'a' || keypress == 'A' ) + { + campos.xyz[SG_X] -= sgSin ( campos.hpr[0] * SG_DEGREES_TO_RADIANS ) * TRANS_SPEED ; + campos.xyz[SG_Y] += sgCos ( campos.hpr[0] * SG_DEGREES_TO_RADIANS ) * TRANS_SPEED ; + } + else if ( keypress == 'z' || keypress == 'Z' ) + { + campos.xyz[SG_X] += sgSin ( campos.hpr[0] * SG_DEGREES_TO_RADIANS ) * TRANS_SPEED ; + campos.xyz[SG_Y] -= sgCos ( campos.hpr[0] * SG_DEGREES_TO_RADIANS ) * TRANS_SPEED ; + } + + ssgSetCamera ( & campos ) ; + + /* update teapot */ + + sgCoord teapotpos ; + sgSetCoord ( & teapotpos, -280.0f, -8.0f, 3.0f, frameno/5.0f, 0.0f, 0.0f ) ; + teapot -> setTransform ( & teapotpos ) ; + + /* update waves */ + + sgCoord wavepos ; + sgSetCoord ( & wavepos, 0, 0, 0, 0, 0, 0 ) ; + wave -> setTransform ( & wavepos ) ; + wave_obj -> updateAnimation ( (float)t ) ; + + /* move heaven & earth ... + if you wish to place sun, moon, planets & stars correctly then + you will need to use SimGear Ephemeris code */ + //bodies[0] -> setDeclination ( bodies[0] -> getDeclination() - 0.01*SGD_DEGREES_TO_RADIANS ); + //bodies[1] -> setDeclination ( bodies[1] -> getDeclination() - 0.01*SGD_DEGREES_TO_RADIANS ); + + /* update sky */ + + sky -> repositionFlat ( campos.xyz, 0, dt ); + sky -> modifyVisibility ( campos.xyz[SG_Z], (float)dt ); + + double sol_angle = bodies[0]->getAngle(); + double sky_brightness = (1.0 + cos(sol_angle))/2.0; // 0.0 - 1.0 + double scene_brightness = pow(sky_brightness,0.5); + + /* set sky color */ + sky_color[0] = base_sky_color[0] * (float)sky_brightness; + sky_color[1] = base_sky_color[1] * (float)sky_brightness; + sky_color[2] = base_sky_color[2] * (float)sky_brightness; + sky_color[3] = base_sky_color[3]; + + /* set cloud and fog color */ + cloud_color[0] = fog_color[0] = base_fog_color[0] * (float)sky_brightness; + cloud_color[1] = fog_color[1] = base_fog_color[1] * (float)sky_brightness; + cloud_color[2] = fog_color[2] = base_fog_color[2] * (float)sky_brightness; + cloud_color[3] = fog_color[3] = base_fog_color[3]; + + /* repaint the sky */ + sky -> repaint ( sky_color, fog_color, cloud_color, sol_angle, nplanets, planet_data, nstars, star_data ); + + /* set light source */ + sgCoord solpos; + bodies[0] -> getPosition ( & solpos ); + ssgGetLight ( 0 ) -> setPosition ( solpos.xyz ) ; + + scene_ambient[0] = base_ambient[0] * (float)scene_brightness; + scene_ambient[1] = base_ambient[1] * (float)scene_brightness; + scene_ambient[2] = base_ambient[2] * (float)scene_brightness; + scene_ambient[3] = 1.0; + + scene_diffuse[0] = base_diffuse[0] * (float)scene_brightness; + scene_diffuse[1] = base_diffuse[1] * (float)scene_brightness; + scene_diffuse[2] = base_diffuse[2] * (float)scene_brightness; + scene_diffuse[3] = 1.0; + + scene_specular[0] = base_specular[0] * (float)scene_brightness; + scene_specular[1] = base_specular[1] * (float)scene_brightness; + scene_specular[2] = base_specular[2] * (float)scene_brightness; + scene_specular[3] = 1.0; + // GL_LIGHT_MODEL_AMBIENT has a default non-zero value so if + // we only update GL_AMBIENT for our lights we will never get + // a completely dark scene. So, we set GL_LIGHT_MODEL_AMBIENT + // explicitely to black. + glLightModelfv( GL_LIGHT_MODEL_AMBIENT, black ); + ssgGetLight( 0 ) -> setColour( GL_AMBIENT, scene_ambient ); + ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, scene_diffuse ); + ssgGetLight( 0 ) -> setColour( GL_SPECULAR, scene_specular ); +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + +/* + The GLUT keyboard/mouse events +*/ + +static void keyboard ( unsigned char key, int x, int y ) +{ + keypress = key; + + if ( key == 'f' || key == 'F' ) + /* fullscreen */ + glutFullScreen(); + else if ( key == 'w' || key == 'W' ) + /* wireframe */ + wireframe = TRUE ; + else if ( key == 's' || key == 'S' ) + /* fill */ + wireframe = FALSE ; + else if ( key == ' ' ) + /* show/hide gui */ + displayGUI = ! displayGUI ; + else if ( key == 'q' || key == 'Q' || key == 27 /* esc */ ) + /* quit */ + exit ( 0 ) ; +} + +static void keyboard_up ( unsigned char key, int x, int y ) +{ + keypress = 0; +} + +static void special ( int key, int x, int y ) +{ + mods = glutGetModifiers(); + special_key = key; +} + +static void special_up ( int key, int x, int y ) +{ + mods = glutGetModifiers(); + if ( special_key == key ) + special_key = 0; +} + +static void motionfn ( int x, int y ) +{ + if ( displayGUI ) + puMouse ( x, y ) ; +} + +static void mousefn ( int button, int updown, int x, int y ) +{ + if ( displayGUI ) + puMouse ( button, updown, x, y ) ; +} + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + update_motion () ; + + glClearColor ( fog_color[0], fog_color[1], fog_color[2], fog_color[3] ) ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + if ( wireframe ) + glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE ) ; + else + glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ) ; + + /* Adjust fog based on visibility (when in clouds) */ + GLfloat fog_exp2_density = (float)sqrt_m_log01 / sky->getVisibility(); + glEnable( GL_FOG ); + glFogf ( GL_FOG_DENSITY, fog_exp2_density); + glFogi ( GL_FOG_MODE, GL_EXP2 ); + glFogfv ( GL_FOG_COLOR, fog_color ); + + /* Draw scene */ + + // we need a white diffuse light for the phase of the moon + ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, white ); + + sky -> preDraw ( ); + + // return to the desired diffuse color + ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, scene_diffuse ); + + ssgCullAndDraw ( scene ) ; + sky -> postDraw ( campos.xyz[SG_Z] ); /* altitude */ + + /* Draw gui */ + + glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ) ; + if ( displayGUI ) + puDisplay () ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Simple Scene Graph : Example Program." ; + fake_argv[2] = NULL ; + + /* Initialise GLUT */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + glutKeyboardUpFunc ( keyboard_up ) ; + glutSpecialFunc ( special ) ; + glutSpecialUpFunc ( special_up ) ; + glutMouseFunc ( mousefn ) ; + glutMotionFunc ( motionfn ) ; + + /* Initialise SSG */ + + puInit () ; + ssgInit () ; + + /* Some basic OpenGL setup */ + + glEnable ( GL_DEPTH_TEST ) ; + + /* Set up the viewing parameters */ + + ssgSetFOV ( 60, 45 ) ; + ssgSetNearFar ( 1, 200000 ) ; /* nb. skydome is scalable ... so far can be reduced */ + + /* Set up the Sun */ + + sgVec3 solposn ; + sgSetVec3 ( solposn, 0, 0, 0 ) ; + ssgGetLight ( 0 ) -> setPosition ( solposn ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* Set up the path to the data files */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* Setup teapot state */ + + teapot_state = new ssgSimpleState () ; + teapot_state -> setTexture ( "data/candy.rgb" ) ; + teapot_state -> enable ( GL_TEXTURE_2D ) ; + teapot_state -> setShadeModel ( GL_SMOOTH ) ; + teapot_state -> enable ( GL_CULL_FACE ) ; + teapot_state -> enable ( GL_BLEND ) ; + teapot_state -> enable ( GL_LIGHTING ) ; + teapot_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + teapot_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + teapot_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + teapot_state -> setShininess ( 2 ) ; + + /* Setup wave state */ + + wave_state = new ssgSimpleState () ; + wave_state -> setTexture ( "data/ocean.rgb" ) ; + wave_state -> enable ( GL_TEXTURE_2D ) ; + wave_state -> setShadeModel ( GL_SMOOTH ) ; + wave_state -> enable ( GL_CULL_FACE ) ; + wave_state -> enable ( GL_BLEND ) ; + wave_state -> enable ( GL_LIGHTING ) ; + wave_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + wave_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + wave_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + wave_state -> setShininess ( 5 ) ; + + wave_train.setSpeed ( 1.0f ) ; + wave_train.setLength ( 3.0f ) ; + wave_train.setLambda ( 0.5f ) ; + wave_train.setHeading ( 30.0f ) ; + wave_train.setWaveHeight ( 1.5f ) ; + + /* Create object */ + + tpt_obj = new ssgaTeapot ( 1000 ) ; + tpt_obj -> setSize ( 2 ) ; + tpt_obj -> setKidState ( teapot_state ) ; + tpt_obj -> regenerate () ; + + sgVec3 pos = {0, 0, 0}; + + wave_obj = new ssgaWaveSystem ( 4000 ) ; + wave_obj->setColour ( white ) ; + wave_obj->setSize ( 80000 ) ; /* todo: fix this ... prefer size 2000, + but wave system has recently changed and + no longer stretches on for 'infinity' :( */ + wave_obj->setTexScale ( 32, 32 ) ; + wave_obj->setCenter ( pos ) ; + wave_obj->setKidState ( wave_state ) ; + wave_obj->setWindSpeed ( 10.0f ) ; + wave_obj->setWaveTrain ( 0, &wave_train ) ; + + /* Build the scene graph */ + + star_data = new sgdVec3[nstars] ; + for ( int i = 0; i < nstars; i++ ) + { + star_data[i][0] = ssgaRandom() * SGD_PI ; + star_data[i][1] = ssgaRandom() * SGD_PI ; + star_data[i][2] = ssgaRandom() ; + } + + /* sky */ + sky = new ssgaSky ; + sky -> build ( 80000, 80000, nplanets, planet_data, nstars, star_data ); + /* sun */ + bodies[0] = sky -> addBody ( + NULL, // body texture + "data/halo.rgba", // halo texture + 5000, // size + 80000, // distance + true ); // is sun - dome painted based on this + bodies[0] -> setDeclination ( 20*SGD_DEGREES_TO_RADIANS ); + /* moon */ + bodies[1] = sky -> addBody ( + "data/moon.rgba", // body texture + NULL, // halo texture + 5000, // size + 80000 ); // distance + bodies[1] -> setDeclination ( 65*SGD_DEGREES_TO_RADIANS ); + /* clouds */ + clouds[0] = sky -> addCloud ( + "data/scattered.rgba", // texture + 80000, // span + 2000, // elevation, + 100, // thickness + 100 ); // transition + clouds[0] -> setSpeed ( 50 ) ; + clouds[0] -> setDirection ( 45 ) ; + clouds[1] = sky -> addCloud ( + "data/scattered.rgba", // texture + 80000, // span + 3000, // elevation, + 100, // thickness + 100 ); // transition + clouds[1] -> setSpeed ( 20 ) ; + clouds[1] -> setDirection ( 30 ) ; + clouds[2] = sky -> addCloud ( + "data/scattered.rgba", // texture + 80000, // span + 1000, // elevation, + 100, // thickness + 100 ); // transition + clouds[2] -> setSpeed ( 5 ) ; + clouds[2] -> setDirection ( 20 ) ; + + teapot = new ssgTransform ; + teapot -> addKid ( tpt_obj ) ; + + wave = new ssgTransform ; + wave -> addKid ( wave_obj ) ; + + scene = new ssgRoot ; + scene -> addKid ( teapot ) ; + scene -> addKid ( wave ) ; +} + + +static void init_gui () +{ + static puFont *sorority ; + static fntTexFont *fnt ; + + fnt = new fntTexFont () ; + fnt -> load ( "data/sorority.txf" ) ; + sorority = new puFont ( fnt, 12 ) ; + + puSetDefaultFonts ( *sorority, *sorority ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.2f, 0.5f, 0.2f, 0.5f ) ; + + puGroup *window_group = new puGroup ( 0, 0 ) ; + + bodyRADial = new puDial ( 150, GUI_BASE+90, 50 ) ; + bodyRADial->setWrap ( 1 ) ; + bodyRADial->setMaxValue ( 360 ) ; + bodyRADial->setMinValue ( 0 ) ; + bodyRADial->setStepSize ( 0.1f ) ; + bodyRADial->setCBMode ( PUSLIDER_ALWAYS ) ; + bodyRADial->setCallback ( bodyRADial_cb ) ; + bodyRADial->setLabel ( "Right Ascension" ) ; + bodyRADial->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + bodyRADial->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + bodyRADial->setLegendPlace ( PUPLACE_BOTTOM_CENTERED ) ; + + bodyDeclDial = new puDial ( 400, GUI_BASE+90, 50 ) ; + bodyDeclDial->setWrap ( 1 ) ; + bodyDeclDial->setMaxValue ( 360 ) ; + bodyDeclDial->setMinValue ( 0 ) ; + bodyDeclDial->setStepSize ( 0.1f ) ; + bodyDeclDial->setCBMode ( PUSLIDER_ALWAYS ) ; + bodyDeclDial->setCallback ( bodyDeclDial_cb ) ; + bodyDeclDial->setLabel ( "Declination" ) ; + bodyDeclDial->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + bodyDeclDial->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + bodyDeclDial->setLegendPlace ( PUPLACE_BOTTOM_CENTERED ) ; + + bodySelectBox = new puaSelectBox ( 150, GUI_BASE+150, 300, GUI_BASE+180, bodyNameList ) ; + bodySelectBox->setCallback ( bodySelectBox_cb ) ; + bodySelectBox->setCurrentItem ( 0 ) ; + bodySelectBox->setLabel ( "Edit Body" ) ; + bodySelectBox->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + bodySelectBox->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + /* Set everything up on the first time around */ + bodySelectBox_cb ( bodySelectBox ) ; + + cloudElevationSlider = new puSlider ( 150, GUI_BASE+20, 90, false, 20 ) ; + cloudElevationSlider->setMaxValue ( 10000.0f ) ; + cloudElevationSlider->setMinValue ( 100.0f ) ; + cloudElevationSlider->setStepSize ( 10.0f ) ; + cloudElevationSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + cloudElevationSlider->setCallback ( cloudElevationSlider_cb ) ; + cloudElevationSlider->setLabel ( "Cloud Elevation" ) ; + cloudElevationSlider->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + cloudElevationSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + cloudEnableButton = new puButton ( 150, GUI_BASE, " " ) ; + cloudEnableButton->setStyle ( PUSTYLE_RADIO ) ; + cloudEnableButton->setCallback ( cloudEnableButton_cb ) ; + cloudEnableButton->setLabel ( "Enable Cloud" ) ; + cloudEnableButton->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + cloudEnableButton->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + cloudSelectBox = new puaSelectBox ( 150, GUI_BASE+50, 300, GUI_BASE+80, cloudNameList ) ; + cloudSelectBox->setCallback ( cloudSelectBox_cb ) ; + cloudSelectBox->setCurrentItem ( 0 ) ; + cloudSelectBox->setLabel ( "Edit Cloud" ) ; + cloudSelectBox->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + cloudSelectBox->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + /* Set everything up on the first time around */ + cloudSelectBox_cb ( cloudSelectBox ) ; + + window_group->close () ; +} + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + init_gui () ; + glutMainLoop () ; + return 0 ; +} diff --git a/examples/src/ssg/sky/sky.dsp b/examples/src/ssg/sky/sky.dsp new file mode 100644 index 0000000..0e3440d --- /dev/null +++ b/examples/src/ssg/sky/sky.dsp @@ -0,0 +1,87 @@ +# Microsoft Developer Studio Project File - Name="sky" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=sky - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sky.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sky.mak" CFG="sky - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sky - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "sky - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sky - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg.lib ssg.lib pui.lib puAux.lib fnt.lib ul.lib ssgAux.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "sky - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg_d.lib ssg_d.lib pui_d.lib puAux_d.lib fnt_d.lib ul_d.lib ssgAux_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "sky - Win32 Release" +# Name "sky - Win32 Debug" +# Begin Source File + +SOURCE=.\sky.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/state_test/Makefile.am b/examples/src/ssg/state_test/Makefile.am new file mode 100644 index 0000000..7ed262f --- /dev/null +++ b/examples/src/ssg/state_test/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SSG + +noinst_PROGRAMS = stest + +stest_SOURCES = stest.cxx + +stest_LDADD = -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = herring.inta stest.dsp + diff --git a/examples/src/ssg/state_test/herring.inta b/examples/src/ssg/state_test/herring.inta new file mode 100644 index 0000000000000000000000000000000000000000..0a54bee1cf056b892de1c3cb33d870ed783fb302 GIT binary patch literal 16896 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 TfC4{=_7dus;a@8BZ(I34yUPvp literal 0 HcmV?d00001 diff --git a/examples/src/ssg/state_test/stest.cxx b/examples/src/ssg/state_test/stest.cxx new file mode 100644 index 0000000..9ddaeeb --- /dev/null +++ b/examples/src/ssg/state_test/stest.cxx @@ -0,0 +1,323 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: stest.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +static ssgRoot *scene = NULL ; +static ssgTransform *object = NULL ; +static ssgStateSelector *big_red_switch = NULL ; + +/* + Something to make some interesting motion + for both Object and the camera. +*/ + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + sgCoord objpos ; + + /* + Spin Object, make the camera pan sinusoidally left and right + */ + + sgSetCoord ( & campos, 0.0f, -5.0f, 1.0f, + /*25.0 * sin(frameno/100.0)*/0.0f, 0.0f, 0.0f ) ; + sgSetCoord ( & objpos, 0.0f, 0.0f, 0.0f, (float)frameno, 0.0f, 0.0f ) ; + + ssgSetCamera ( & campos ) ; + object -> setTransform ( & objpos ) ; + + if ( frameno % 600 == 0 ) + big_red_switch -> selectStep ( 0 ) ; + + if ( frameno % 600 == 300 ) + big_red_switch -> selectStep ( 1 ) ; +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard ( unsigned char, int, int ) +{ + exit ( 0 ) ; +} + + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "Viewer" ; + fake_argv[1] = "Simple Scene Graph : Viewer Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + + +static ssgEntity *make_herring () +{ + big_red_switch = new ssgStateSelector ( 2 ) ; + + ssgSimpleState *st0 = new ssgSimpleState ; + ssgSimpleState *st1 = new ssgSimpleState ; + + /* Texture ... or not */ + + st0 -> setTexture ( "herring.inta" ) ; + st0 -> enable ( GL_TEXTURE_2D ) ; + + st1 -> disable ( GL_TEXTURE_2D ) ; + + /* Other Stuff */ + + st0 -> setShadeModel ( GL_SMOOTH ) ; + st1 -> setShadeModel ( GL_SMOOTH ) ; + + st0 -> enable ( GL_CULL_FACE ) ; + st1 -> enable ( GL_CULL_FACE ) ; + + st0 -> setTranslucent () ; + st1 -> setTranslucent () ; + + st0 -> enable ( GL_ALPHA_TEST ) ; + st1 -> enable ( GL_ALPHA_TEST ) ; + + st0 -> setAlphaClamp ( 0.9f ) ; + st1 -> setAlphaClamp ( 0.9f ) ; + + st0 -> enable ( GL_BLEND ) ; + st1 -> enable ( GL_BLEND ) ; + + /* Lighting (where the magic happens) */ + + /* A RED herring (because the polygon is red). */ + st0 -> enable ( GL_LIGHTING ) ; + st0 -> enable ( GL_COLOR_MATERIAL ) ; + st0 -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + st0 -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + st0 -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + st0 -> setShininess ( 0 ) ; + + /* Hopefully, a GREEN rectangle. */ + st1 -> enable ( GL_LIGHTING ) ; + st1 -> disable ( GL_COLOR_MATERIAL ) ; + st1 -> setMaterial ( GL_AMBIENT , 0, 1, 0, 1 ) ; + st1 -> setMaterial ( GL_DIFFUSE , 0, 1, 0, 1 ) ; + st1 -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + st1 -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + st1 -> setShininess ( 0 ) ; + + /* Set up the selector */ + + big_red_switch -> setStep ( 0, st0 ) ; + big_red_switch -> setStep ( 1, st1 ) ; + + big_red_switch -> selectStep ( 0 ) ; /* Textured! */ + + sgVec4 *scolors = new sgVec4 [ 1 ] ; + sgVec3 *scoords = new sgVec3 [ 6 ] ; + sgVec2 *tcoords = new sgVec2 [ 6 ] ; + sgVec3 *snorms = new sgVec3 [ 1 ] ; + + /* RED! */ + sgSetVec4 ( scolors [ 0 ], 1.0f, 0.0f, 0.0f, 1.0f ) ; + sgSetVec3 ( snorms[0], 0.0f, 1.0f, 0.0f ) ; + + sgSetVec3(scoords[0], -1.0, 0.0, 0.0 ) ; + sgSetVec3(scoords[1], 1.0, 0.0, 0.0 ) ; + sgSetVec3(scoords[2], -1.0, 0.0, 1.0 ) ; + sgSetVec3(scoords[3], 1.0, 0.0, 1.0 ) ; + sgSetVec3(scoords[4], -1.0, 0.0, 0.0 ) ; + sgSetVec3(scoords[5], 1.0, 0.0, 0.0 ) ; + + sgSetVec2(tcoords[0], 0.0, 0.0 ) ; + sgSetVec2(tcoords[1], 1.0, 0.0 ) ; + sgSetVec2(tcoords[2], 0.0, 1.0 ) ; + sgSetVec2(tcoords[3], 1.0, 1.0 ) ; + sgSetVec2(tcoords[4], 0.0, 0.0 ) ; + sgSetVec2(tcoords[5], 1.0, 0.0 ) ; + + ssgLeaf *gset = new ssgVTable ( GL_TRIANGLE_STRIP, + 6, scoords, + 1, snorms, + 6, tcoords, + 1, scolors ) ; + + gset -> setState ( big_red_switch ) ; + + return gset ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "." ) ; + ssgTexturePath ( "." ) ; + + /* + Create a root node - and a transform to position + the object beneath that (in the tree that is). + */ + + scene = new ssgRoot ; + object = new ssgTransform ; + + /* + Load the models - optimise them a bit + and then add them into the scene. + */ + + ssgEntity *obj_obj = make_herring () ; + + object -> addKid ( obj_obj ) ; + + //ssgFlatten ( obj_obj ) ; + //ssgStripify ( object ) ; + scene -> addKid ( object ) ; + scene -> print () ; +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + + + diff --git a/examples/src/ssg/state_test/stest.dsp b/examples/src/ssg/state_test/stest.dsp new file mode 100644 index 0000000..3ed5698 --- /dev/null +++ b/examples/src/ssg/state_test/stest.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="stest" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stest - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stest.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stest.mak" CFG="stest - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stest - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stest - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stest - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"stest.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "stest - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib Shlwapi.lib /nologo /subsystem:console /debug /machine:I386 /out:"stest.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "stest - Win32 Release" +# Name "stest - Win32 Debug" +# Begin Source File + +SOURCE=.\stest.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/tux/Makefile.am b/examples/src/ssg/tux/Makefile.am new file mode 100644 index 0000000..329c474 --- /dev/null +++ b/examples/src/ssg/tux/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_SSG + +noinst_PROGRAMS = tux_example WavingFlagDemo + +tux_example_SOURCES = tux_example.cxx +WavingFlagDemo_SOURCES = WavingFlag.cxx WavingFlagDemo.cxx WavingFlag.h + +tux_example_LDADD = -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) +WavingFlagDemo_LDADD = -lplibssg -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = tux_example.dsp WavingFlag.dsp + diff --git a/examples/src/ssg/tux/WavingFlag.cxx b/examples/src/ssg/tux/WavingFlag.cxx new file mode 100644 index 0000000..682c97d --- /dev/null +++ b/examples/src/ssg/tux/WavingFlag.cxx @@ -0,0 +1,203 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: WavingFlag.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#ifdef WIN32 +# include +#else +# include +#endif + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include "WavingFlag.h" + +WavingFlag::WavingFlag( sgVec4 color, char *texture1, char *texture2, int s ) +{ + size = s; + int numVertices = size*size; + branch = new ssgBranch(); + branch->clrTraversalMaskBits ( SSGTRAV_HOT ) ; + + colors = new ssgColourArray( numVertices ); + normals = new ssgNormalArray( numVertices ); + texCoords = new ssgTexCoordArray( numVertices ); + vertices = new ssgVertexArray( numVertices ); + int i; + for ( i = 0; i < numVertices; i++ ) + { + sgVec3 n = { 0.0, 0.0, 0.0 }; + sgVec2 t = { 0.0, 0.0 }; + colors->add( color ); + normals->add( n ); + texCoords->add( t ); + vertices->add( n ); + } + ssgSimpleState *state1 = new ssgSimpleState ; + if ( texture1 ) + { + state1->setTexture( texture1 ) ; + state1->enable( GL_TEXTURE_2D ) ; + } + else + { + state1->disable( GL_TEXTURE_2D ) ; + } + state1->enable( GL_LIGHTING ) ; + state1->setShadeModel( GL_SMOOTH ) ; + state1->enable( GL_COLOR_MATERIAL ) ; + state1->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ) ; + state1->setMaterial( GL_EMISSION, 0.91f, 0.41f, 0.81f, .94f ) ; + state1->setMaterial( GL_SPECULAR, 0.91f, 0.41f, 0.81f, .94f ) ; + state1->setShininess( 0.3f ) ; + state1->setTranslucent() ; + state1->enable( GL_BLEND ) ; + state1->enable ( GL_ALPHA_TEST ) ; + state1->setAlphaClamp ( 0.0f ) ; + + ssgSimpleState *state2 = new ssgSimpleState ; + if ( texture2 ) + { + state2->setTexture( texture2 ) ; + state2->enable( GL_TEXTURE_2D ) ; + } + else + { + if ( texture1 ) + { + state2->setTexture( texture1 ) ; + state2->enable( GL_TEXTURE_2D ) ; + } + else + { + state2->disable( GL_TEXTURE_2D ) ; + } + } + state2->enable( GL_LIGHTING ) ; + state2->setShadeModel( GL_SMOOTH ) ; + state2->enable( GL_COLOR_MATERIAL ) ; + state2->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ) ; + state2->setMaterial( GL_EMISSION, 0.91f, 0.41f, 0.81f, .94f ) ; + state2->setMaterial( GL_SPECULAR, 0.91f, 0.41f, 0.81f, .94f ) ; + state2->setShininess( 0.3f ) ; + state2->setTranslucent() ; + state2->enable( GL_BLEND ) ; + state2->enable ( GL_ALPHA_TEST ) ; + state2->setAlphaClamp ( 0.0f ) ; + + branch = new ssgBranch; + int x,z; + for ( x = 0; x < size; x++ ) + { + ssgIndexArray *indices = new ssgIndexArray( size*2 ); // one side of the flag + ssgIndexArray *indices2 = new ssgIndexArray( size*2 ); // and the other side + for ( z = 0; z < size; z++ ) + { + int i = x*size + z; + + float *v = vertices->get( i ); + v[0] = (float)x/(size - 1.0f); + v[1] = 0.0; + v[2] = (float)z/(size - 1.0f); + + float *t = texCoords->get( i ); + sgSetVec2( t, v[0], v[2] ); + + indices->add( i ); + indices->add( i+size ); + indices2->add( i+size ); + indices2->add( i ); + } + if ( x < size-1 ) + { + ssgLeaf *l = new ssgVtxArray( GL_TRIANGLE_STRIP, + vertices, + normals, + texCoords, + colors, + indices ); + l->setState ( state1 ); + branch->addKid( l ); + ssgLeaf *l2 = new ssgVtxArray( GL_TRIANGLE_STRIP, + vertices, + normals, + texCoords, + colors, + indices2 ); + l2->setState ( state2 ); + branch->addKid( l2 ); + } + } +} + +WavingFlag::~WavingFlag() +{ +} + +void WavingFlag::animate( float time, float windVelocity ) +{ + if ( windVelocity < 3.0 ) windVelocity = 3.0; + if ( windVelocity > 25.0 ) windVelocity = 25.0; + float r = 0.6f/windVelocity; // half wave height + float w = windVelocity/2.0f; // angular acceleration + float k = windVelocity*3.0f; // wave number + float d = (1.0f-windVelocity/25.0f)*(SG_PI/2.0f); + + int x,z; + for ( z = size-1; z >= 0; z-- ) + { + for ( x = 1; x < size; x++ ) + { + int i = x*size + z; + + float *v = vertices->get( i ); + v[0] = ((float)x * (float)cos(d)) / (size - 1.0f); + float a = k*(v[0]) - w*time; // phase angle + v[1] = r * (float)cos( a ); + v[0] -= v[1]; + v[2] = ((float)z - (float)x * (float)sin(d)) / (size - 1.0f); + float *n = normals->get( i ); + n[0] = (float)cos(d); + n[1] = v[1]; + //d *= 1.002; + } + k *= 0.9f; + } + if ( d > SG_PI/2.0 ) printf ("Problems\n"); + branch -> dirtyBSphere () ; +} + +ssgEntity *WavingFlag::getObject() +{ + return branch; +} + diff --git a/examples/src/ssg/tux/WavingFlag.dsp b/examples/src/ssg/tux/WavingFlag.dsp new file mode 100644 index 0000000..3895f75 --- /dev/null +++ b/examples/src/ssg/tux/WavingFlag.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="WavingFlag" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=WavingFlag - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "WavingFlag.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "WavingFlag.mak" CFG="WavingFlag - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "WavingFlag - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "WavingFlag - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "WavingFlag - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib glut32.lib opengl32.lib glu32.lib /nologo /subsystem:console /machine:I386 /out:"WavingFlag.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "WavingFlag - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib glut32.lib opengl32.lib glu32.lib /nologo /subsystem:console /debug /machine:I386 /out:"WavingFlag.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "WavingFlag - Win32 Release" +# Name "WavingFlag - Win32 Debug" +# Begin Source File + +SOURCE=.\WavingFlag.cxx +# End Source File +# Begin Source File + +SOURCE=.\WavingFlag.h +# End Source File +# Begin Source File + +SOURCE=.\WavingFlagDemo.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/tux/WavingFlag.h b/examples/src/ssg/tux/WavingFlag.h new file mode 100644 index 0000000..97943c0 --- /dev/null +++ b/examples/src/ssg/tux/WavingFlag.h @@ -0,0 +1,44 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: WavingFlag.h 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#ifndef _WAVINGFLAG_ +#define _WAVINGFLAG_ + +class WavingFlag +{ +public: + WavingFlag( sgVec4 color, char *texture1=NULL, char *texture2=NULL, int s=5 ); + ~WavingFlag(); + void animate( float time, float windVelocity ); + ssgEntity *getObject(); +private: + ssgBranch *branch; + ssgColourArray *colors; + ssgNormalArray *normals; + ssgTexCoordArray *texCoords; + ssgVertexArray *vertices; + int size; +}; + +#endif diff --git a/examples/src/ssg/tux/WavingFlagDemo.cxx b/examples/src/ssg/tux/WavingFlagDemo.cxx new file mode 100644 index 0000000..8cea52e --- /dev/null +++ b/examples/src/ssg/tux/WavingFlagDemo.cxx @@ -0,0 +1,194 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: WavingFlagDemo.cxx 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#ifdef WIN32 +# include +#else +# include +#endif + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include "WavingFlag.h" + + +static WavingFlag* flag = NULL ; +static float flagTime = 0.0f ; +static ssgRoot* scene = NULL ; + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard ( unsigned char, int, int ) +{ + exit ( 0 ) ; +} + + + +static void idle () +{ + static int lastTime = 0; + int time = glutGet((GLenum)GLUT_ELAPSED_TIME); + if (time != lastTime) { + lastTime = time; + flagTime += 0.02f ; + } +} + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + sgCoord campos ; + sgSetCoord ( & campos, 0.0f, -3.0f, 0.0f, 0.0f, 0.0f, 0.0f ) ; + ssgSetCamera ( & campos ) ; + + flag -> animate( flagTime, 20 ); + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Waving Flag Example Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + glutIdleFunc ( idle ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* + Create a root node and the flag. + */ + + scene = new ssgRoot ; + + sgVec4 flagColor = { 1.0f, 0.0f, 0.0f, 1.0f }; + flag = new WavingFlag ( flagColor, "data/Penguin_body.rgb", "data/Penguin_body.rgb" ) ; + + scene -> addKid ( flag -> getObject() ) ; +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + diff --git a/examples/src/ssg/tux/data/Penguin_beak.rgb b/examples/src/ssg/tux/data/Penguin_beak.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b1e0e799dad81ab6bd43bea26320cc1867836989 GIT binary patch literal 11194 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/tux/data/Penguin_body.rgb b/examples/src/ssg/tux/data/Penguin_body.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/examples/src/ssg/tux/data/embossed_herring.bmp b/examples/src/ssg/tux/data/embossed_herring.bmp new file mode 100644 index 0000000000000000000000000000000000000000..795e5d1f7c86118caf32b989ac461972d6652b47 GIT binary patch literal 24630 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/tux/data/pedestal.ac b/examples/src/ssg/tux/data/pedestal.ac new file mode 100644 index 0000000..3c112e2 --- /dev/null +++ b/examples/src/ssg/tux/data/pedestal.ac @@ -0,0 +1,81 @@ +AC3Db +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0.502 0.502 0.502 shi 0 trans 0 +OBJECT world +kids 2 +OBJECT poly +name "box" +texture "wood.rgb" +texrep 5 5 +numvert 8 +-1.29615 -0.6 -1.29615 +-1 0 -1 +1 0 -1 +1.3 -0.6 -1.29615 +-1.29615 -0.6 1.3 +-1 0 1 +1 0 1 +1.3 -0.6 1.3 +numsurf 6 +SURF 0x0 +mat 0 +refs 4 +0 0 0 +1 0.114074 0.114074 +2 0.884445 0.114074 +3 1 0 +SURF 0x0 +mat 0 +refs 4 +6 0.884445 0.884445 +7 1 1 +3 1 0 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +7 1 1 +6 0.884445 0.884445 +5 0.114074 0.884445 +4 0 1 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +5 0.114074 0.884445 +1 0.114074 0.114074 +0 0 0 +SURF 0x0 +mat 0 +refs 4 +1 0.114074 0.114074 +5 0.114074 0.884445 +6 0.884445 0.884445 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +0 0 0 +3 1 0 +7 1 1 +kids 0 +OBJECT poly +name "rect" +loc -0.0500002 0 -2.5 +texture "embossed_herring.bmp" +texrep 20 30 +numvert 4 +-17.95 -0.7 -18.6 +17.95 -0.7 -18.6 +17.95 -0.7 18.6 +-17.95 -0.7 18.6 +numsurf 1 +SURF 0x0 +mat 1 +refs 4 +3 0 0 +2 1 0 +1 1 1 +0 0 1 +kids 0 diff --git a/examples/src/ssg/tux/data/pedestal.safe b/examples/src/ssg/tux/data/pedestal.safe new file mode 100644 index 0000000..3d613b9 --- /dev/null +++ b/examples/src/ssg/tux/data/pedestal.safe @@ -0,0 +1,81 @@ +AC3Db +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0.502 0.502 0.502 shi 0 trans 0 +OBJECT world +kids 2 +OBJECT poly +name "box" +texture "wood.rgb" +texrep 5 5 +numvert 8 +-1.29615 -0.6 -1.29615 +-1 0 -1 +1 0 -1 +1.3 -0.6 -1.29615 +-1.29615 -0.6 1.3 +-1 0 1 +1 0 1 +1.3 -0.6 1.3 +numsurf 6 +SURF 0x0 +mat 0 +refs 4 +0 0 0 +1 0.114074 0.114074 +2 0.884445 0.114074 +3 1 0 +SURF 0x0 +mat 0 +refs 4 +6 0.884445 0.884445 +7 1 1 +3 1 0 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +7 1 1 +6 0.884445 0.884445 +5 0.114074 0.884445 +4 0 1 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +5 0.114074 0.884445 +1 0.114074 0.114074 +0 0 0 +SURF 0x0 +mat 0 +refs 4 +1 0.114074 0.114074 +5 0.114074 0.884445 +6 0.884445 0.884445 +2 0.884445 0.114074 +SURF 0x0 +mat 0 +refs 4 +4 0 1 +0 0 0 +3 1 0 +7 1 1 +kids 0 +OBJECT poly +name "rect" +loc -0.0500002 0 -2.5 +texture "embossed_herring.rgb" +texrep 20 30 +numvert 4 +-17.95 -0.7 -18.6 +17.95 -0.7 -18.6 +17.95 -0.7 18.6 +-17.95 -0.7 18.6 +numsurf 1 +SURF 0x0 +mat 1 +refs 4 +3 0 0 +2 1 0 +1 1 1 +0 0 1 +kids 0 diff --git a/examples/src/ssg/tux/data/tuxedo.ac b/examples/src/ssg/tux/data/tuxedo.ac new file mode 100644 index 0000000..12e4754 --- /dev/null +++ b/examples/src/ssg/tux/data/tuxedo.ac @@ -0,0 +1,1695 @@ +AC3Db +MATERIAL "" rgb 0 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "" rgb 1 0.702 0 amb 1 0.702 0 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +MATERIAL "" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0 0 0 shi 0 trans 0 +OBJECT world +kids 1 +OBJECT group +name "Tuxedo.T.Penguin" +kids 3 +OBJECT poly +name "tuxfeet" +loc 0.0203744 -0.479517 0.101666 +numvert 14 +-0.0744405 0.473224 0.119889 +0.100559 0.473224 -0.200111 +0.175559 0.473224 0.199889 +0.250559 0.473224 -0.200111 +0.425559 0.473224 0.119889 +0.250559 0.523224 -0.200111 +0.100559 0.523224 -0.200111 +-0.425559 0.476776 0.120111 +-0.25056 0.476776 -0.199889 +-0.17556 0.476776 0.200111 +-0.100559 0.476776 -0.199889 +0.0744405 0.476776 0.120111 +-0.100559 0.526776 -0.199889 +-0.25056 0.526776 -0.199889 +numsurf 12 +SURF 0x0 +mat 1 +refs 3 +0 0 0.1 +1 0.9 0 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +3 1 0.1 +2 0 0.8 +1 0.9 0 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +3 1 0.1 +4 0.5 1 +SURF 0x0 +mat 1 +refs 3 +4 0.5 1 +5 1 0.1 +2 0 0.8 +SURF 0x0 +mat 1 +refs 3 +6 0.9 0 +2 0 0.8 +5 1 0.1 +SURF 0x0 +mat 1 +refs 3 +2 0 0.8 +6 0.9 0 +0 0 0.1 +SURF 0x0 +mat 1 +refs 3 +7 0 0.1 +8 0.9 0 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +10 1 0.1 +9 0 0.8 +8 0.9 0 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +10 1 0.1 +11 0.5 1 +SURF 0x0 +mat 1 +refs 3 +11 0.5 1 +12 1 0.1 +9 0 0.8 +SURF 0x0 +mat 1 +refs 3 +13 0.9 0 +9 0 0.8 +12 1 0.1 +SURF 0x0 +mat 1 +refs 3 +9 0 0.8 +13 0.9 0 +7 0 0.1 +kids 0 +OBJECT poly +name "tuxbody" +loc 0.0124385 0.107258 -0.188333 +texture "Penguin_body.rgb" +numvert 115 +0.097524 1.07 -0.0756895 +-0.0202512 1.11 0 +0.119749 1.07 0 +0.0379068 1.07 -0.127348 +-0.0401753 1.07 -0.138575 +-0.111932 1.07 -0.105805 +-0.154581 1.07 -0.0394426 +-0.154581 1.07 0.0394426 +-0.111932 1.07 0.105805 +-0.0401753 1.07 0.138575 +0.0379068 1.07 0.127348 +0.097524 1.07 0.0756895 +0.184749 0.975 0 +0.152205 0.975 -0.110831 +0.064909 0.975 -0.186475 +-0.0494259 0.975 -0.202913 +-0.154498 0.975 -0.154929 +-0.216947 0.975 -0.057755 +-0.216947 0.975 0.057755 +-0.154498 0.975 0.154929 +-0.0494259 0.975 0.202913 +0.064909 0.975 0.186475 +0.152205 0.975 0.110831 +0.199749 0.68 0 +0.164825 0.68 -0.118941 +0.07114 0.68 -0.200119 +-0.0515605 0.68 -0.217761 +-0.16432 0.68 -0.166265 +-0.231339 0.68 -0.061981 +-0.231339 0.68 0.061981 +-0.16432 0.68 0.166265 +-0.0515605 0.68 0.217761 +0.07114 0.68 0.200119 +0.164825 0.68 0.118941 +0.284749 0.555 0 +0.236331 0.555 -0.164896 +0.10645 0.555 -0.277438 +-0.0636575 0.555 -0.301895 +-0.219984 0.555 -0.230503 +-0.312897 0.555 -0.0859285 +-0.312897 0.555 0.0859285 +-0.219984 0.555 0.230503 +-0.0636575 0.555 0.301895 +0.10645 0.555 0.277438 +0.236331 0.555 0.164896 +0.369749 0.43 0 +0.307837 0.43 -0.21085 +0.14176 0.43 -0.354757 +-0.075754 0.43 -0.38603 +-0.275647 0.43 -0.294742 +-0.394453 0.43 -0.109876 +-0.394453 0.43 0.109876 +-0.275647 0.43 0.294742 +-0.075754 0.43 0.38603 +0.14176 0.43 0.354757 +0.307837 0.43 0.21085 +0.414749 0.305 0 +0.345694 0.305 -0.235179 +0.160454 0.305 -0.39569 +-0.0821585 0.305 -0.430573 +-0.305115 0.305 -0.328751 +-0.437631 0.305 -0.122553 +-0.437631 0.305 0.122553 +-0.305115 0.305 0.328751 +-0.0821585 0.305 0.430573 +0.160454 0.305 0.39569 +0.345694 0.305 0.235179 +0.409749 0.185 0 +0.341488 0.185 -0.232476 +0.158377 0.185 -0.391142 +-0.081447 0.185 -0.425623 +-0.301841 0.185 -0.324973 +-0.432833 0.185 -0.121145 +-0.432833 0.185 0.121145 +-0.301841 0.185 0.324973 +-0.081447 0.185 0.425623 +0.158377 0.185 0.391142 +0.341488 0.185 0.232476 +0.354748 2.23517e-08 0 +0.295219 2.23517e-08 -0.202741 +0.135529 2.23517e-08 -0.341112 +-0.0736195 2.23517e-08 -0.371183 +-0.265824 2.23517e-08 -0.283406 +-0.380061 2.23517e-08 -0.10565 +-0.380061 2.23517e-08 0.10565 +-0.265824 2.23517e-08 0.283406 +-0.0736195 2.23517e-08 0.371183 +0.135529 2.23517e-08 0.341112 +0.295219 2.23517e-08 0.202741 +0.154748 -0.1 0 +0.126968 -0.1 -0.094612 +0.052446 -0.1 -0.159185 +-0.0451563 -0.1 -0.173219 +-0.134852 -0.1 -0.132256 +-0.188163 -0.1 -0.0493032 +-0.188163 -0.1 0.0493032 +-0.134852 -0.1 0.132256 +-0.0451564 -0.1 0.173219 +0.052446 -0.1 0.159185 +0.126968 -0.1 0.094612 +0.211872 0.567025 0.1 +0.211872 0.567025 -0.1 +0.316872 0.532025 0.1 +0.316872 0.532025 -0.1 +0.456872 0.392025 0.1 +0.456872 0.392025 -0.1 +0.56187 0.217025 0 +-0.211873 0.58637 0.1 +-0.211873 0.58637 -0.1 +-0.316873 0.55137 0.1 +-0.316873 0.55137 -0.1 +-0.456872 0.41137 0.1 +-0.456872 0.41137 -0.1 +-0.56187 0.23637 0 +-0.0202512 -0.11 0 +numsurf 218 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +1 -1 1 +2 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +1 -1 1 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +1 -1 1 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +1 -1 1 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +1 -1 1 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +1 -0.5 1 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +1 -0.5 1 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +1 -0.5 1 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +1 -0.5 1 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +1 -0.5 1 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -0.380214 0.967213 +1 -0.5 1 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +2 -1 0.967213 +12 -1 0.889344 +0 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +0 -1 0.967213 +12 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +0 -1 0.967213 +13 -1 0.889344 +3 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +3 -1 0.967213 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +3 -1 0.967213 +14 -1 0.889344 +4 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +4 -1 0.967213 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +4 -1 0.967213 +15 -1 0.889344 +5 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +5 -1 0.967213 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +5 -1 0.967213 +16 -1 0.889344 +6 -1 0.967213 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +6 -1 0.967213 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +6 -1 0.967213 +17 -1 0.889344 +7 -0.614934 0.967213 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +7 -0.614934 0.967213 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +7 -0.614934 0.967213 +18 -0.668296 0.889344 +8 -0.578443 0.967213 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +8 -0.578443 0.967213 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +8 -0.578443 0.967213 +19 -0.614863 0.889344 +9 -0.517047 0.967213 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +9 -0.517047 0.967213 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +9 -0.517047 0.967213 +20 -0.524962 0.889344 +10 -0.450239 0.967213 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +10 -0.450239 0.967213 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +10 -0.450239 0.967213 +21 -0.427136 0.889344 +11 -0.39923 0.967213 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +11 -0.39923 0.967213 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +11 -0.39923 0.967213 +22 -0.352443 0.889344 +2 -0.380214 0.967213 +SURF 0x10 +mat 2 +refs 3 +12 -0.324599 0.889344 +2 -0.380214 0.967213 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +12 -1 0.889344 +23 -1 0.647541 +13 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +13 -1 0.889344 +23 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +13 -1 0.889344 +24 -1 0.647541 +14 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +14 -1 0.889344 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +14 -1 0.889344 +25 -1 0.647541 +15 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +15 -1 0.889344 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +15 -1 0.889344 +26 -1 0.647541 +16 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +16 -1 0.889344 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +16 -1 0.889344 +27 -1 0.647541 +17 -1 0.889344 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +17 -1 0.889344 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +17 -1 0.889344 +28 -1 0.647541 +18 -0.668296 0.889344 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +18 -0.668296 0.889344 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +18 -0.668296 0.889344 +29 -0.68061 0.647541 +19 -0.614863 0.889344 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +19 -0.614863 0.889344 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +19 -0.614863 0.889344 +30 -0.623268 0.647541 +20 -0.524962 0.889344 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +20 -0.524962 0.889344 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +20 -0.524962 0.889344 +31 -0.526789 0.647541 +21 -0.427136 0.889344 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +21 -0.427136 0.889344 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +21 -0.427136 0.889344 +32 -0.421804 0.647541 +22 -0.352443 0.889344 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +22 -0.352443 0.889344 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +22 -0.352443 0.889344 +33 -0.341646 0.647541 +12 -0.324599 0.889344 +SURF 0x10 +mat 2 +refs 3 +23 -0.311765 0.647541 +12 -0.324599 0.889344 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +23 -1 0.647541 +34 -1 0.545082 +24 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +24 -1 0.647541 +34 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +24 -1 0.647541 +35 -1 0.545082 +25 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +25 -1 0.647541 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +25 -1 0.647541 +36 -1 0.545082 +26 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +26 -1 0.647541 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +26 -1 0.647541 +37 -1 0.545082 +27 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +27 -1 0.647541 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +27 -1 0.647541 +38 -1 0.545082 +28 -1 0.647541 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +28 -1 0.647541 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +28 -1 0.647541 +39 -1 0.545082 +29 -0.68061 0.647541 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +29 -0.68061 0.647541 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +29 -0.68061 0.647541 +40 -0.750392 0.545082 +30 -0.623268 0.647541 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +30 -0.623268 0.647541 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +30 -0.623268 0.647541 +41 -0.670894 0.545082 +31 -0.526789 0.647541 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +31 -0.526789 0.647541 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +31 -0.526789 0.647541 +42 -0.537139 0.545082 +32 -0.421804 0.647541 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +32 -0.421804 0.647541 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +32 -0.421804 0.647541 +43 -0.391592 0.545082 +33 -0.341646 0.647541 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +33 -0.341646 0.647541 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +33 -0.341646 0.647541 +44 -0.280464 0.545082 +23 -0.311765 0.647541 +SURF 0x10 +mat 2 +refs 3 +34 -0.239037 0.545082 +23 -0.311765 0.647541 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +34 -1 0.545082 +45 -1 0.442623 +35 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +35 -1 0.545082 +45 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +35 -1 0.545082 +46 -1 0.442623 +36 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +36 -1 0.545082 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +36 -1 0.545082 +47 -1 0.442623 +37 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +37 -1 0.545082 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +37 -1 0.545082 +48 -1 0.442623 +38 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +38 -1 0.545082 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +38 -1 0.545082 +49 -1 0.442623 +39 -1 0.545082 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +39 -1 0.545082 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +39 -1 0.545082 +50 -1 0.442623 +40 -0.750392 0.545082 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +40 -0.750392 0.545082 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +40 -0.750392 0.545082 +51 -0.820173 0.442623 +41 -0.670894 0.545082 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +41 -0.670894 0.545082 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +41 -0.670894 0.545082 +52 -0.71852 0.442623 +42 -0.537139 0.545082 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +42 -0.537139 0.545082 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +42 -0.537139 0.545082 +53 -0.547489 0.442623 +43 -0.391592 0.545082 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +43 -0.391592 0.545082 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +43 -0.391592 0.545082 +54 -0.36138 0.442623 +44 -0.280464 0.545082 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +44 -0.280464 0.545082 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +44 -0.280464 0.545082 +55 -0.219282 0.442623 +34 -0.239037 0.545082 +SURF 0x10 +mat 2 +refs 3 +45 -0.16631 0.442623 +34 -0.239037 0.545082 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +45 -1 0.442623 +56 -1 0.340164 +46 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +46 -1 0.442623 +56 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +46 -1 0.442623 +57 -1 0.340164 +47 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +47 -1 0.442623 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +47 -1 0.442623 +58 -1 0.340164 +48 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +48 -1 0.442623 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +48 -1 0.442623 +59 -1 0.340164 +49 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +49 -1 0.442623 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +49 -1 0.442623 +60 -1 0.340164 +50 -1 0.442623 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +50 -1 0.442623 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +50 -1 0.442623 +61 -1 0.340164 +51 -0.820173 0.442623 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +51 -0.820173 0.442623 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +51 -0.820173 0.442623 +62 -0.857116 0.340164 +52 -0.71852 0.442623 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +52 -0.71852 0.442623 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +52 -0.71852 0.442623 +63 -0.743734 0.340164 +53 -0.547489 0.442623 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +53 -0.547489 0.442623 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +53 -0.547489 0.442623 +64 -0.552969 0.340164 +54 -0.36138 0.442623 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +54 -0.36138 0.442623 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +54 -0.36138 0.442623 +65 -0.345386 0.340164 +55 -0.219282 0.442623 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +55 -0.219282 0.442623 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +55 -0.219282 0.442623 +66 -0.186892 0.340164 +45 -0.16631 0.442623 +SURF 0x10 +mat 2 +refs 3 +56 -0.127807 0.340164 +45 -0.16631 0.442623 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +56 -1 0.340164 +67 -1 0.241803 +57 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +57 -1 0.340164 +67 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +57 -1 0.340164 +68 -1 0.241803 +58 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +58 -1 0.340164 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +58 -1 0.340164 +69 -1 0.241803 +59 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +59 -1 0.340164 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +59 -1 0.340164 +70 -1 0.241803 +60 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +60 -1 0.340164 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +60 -1 0.340164 +71 -1 0.241803 +61 -1 0.340164 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +61 -1 0.340164 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +61 -1 0.340164 +72 -1 0.241803 +62 -0.857116 0.340164 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +62 -0.857116 0.340164 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +62 -0.857116 0.340164 +73 -0.853011 0.241803 +63 -0.743734 0.340164 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +63 -0.743734 0.340164 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +63 -0.743734 0.340164 +74 -0.740933 0.241803 +64 -0.552969 0.340164 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +64 -0.552969 0.340164 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +64 -0.552969 0.340164 +75 -0.55236 0.241803 +65 -0.345386 0.340164 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +65 -0.345386 0.340164 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +65 -0.345386 0.340164 +76 -0.347163 0.241803 +66 -0.186892 0.340164 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +66 -0.186892 0.340164 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +66 -0.186892 0.340164 +77 -0.190491 0.241803 +56 -0.127807 0.340164 +SURF 0x10 +mat 2 +refs 3 +67 -0.132086 0.241803 +56 -0.127807 0.340164 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +67 -1 0.241803 +78 -1 0.0901639 +68 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +68 -1 0.241803 +78 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +68 -1 0.241803 +79 -1 0.0901639 +69 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +69 -1 0.241803 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +69 -1 0.241803 +80 -1 0.0901639 +70 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +70 -1 0.241803 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +70 -1 0.241803 +81 -1 0.0901639 +71 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +71 -1 0.241803 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +71 -1 0.241803 +82 -1 0.0901639 +72 -1 0.241803 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +72 -1 0.241803 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +72 -1 0.241803 +83 -1 0.0901639 +73 -0.853011 0.241803 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +73 -0.853011 0.241803 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +73 -0.853011 0.241803 +84 -0.807859 0.0901639 +74 -0.740933 0.241803 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +74 -0.740933 0.241803 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +74 -0.740933 0.241803 +85 -0.710116 0.0901639 +75 -0.55236 0.241803 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +75 -0.55236 0.241803 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +75 -0.55236 0.241803 +86 -0.545663 0.0901639 +76 -0.347163 0.241803 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +76 -0.347163 0.241803 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +76 -0.347163 0.241803 +87 -0.366712 0.0901639 +77 -0.190491 0.241803 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +77 -0.190491 0.241803 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +77 -0.190491 0.241803 +88 -0.230079 0.0901639 +67 -0.132086 0.241803 +SURF 0x10 +mat 2 +refs 3 +78 -0.179144 0.0901639 +67 -0.132086 0.241803 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +78 -1 0.0901639 +89 -1 0.00819672 +79 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +79 -1 0.0901639 +89 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +79 -1 0.0901639 +90 -1 0.00819672 +80 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +80 -1 0.0901639 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +80 -1 0.0901639 +91 -1 0.00819672 +81 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +81 -1 0.0901639 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +81 -1 0.0901639 +92 -1 0.00819672 +82 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +82 -1 0.0901639 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +82 -1 0.0901639 +93 -1 0.00819672 +83 -1 0.0901639 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +83 -1 0.0901639 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +83 -1 0.0901639 +94 -1 0.00819672 +84 -0.807859 0.0901639 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +84 -0.807859 0.0901639 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +84 -0.807859 0.0901639 +95 -0.643667 0.00819672 +85 -0.710116 0.0901639 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +85 -0.710116 0.0901639 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +85 -0.710116 0.0901639 +96 -0.598054 0.00819672 +86 -0.545663 0.0901639 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +86 -0.545663 0.0901639 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +86 -0.545663 0.0901639 +97 -0.521309 0.00819672 +87 -0.366712 0.0901639 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +87 -0.366712 0.0901639 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +87 -0.366712 0.0901639 +98 -0.437799 0.00819672 +88 -0.230079 0.0901639 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +88 -0.230079 0.0901639 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +88 -0.230079 0.0901639 +99 -0.374037 0.00819672 +78 -0.179144 0.0901639 +SURF 0x10 +mat 2 +refs 3 +89 -0.350267 0.00819672 +78 -0.179144 0.0901639 +99 -0.374037 0.00819672 +SURF 0x10 +mat 0 +refs 3 +100 0 0 +101 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +102 0 0 +101 0 0 +SURF 0x10 +mat 0 +refs 3 +102 0 0 +103 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +105 0 0 +104 0 0 +103 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 2 +105 0 0 +106 0 0 +SURF 0x10 +mat 0 +refs 3 +106 0 0 +105 0 0 +104 0 0 +SURF 0x10 +mat 0 +refs 3 +103 0 0 +104 0 0 +105 0 0 +SURF 0x10 +mat 0 +refs 3 +104 0 0 +103 0 0 +102 0 0 +SURF 0x10 +mat 0 +refs 3 +107 0 0 +108 0 0 +109 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +109 0 0 +108 0 0 +SURF 0x10 +mat 0 +refs 3 +109 0 0 +110 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +112 0 0 +111 0 0 +110 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 2 +112 0 0 +113 0 0 +SURF 0x10 +mat 0 +refs 3 +113 0 0 +112 0 0 +111 0 0 +SURF 0x10 +mat 0 +refs 3 +110 0 0 +111 0 0 +112 0 0 +SURF 0x10 +mat 0 +refs 3 +111 0 0 +110 0 0 +109 0 0 +SURF 0x10 +mat 2 +refs 3 +89 -1 0.00819672 +114 -1 0 +90 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +90 -1 0.00819672 +114 -1 0 +91 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +91 -1 0.00819672 +114 -1 0 +92 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +92 -1 0.00819672 +114 -1 0 +93 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +93 -1 0.00819672 +114 -1 0 +94 -1 0.00819672 +SURF 0x10 +mat 2 +refs 3 +94 -1 0.00819672 +114 -1 0 +95 -0.643667 0.00819672 +SURF 0x10 +mat 2 +refs 3 +95 -0.643667 0.00819672 +114 -0.5 0 +96 -0.598054 0.00819672 +SURF 0x10 +mat 2 +refs 3 +96 -0.598054 0.00819672 +114 -0.5 0 +97 -0.521309 0.00819672 +SURF 0x10 +mat 2 +refs 3 +97 -0.521309 0.00819672 +114 -0.5 0 +98 -0.437799 0.00819672 +SURF 0x10 +mat 2 +refs 3 +98 -0.437799 0.00819672 +114 -0.5 0 +99 -0.374037 0.00819672 +SURF 0x10 +mat 2 +refs 3 +99 -0.374037 0.00819672 +114 -0.5 0 +89 -0.350267 0.00819672 +kids 0 +OBJECT poly +name "tuxbeak" +loc -0.0328128 0.372259 0.086667 +texture "Penguin_beak.rgb" +numvert 17 +0.0565685 0.514 0.1 +1.11759e-08 0.5 0.125 +0.08 0.5 0.1 +1.11759e-08 0.528 0.1 +-0.0565685 0.514 0.1 +-0.08 0.5 0.1 +-0.0565685 0.486 0.1 +1.11759e-08 0.472 0.1 +0.0565685 0.486 0.1 +0.16 0.5 -0.125 +0.113137 0.556 -0.125 +1.11759e-08 0.612 -0.125 +-0.113137 0.556 -0.125 +-0.16 0.5 -0.125 +-0.113137 0.444 -0.125 +1.11759e-08 0.388 -0.125 +0.113137 0.444 -0.125 +numsurf 24 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +1 -0.5 -0.5 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +1 -0.5 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +1 -0.5 -0.5 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +1 -0.5 -0.5 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +1 -0.5 -0.5 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +1 -0.5 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +1 -0.5 -0.5 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +1 -0.5 -0.5 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +2 -0.277778 -0.5 +9 -0.0555556 -0.5 +0 -0.342865 -0.342865 +SURF 0x10 +mat 2 +refs 3 +10 -0.18573 -0.18573 +0 -0.342865 -0.342865 +9 -0.0555556 -0.5 +SURF 0x10 +mat 2 +refs 3 +0 -0.342865 -0.342865 +10 -0.18573 -0.18573 +3 -0.5 -0.277778 +SURF 0x10 +mat 2 +refs 3 +11 -0.5 -0.0555556 +3 -0.5 -0.277778 +10 -0.18573 -0.18573 +SURF 0x10 +mat 2 +refs 3 +3 -0.5 -0.277778 +11 -0.5 -0.0555556 +4 -0.657135 -0.342865 +SURF 0x10 +mat 2 +refs 3 +12 -0.81427 -0.18573 +4 -0.657135 -0.342865 +11 -0.5 -0.0555556 +SURF 0x10 +mat 2 +refs 3 +4 -0.657135 -0.342865 +12 -0.81427 -0.18573 +5 -0.722222 -0.5 +SURF 0x10 +mat 2 +refs 3 +13 -0.944444 -0.5 +5 -0.722222 -0.5 +12 -0.81427 -0.18573 +SURF 0x10 +mat 2 +refs 3 +5 -0.722222 -0.5 +13 -0.944444 -0.5 +6 -0.657135 -0.657135 +SURF 0x10 +mat 2 +refs 3 +14 -0.81427 -0.81427 +6 -0.657135 -0.657135 +13 -0.944444 -0.5 +SURF 0x10 +mat 2 +refs 3 +6 -0.657135 -0.657135 +14 -0.81427 -0.81427 +7 -0.5 -0.722222 +SURF 0x10 +mat 2 +refs 3 +15 -0.5 -0.944444 +7 -0.5 -0.722222 +14 -0.81427 -0.81427 +SURF 0x10 +mat 2 +refs 3 +7 -0.5 -0.722222 +15 -0.5 -0.944444 +8 -0.342865 -0.657135 +SURF 0x10 +mat 2 +refs 3 +16 -0.18573 -0.81427 +8 -0.342865 -0.657135 +15 -0.5 -0.944444 +SURF 0x10 +mat 2 +refs 3 +8 -0.342865 -0.657135 +16 -0.18573 -0.81427 +2 -0.277778 -0.5 +SURF 0x10 +mat 2 +refs 3 +9 -0.0555556 -0.5 +2 -0.277778 -0.5 +16 -0.18573 -0.81427 +kids 0 diff --git a/examples/src/ssg/tux/data/wood.rgb b/examples/src/ssg/tux/data/wood.rgb new file mode 100644 index 0000000000000000000000000000000000000000..3094af0a59e91cefe26d47e4531e06440d145876 GIT binary patch literal 11453 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 Awg3PC literal 0 HcmV?d00001 diff --git a/examples/src/ssg/tux/tux_example.cxx b/examples/src/ssg/tux/tux_example.cxx new file mode 100644 index 0000000..d767ad7 --- /dev/null +++ b/examples/src/ssg/tux/tux_example.cxx @@ -0,0 +1,228 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: tux_example.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +static ssgRoot *scene = NULL ; +static ssgTransform *penguin = NULL ; +static ssgTransform *pedestal = NULL ; + + +/* + Something to make some interesting motion + for both Tux and the camera. +*/ + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + sgCoord tuxpos ; + + /* + Spin Tux, make the camera pan sinusoidally left and right + */ + + sgSetCoord ( & campos, 0.0f, -5.0f, 1.0f, 25.0f * (float)sin(frameno/100.0), 0.0f, 0.0f ) ; + sgSetCoord ( & tuxpos, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, frameno/100.0f ) ; + + ssgSetCamera ( & campos ) ; + penguin -> setTransform ( & tuxpos ) ; +} + + + +/* + The GLUT window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard ( unsigned char, int, int ) +{ + exit ( 0 ) ; +} + + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + ssgCullAndDraw ( scene ) ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + + + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "ssgExample" ; + fake_argv[1] = "Simple Scene Graph : Example Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInitWindowPosition ( 0, 0 ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInit ( &fake_argc, fake_argv ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( redraw ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.5f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Set up the path to the data files + */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* + Create a root node - and a transform to position + the pedestal - and another to position the penguin + beneath that (in the tree that is). + */ + + scene = new ssgRoot ; + pedestal = new ssgTransform ; + penguin = new ssgTransform ; + + /* + Load the models - optimise them a bit + and then add them into the scene. + */ + + ssgEntity *ped_obj = ssgLoadAC ( "pedestal.ac" ) ; + ssgEntity *tux_obj = ssgLoadAC ( "tuxedo.ac" ) ; + + penguin -> addKid ( tux_obj ) ; + pedestal -> addKid ( ped_obj ) ; + + ssgFlatten ( tux_obj ) ; + ssgFlatten ( ped_obj ) ; + ssgStripify ( penguin ) ; + ssgStripify ( pedestal ) ; + + pedestal -> addKid ( penguin ) ; + scene -> addKid ( pedestal ) ; +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + glutMainLoop () ; + return 0 ; +} + + + diff --git a/examples/src/ssg/tux/tux_example.dsp b/examples/src/ssg/tux/tux_example.dsp new file mode 100644 index 0000000..4615389 --- /dev/null +++ b/examples/src/ssg/tux/tux_example.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="tux_example" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tux_example - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tux_example.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tux_example.mak" CFG="tux_example - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tux_example - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tux_example - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tux_example - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"tux_example.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "tux_example - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"tux_example.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "tux_example - Win32 Release" +# Name "tux_example - Win32 Debug" +# Begin Source File + +SOURCE=.\tux_example.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/tween_test/Makefile.am b/examples/src/ssg/tween_test/Makefile.am new file mode 100644 index 0000000..dcfaafd --- /dev/null +++ b/examples/src/ssg/tween_test/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SSG + +noinst_PROGRAMS = tween_test + +tween_test_SOURCES = tween_test.cxx + +tween_test_LDADD = -lplibssgaux -lplibssg -lplibsg -lplibpw -lplibul $(OGL_LIBS) + +endif + +EXTRA_DIST = tween_test.dsp + diff --git a/examples/src/ssg/tween_test/tween_test.cxx b/examples/src/ssg/tween_test/tween_test.cxx new file mode 100644 index 0000000..b9f23cc --- /dev/null +++ b/examples/src/ssg/tween_test/tween_test.cxx @@ -0,0 +1,255 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: tween_test.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include + +static ssgRoot *scene = NULL ; +static ssgTweenController *tween_ctrl = NULL ; + + +/* + Something to make some interesting motion +*/ + +static void update_motion () +{ + static int frameno = 0 ; + + frameno++ ; + + sgCoord campos ; + + /* + Make the camera pan sinusoidally left and right + morphing the sphere/spike as we go. + */ + + tween_ctrl -> selectBank ( (float)fabs(sin(frameno/100.0)) ) ; + + sgSetCoord ( & campos, 0.0f, -5.0f, 0.0f, 25.0f * (float)sin(frameno/100.0), 0.0f, 0.0f ) ; + ssgSetCamera ( & campos ) ; +} + + +/* + The GLUT redraw event +*/ + +static void redraw () +{ + glClearColor ( 0.5f, 0.1f, 0.1f, 1.0 ) ; + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + update_motion () ; + + ssgCullAndDraw ( scene ) ; +} + + +static void init_graphics () +{ + pwInit ( 100, 100, 640, 480, false, "PLIB Window Demo", true, 0 ) ; + pwSetCallbacks ( NULL, NULL, NULL, NULL, NULL ) ; + + /* + Initialise SSG + */ + + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + + ssgSetFOV ( 60.0f, 0.0f ) ; + ssgSetNearFar ( 1.0f, 700.0f ) ; + + /* + Set up the Sun. + */ + + sgVec4 black = { 0,0,0,1 } ; + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 0.2f, -0.0f, 0.5f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + ssgGetLight ( 0 ) -> setColour ( GL_SPECULAR, black ) ; +} + + +/* + Load a simple database +*/ + +static void load_database () +{ + /* + Create a root node with a Tween controller beneath it. + */ + + scene = new ssgRoot ; + tween_ctrl = new ssgTweenController ; + scene -> addKid ( tween_ctrl ) ; + + /* + Construct a sphere (which we'll use as a template + and then discard + */ + + ssgaSphere *sp = new ssgaSphere ( 100 ) ; + + /* + For each ssgVtxTable within the sphere, generate + an ssgTween node + */ + + for ( int i = 0 ; i < sp -> getNumKids () ; i++ ) + { + ssgVtxTable *vt = (ssgVtxTable *)( sp -> getKid ( i ) ) ; + + assert ( vt -> isAKindOf ( ssgTypeVtxTable () ) ) ; + + /* + Make two new sets of vertex array. + */ + + ssgVertexArray *v0 = new ssgVertexArray ; + ssgNormalArray *n0 = new ssgNormalArray ; + ssgTexCoordArray *t0 = new ssgTexCoordArray ; + ssgColourArray *c0 = new ssgColourArray ; + + ssgVertexArray *v1 = new ssgVertexArray ; + ssgNormalArray *n1 = new ssgNormalArray ; + ssgTexCoordArray *t1 = new ssgTexCoordArray ; + ssgColourArray *c1 = new ssgColourArray ; + + /* + For every vertex in the sphere's VtxTable... + */ + + for ( int i = 0 ; i < vt -> getNumVertices () ; i++ ) + { + /* + Copy the sphere's vertex (unmolested) into + the vertex arrays that are destined to become + bank zero. + */ + + v0 -> add ( vt -> getVertex (i) ) ; + n0 -> add ( vt -> getNormal (i) ) ; + t0 -> add ( vt -> getTexCoord (i) ) ; + c0 -> add ( vt -> getColour (i) ) ; + + /* + For bank one, make some vertices half + the usual size - and others twice the + usual size. This is a spikey ball. + */ + + sgVec3 vx ; + sgVec4 co ; + + sgCopyVec3 ( vx, vt -> getVertex (i) ) ; + sgScaleVec3 ( vx, (i&7) ? 0.5f : 2.0f ) ; + + /* + Put random colours on the vertices. + */ + + sgSetVec4 ( co, (float)(rand()&0xFF)/255.0f, + (float)(rand()&0xFF)/255.0f, + (float)(rand()&0xFF)/255.0f, 1.0f ) ; + + /* + Cheat and make the normals and texture coords be + the same as the sphere. + */ + + v1 -> add ( vx ) ; + n1 -> add ( vt -> getNormal (i) ) ; + t1 -> add ( vt -> getTexCoord (i) ) ; + c1 -> add ( co ) ; + } + + /* + For each VtxTable in the sphere, create a + Tween node that morphs between the sphere + and the spikey sphere. + */ + + ssgTween *tw = new ssgTween ( GL_TRIANGLE_STRIP ) ; + tw -> newBank ( v0, n0, t0, c0 ) ; + tw -> newBank ( v1, n1, t1, c1 ) ; + + /* + Add it into the tween controller + (although there could be a lot of hierarchy + between the two if you wanted that) + */ + + tween_ctrl -> addKid ( tw ) ; + } +} + + + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + + while ( 1 ) + { + ulMilliSecondSleep ( 16 ) ; + redraw () ; + pwSwapBuffers () ; + } + + return 0 ; +} + diff --git a/examples/src/ssg/tween_test/tween_test.dsp b/examples/src/ssg/tween_test/tween_test.dsp new file mode 100644 index 0000000..490a3b5 --- /dev/null +++ b/examples/src/ssg/tween_test/tween_test.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="tween_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tween_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tween_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tween_test.mak" CFG="tween_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tween_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tween_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tween_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg.lib sg.lib ul.lib pw.lib winmm.lib opengl32.lib glu32.lib glut32.lib ssgAux.lib /nologo /subsystem:console /machine:I386 /out:"tween_test.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "tween_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib ssgAux_d.lib sg_d.lib ul_d.lib pw_d.lib winmm.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"tween_test.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "tween_test - Win32 Release" +# Name "tween_test - Win32 Debug" +# Begin Source File + +SOURCE=.\tween_test.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/viewer/Makefile.am b/examples/src/ssg/viewer/Makefile.am new file mode 100644 index 0000000..8550a6e --- /dev/null +++ b/examples/src/ssg/viewer/Makefile.am @@ -0,0 +1,17 @@ +if BUILD_SSG + +noinst_PROGRAMS = viewer pview + +viewer_SOURCES = viewer.cxx + +pview_SOURCES = pview.cxx + +viewer_LDADD = -lplibssg -lplibpuaux -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) \ + $(OGL_LIBS) + +pview_LDADD = -lplibssg -lplibpu -lplibfnt -lplibsg -lplibul $(GLUT_LIBS) $(OGL_LIBS) + +endif + +EXTRA_DIST = viewer.dsp + diff --git a/examples/src/ssg/viewer/data/NOTES b/examples/src/ssg/viewer/data/NOTES new file mode 100644 index 0000000..e5b4ddb --- /dev/null +++ b/examples/src/ssg/viewer/data/NOTES @@ -0,0 +1,10 @@ +This directory contains sample VRML scenes made at the IICM. +Distribute them freely as long as you mention their origin. +They are also available at http://hgiicm.tu-graz.ac.at/C3d. + +Most of the scenes were created with Wavefont's Advanced Visualizer +and converted with scripts into SDF, the (old) native format of the +Harmony 3D scene viewer. VRweb was used to convert these files into +VRML. + +mpichler@iicm.tu-graz.ac.at diff --git a/examples/src/ssg/viewer/data/Pbody.rgb b/examples/src/ssg/viewer/data/Pbody.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/Penguin_beak.rgb b/examples/src/ssg/viewer/data/Penguin_beak.rgb new file mode 100644 index 0000000000000000000000000000000000000000..b1e0e799dad81ab6bd43bea26320cc1867836989 GIT binary patch literal 11194 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 literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/Penguin_body.rgb b/examples/src/ssg/viewer/data/Penguin_body.rgb new file mode 100644 index 0000000000000000000000000000000000000000..1ba0f4b037da6c851281413c27407ffc81822710 GIT binary patch literal 18495 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 Q)}PZGkXsqmH+Ag)0GP4qQUCw| literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/animated.ASE b/examples/src/ssg/viewer/data/animated.ASE new file mode 100644 index 0000000..b4ad7a8 --- /dev/null +++ b/examples/src/ssg/viewer/data/animated.ASE @@ -0,0 +1,299 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Sun Oct 24 23:45:37 2004" +*SCENE { + *SCENE_FILENAME "" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 100 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 2 + *MATERIAL 0 { + *MATERIAL_NAME "Brown" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5020 0.5020 0.5020 + *MATERIAL_DIFFUSE 0.5020 0.5020 0.5020 + *MATERIAL_SPECULAR 0.5020 0.5020 0.5020 + *MATERIAL_SHINE 0.2500 + *MATERIAL_SHINESTRENGTH 0.0500 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #11" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "brown.bmp" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 1 { + *MATERIAL_NAME "Grey" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5020 0.5020 0.5020 + *MATERIAL_DIFFUSE 0.5020 0.5020 0.5020 + *MATERIAL_SPECULAR 0.5020 0.5020 0.5020 + *MATERIAL_SHINE 0.2500 + *MATERIAL_SHINESTRENGTH 0.0500 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #12" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "gray.bmp" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } +} +*GEOMOBJECT { + *NODE_NAME "Box01" + *NODE_TM { + *NODE_NAME "Box01" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 0.0000 0.0000 + *TM_ROW1 0.0000 1.0000 0.0000 + *TM_ROW2 0.0000 0.0000 1.0000 + *TM_ROW3 0.0000 0.0000 0.0000 + *TM_POS 0.0000 0.0000 0.0000 + *TM_ROTAXIS 0.0000 0.0000 0.0000 + *TM_ROTANGLE 0.0000 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 8 + *MESH_NUMFACES 12 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -5.0000 -5.0000 0.0000 + *MESH_VERTEX 1 5.0000 -5.0000 0.0000 + *MESH_VERTEX 2 -5.0000 5.0000 0.0000 + *MESH_VERTEX 3 5.0000 5.0000 0.0000 + *MESH_VERTEX 4 -5.0000 -5.0000 2.0000 + *MESH_VERTEX 5 5.0000 -5.0000 2.0000 + *MESH_VERTEX 6 -5.0000 5.0000 2.0000 + *MESH_VERTEX 7 5.0000 5.0000 2.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 5 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 5 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 5 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 6 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 3 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 7 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 7 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 7 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 8 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 6 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 9 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 6 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 7 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 2 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 0 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 4 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 11 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 4 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 6 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 2 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *WIREFRAME_COLOR 0.5255 0.0235 0.0235 +} +*GEOMOBJECT { + *NODE_NAME "Box02" + *NODE_TM { + *NODE_NAME "Box02" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 0.0000 0.0000 + *TM_ROW1 0.0000 1.0000 0.0000 + *TM_ROW2 0.0000 0.0000 1.0000 + *TM_ROW3 5.0000 0.0000 0.0000 + *TM_POS 5.0000 0.0000 0.0000 + *TM_ROTAXIS 0.0000 0.0000 0.0000 + *TM_ROTANGLE 0.0000 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 8 + *MESH_NUMFACES 12 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0000 -5.0000 0.0000 + *MESH_VERTEX 1 15.0000 -5.0000 0.0000 + *MESH_VERTEX 2 5.0000 5.0000 0.0000 + *MESH_VERTEX 3 15.0000 5.0000 0.0000 + *MESH_VERTEX 4 5.0000 -5.0000 2.0000 + *MESH_VERTEX 5 15.0000 -5.0000 2.0000 + *MESH_VERTEX 6 5.0000 5.0000 2.0000 + *MESH_VERTEX 7 15.0000 5.0000 2.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 5 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 5 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 5 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 6 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 3 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 7 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 7 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 7 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 8 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 6 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 9 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 6 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 7 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 2 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 0 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 4 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 11 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 4 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 6 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 2 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *TM_ANIMATION { + *NODE_NAME "Box02" + *CONTROL_ROT_TCB { + *CONTROL_TCB_ROT_KEY 0 1.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 + *CONTROL_TCB_ROT_KEY 1600 0.0000 -1.0000 -0.0000 0.3491 0.0000 0.0000 0.0000 0.0000 0.0000 + } + } + *WIREFRAME_COLOR 0.2235 0.0314 0.5333 +} diff --git a/examples/src/ssg/viewer/data/ant_vr.wrl b/examples/src/ssg/viewer/data/ant_vr.wrl new file mode 100644 index 0000000..9a7d554 --- /dev/null +++ b/examples/src/ssg/viewer/data/ant_vr.wrl @@ -0,0 +1,571 @@ +#VRML V1.0 ascii + +Separator { + + DEF SceneInfo Info { + string "Converted by wcvt2pov v2.6c and the datafat munger" + } + DEF RearBody Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.08 0.17 0.70, +-0.08 0.01 0.70, +-0.24 -0.15 0.52, +-0.24 0.34 0.52, +0.08 0.01 0.70, +0.24 -0.15 0.52, +0.08 0.17 0.70, +0.24 0.34 0.52, +-0.21 -0.12 0.19, +-0.21 0.31 0.19, +0.21 -0.12 0.19, +0.21 0.31 0.19, +-0.08 0.01 0, +-0.08 0.17 0, +0.08 0.01 0, +0.08 0.17 0, +-0 0.09 0.70, +-0 0.09 0 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +3, 2, 0, -1, +5, 4, 1, -1, +2, 5, 1, -1, +7, 6, 4, -1, +5, 7, 4, -1, +3, 0, 6, -1, +7, 3, 6, -1, +8, 2, 3, -1, +9, 8, 3, -1, +10, 5, 2, -1, +8, 10, 2, -1, +11, 7, 5, -1, +10, 11, 5, -1, +9, 3, 7, -1, +11, 9, 7, -1, +12, 8, 9, -1, +13, 12, 9, -1, +14, 10, 8, -1, +12, 14, 8, -1, +15, 11, 10, -1, +14, 15, 10, -1, +13, 9, 11, -1, +15, 13, 11, -1, +16, 0, 1, -1, +16, 1, 4, -1, +16, 4, 6, -1, +16, 6, 0, -1, +17, 12, 13, -1, +17, 14, 12, -1, +17, 15, 14, -1, +17, 13, 15, -1 + ] + } + } + DEF MidBody Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.10 -0 -0.29, +-0.10 0.19 -0.29, +-0.13 0.22 -0.14, +-0.13 -0.03 -0.14, +0.09 0.19 -0.29, +0.13 0.22 -0.14, +0.09 -0 -0.29, +0.13 -0.03 -0.14, +-0.10 0.19 0, +-0.10 -0 0, +0.09 0.19 0, +0.09 -0 0, +-0 0.09 -0.29, +-0 0.09 0 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +3, 2, 0, -1, +5, 4, 1, -1, +2, 5, 1, -1, +7, 6, 4, -1, +5, 7, 4, -1, +3, 0, 6, -1, +7, 3, 6, -1, +8, 2, 3, -1, +9, 8, 3, -1, +10, 5, 2, -1, +8, 10, 2, -1, +11, 7, 5, -1, +10, 11, 5, -1, +9, 3, 7, -1, +11, 9, 7, -1, +12, 0, 1, -1, +12, 1, 4, -1, +12, 4, 6, -1, +12, 6, 0, -1, +13, 8, 9, -1, +13, 10, 8, -1, +13, 11, 10, -1, +13, 9, 11, -1 + ] + } + } + DEF Head Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.10 0.19 -0.30, +-0.15 0.25 -0.41, +0.10 0.19 -0.30, +0.15 0.25 -0.41, +-0.10 -0.01 -0.30, +-0.15 -0.06 -0.41, +0.10 -0.01 -0.30, +0.15 -0.06 -0.41, +-0.10 0.19 -0.52, +0.10 0.19 -0.52, +-0.05 0.02 -0.65, +0.05 0.02 -0.65, +-0 0.09 -0.30, +-0 0.11 -0.59 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +2, 3, 1, -1, +0, 5, 4, -1, +0, 1, 5, -1, +4, 7, 6, -1, +4, 5, 7, -1, +6, 3, 2, -1, +6, 7, 3, -1, +3, 8, 1, -1, +3, 9, 8, -1, +1, 10, 5, -1, +1, 8, 10, -1, +5, 11, 7, -1, +5, 10, 11, -1, +7, 9, 3, -1, +7, 11, 9, -1, +0, 12, 2, -1, +4, 12, 0, -1, +6, 12, 4, -1, +2, 12, 6, -1, +9, 13, 8, -1, +8, 13, 10, -1, +10, 13, 11, -1, +11, 13, 9, -1 + ] + } + } + DEF RightFrontLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.61 -0.34 -0.70, +-0.61 -0.34 -0.70, +-0.55 -0.09 -0.67, +-0.57 -0.09 -0.64, +-0.61 -0.34 -0.70, +-0.52 -0.11 -0.65, +-0.61 -0.34 -0.70, +-0.55 -0.11 -0.62, +-0.29 0.10 -0.46, +-0.32 0.10 -0.42, +-0.27 0.06 -0.45, +-0.30 0.06 -0.41, +-0.04 0.10 -0.25, +-0.07 0.10 -0.22, +-0.04 0.05 -0.25, +-0.07 0.05 -0.22, +-0.61 -0.34 -0.70, +-0.06 0.08 -0.24 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +3, 2, 0, -1, +5, 4, 1, -1, +2, 5, 1, -1, +7, 6, 4, -1, +5, 7, 4, -1, +3, 0, 6, -1, +7, 3, 6, -1, +8, 2, 3, -1, +9, 8, 3, -1, +10, 5, 2, -1, +8, 10, 2, -1, +11, 7, 5, -1, +10, 11, 5, -1, +9, 3, 7, -1, +11, 9, 7, -1, +12, 8, 9, -1, +13, 12, 9, -1, +14, 10, 8, -1, +12, 14, 8, -1, +15, 11, 10, -1, +14, 15, 10, -1, +13, 9, 11, -1, +15, 13, 11, -1, +16, 0, 1, -1, +16, 1, 4, -1, +16, 4, 6, -1, +16, 6, 0, -1, +17, 12, 13, -1, +17, 14, 12, -1, +17, 15, 14, -1, +17, 13, 15, -1 + ] + } + } + DEF Right3rdLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.80 -0.34 0.01, +-0.80 -0.34 0.01, +-0.73 -0.09 -0.01, +-0.72 -0.09 0.01, +-0.80 -0.34 0.01, +-0.70 -0.11 -0.01, +-0.79 -0.34 0.01, +-0.69 -0.11 0.01, +-0.40 0.10 -0.07, +-0.40 0.10 -0.03, +-0.38 0.06 -0.08, +-0.37 0.06 -0.03, +-0.09 0.10 -0.13, +-0.08 0.10 -0.08, +-0.09 0.05 -0.13, +-0.08 0.05 -0.08, +-0.80 -0.34 0.01, +-0.08 0.08 -0.11 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +3, 2, 0, -1, +5, 4, 1, -1, +2, 5, 1, -1, +7, 6, 4, -1, +5, 7, 4, -1, +3, 0, 6, -1, +7, 3, 6, -1, +8, 2, 3, -1, +9, 8, 3, -1, +10, 5, 2, -1, +8, 10, 2, -1, +11, 7, 5, -1, +10, 11, 5, -1, +9, 3, 7, -1, +11, 9, 7, -1, +12, 8, 9, -1, +13, 12, 9, -1, +14, 10, 8, -1, +12, 14, 8, -1, +15, 11, 10, -1, +14, 15, 10, -1, +13, 9, 11, -1, +15, 13, 11, -1, +16, 0, 1, -1, +16, 1, 4, -1, +16, 4, 6, -1, +16, 6, 0, -1, +17, 12, 13, -1, +17, 14, 12, -1, +17, 15, 14, -1, +17, 13, 15, -1 + ] + } + } + DEF RightRearLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +-0.62 -0.34 0.41, +-0.63 -0.34 0.41, +-0.58 -0.09 0.35, +-0.56 -0.09 0.38, +-0.62 -0.34 0.41, +-0.56 -0.11 0.33, +-0.62 -0.34 0.41, +-0.54 -0.11 0.36, +-0.33 0.10 0.13, +-0.30 0.10 0.17, +-0.31 0.06 0.12, +-0.28 0.06 0.15, +-0.09 0.10 -0.06, +-0.06 0.10 -0.03, +-0.09 0.05 -0.06, +-0.06 0.05 -0.03, +-0.62 -0.34 0.41, +-0.07 0.08 -0.05 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +3, 2, 0, -1, +5, 4, 1, -1, +2, 5, 1, -1, +7, 6, 4, -1, +5, 7, 4, -1, +3, 0, 6, -1, +7, 3, 6, -1, +8, 2, 3, -1, +9, 8, 3, -1, +10, 5, 2, -1, +8, 10, 2, -1, +11, 7, 5, -1, +10, 11, 5, -1, +9, 3, 7, -1, +11, 9, 7, -1, +12, 8, 9, -1, +13, 12, 9, -1, +14, 10, 8, -1, +12, 14, 8, -1, +15, 11, 10, -1, +14, 15, 10, -1, +13, 9, 11, -1, +15, 13, 11, -1, +16, 0, 1, -1, +16, 1, 4, -1, +16, 4, 6, -1, +16, 6, 0, -1, +17, 12, 13, -1, +17, 14, 12, -1, +17, 15, 14, -1, +17, 13, 15, -1 + ] + } + } + DEF LeftFrontLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +0.55 -0.09 -0.67, +0.61 -0.34 -0.70, +0.61 -0.34 -0.70, +0.57 -0.09 -0.64, +0.52 -0.11 -0.65, +0.61 -0.34 -0.70, +0.55 -0.11 -0.62, +0.61 -0.34 -0.70, +0.29 0.10 -0.46, +0.32 0.10 -0.42, +0.27 0.06 -0.45, +0.30 0.06 -0.41, +0.04 0.10 -0.25, +0.07 0.10 -0.22, +0.04 0.05 -0.25, +0.07 0.05 -0.22, +0.61 -0.34 -0.70, +0.06 0.08 -0.24 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +2, 0, 3, -1, +1, 5, 4, -1, +1, 4, 0, -1, +5, 7, 6, -1, +5, 6, 4, -1, +7, 2, 3, -1, +7, 3, 6, -1, +3, 0, 8, -1, +3, 8, 9, -1, +0, 4, 10, -1, +0, 10, 8, -1, +4, 6, 11, -1, +4, 11, 10, -1, +6, 3, 9, -1, +6, 9, 11, -1, +9, 8, 12, -1, +9, 12, 13, -1, +8, 10, 14, -1, +8, 14, 12, -1, +10, 11, 15, -1, +10, 15, 14, -1, +11, 9, 13, -1, +11, 13, 15, -1, +1, 2, 16, -1, +5, 1, 16, -1, +7, 5, 16, -1, +2, 7, 16, -1, +13, 12, 17, -1, +12, 14, 17, -1, +14, 15, 17, -1, +15, 13, 17, -1 + ] + } + } + DEF Left3rdLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +0.73 -0.09 -0.01, +0.80 -0.34 0.01, +0.80 -0.34 0.01, +0.72 -0.09 0.01, +0.70 -0.11 -0.01, +0.80 -0.34 0.01, +0.69 -0.11 0.01, +0.79 -0.34 0.01, +0.40 0.10 -0.07, +0.40 0.10 -0.03, +0.38 0.06 -0.08, +0.37 0.06 -0.03, +0.09 0.10 -0.13, +0.08 0.10 -0.08, +0.09 0.05 -0.13, +0.08 0.05 -0.08, +0.80 -0.34 0.01, +0.08 0.08 -0.11 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +2, 0, 3, -1, +1, 5, 4, -1, +1, 4, 0, -1, +5, 7, 6, -1, +5, 6, 4, -1, +7, 2, 3, -1, +7, 3, 6, -1, +3, 0, 8, -1, +3, 8, 9, -1, +0, 4, 10, -1, +0, 10, 8, -1, +4, 6, 11, -1, +4, 11, 10, -1, +6, 3, 9, -1, +6, 9, 11, -1, +9, 8, 12, -1, +9, 12, 13, -1, +8, 10, 14, -1, +8, 14, 12, -1, +10, 11, 15, -1, +10, 15, 14, -1, +11, 9, 13, -1, +11, 13, 15, -1, +1, 2, 16, -1, +5, 1, 16, -1, +7, 5, 16, -1, +2, 7, 16, -1, +13, 12, 17, -1, +12, 14, 17, -1, +14, 15, 17, -1, +15, 13, 17, -1 + ] + } + } + DEF LeftRearLeg Separator { + Material { + diffuseColor 0.36 0.20 0.09 + ambientColor 0.03 0.02 0 + } + Coordinate3 { + point [ +0.58 -0.09 0.35, +0.63 -0.34 0.41, +0.62 -0.34 0.41, +0.56 -0.09 0.38, +0.56 -0.11 0.33, +0.62 -0.34 0.41, +0.54 -0.11 0.36, +0.62 -0.34 0.41, +0.33 0.10 0.13, +0.30 0.10 0.17, +0.31 0.06 0.12, +0.28 0.06 0.15, +0.09 0.10 -0.06, +0.06 0.10 -0.03, +0.09 0.05 -0.06, +0.06 0.05 -0.03, +0.62 -0.34 0.41, +0.07 0.08 -0.05 + ] + } + + IndexedFaceSet { + coordIndex [ +2, 1, 0, -1, +2, 0, 3, -1, +1, 5, 4, -1, +1, 4, 0, -1, +5, 7, 6, -1, +5, 6, 4, -1, +7, 2, 3, -1, +7, 3, 6, -1, +3, 0, 8, -1, +3, 8, 9, -1, +0, 4, 10, -1, +0, 10, 8, -1, +4, 6, 11, -1, +4, 11, 10, -1, +6, 3, 9, -1, +6, 9, 11, -1, +9, 8, 12, -1, +9, 12, 13, -1, +8, 10, 14, -1, +8, 14, 12, -1, +10, 11, 15, -1, +10, 15, 14, -1, +11, 9, 13, -1, +11, 13, 15, -1, +1, 2, 16, -1, +5, 1, 16, -1, +7, 5, 16, -1, +2, 7, 16, -1, +13, 12, 17, -1, +12, 14, 17, -1, +14, 15, 17, -1, +15, 13, 17, -1 + ] + } + } + +} + diff --git a/examples/src/ssg/viewer/data/brown.bmp b/examples/src/ssg/viewer/data/brown.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d268439110e11ee77f4bb7f47f5f676f9260863e GIT binary patch literal 12342 zcmeIup%H*E3x z^O3W4O@IIa0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs L0RjXF{9NDx5Avji literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/default.txf b/examples/src/ssg/viewer/data/default.txf new file mode 100644 index 0000000000000000000000000000000000000000..dfcb4382414e39ded959285696b8531304ba8758 GIT binary patch literal 66708 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 CUeX=_ literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/door.ase b/examples/src/ssg/viewer/data/door.ase new file mode 100644 index 0000000..b30f7e4 --- /dev/null +++ b/examples/src/ssg/viewer/data/door.ase @@ -0,0 +1,2978 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Aug 08 13:10:03 2000" +*SCENE { + *SCENE_FILENAME "DragonsCaveDoor808c.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 12 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.5020 0.5020 0.5020 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 2 + *MATERIAL 0 { + *MATERIAL_NAME "Brown" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5020 0.5020 0.5020 + *MATERIAL_DIFFUSE 0.5020 0.5020 0.5020 + *MATERIAL_SPECULAR 0.5020 0.5020 0.5020 + *MATERIAL_SHINE 0.2500 + *MATERIAL_SHINESTRENGTH 0.0500 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #11" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "brown.bmp" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 1 { + *MATERIAL_NAME "Grey" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5020 0.5020 0.5020 + *MATERIAL_DIFFUSE 0.5020 0.5020 0.5020 + *MATERIAL_SPECULAR 0.5020 0.5020 0.5020 + *MATERIAL_SHINE 0.2500 + *MATERIAL_SHINESTRENGTH 0.0500 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #12" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "gray.bmp" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } +} +*GEOMOBJECT { + *NODE_NAME "FrontDoor" + *NODE_TM { + *NODE_NAME "FrontDoor" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 -0.0000 0.0000 + *TM_ROW1 -0.0000 0.0000 1.0000 + *TM_ROW2 -0.0000 -1.0000 0.0000 + *TM_ROW3 0.0299 1.4813 0.0034 + *TM_POS 0.0299 1.4813 0.0034 + *TM_ROTAXIS -1.0000 0.0000 0.0000 + *TM_ROTANGLE 1.5708 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -23.8849 -0.3323 + *MESH_VERTEX 1 4.7776 -23.8849 -0.3323 + *MESH_VERTEX 2 -4.7153 -22.3465 -1.5943 + *MESH_VERTEX 3 4.7776 -22.3465 -1.5943 + *MESH_VERTEX 4 -4.7153 -24.0885 -0.0147 + *MESH_VERTEX 5 -4.9945 -23.5694 -0.3323 + *MESH_VERTEX 6 -4.9945 -22.0310 -1.5942 + *MESH_VERTEX 7 -4.7153 -21.9659 -1.9097 + *MESH_VERTEX 8 4.7776 -21.9659 -1.9097 + *MESH_VERTEX 9 5.0568 -22.0310 -1.5943 + *MESH_VERTEX 10 5.0568 -23.5694 -0.3323 + *MESH_VERTEX 11 4.7776 -24.0885 -0.0147 + *MESH_VERTEX 12 -4.7153 1.3696 0.0001 + *MESH_VERTEX 13 -4.9945 1.3696 -0.3322 + *MESH_VERTEX 14 -4.9945 1.3696 -1.5942 + *MESH_VERTEX 15 -4.7153 1.3696 -1.9097 + *MESH_VERTEX 16 4.7776 1.3696 -1.9097 + *MESH_VERTEX 17 5.0568 1.3696 -1.5942 + *MESH_VERTEX 18 5.0568 1.3696 -0.3322 + *MESH_VERTEX 19 4.7776 1.3696 0.0001 + *MESH_VERTEX 20 4.7776 1.4947 -1.5943 + *MESH_VERTEX 21 -4.7153 1.4947 -1.5943 + *MESH_VERTEX 22 4.7776 1.4947 -0.3323 + *MESH_VERTEX 23 -4.7153 1.4947 -0.3323 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 0 + *MESH_ANIMATION { + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -23.8849 -0.3323 + *MESH_VERTEX 1 4.7776 -23.8849 -0.3323 + *MESH_VERTEX 2 -4.7153 -22.3465 -1.5943 + *MESH_VERTEX 3 4.7776 -22.3465 -1.5943 + *MESH_VERTEX 4 -4.7153 -24.0885 -0.0147 + *MESH_VERTEX 5 -4.9945 -23.5694 -0.3323 + *MESH_VERTEX 6 -4.9945 -22.0310 -1.5942 + *MESH_VERTEX 7 -4.7153 -21.9659 -1.9097 + *MESH_VERTEX 8 4.7776 -21.9659 -1.9097 + *MESH_VERTEX 9 5.0568 -22.0310 -1.5943 + *MESH_VERTEX 10 5.0568 -23.5694 -0.3323 + *MESH_VERTEX 11 4.7776 -24.0885 -0.0147 + *MESH_VERTEX 12 -4.7153 1.3696 0.0001 + *MESH_VERTEX 13 -4.9945 1.3696 -0.3322 + *MESH_VERTEX 14 -4.9945 1.3696 -1.5942 + *MESH_VERTEX 15 -4.7153 1.3696 -1.9097 + *MESH_VERTEX 16 4.7776 1.3696 -1.9097 + *MESH_VERTEX 17 5.0568 1.3696 -1.5942 + *MESH_VERTEX 18 5.0568 1.3696 -0.3322 + *MESH_VERTEX 19 4.7776 1.3696 0.0001 + *MESH_VERTEX 20 4.7776 1.4947 -1.5943 + *MESH_VERTEX 21 -4.7153 1.4947 -1.5943 + *MESH_VERTEX 22 4.7776 1.4947 -0.3323 + *MESH_VERTEX 23 -4.7153 1.4947 -0.3323 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 160 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -23.7810 2.3211 + *MESH_VERTEX 1 4.7776 -23.7810 2.3211 + *MESH_VERTEX 2 -4.7153 -22.3829 0.9052 + *MESH_VERTEX 3 4.7776 -22.3829 0.9052 + *MESH_VERTEX 4 -4.7153 -23.9504 2.6582 + *MESH_VERTEX 5 -4.9945 -23.4673 2.2881 + *MESH_VERTEX 6 -4.9945 -22.0692 0.8722 + *MESH_VERTEX 7 -4.7153 -22.0374 0.5516 + *MESH_VERTEX 8 4.7776 -22.0374 0.5516 + *MESH_VERTEX 9 5.0568 -22.0692 0.8722 + *MESH_VERTEX 10 5.0568 -23.4673 2.2881 + *MESH_VERTEX 11 4.7776 -23.9503 2.6582 + *MESH_VERTEX 12 -4.7153 1.3698 0.0118 + *MESH_VERTEX 13 -4.9945 1.3351 -0.3187 + *MESH_VERTEX 14 -4.9945 1.2032 -1.5738 + *MESH_VERTEX 15 -4.7153 1.1702 -1.8875 + *MESH_VERTEX 16 4.7776 1.1702 -1.8875 + *MESH_VERTEX 17 5.0568 1.2032 -1.5738 + *MESH_VERTEX 18 5.0568 1.3351 -0.3187 + *MESH_VERTEX 19 4.7776 1.3698 0.0118 + *MESH_VERTEX 20 4.7776 1.3276 -1.5869 + *MESH_VERTEX 21 -4.7153 1.3276 -1.5869 + *MESH_VERTEX 22 4.7776 1.4595 -0.3318 + *MESH_VERTEX 23 -4.7153 1.4595 -0.3318 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 320 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -22.7471 7.5228 + *MESH_VERTEX 1 4.7776 -22.7471 7.5228 + *MESH_VERTEX 2 -4.7153 -21.6740 5.8471 + *MESH_VERTEX 3 4.7776 -21.6740 5.8471 + *MESH_VERTEX 4 -4.7153 -22.8427 7.8877 + *MESH_VERTEX 5 -4.9945 -22.4471 7.4253 + *MESH_VERTEX 6 -4.9945 -21.3739 5.7496 + *MESH_VERTEX 7 -4.7153 -21.4095 5.4295 + *MESH_VERTEX 8 4.7776 -21.4095 5.4295 + *MESH_VERTEX 9 5.0568 -21.3739 5.7496 + *MESH_VERTEX 10 5.0568 -22.4471 7.4253 + *MESH_VERTEX 11 4.7776 -22.8427 7.8877 + *MESH_VERTEX 12 -4.7153 1.3740 0.0348 + *MESH_VERTEX 13 -4.9945 1.2714 -0.2812 + *MESH_VERTEX 14 -4.9945 0.8814 -1.4815 + *MESH_VERTEX 15 -4.7153 0.7839 -1.7815 + *MESH_VERTEX 16 4.7776 0.7839 -1.7815 + *MESH_VERTEX 17 5.0568 0.8814 -1.4815 + *MESH_VERTEX 18 5.0568 1.2714 -0.2812 + *MESH_VERTEX 19 4.7776 1.3740 0.0348 + *MESH_VERTEX 20 4.7776 1.0003 -1.5202 + *MESH_VERTEX 21 -4.7153 1.0003 -1.5202 + *MESH_VERTEX 22 4.7776 1.3903 -0.3200 + *MESH_VERTEX 23 -4.7153 1.3903 -0.3200 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 480 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -19.9754 13.5373 + *MESH_VERTEX 1 4.7776 -19.9754 13.5373 + *MESH_VERTEX 2 -4.7153 -19.3725 11.6410 + *MESH_VERTEX 3 4.7776 -19.3725 11.6410 + *MESH_VERTEX 4 -4.7153 -19.9732 13.9146 + *MESH_VERTEX 5 -4.9945 -19.7108 13.3655 + *MESH_VERTEX 6 -4.9945 -19.1079 11.4692 + *MESH_VERTEX 7 -4.7153 -19.2252 11.1692 + *MESH_VERTEX 8 4.7776 -19.2252 11.1692 + *MESH_VERTEX 9 5.0568 -19.1079 11.4692 + *MESH_VERTEX 10 5.0568 -19.7108 13.3655 + *MESH_VERTEX 11 4.7776 -19.9732 13.9146 + *MESH_VERTEX 12 -4.7153 1.3858 0.0615 + *MESH_VERTEX 13 -4.9945 1.2049 -0.2172 + *MESH_VERTEX 14 -4.9945 0.5175 -1.2756 + *MESH_VERTEX 15 -4.7153 0.3457 -1.5402 + *MESH_VERTEX 16 4.7776 0.3457 -1.5402 + *MESH_VERTEX 17 5.0568 0.5175 -1.2756 + *MESH_VERTEX 18 5.0568 1.2049 -0.2172 + *MESH_VERTEX 19 4.7776 1.3858 0.0615 + *MESH_VERTEX 20 4.7776 0.6223 -1.3438 + *MESH_VERTEX 21 -4.7153 0.6223 -1.3438 + *MESH_VERTEX 22 4.7776 1.3097 -0.2854 + *MESH_VERTEX 23 -4.7153 1.3097 -0.2854 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 640 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -15.7415 18.6296 + *MESH_VERTEX 1 4.7776 -15.7415 18.6296 + *MESH_VERTEX 2 -4.7153 -15.6499 16.6418 + *MESH_VERTEX 3 4.7776 -15.6499 16.6418 + *MESH_VERTEX 4 -4.7153 -15.6417 18.9934 + *MESH_VERTEX 5 -4.9945 -15.5304 18.3951 + *MESH_VERTEX 6 -4.9945 -15.4388 16.4074 + *MESH_VERTEX 7 -4.7153 -15.6297 16.1479 + *MESH_VERTEX 8 4.7776 -15.6297 16.1479 + *MESH_VERTEX 9 5.0568 -15.4388 16.4074 + *MESH_VERTEX 10 5.0568 -15.5303 18.3951 + *MESH_VERTEX 11 4.7776 -15.6417 18.9934 + *MESH_VERTEX 12 -4.7153 1.4041 0.0842 + *MESH_VERTEX 13 -4.9945 1.1572 -0.1382 + *MESH_VERTEX 14 -4.9945 0.2193 -0.9826 + *MESH_VERTEX 15 -4.7153 -0.0151 -1.1937 + *MESH_VERTEX 16 4.7776 -0.0151 -1.1937 + *MESH_VERTEX 17 5.0568 0.2193 -0.9826 + *MESH_VERTEX 18 5.0568 1.1572 -0.1382 + *MESH_VERTEX 19 4.7776 1.4041 0.0842 + *MESH_VERTEX 20 4.7776 0.3029 -1.0756 + *MESH_VERTEX 21 -4.7153 0.3029 -1.0756 + *MESH_VERTEX 22 4.7776 1.2408 -0.2312 + *MESH_VERTEX 23 -4.7153 1.2408 -0.2312 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 800 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -10.3338 22.4525 + *MESH_VERTEX 1 4.7776 -10.3338 22.4525 + *MESH_VERTEX 2 -4.7153 -10.7598 20.5088 + *MESH_VERTEX 3 4.7776 -10.7598 20.5088 + *MESH_VERTEX 4 -4.7153 -10.1433 22.7781 + *MESH_VERTEX 5 -4.9945 -10.1906 22.1713 + *MESH_VERTEX 6 -4.9945 -10.6166 20.2276 + *MESH_VERTEX 7 -4.7153 -10.8682 20.0265 + *MESH_VERTEX 8 4.7776 -10.8682 20.0265 + *MESH_VERTEX 9 5.0568 -10.6166 20.2276 + *MESH_VERTEX 10 5.0568 -10.1906 22.1713 + *MESH_VERTEX 11 4.7776 -10.1433 22.7781 + *MESH_VERTEX 12 -4.7153 1.4276 0.1014 + *MESH_VERTEX 13 -4.9945 1.1316 -0.0495 + *MESH_VERTEX 14 -4.9945 0.0071 -0.6224 + *MESH_VERTEX 15 -4.7153 -0.2740 -0.7656 + *MESH_VERTEX 16 4.7776 -0.2740 -0.7656 + *MESH_VERTEX 17 5.0568 0.0071 -0.6224 + *MESH_VERTEX 18 5.0568 1.1316 -0.0494 + *MESH_VERTEX 19 4.7776 1.4276 0.1014 + *MESH_VERTEX 20 4.7776 0.0638 -0.7338 + *MESH_VERTEX 21 -4.7153 0.0638 -0.7338 + *MESH_VERTEX 22 4.7776 1.1883 -0.1609 + *MESH_VERTEX 23 -4.7153 1.1883 -0.1609 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 960 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -4.1210 24.7455 + *MESH_VERTEX 1 4.7776 -4.1210 24.7455 + *MESH_VERTEX 2 -4.7153 -5.0355 22.9783 + *MESH_VERTEX 3 4.7776 -5.0355 22.9783 + *MESH_VERTEX 4 -4.7153 -3.8527 25.0107 + *MESH_VERTEX 5 -4.9945 -4.0554 24.4369 + *MESH_VERTEX 6 -4.9945 -4.9699 22.6697 + *MESH_VERTEX 7 -4.7153 -5.2650 22.5405 + *MESH_VERTEX 8 4.7776 -5.2650 22.5405 + *MESH_VERTEX 9 5.0568 -4.9700 22.6697 + *MESH_VERTEX 10 5.0568 -4.0554 24.4369 + *MESH_VERTEX 11 4.7776 -3.8527 25.0107 + *MESH_VERTEX 12 -4.7153 1.4548 0.1120 + *MESH_VERTEX 13 -4.9945 1.1298 0.0429 + *MESH_VERTEX 14 -4.9945 -0.1046 -0.2195 + *MESH_VERTEX 15 -4.7153 -0.4132 -0.2851 + *MESH_VERTEX 16 4.7776 -0.4132 -0.2851 + *MESH_VERTEX 17 5.0568 -0.1046 -0.2195 + *MESH_VERTEX 18 5.0568 1.1298 0.0429 + *MESH_VERTEX 19 4.7776 1.4548 0.1120 + *MESH_VERTEX 20 4.7776 -0.0787 -0.3419 + *MESH_VERTEX 21 -4.7153 -0.0787 -0.3419 + *MESH_VERTEX 22 4.7776 1.1557 -0.0795 + *MESH_VERTEX 23 -4.7153 1.1557 -0.0795 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1120 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -10.3338 22.4525 + *MESH_VERTEX 1 4.7776 -10.3338 22.4525 + *MESH_VERTEX 2 -4.7153 -10.7598 20.5088 + *MESH_VERTEX 3 4.7776 -10.7598 20.5088 + *MESH_VERTEX 4 -4.7153 -10.1433 22.7781 + *MESH_VERTEX 5 -4.9945 -10.1906 22.1713 + *MESH_VERTEX 6 -4.9945 -10.6166 20.2276 + *MESH_VERTEX 7 -4.7153 -10.8682 20.0265 + *MESH_VERTEX 8 4.7776 -10.8682 20.0265 + *MESH_VERTEX 9 5.0568 -10.6166 20.2276 + *MESH_VERTEX 10 5.0568 -10.1906 22.1713 + *MESH_VERTEX 11 4.7776 -10.1433 22.7781 + *MESH_VERTEX 12 -4.7153 1.4276 0.1014 + *MESH_VERTEX 13 -4.9945 1.1316 -0.0495 + *MESH_VERTEX 14 -4.9945 0.0071 -0.6224 + *MESH_VERTEX 15 -4.7153 -0.2740 -0.7656 + *MESH_VERTEX 16 4.7776 -0.2740 -0.7656 + *MESH_VERTEX 17 5.0568 0.0071 -0.6224 + *MESH_VERTEX 18 5.0568 1.1316 -0.0494 + *MESH_VERTEX 19 4.7776 1.4276 0.1014 + *MESH_VERTEX 20 4.7776 0.0638 -0.7338 + *MESH_VERTEX 21 -4.7153 0.0638 -0.7338 + *MESH_VERTEX 22 4.7776 1.1883 -0.1609 + *MESH_VERTEX 23 -4.7153 1.1883 -0.1609 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1280 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -15.7415 18.6296 + *MESH_VERTEX 1 4.7776 -15.7415 18.6296 + *MESH_VERTEX 2 -4.7153 -15.6499 16.6418 + *MESH_VERTEX 3 4.7776 -15.6499 16.6418 + *MESH_VERTEX 4 -4.7153 -15.6417 18.9934 + *MESH_VERTEX 5 -4.9945 -15.5304 18.3951 + *MESH_VERTEX 6 -4.9945 -15.4388 16.4074 + *MESH_VERTEX 7 -4.7153 -15.6297 16.1479 + *MESH_VERTEX 8 4.7776 -15.6297 16.1479 + *MESH_VERTEX 9 5.0568 -15.4388 16.4074 + *MESH_VERTEX 10 5.0568 -15.5303 18.3951 + *MESH_VERTEX 11 4.7776 -15.6417 18.9934 + *MESH_VERTEX 12 -4.7153 1.4041 0.0842 + *MESH_VERTEX 13 -4.9945 1.1572 -0.1382 + *MESH_VERTEX 14 -4.9945 0.2193 -0.9826 + *MESH_VERTEX 15 -4.7153 -0.0151 -1.1937 + *MESH_VERTEX 16 4.7776 -0.0151 -1.1937 + *MESH_VERTEX 17 5.0568 0.2193 -0.9826 + *MESH_VERTEX 18 5.0568 1.1572 -0.1382 + *MESH_VERTEX 19 4.7776 1.4041 0.0842 + *MESH_VERTEX 20 4.7776 0.3029 -1.0756 + *MESH_VERTEX 21 -4.7153 0.3029 -1.0756 + *MESH_VERTEX 22 4.7776 1.2408 -0.2312 + *MESH_VERTEX 23 -4.7153 1.2408 -0.2312 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1440 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -19.9754 13.5373 + *MESH_VERTEX 1 4.7776 -19.9754 13.5373 + *MESH_VERTEX 2 -4.7153 -19.3725 11.6410 + *MESH_VERTEX 3 4.7776 -19.3725 11.6410 + *MESH_VERTEX 4 -4.7153 -19.9732 13.9146 + *MESH_VERTEX 5 -4.9945 -19.7108 13.3655 + *MESH_VERTEX 6 -4.9945 -19.1079 11.4692 + *MESH_VERTEX 7 -4.7153 -19.2252 11.1692 + *MESH_VERTEX 8 4.7776 -19.2252 11.1692 + *MESH_VERTEX 9 5.0568 -19.1079 11.4692 + *MESH_VERTEX 10 5.0568 -19.7108 13.3655 + *MESH_VERTEX 11 4.7776 -19.9732 13.9146 + *MESH_VERTEX 12 -4.7153 1.3858 0.0615 + *MESH_VERTEX 13 -4.9945 1.2049 -0.2172 + *MESH_VERTEX 14 -4.9945 0.5175 -1.2756 + *MESH_VERTEX 15 -4.7153 0.3457 -1.5402 + *MESH_VERTEX 16 4.7776 0.3457 -1.5402 + *MESH_VERTEX 17 5.0568 0.5175 -1.2756 + *MESH_VERTEX 18 5.0568 1.2049 -0.2172 + *MESH_VERTEX 19 4.7776 1.3858 0.0615 + *MESH_VERTEX 20 4.7776 0.6223 -1.3438 + *MESH_VERTEX 21 -4.7153 0.6223 -1.3438 + *MESH_VERTEX 22 4.7776 1.3097 -0.2854 + *MESH_VERTEX 23 -4.7153 1.3097 -0.2854 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1600 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -22.7471 7.5228 + *MESH_VERTEX 1 4.7776 -22.7471 7.5228 + *MESH_VERTEX 2 -4.7153 -21.6739 5.8471 + *MESH_VERTEX 3 4.7776 -21.6739 5.8471 + *MESH_VERTEX 4 -4.7153 -22.8427 7.8877 + *MESH_VERTEX 5 -4.9945 -22.4471 7.4253 + *MESH_VERTEX 6 -4.9945 -21.3739 5.7496 + *MESH_VERTEX 7 -4.7153 -21.4095 5.4295 + *MESH_VERTEX 8 4.7776 -21.4095 5.4295 + *MESH_VERTEX 9 5.0568 -21.3739 5.7496 + *MESH_VERTEX 10 5.0568 -22.4471 7.4253 + *MESH_VERTEX 11 4.7776 -22.8426 7.8877 + *MESH_VERTEX 12 -4.7153 1.3740 0.0348 + *MESH_VERTEX 13 -4.9945 1.2714 -0.2812 + *MESH_VERTEX 14 -4.9945 0.8814 -1.4815 + *MESH_VERTEX 15 -4.7153 0.7839 -1.7815 + *MESH_VERTEX 16 4.7776 0.7839 -1.7815 + *MESH_VERTEX 17 5.0568 0.8814 -1.4815 + *MESH_VERTEX 18 5.0568 1.2714 -0.2812 + *MESH_VERTEX 19 4.7776 1.3740 0.0348 + *MESH_VERTEX 20 4.7776 1.0003 -1.5202 + *MESH_VERTEX 21 -4.7153 1.0003 -1.5202 + *MESH_VERTEX 22 4.7776 1.3903 -0.3200 + *MESH_VERTEX 23 -4.7153 1.3903 -0.3200 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1760 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -23.7810 2.3211 + *MESH_VERTEX 1 4.7776 -23.7810 2.3211 + *MESH_VERTEX 2 -4.7153 -22.3829 0.9052 + *MESH_VERTEX 3 4.7776 -22.3829 0.9052 + *MESH_VERTEX 4 -4.7153 -23.9504 2.6582 + *MESH_VERTEX 5 -4.9945 -23.4673 2.2881 + *MESH_VERTEX 6 -4.9945 -22.0692 0.8722 + *MESH_VERTEX 7 -4.7153 -22.0374 0.5516 + *MESH_VERTEX 8 4.7776 -22.0374 0.5516 + *MESH_VERTEX 9 5.0568 -22.0692 0.8722 + *MESH_VERTEX 10 5.0568 -23.4673 2.2881 + *MESH_VERTEX 11 4.7776 -23.9503 2.6582 + *MESH_VERTEX 12 -4.7153 1.3698 0.0118 + *MESH_VERTEX 13 -4.9945 1.3351 -0.3187 + *MESH_VERTEX 14 -4.9945 1.2032 -1.5738 + *MESH_VERTEX 15 -4.7153 1.1702 -1.8875 + *MESH_VERTEX 16 4.7776 1.1702 -1.8875 + *MESH_VERTEX 17 5.0568 1.2032 -1.5738 + *MESH_VERTEX 18 5.0568 1.3351 -0.3187 + *MESH_VERTEX 19 4.7776 1.3698 0.0118 + *MESH_VERTEX 20 4.7776 1.3276 -1.5869 + *MESH_VERTEX 21 -4.7153 1.3276 -1.5869 + *MESH_VERTEX 22 4.7776 1.4595 -0.3318 + *MESH_VERTEX 23 -4.7153 1.4595 -0.3318 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + *MESH { + *TIMEVALUE 1920 + *MESH_NUMVERTEX 24 + *MESH_NUMFACES 44 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -4.7153 -23.8849 -0.3323 + *MESH_VERTEX 1 4.7776 -23.8849 -0.3323 + *MESH_VERTEX 2 -4.7153 -22.3465 -1.5943 + *MESH_VERTEX 3 4.7776 -22.3465 -1.5943 + *MESH_VERTEX 4 -4.7153 -24.0885 -0.0147 + *MESH_VERTEX 5 -4.9945 -23.5694 -0.3323 + *MESH_VERTEX 6 -4.9945 -22.0310 -1.5942 + *MESH_VERTEX 7 -4.7153 -21.9659 -1.9097 + *MESH_VERTEX 8 4.7776 -21.9659 -1.9097 + *MESH_VERTEX 9 5.0568 -22.0310 -1.5943 + *MESH_VERTEX 10 5.0568 -23.5694 -0.3323 + *MESH_VERTEX 11 4.7776 -24.0885 -0.0147 + *MESH_VERTEX 12 -4.7153 1.3696 0.0001 + *MESH_VERTEX 13 -4.9945 1.3696 -0.3322 + *MESH_VERTEX 14 -4.9945 1.3696 -1.5942 + *MESH_VERTEX 15 -4.7153 1.3696 -1.9097 + *MESH_VERTEX 16 4.7776 1.3696 -1.9097 + *MESH_VERTEX 17 5.0568 1.3696 -1.5942 + *MESH_VERTEX 18 5.0568 1.3696 -0.3322 + *MESH_VERTEX 19 4.7776 1.3696 0.0001 + *MESH_VERTEX 20 4.7776 1.4947 -1.5943 + *MESH_VERTEX 21 -4.7153 1.4947 -1.5943 + *MESH_VERTEX 22 4.7776 1.4947 -0.3323 + *MESH_VERTEX 23 -4.7153 1.4947 -0.3323 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 1: A: 0 B: 3 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 2: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 3: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 6 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 5: A: 2 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 6: A: 2 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 7: A: 2 B: 8 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 8: A: 3 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 9: A: 3 B: 9 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 10: A: 3 B: 10 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 11: A: 1 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 12: A: 1 B: 11 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 13: A: 1 B: 4 C: 0 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 14: A: 4 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 15: A: 4 B: 13 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 16: A: 5 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 17: A: 5 B: 14 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 18: A: 6 B: 14 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 19: A: 6 B: 15 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 20: A: 7 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 21: A: 7 B: 16 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 22: A: 8 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 23: A: 8 B: 17 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 24: A: 9 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 25: A: 9 B: 18 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 26: A: 10 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 27: A: 10 B: 19 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 28: A: 11 B: 19 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 29: A: 11 B: 12 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 30: A: 12 B: 23 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 31: A: 13 B: 23 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 32: A: 13 B: 21 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 33: A: 14 B: 21 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 34: A: 15 B: 21 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 35: A: 15 B: 20 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 36: A: 16 B: 20 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 17 B: 20 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 17 B: 22 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 18 B: 22 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 19 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 23 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 22 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 22 B: 21 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 24 + *MESH_CVERTLIST { + *MESH_VERTCOL 0 0.4236 0.4236 0.4236 + *MESH_VERTCOL 1 0.4202 0.4202 0.4202 + *MESH_VERTCOL 2 0.3837 0.3837 0.3837 + *MESH_VERTCOL 3 0.3717 0.3717 0.3717 + *MESH_VERTCOL 4 0.6691 0.6691 0.6691 + *MESH_VERTCOL 5 0.5788 0.5788 0.5788 + *MESH_VERTCOL 6 0.4225 0.4225 0.4225 + *MESH_VERTCOL 7 0.2765 0.2765 0.2765 + *MESH_VERTCOL 8 0.3049 0.3049 0.3049 + *MESH_VERTCOL 9 0.5108 0.5108 0.5108 + *MESH_VERTCOL 10 0.6195 0.6195 0.6195 + *MESH_VERTCOL 11 0.7210 0.7210 0.7210 + *MESH_VERTCOL 12 0.6054 0.6054 0.6054 + *MESH_VERTCOL 13 0.7167 0.7167 0.7167 + *MESH_VERTCOL 14 0.5792 0.5792 0.5792 + *MESH_VERTCOL 15 0.3464 0.3464 0.3464 + *MESH_VERTCOL 16 0.2693 0.2693 0.2693 + *MESH_VERTCOL 17 0.4903 0.4903 0.4903 + *MESH_VERTCOL 18 0.6958 0.6958 0.6958 + *MESH_VERTCOL 19 0.6274 0.6274 0.6274 + *MESH_VERTCOL 20 0.4361 0.4361 0.4361 + *MESH_VERTCOL 21 0.5644 0.5644 0.5644 + *MESH_VERTCOL 22 0.5445 0.5445 0.5445 + *MESH_VERTCOL 23 0.6008 0.6008 0.6008 + } + *MESH_NUMCVFACES 44 + *MESH_CFACELIST { + *MESH_CFACE 0 0 2 3 + *MESH_CFACE 1 0 3 1 + *MESH_CFACE 2 0 4 5 + *MESH_CFACE 3 0 5 6 + *MESH_CFACE 4 0 6 2 + *MESH_CFACE 5 2 6 7 + *MESH_CFACE 6 2 7 8 + *MESH_CFACE 7 2 8 3 + *MESH_CFACE 8 3 8 9 + *MESH_CFACE 9 3 9 10 + *MESH_CFACE 10 3 10 1 + *MESH_CFACE 11 1 10 11 + *MESH_CFACE 12 1 11 4 + *MESH_CFACE 13 1 4 0 + *MESH_CFACE 14 4 12 13 + *MESH_CFACE 15 4 13 5 + *MESH_CFACE 16 5 13 14 + *MESH_CFACE 17 5 14 6 + *MESH_CFACE 18 6 14 15 + *MESH_CFACE 19 6 15 7 + *MESH_CFACE 20 7 15 16 + *MESH_CFACE 21 7 16 8 + *MESH_CFACE 22 8 16 17 + *MESH_CFACE 23 8 17 9 + *MESH_CFACE 24 9 17 18 + *MESH_CFACE 25 9 18 10 + *MESH_CFACE 26 10 18 19 + *MESH_CFACE 27 10 19 11 + *MESH_CFACE 28 11 19 12 + *MESH_CFACE 29 11 12 4 + *MESH_CFACE 30 12 23 13 + *MESH_CFACE 31 13 23 21 + *MESH_CFACE 32 13 21 14 + *MESH_CFACE 33 14 21 15 + *MESH_CFACE 34 15 21 20 + *MESH_CFACE 35 15 20 16 + *MESH_CFACE 36 16 20 17 + *MESH_CFACE 37 17 20 22 + *MESH_CFACE 38 17 22 18 + *MESH_CFACE 39 18 22 19 + *MESH_CFACE 40 19 22 23 + *MESH_CFACE 41 19 23 12 + *MESH_CFACE 42 22 20 21 + *MESH_CFACE 43 22 21 23 + } + } + } +} +*GEOMOBJECT { + *NODE_NAME "Chain1" + *NODE_TM { + *NODE_NAME "Chain1" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 -0.0000 0.0000 + *TM_ROW1 -0.0000 0.8192 0.5736 + *TM_ROW2 -0.0000 -0.5736 0.8192 + *TM_ROW3 6.0480 -21.8398 -0.6886 + *TM_POS 6.0480 -21.8398 -0.6886 + *TM_ROTAXIS -1.0000 0.0000 0.0000 + *TM_ROTANGLE 0.6109 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -22.0380 -1.5503 + *MESH_VERTEX 1 5.6493 -22.0380 -1.5503 + *MESH_VERTEX 2 5.0645 2.6351 15.7672 + *MESH_VERTEX 3 5.6493 2.6351 15.7672 + *MESH_VERTEX 4 5.0645 -22.7418 -0.5451 + *MESH_VERTEX 5 5.6493 -22.7418 -0.5451 + *MESH_VERTEX 6 5.0645 1.9312 16.7724 + *MESH_VERTEX 7 5.6493 1.9312 16.7724 + *MESH_VERTEX 8 -5.5472 -22.0380 -1.5503 + *MESH_VERTEX 9 -4.9624 -22.0380 -1.5503 + *MESH_VERTEX 10 -5.5472 2.6351 15.7672 + *MESH_VERTEX 11 -4.9624 2.6351 15.7672 + *MESH_VERTEX 12 -5.5472 -22.7418 -0.5451 + *MESH_VERTEX 13 -4.9624 -22.7418 -0.5451 + *MESH_VERTEX 14 -5.5472 1.9312 16.7724 + *MESH_VERTEX 15 -4.9624 1.9312 16.7724 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 + *MESH_ANIMATION { + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -22.0380 -1.5503 + *MESH_VERTEX 1 5.6493 -22.0380 -1.5503 + *MESH_VERTEX 2 5.0645 2.6351 15.7672 + *MESH_VERTEX 3 5.6493 2.6351 15.7672 + *MESH_VERTEX 4 5.0645 -22.7418 -0.5451 + *MESH_VERTEX 5 5.6493 -22.7418 -0.5451 + *MESH_VERTEX 6 5.0645 1.9312 16.7724 + *MESH_VERTEX 7 5.6493 1.9312 16.7724 + *MESH_VERTEX 8 -5.5472 -22.0380 -1.5503 + *MESH_VERTEX 9 -4.9624 -22.0380 -1.5503 + *MESH_VERTEX 10 -5.5472 2.6351 15.7672 + *MESH_VERTEX 11 -4.9624 2.6351 15.7672 + *MESH_VERTEX 12 -5.5472 -22.7418 -0.5451 + *MESH_VERTEX 13 -4.9624 -22.7418 -0.5451 + *MESH_VERTEX 14 -5.5472 1.9312 16.7724 + *MESH_VERTEX 15 -4.9624 1.9312 16.7724 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 160 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -22.0266 0.8322 + *MESH_VERTEX 1 5.6493 -22.0266 0.8322 + *MESH_VERTEX 2 5.0645 2.6683 15.6438 + *MESH_VERTEX 3 5.6493 2.6683 15.6438 + *MESH_VERTEX 4 5.0645 -22.6586 1.8841 + *MESH_VERTEX 5 5.6493 -22.6586 1.8841 + *MESH_VERTEX 6 5.0645 2.0362 16.6957 + *MESH_VERTEX 7 5.6493 2.0362 16.6957 + *MESH_VERTEX 8 -5.5472 -22.0266 0.8322 + *MESH_VERTEX 9 -4.9624 -22.0266 0.8322 + *MESH_VERTEX 10 -5.5472 2.6683 15.6438 + *MESH_VERTEX 11 -4.9624 2.6683 15.6438 + *MESH_VERTEX 12 -5.5472 -22.6586 1.8841 + *MESH_VERTEX 13 -4.9624 -22.6586 1.8841 + *MESH_VERTEX 14 -5.5472 2.0362 16.6957 + *MESH_VERTEX 15 -4.9624 2.0362 16.6957 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 320 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -21.4047 5.7018 + *MESH_VERTEX 1 5.6493 -21.4047 5.7018 + *MESH_VERTEX 2 5.0645 2.2307 15.1920 + *MESH_VERTEX 3 5.6493 2.2307 15.1920 + *MESH_VERTEX 4 5.0645 -21.8644 6.8396 + *MESH_VERTEX 5 5.6493 -21.8644 6.8396 + *MESH_VERTEX 6 5.0645 1.7710 16.3298 + *MESH_VERTEX 7 5.6493 1.7710 16.3298 + *MESH_VERTEX 8 -5.5472 -21.4047 5.7018 + *MESH_VERTEX 9 -4.9624 -21.4047 5.7018 + *MESH_VERTEX 10 -5.5472 2.2307 15.1920 + *MESH_VERTEX 11 -4.9624 2.2307 15.1920 + *MESH_VERTEX 12 -5.5472 -21.8644 6.8396 + *MESH_VERTEX 13 -4.9624 -21.8644 6.8396 + *MESH_VERTEX 14 -5.5472 1.7710 16.3298 + *MESH_VERTEX 15 -4.9624 1.7710 16.3298 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 480 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -19.0583 11.4505 + *MESH_VERTEX 1 5.6493 -19.0583 11.4505 + *MESH_VERTEX 2 5.0645 2.0099 14.9183 + *MESH_VERTEX 3 5.6493 2.0099 14.9183 + *MESH_VERTEX 4 5.0645 -19.2608 12.6608 + *MESH_VERTEX 5 5.6493 -19.2608 12.6608 + *MESH_VERTEX 6 5.0645 1.8073 16.1286 + *MESH_VERTEX 7 5.6493 1.8073 16.1286 + *MESH_VERTEX 8 -5.5472 -19.0583 11.4505 + *MESH_VERTEX 9 -4.9624 -19.0583 11.4505 + *MESH_VERTEX 10 -5.5472 2.0099 14.9183 + *MESH_VERTEX 11 -4.9624 2.0099 14.9183 + *MESH_VERTEX 12 -5.5472 -19.2608 12.6608 + *MESH_VERTEX 13 -4.9624 -19.2608 12.6608 + *MESH_VERTEX 14 -5.5472 1.8073 16.1286 + *MESH_VERTEX 15 -4.9624 1.8073 16.1286 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 640 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -15.4384 16.3669 + *MESH_VERTEX 1 5.6493 -15.4384 16.3669 + *MESH_VERTEX 2 5.0645 1.9927 14.5226 + *MESH_VERTEX 3 5.6493 1.9927 14.5226 + *MESH_VERTEX 4 5.0645 -15.3101 17.5873 + *MESH_VERTEX 5 5.6493 -15.3101 17.5873 + *MESH_VERTEX 6 5.0645 2.1209 15.7431 + *MESH_VERTEX 7 5.6493 2.1209 15.7431 + *MESH_VERTEX 8 -5.5472 -15.4384 16.3669 + *MESH_VERTEX 9 -4.9624 -15.4384 16.3669 + *MESH_VERTEX 10 -5.5472 1.9927 14.5226 + *MESH_VERTEX 11 -4.9624 1.9927 14.5226 + *MESH_VERTEX 12 -5.5472 -15.3101 17.5873 + *MESH_VERTEX 13 -4.9624 -15.3101 17.5873 + *MESH_VERTEX 14 -5.5472 2.1209 15.7431 + *MESH_VERTEX 15 -4.9624 2.1209 15.7431 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 800 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -10.6435 20.1926 + *MESH_VERTEX 1 5.6493 -10.6435 20.1926 + *MESH_VERTEX 2 5.0645 1.9791 13.9747 + *MESH_VERTEX 3 5.6493 1.9791 13.9747 + *MESH_VERTEX 4 5.0645 -10.1055 21.2956 + *MESH_VERTEX 5 5.6493 -10.1055 21.2956 + *MESH_VERTEX 6 5.0645 2.5170 15.0777 + *MESH_VERTEX 7 5.6493 2.5170 15.0777 + *MESH_VERTEX 8 -5.5472 -10.6435 20.1926 + *MESH_VERTEX 9 -4.9624 -10.6435 20.1926 + *MESH_VERTEX 10 -5.5472 1.9791 13.9747 + *MESH_VERTEX 11 -4.9624 1.9791 13.9747 + *MESH_VERTEX 12 -5.5472 -10.1055 21.2956 + *MESH_VERTEX 13 -4.9624 -10.1055 21.2956 + *MESH_VERTEX 14 -5.5472 2.5170 15.0777 + *MESH_VERTEX 15 -4.9624 2.5170 15.0777 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 960 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -4.9514 22.6719 + *MESH_VERTEX 1 5.6493 -4.9514 22.6719 + *MESH_VERTEX 2 5.0645 2.4486 10.9225 + *MESH_VERTEX 3 5.6493 2.4486 10.9225 + *MESH_VERTEX 4 5.0645 -3.8580 23.2290 + *MESH_VERTEX 5 5.6493 -3.8580 23.2290 + *MESH_VERTEX 6 5.0645 3.5420 11.4797 + *MESH_VERTEX 7 5.6493 3.5420 11.4797 + *MESH_VERTEX 8 -5.5472 -4.9514 22.6718 + *MESH_VERTEX 9 -4.9624 -4.9514 22.6719 + *MESH_VERTEX 10 -5.5472 2.4486 10.9225 + *MESH_VERTEX 11 -4.9624 2.4486 10.9225 + *MESH_VERTEX 12 -5.5472 -3.8580 23.2290 + *MESH_VERTEX 13 -4.9624 -3.8580 23.2290 + *MESH_VERTEX 14 -5.5472 3.5420 11.4797 + *MESH_VERTEX 15 -4.9624 3.5420 11.4797 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1120 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -10.6435 20.1926 + *MESH_VERTEX 1 5.6493 -10.6435 20.1926 + *MESH_VERTEX 2 5.0645 1.9791 13.9747 + *MESH_VERTEX 3 5.6493 1.9791 13.9747 + *MESH_VERTEX 4 5.0645 -10.1055 21.2956 + *MESH_VERTEX 5 5.6493 -10.1055 21.2956 + *MESH_VERTEX 6 5.0645 2.5170 15.0777 + *MESH_VERTEX 7 5.6493 2.5170 15.0777 + *MESH_VERTEX 8 -5.5472 -10.6435 20.1926 + *MESH_VERTEX 9 -4.9624 -10.6435 20.1926 + *MESH_VERTEX 10 -5.5472 1.9791 13.9747 + *MESH_VERTEX 11 -4.9624 1.9791 13.9747 + *MESH_VERTEX 12 -5.5472 -10.1055 21.2956 + *MESH_VERTEX 13 -4.9624 -10.1055 21.2956 + *MESH_VERTEX 14 -5.5472 2.5170 15.0777 + *MESH_VERTEX 15 -4.9624 2.5170 15.0777 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1280 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -15.4384 16.3669 + *MESH_VERTEX 1 5.6493 -15.4384 16.3669 + *MESH_VERTEX 2 5.0645 1.9927 14.5226 + *MESH_VERTEX 3 5.6493 1.9927 14.5226 + *MESH_VERTEX 4 5.0645 -15.3101 17.5873 + *MESH_VERTEX 5 5.6493 -15.3101 17.5873 + *MESH_VERTEX 6 5.0645 2.1209 15.7431 + *MESH_VERTEX 7 5.6493 2.1209 15.7431 + *MESH_VERTEX 8 -5.5472 -15.4384 16.3669 + *MESH_VERTEX 9 -4.9624 -15.4384 16.3669 + *MESH_VERTEX 10 -5.5472 1.9927 14.5226 + *MESH_VERTEX 11 -4.9624 1.9927 14.5226 + *MESH_VERTEX 12 -5.5472 -15.3101 17.5873 + *MESH_VERTEX 13 -4.9624 -15.3101 17.5873 + *MESH_VERTEX 14 -5.5472 2.1209 15.7431 + *MESH_VERTEX 15 -4.9624 2.1209 15.7431 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1440 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -19.0583 11.4505 + *MESH_VERTEX 1 5.6493 -19.0583 11.4505 + *MESH_VERTEX 2 5.0645 2.0099 14.9183 + *MESH_VERTEX 3 5.6493 2.0099 14.9183 + *MESH_VERTEX 4 5.0645 -19.2608 12.6608 + *MESH_VERTEX 5 5.6493 -19.2608 12.6608 + *MESH_VERTEX 6 5.0645 1.8073 16.1286 + *MESH_VERTEX 7 5.6493 1.8073 16.1286 + *MESH_VERTEX 8 -5.5472 -19.0583 11.4505 + *MESH_VERTEX 9 -4.9624 -19.0583 11.4505 + *MESH_VERTEX 10 -5.5472 2.0099 14.9183 + *MESH_VERTEX 11 -4.9624 2.0099 14.9183 + *MESH_VERTEX 12 -5.5472 -19.2608 12.6608 + *MESH_VERTEX 13 -4.9624 -19.2608 12.6608 + *MESH_VERTEX 14 -5.5472 1.8073 16.1286 + *MESH_VERTEX 15 -4.9624 1.8073 16.1286 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1600 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -21.4047 5.7018 + *MESH_VERTEX 1 5.6493 -21.4047 5.7018 + *MESH_VERTEX 2 5.0645 2.2307 15.1920 + *MESH_VERTEX 3 5.6493 2.2307 15.1920 + *MESH_VERTEX 4 5.0645 -21.8644 6.8396 + *MESH_VERTEX 5 5.6493 -21.8644 6.8396 + *MESH_VERTEX 6 5.0645 1.7710 16.3298 + *MESH_VERTEX 7 5.6493 1.7710 16.3298 + *MESH_VERTEX 8 -5.5472 -21.4047 5.7018 + *MESH_VERTEX 9 -4.9624 -21.4047 5.7018 + *MESH_VERTEX 10 -5.5472 2.2307 15.1920 + *MESH_VERTEX 11 -4.9624 2.2307 15.1920 + *MESH_VERTEX 12 -5.5472 -21.8644 6.8396 + *MESH_VERTEX 13 -4.9624 -21.8644 6.8396 + *MESH_VERTEX 14 -5.5472 1.7710 16.3298 + *MESH_VERTEX 15 -4.9624 1.7710 16.3298 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1760 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -22.0266 0.8322 + *MESH_VERTEX 1 5.6493 -22.0266 0.8322 + *MESH_VERTEX 2 5.0645 2.6683 15.6438 + *MESH_VERTEX 3 5.6493 2.6683 15.6438 + *MESH_VERTEX 4 5.0645 -22.6586 1.8841 + *MESH_VERTEX 5 5.6493 -22.6586 1.8841 + *MESH_VERTEX 6 5.0645 2.0362 16.6957 + *MESH_VERTEX 7 5.6493 2.0362 16.6957 + *MESH_VERTEX 8 -5.5472 -22.0266 0.8322 + *MESH_VERTEX 9 -4.9624 -22.0266 0.8322 + *MESH_VERTEX 10 -5.5472 2.6683 15.6438 + *MESH_VERTEX 11 -4.9624 2.6683 15.6438 + *MESH_VERTEX 12 -5.5472 -22.6586 1.8841 + *MESH_VERTEX 13 -4.9624 -22.6586 1.8841 + *MESH_VERTEX 14 -5.5472 2.0362 16.6957 + *MESH_VERTEX 15 -4.9624 2.0362 16.6957 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + *MESH { + *TIMEVALUE 1920 + *MESH_NUMVERTEX 16 + *MESH_NUMFACES 24 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 5.0645 -22.0380 -1.5503 + *MESH_VERTEX 1 5.6493 -22.0380 -1.5503 + *MESH_VERTEX 2 5.0645 2.6351 15.7672 + *MESH_VERTEX 3 5.6493 2.6351 15.7672 + *MESH_VERTEX 4 5.0645 -22.7418 -0.5451 + *MESH_VERTEX 5 5.6493 -22.7418 -0.5451 + *MESH_VERTEX 6 5.0645 1.9312 16.7724 + *MESH_VERTEX 7 5.6493 1.9312 16.7724 + *MESH_VERTEX 8 -5.5472 -22.0380 -1.5503 + *MESH_VERTEX 9 -4.9624 -22.0380 -1.5503 + *MESH_VERTEX 10 -5.5472 2.6351 15.7672 + *MESH_VERTEX 11 -4.9624 2.6351 15.7672 + *MESH_VERTEX 12 -5.5472 -22.7418 -0.5451 + *MESH_VERTEX 13 -4.9624 -22.7418 -0.5451 + *MESH_VERTEX 14 -5.5472 1.9312 16.7724 + *MESH_VERTEX 15 -4.9624 1.9312 16.7724 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 4: A: 0 B: 1 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 5: A: 5 B: 4 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 6: A: 1 B: 3 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 7: A: 7 B: 5 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 8: A: 3 B: 2 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 9: A: 6 B: 7 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 10: A: 2 B: 0 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 11: A: 4 B: 6 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 12: A: 8 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 13: A: 11 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 14: A: 12 B: 13 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 15: A: 15 B: 14 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0 + *MESH_FACE 16: A: 8 B: 9 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 17: A: 13 B: 12 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 18: A: 9 B: 11 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 19: A: 15 B: 13 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 20: A: 11 B: 10 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 21: A: 14 B: 15 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 22: A: 10 B: 8 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 23: A: 12 B: 14 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 0 + *MESH_NUMCVERTEX 0 + } + } +} diff --git a/examples/src/ssg/viewer/data/embossed_herring.bmp b/examples/src/ssg/viewer/data/embossed_herring.bmp new file mode 100644 index 0000000000000000000000000000000000000000..795e5d1f7c86118caf32b989ac461972d6652b47 GIT binary patch literal 24630 zcmeHv2Y6Ihmgal+y$YZRmL&l;m}Fy&L)-0kw;M1~Ygt^wkrSWK?RJb=pT_wla=y9T z+B9xKEVnFy^DpH>&0Lsmf8S>T7yeF?*(-!t+4g*4Zw2$s=9k3rD-*fh8ZJQ3ZPRc& zi@C!VF4)3_S)a@wf^CK47z^_kZ zD-zk(T(%*NUmVL;sRjBhJEyEZIPf3+?IUKuY%i{%`J?h_rf#xX}N7h z+^%AF)FOmggb3^OqCp|h#Ba~zmeQQL0~MU2nhUR;Q+s!A>j&e@E^y&>G=T%(%=tSu zJbL2udEFn0ss^??hOLTW+jE3q!#w+C-OYa*m)py>X0YY4ETC9Wn1#?PwlkZpOZono zZRyM>n)&BDrFFBNd2DSm+mX#eE5#^tNdE_OI`7G|I=Pk6Y*UJGq{6LeNZ42Ma7<8@ z5N2XWDuwMiY<~&EbAl>-hrfEV;ihmz&(s@n0me9rmN-!lMNGC=Z zg^()xptXN7pP%TQmnqrKEVeO2*k9^adUjUJEk0PwuT-(kDMFx5R9U3zIw7!}`4v4f zCa?ly__YbJiBr*Owj&nzD zPj}9%W5mD;wzn95MhG?whl=@CQQYECepNKvg+oyxs!U?EMO2s`8MD$cW_bkLoXoan zvb{wt)F>*gKF99A(s|#x_yixM<(DbBH^TT8k!)KA?EL33OOD-tvHe}=yc2A{2LH+} zQt<2InO_0hmi^~3k30vTg)wX6*xr00R4=NGKQN!4?wl8gJ4ITA5Ix(T#a1Wq%TzEL zw=aSi?+@v84!YgXw0g3wlfFT9%r>NBXG)^0UuwA_iLrfu%o^I0Fupv_mNNXswliNil@75zMQnXC zzaoYnvRJ`LrE|MEGD zv4}V$32b93+lcw(OQP*BwOs#!`TXSOj8|J2!U786WF#>bG0G~08rilKepv)tpUC`+ z*!BXpD_=OGqu8f3d^=_f#weL@GW|TtzN`Pkr<>n)Djr~=25fbN61F{qe*;bfZ`vR&rfmA zK%Ps&u>`{5Vll?&?d0>ZBAb$B zNi9x!{R9zr=d%doFLh@suKY1_^lx3t&tN%tEE#-wNF`z@A(yb% zg8Ah>`kY)05RU5H;%c|`yf?{yU5u~+ivl(M)%pCi=DawZn;*S$3yeWmOnNjcZ*M1FB3p|3GS1k6v*(Rrpdh7Nr?x%Qegrjhc6r34RX%zutKA1&uz z59C+J@+)H5vS_wWjT8X(hpQK(ER_82%A!4qNbRHIePYQPCwjR5e0V&WtvNGoNip(WE@L50h zIY*cnVB7veWJBaSumlLlVO2nPvg8Iyl%3qdh9m+ZThiI?JhBXyz%hnJTNgFoa4#GX zj+8PCSg%GHWs&A_2{n5MKUqEaktDiC2++WiLa0Fq#xkYAbr7=Jt6@8VWD0~sTKtLw z$yh1Y9In7a_+_z#hyttJ5*jx5eKe!)hEslz7^q{r@`MBB2p?opz|nvon6o6gL5Q;9 z^a)3b`PIlfGJouIR+_=U2q6X`)J$v)@*t2ld-Fu4d1n2aufu!5Zo6~XdTihf;eeLx zuquwN0P~YABsSV_|7l*wyJDCHn4jJ>R6ixHK5OKQnQd=L)%9$d3M_=EFw<#@hI#S) z7=|#tD@T^rEd*AwJ%u<6LYQ6>UpvQeP6W4uhv2ieW(kKXUTnDWeA`_~Y&|ePP8R-k zf_7L4D8(sYyAeVDV$PA~X${x+_CIhfJ1K-z@Jm%}d*+XO&N=cZn{c?)OWm}l`(4+< zL5glGqxto~?Yart^RKkLGb^u$oCDZ1-%KIcB+Kn%{yAVL`1P?Y!aCV@Wn4xNoH+s| zP^DKwy=L_Hhljs%E;&g~=?w+jlLH?m%W8Hh?iE#L=9fJd*5TQ}FYz1IJV?C#g|du} z=UZ-0%j^E;vw(>7u7#(ZvwFz^V$7x_X=0-s@fWP!z7qPZznt??y>rQ_@R8q3BP#jj z;ry>dVSh2i^6x)q9QJ@hS^EF=oHxcv50{RMwlC~@Z)WYAGIa}F1l%UKD4gGaM;6aA5eD|^Dp36$5BLH9Ks|2g*AM$*#2UsFpi6_o8NVBX6+4mdONqj5J@Y7 zGQTcP2&|B1wz-s@aM$!AiGV#BVhvE!if9h#3z2kn91GA0fqGG4K(fYy%^X1KmMkz! zJlxSz1my^FfSb}B$f_9hsdHTYg02VOpYz5fe!s>gyPI1c!Mzc}zY$6a$aWyufA2Yi z*IE|`k1Yg$*FGimS@0p-()lqo2+m}E67$XYj(y&JWX?-MLDbAHIXR{7O-W=eTmfPq zp$j}8oXqk#VtoV4k?;S!`Vu*CM8LI4gg{__j9IK?Ym)FoK1BaiPDj+)-%hT%CRG9L zWQ@T`vhC7v2XzK2Pb*2N;iD{EXcdPv_L_j+T+*l5{FL2<2`pK3HdwSoWVH=eM zng> z2Yx90H_q8V2U)fxzJ|~sa-suBLLGA+LxZrb5A2iqefz9V!ZZjC0Ip)87FRaqHs`y$R&xl zQTz%sz<91e)zgQeKF?y1d*BVT`OpYPgv zUA#QTj`w2VWDpR2lWEnqrxRicGg9FL#QtHuaHv#_Fh~G6Rfrd3h-eI@7(A0(d{2Jz zMD>;NSzWLs(q7@9j_t_AQQ|kmv;9TvAXQJ;#x%Agi#w{FYrZHCHOZ7V;b1Y9cn+3G zlr=)2R-W7jvPd{mA_SHQ2Mh6u!co287=WOFZ6*YkJ^UV!FYGH|{)C{YFo7VxGncJT zVH>m9mVCB3i(edt5HC^HIOTN8lbYt2owx0^w{wo*K?VW}Vw-JErERk@2|Nj2 zT%O(xKY$a8pU`u#^Y?4%)4@jp#T_magAMcR-;~ERu-(`IF#wIiQ7uzh#wFIzYQFiT z?Hl`i&(4QS8M3NPsq*A@X=*zVII4}p(NZqT!be**goyS4 zil;kJO8LBo8*l}ps+u?l-*ic+X`=pugMy9~L-cM1{S#39I;0c)i(O+Ioz%@Q_T2X} z3{R{amL<0fhjp$6{jMp^LST_3)F4KhgrExNb=O4jY(Pf|)y}Dnq7t-Q zDgFRg=2!?UO2x$dladG<%qzeYU<>z_vu$~7V>Th!gB3zRzI#sRuUc=-wp|n8nQ6|M zsPD{cxcj190cRX9}s#=vJ&>u*sNZ$lcHC{+J+c3a`-@4akSa7MsP{-vFz(RQLhH>!nj zV83hzK4Et*r!vjW?`*yP$9e7VNMP8_sqjl|U%o80V`j~@>3O|Go$W7UTe5*toO4ge zQ#*<9#P4oM6hlq$49K~JgIdR7DHRSB$s+AjY*)SQSLCsE^4JDxxJ?4ciuKx_DNATT zB20yq5bf;xD`H5wTUN8Es1U=o(r7cjixFBe+BmVIe@;mc{w!D9MTM5WLMntXECdcq z0UeOTWx_!mA1&BdVLu!i0(zxz1gH#~vse;RC5>-k{w2)6RH|-w&N(iNZ(>_g$L1`F zsg=bwq1XWwCylFr)p}*!@sHh5kK0!Ow?+`+h}Ny*{G7TsXBG5)`|d~dP6fvS-SO5u zaw7kHm;4j*q;|G7n_&*YmD7@&3oid*+v(587xs}ufDIqDGfsyrzK(m%!9T!yC|>N& z;lnEDlk+j402HzNp!a|;#x3Y6&! zu!X>LiiaO&OkTOYPq*K3D?KH`zoEzlzk$60&$$%=&W!jX94HZEs=W()q){eOsTV`b zB+=&Q%Lk-!R!N*ip3>-)ST9Sg!2-#X>%}nbtirA-K(knv2(aQtAxtMK@LLsP7}ilI zgn%ulQzM4zWr@`oF9u^vm&23Oy6h_xunGVYy9)RP2;2$+$!im7FYPZ!P9cq|ex>== zRQpvaXcOOLieex-)+drDJX-O{y91%^$_2VWxzG{rRcvE2p&h)w(llP(@LKo%C#|ER zx{*WF7^?>2QQxui+`^vCw#(cvj}j-aBb^~XQW~dbwJI)tJ=H$SML>{18LI=OP!R;K z3C~ByDv7Z#Y<-hT;eM#RBV&ywwi!jnb*YpHgkXVe!u|?!P>0K>8As&N)o|tr0m2@Q zr}c(3sgt|}!X+Th>STKNgC)+e6-=r?k=@{u+&tGlGEvhD`+I3my<{BzdBY9Z|CcS- zWQldFTW?Iv>tK*EQ2W8ZGifj9FkIXSCvz&roAv$~bu z1+WL%0Wjpj5*Uh7tt}aBFE*YLBnDD8ga+F1ekE?jXNi>9mCKgJ5LvfAgZXL15F={M z^0Y=FqUx8%3oln+lSEivDIIXm=>SJJ)qcfAWyhA5MOkEVc1es`45}EPROff@H)3Ml zEc->A0+-Z!_YA}cn7vAx(%_uZ;97iK46m4$-QtvN7lX>4ZM`W;pxQmyZ}<@S2Vq|! z#Z#Qe?FbfC5>>4TvSmve6)Cpnu+8aI#e-p2MiLvm3$K7>BDCXKc2bP3++P@qJ>6u{@w-dx{|hV%XaOn)xkndF1wpl!9d= zypq2StD<}@q#X1SvL1>8a718%FtH#5?LEJI@qk960;0CFP3ioS7`6q7u}}=L3Lrhg ztHf}FZ`TLEHjm;D%lht0R3`WQPUqZqS-gEl&8Ty7y)31kDJxlU#Y??+pUUq<)pEA= zf=gbPTSlV{^`p>opOYUTYRom9o@+b{=i`ypCR4+ARlIWYBbW4MA*ci=g1(39qyT<_ zI4l+_Fzv{PZJ&!yOU@&}sfM#yzJ-D4$j@=^A zqC_avN<*Pluu&d?xF1}#uMO1OfwB*$Ihx4a4YO~$!>MeJ;uT+q;a;XWhV#=;hEvO3A#bA zvH=!evE$6AFVtP8%}~@UrFGOv2sP;>D)X})@4(<5nJrG)9TJ2IOjV(q0p2MHlxYQN z;cyTDF99I21lU>|G6er3F1q^sy+6I$_r4^$j%`HkTgCR~N^o$tL#P(T0Wm5}hib0& zzx}7x9rxWU&Oq`cs_a5Qp#*-_FPCy3xbD40vokv%u`@RR$|&cA`d7Q(f7~$4Vyd}a zPzgG8J1DPjPKA?!8X?!DyBg|Y9R4E?SiUg1h z(v&)BT8ntF0D!?YryKqgIE|z`z$z3R^_S1QlAX^ykd zit+0DXY@mSn08!p6R0P7V&gp12tgYBVia5f){79%^y-U1N|IPJekh4Dzt)DO*E4K# zg#lkv%Z66M+EisTI6l<4vgbbB5$532cNagQUyOvH$n>=Bycnt_j6`SCMETBc4cnMW z=|2wmvS`jf?`J&^ob!$oJ_Kh4u49>kA>giz1!2pF7!&$FUEA@lSNSk^w37Thaa?hT z&TK;_e1%lm!0!QBWO=pc!|&U9LGYZ?!O8Y(68M>QsAMS!?NG#zU_J?yWgM=MMBCRL zyW^4DFGe>~2!MXlClLxj!A~Ozx6G>?o^|Z*Wb*|HIJ%vg!mb>o>dpn-lBj9`V>q0Z z@F!qFl}7in<9OMZt4F5hw!0PgNa8JGq+y=r{FA10+1LMWrhe#Gwh?blFZSdUwqdNh zG_h_+{{uv;d39GH5?fh4I=O6skF45o`jaKy_ZIa&7?)Vb!n8ODOj*US5Q#R4!nnXZ zJgI!pJ+sBT=F*g`_7}j>Mz%Wsp?xUef?CgDN6UU$etLQDdrtvprW|93km7;S zjdG+FjtGLK+*v5>Dnj7!?RqfDdiDEuo*(3v*5;IRoECaZCPj0QHp^5z0yk7Ti1^#H zB}(hE;~#h%M`&eHlSN$KL9t1UZ9od+6j$Sv*$veM$y6u_Dn{^?rZhnd0m2_p912EB zI127&*Md&(ij&x=SPOY(3mpge-f)9+)-hgT;8kX~(v$L(28ax?Ips<9lXXK(2}YFm zwisq`PHOZ_YZ;$hziZ^{@$t3N1UsT|`0#IEX}RHD(35%f_fF|ePU$VH>#xd^>OIN_ zymQ(po6Bs(VVqdh`=a68oXWG4v?rg{4`LfEYPc#-u7lTFRejMjqnUtflo3nsfWy3Fbe>6U;@B4OM8!v(Th5QKz1d)A5F6H2xQhc)`o0CKn90xpwI(r&C(44?ur!gLAKa&28q1F z5^RA^CY^0B1kwYCKpp`00qqk2(WcH(Gg4FTgF+!T{~Bl#02fn9TD&oZt%;WZw-8%L6TTA*K~H>@E1<$ z4R9~6MLkcnT$d))%x=AbDDYzaC3$+2pw#;gz_)j}7IaOqk05Ja*LM$bA8IJLJNKeK z090g=lqu}U7j`&O10+F!h|A(BDFcR~?@7XbEJP9c@Z}CE$Z{2kX#Q6raLCC0fix&+ z`u>HTLo50DLHyu6^Nb&GF=pmV`EA!l2$GR z|7sNBz3G_LU}MAnLaY_J?f9fdkK9gqV#ECEQRkHUmy3J-TdxD{zi1tX&p$Hq+Zk1- zCRU!B1ec=FK3{fnRmYui35_qd+>piB$TOO~bwl9m+>`2XtfYxHf zMIulxkEO`C87Wd3zdDAZ=N5=Ckwc|TH(YSa?d7*7qmTrV$+vbs1lq(XRav=Lk8*E> zQo#i^sRJ6|*rg>WwspPhZNG*j2<#KLFa%40KrBr?#t@Izq_Q;`Y+VL;X{zsROu%D_ zfZCBu+*gne?$oX7EMTn|ZO0eU{)bAXk=AGJm*!Sqno&N0t>s0r*nmImxb!89Hl|$o z!b8)A0|NWwI~K($w_}$5{QR3 z6CfUgRikVoYCTHRM8i3!v=(FsLPVuB+9)Uqrb?oXV>E>%%7{G9E4R}nuM?=rv+DH3 zf}WQ&$31|B@JxkiQT=rn_`@*hlyp*Lc0jpcWsx`orlRtN(1v^yf&nB8ATF>Q5hHdN z2r4_j6C#ahWNpZ~yo?v&1o%w}urL*6zH4WtdAat)3z`Aq-=Go)^N*@Wq$TInZ(82^ z7ca|IZd*3LDAGZ!g>mykL?rBh#~{#<0kIl_5@ryC^-mkmPpddBQJ5r=CgeWIswk?eOfVy-)Vr1s;83`vHX<%VWlA19$^!S! zE3~dja2*DCX_q+5c$Eq9?3TB_J8+0i?E=z6vIebfm{t4aS zOat;?-D@=?E=Ap2FMJ_Yqj*8U*rl**QpHJGO2d;?LqV6mp5J&CEccuSaNWA8mSN`% zdhE;1w_k01iyf{Qhd2^mj-Ozmr<9)hdG7<4ght|21GTQH%|v1x2FqQ-fN+q`qg1*Y z!sAeGK^VNc7-9xu;TM7B3wg+)eb>%IziE5tUmnlv;TMHbfe)HDg~_k)y#wdJoZfs> zNNlC70nJC-;bc@~VU)vw`HxgNdQS@I2>2Hh;khb?Ulv2zy1zz*I2<__ur!V;YA8pw z^V)AuF+$j&1+bq`b=o<%)2nRog_$u<#3zo_RiRtm^$x&~YhLHP-aETT zzZT-m^0bEPdw+Vq?us;}K791cH3J{`9(xDA)I-xdsib#8Vb8MGTNFBuBXr7C zW(inOMU?|(kxPLOktW#27j^Ox+VKgN@iE5nG3MtQuSlVEjWn{raxSdgJ)wU2(8tIG zAJg=C=XXy+xJhoBYrp<@`I#dZzFu0PhkMmgz-2qrWtqLSEs;}`e~6&2mhaiQ^`q~{||p4r_` zwZHAG85CmcAafNCl#(t5loZk~xHz0L&}9fB%i@IHC}|Y{O7Uyd4#6QR3+c#kor`;+ z1@dXU=At<%L&5Gy`5UJ`b2Tzb=F+r=q4oznOkq8${*mu;it z;%hxM15>N7pebof>)YTw7g~mA7xu#cNTV$*!W7(n*A0JF+e&Z$<5NXlFPP3w%L5~7 z2EopPb%0OsFc6x)Xdm?`JvqDZxOYaoYix~A)0@xK-I$!wxuEIx>-v!w45QxKGfv5M zOHO@+#AIsKX+ErKO6f_g5h`xeHGNA5Kas}QKht)5Uh$wac!?Mb9Mt2MOWx+I6Dlsa zX->J9pG9^F8LA}047Hvlrdq0Ql&5#d)NMkL5l#jiBM}ZTFB(`#9D)Qbn%|xUwXlcj zDmf@B$`t*8owuaI`@LK?q#XU*pS9nSB)9NjJC`~{>4(sK1VXP48w_bI^}-yekfnD@ z)y)o7RW*nRerYUtW|s)N45b_(d_{#k$|T3phFl*yM!3g-Jh!BVaY+pkgP%;w?|ad9 zd3D7L!YcZ`-xjl$Hul>o`wAj+TMOVuX}1v=c?`x<{rB< zp?Gj++dEGiFSwNt$x>ULvb$w5jVR$cDin!;8I+UyDEZ}}qwyU`CJBr*0YWfEs}dC?Q4oQsCK_^9Y-8~#Rh?i!G2HkAc3u-ZF|F-+h_4{j1Qy+>rV~W`2105w z6}+HDjIuh#)gs*>5@|~sf;;y{Fe#ANtBEb#RXE3Zd7|b7IGORW_9qi-K(J5M48Cjy zX#k$HbfJB8Qpw3ReeXSCIKS!ir&5(=b>F+;m;X4kYWT&*n=Y}nP7$W(o8OYDtWT5- zP1T=sOY4Xo{Bmx^>8DDE5H%*34$f&nh^$y@8eUX$Z9(&`-Gg5|*ZlUjfludX`dtcp z!cTv`u=c7nx@O<`-+5J@bxW#uNv>lD3!RdivSwgnTH}PQ_E{w- zy)wFHHs6|%aqQ*e?|T?7OtD{cFB`^w1~HEO9eR7n(}!n#Cq*W2@iy`@wYjkL_roOrJL-uLai zAb57k2`}4a5q%a&rj|PTzli@qwF@mjnW&~o;%a>Q?x6;aybR<6*&hm6j6o?43>Czj z`1y7yEpxqe7dCdi?^HB6x9#4usG%rL?vd7|-4DjcHS8MsYFhD07#|h(2^r0htT?Ib zp(W551C) z&9IDks+-|J7{o9TX-68SY5JTJ>Xy`AUC{d>s5zpPLn@{i&P*!mot5A7bjxk$nEJWZ zSLYqOD~q%&?0f&^;~&gveS1RHd6>ezWJns{EW(EX%dJzhRSDR%BtQlK20{kfj|jUY zwvJFV+%h68_!&odrMdvBmgsTVoGI+95cZ(uCy%~hjdjUHUE*Aj9wUz`BS$M28-_D4 z|HI>!(MRkIH}&g4@Sn2e4k}lH*xXwPzz^e7wTtu&D3Rxip;lQksV@K*aU4*HN4bZ} zZRlhH;i)itrnX5DsZp))D@K8vN1Y4R(x6JIy5YsP`;*M0EW|Lq^(NS=$+efHktP>} zMTK6bG)sbX<1^a89(bfQ&H@++)jOv*%VO+qVP^N#)(Pq+X^hnk*j}NXSkOJWdIT(- zTVCgc?5;Q3Aw|%>(0tVmbWMbIhUWOHwzr)yIKJAWpxZN}V}kB1+#q88^F0r|ESKhW z+?!B(#shFgcfmEQ4_X(hiNI-~YzGLj5^WA@9yN}GWjshr06%;|UIht|Bd$UJLlPhe z!VhZ5u>j1hO`ud1zl+Kggu$pC?}Z=%PeY#NsO!OWTahmp3;uSC;NG_7DKfPz~Ir-$FF ze71CG&6&^KN(Ut-Fp-PS#L?}9fjT80f(aq$V+v{n$=#|y+eDlpI z_R+caD|6az%hQ@yp7_`!t<5FO=#|p}>f`nLt4|Z5YMj?|cS2nCq?G!J@pY2WDz}uj zRmVS)gqRj}K9DM`%TE91vFu~++3gczt6d_kk7sr68UFf(uDdUG+?~?&rfcbtd--Xn zv`(q2My75g@fZXin=|+|@ubdL1q~!v+*A}mQ4W@dgDNTD-%tTD5~N4G!(b>D){q)U zjIupeeiEDjL&+Pg4&EKrkbNLX9gRY;)nQ}=CQ6RBj5*(Z{L=Li3TvY-n6Uv_CiHM}>8%(3*oDY8sRKI~MdUI#T%xh%D1S^s^v zcz9QcZK!$$h&8SH%4FLpv;l0B8qq#^@!Xu6t6*T{X|0R;KXlLPm{mM9-F{7`umGPdIr+)# z>T9oezB{dIcy{IRi}q{Jw!H1B>GRT_*m(ZS*Lv>FANb^%);p677ssV`NTaJIu{9u_ zu$REl0$`((OYN=;l>`{JW}(iDV=I7B+?XuOZ@ZF&X53G`-Ob?!cvfVW2)bGh8aLm;2sI|agaC)v@P1zk5)P-woFWEL+nE;F_tV)zKoha z0*7x3spGK6KmhKg>`$K3B28#?%I?8D-HQk1nH}yx-SuxyslMi#-#^8E)ua5Zr|yDG zeN3L%>XLWDLw^Abt$W$&$Fygp@paGr!+=ga~tcCWrb3qJ}TRQyTAslrV-a9{{df z0?wRXli!w%6b4Bm^@I4Mrd*DlLzK(Dd@9Z354Z_oZypo{w907J$2o;MeM3A1&-@Cg zf)(7aBe?mf-@zbM1R<0NZONV@=-I%-xF$7`o&VK2|Gee)+_u{&bqPmHz@!n#?nq0K0ez!Jrgum0RRRPLcz0f9pPJc8Iywkl;EUEIBUnJUDTZ1|kf8X+ z&M40A%_sY#>jz7Kn1VV!^_kjHHAZCwb^w~C_ZoXXcyCaz`-_2ymiFBPS|hnNtW8RC zN7wusH1lTh2a36%%B|)Rzv15@tS8U81iGCu`^V7bAt)w(^X_7#l^UkdjI-*SHNuSz2nPWr ze3;R{=`H1@zjd!Vk9x6q7@ZDAWUP|7W`gTTUBO-Z<&vxvb_T-<{s%FI!b%K9oZawI zW=Wh4UsPl{NZWw+?Uoeqd-Mc|d_pWJ4M1e&u5JQJ1t3Hl7!wFUtC3L%H*d6G4ITd7 z#PYM$e1}rn_Dph6V~SjW)dLX8=R$8FhMC>K`9qF^oq%Eow*A@|(&_C&uLmml2B`0Z zy#+uWRLFol9_SYZK6HLTRt=dkB|+Kbw-1+6=?x;`Ffh6nL^{u^5to8~Sym60fbE35 zB=_HK&LL)Ld@XWefK&vfH_0ka}-II>6g~g4JSXFWWOvXG|`G6eW1Hp;)n>^ zLQz{1W~Z%AvA`?=sz;UfC`zB`2Fju4BQW_O@KrXi$}>QE1hc`sf$)df4`}5I8wn19 zh~Q}h^_x$9?pbjjKc{R3sb5GHuQV?*Us}-jv8VPt2QAa)6iJ|7nuMwix)M~_nPhFG zInW{^MnFe|X+UHWV{=Y{u(pb(3e^GFkE4|@=+3{?__i#)he~^NLra;Hx}5+vV$&AL z@N{sqP`$zpg6P2QD4?FCiM62Y+{*_&Ea&8z(0HTNY$T?6xhRb2?|>RKaj`lE1q8e_Tz?2NHORq`-{2Y%7~V?4LAO$ zx8({*aySi0LuILu92f~sL-!t11uYFq-mY24Nc4_HT#K%~QdxTIvVjL4$xRHQAN4+P zO8d*FSgv}RE~0jWkcywk)UED@D}d890F(>noZU0od=ZKZS)A1+vBo8)T83hDPMb?{ zzXaWUSYjN#H3`6zR9gD~H0LW`rV;S)kIWfp5C%NpnDe7{cEULd(J!dEy6n_vu4Sj# zzC5aDfu{D$l_s}2WgG(&OKBHY8QF?|kq8|%dc@#u7J+$75rZkj(9sC062naMtyi}V zeK8Gp`$X43Q%XDrGBtQMc;+>U{DBfqY0dAt-+B8_FLb>N7fPN4?FL)ZBvG~U^fq_s z6TbN@tf?fb0XUq-K>fHX8ZQMbP20@zSqtn}SDgHGO8s?&X?a@Pt9>8MXuLJibl$b( zI8l6X&Jc;MONC0G=8StnejIcDzMVHEP0sCpy6vuWW-sPU+`<@r2gNDCFfuHJi73ki z)EXe51&04~#7A$}qHrpGZiB1~^tENa%flGscIYB=pT^&1!nbC9_vV zaNmYjh|=kn%idP--Y1-LyQImDj{Fpvl%8@T@U&w+Q$K<^f8WjvL#7w?dzdc>0TrN( z9?lb}16p)kd;xJR80@&3H@feDe{eVlN(UT>RKde7?9L^HfWi{qabJ1ij}!Fg3GS{< zge3Cem@UZ|ASg`GPi|~@Yf;~aa&-&01I+@l^jQy)Erd*{ETljBS+v!GY6t;hKHx*3 zfPLh%(2H6|a2LYVeWeo$2joEYhcxp1<7h=eiYHBMA`vEjg<6JxJac~3&PyXEbc4+ilq2qpB_1esubWCt_zdjy4rV#J~dFr`H6DNt3nK4ZDEE`^tk7OV~P!d9j!JTqwNkFeFA`!gwm{$i% z2{O`WDb1c~tql1Es0_-0VvyYcR*4Gh9P^()3+hwIk_&JIB@!msO*5&IjWBOGLR_)9t3H9&wS-=x=f-1l(unMR!AkUG%c^b{$%Yf z=hSX7$N-KJbC#j$YzLIU7N^`Ec}_2tB$mXnedryl{L#+&yLOh~{(+@*jhi&JjY`0v zJ2oYAp=7l6wh@nlUh3%s#$FkR@~FFZi1<(J#<4MxrkJifF{6KU*AF}59Z*V|ET!E| zKT5y`U8te@C)1i@20!h7>z`lm{QzPrqDpXg0d8$lS#aqVQZP~!Z%jvyHqkihKqLTK-q)e^K$MPQoo9yE$CSMVhQbpVYnm&+|I&z!mVTqv*1c%?ZGz zjxmX2W6Xn}G`;;l=J&oYO==`o2To~aG?+K^je>)M5m1l~yzu4n{*Sz^Bci&2ZGwI> zl)AQYlOBp$xYiHy8{`+%#+VO3M(1S(b$5dJLakGP(wm-R0J2)%aBD^Xr=)>|9%&2_ z0na>~a}x0_zy>InY)tkpKLd`-QGWU{&zbBjMA=?0>UZ2fgMb@D&LucFS#X-=Kz#`GiMf2&FwCb?AOW?8M#C@Nq{kg8<-#ftwcOlt=C|&; zbHtOQYaTjzJZC8iqv&==OAZQWML<>yfUIKd4A;cr1}zBfz_+0KGj<~%ei&g^_kSFC z{txaI!<6NrmkR5+^!xMK)%9Rr+dXM)Be9V`kvYT8uXa9=fjY(wgnkg}TlpxPU-!E& zcYi>%D!@OW9cs$BlM4I>I+1^H4Ez}UJg#Y4q9VrvvPfYD^BK_f-l37N<#|0U+)9)Y zOb4+dQED-CC&K~si{WNtv|kwktHAtB&gnwC4FN?IZoYXAVC~$RYrp7ve`4i1L0Lm! z48MrG3&6`!W$Hi0obj?$KFAS+jn7#w8E^j6_`-hD&LOCQ`$FGS2#6Xeujqb;N5-IE z5;VSFu3X(l$u%y5fc4NvrZoC@-ksO=o>T5|4l-as!Goo)IRn(x03@>m*IuD}!bH`4 z6w82i(9jX6-7@_3l$sj=ja1Piv_sF?(t3MR&6{#{hj6f*2p=?&A&)>N{)6-RiOv~1 zhwT~M#w;#C>smQFr}F`N-Z^lpzG)o#`(mtoavc|><@|~`-%Ku`bewj0R{J|+B{ptd zJQt!LSAL%JE8zS}xWGy-(yF=e`}|w~>~6Wt?ao8JiQAGX$>23vWDA{OE+d8DD|km>8u^){513xr~?2fyzo`_t$%VhU*sT3U7N`5%HhHdT&z`i zI3Eq?o6Q~2y6H!rZNJY2S8?FLeN!mjVgY5^ir5l zq$%(GmsvOe!P$)7s|;>oC}=}5qjNmXxsdZK#(VLqn&mBb9Q literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/gray.bmp b/examples/src/ssg/viewer/data/gray.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3c4376bc1dad0dcb2d04f1f7ae40389c34b66f8d GIT binary patch literal 12342 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 C%-iVz literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/data/plib.ac b/examples/src/ssg/viewer/data/plib.ac new file mode 100644 index 0000000..17e479f --- /dev/null +++ b/examples/src/ssg/viewer/data/plib.ac @@ -0,0 +1,589 @@ +AC3Db +MATERIAL "ac3dmat3" rgb 1 0 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat5" rgb 1 1 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat6" rgb 0 1 0 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +MATERIAL "ac3dmat9" rgb 0 0 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.5 0.5 0.5 shi 10 trans 0 +OBJECT world +kids 1 +OBJECT poly +name "poly" +loc 0.05 -0.05 0.15 +numvert 64 +-1.85 -0.25 1.05 +-1.45 -0.25 0.45 +-1.45 -0.25 1.05 +-1.15 -0.25 0.35 +-0.85 -0.25 -0.15 +-0.95 -0.25 0.15 +-0.85 -0.25 -0.45 +-1.45 0.25 1.05 +-1.45 0.25 0.45 +-1.85 0.25 1.05 +-0.95 0.25 0.15 +-0.85 0.25 -0.15 +-1.15 0.25 0.35 +-0.85 0.25 -0.45 +0.05 -0.25 0.75 +-0.35 -0.25 0.75 +0.05 -0.25 1.05 +-0.75 -0.25 1.05 +0.05 0.25 1.05 +-0.35 0.25 0.75 +0.05 0.25 0.75 +-0.75 0.25 1.05 +0.25 -0.25 1.05 +0.25 0.25 1.05 +0.65 0.25 1.05 +0.65 -0.25 1.05 +0.85 -0.25 1.05 +0.85 0.25 1.05 +1.25 0.25 1.05 +1.25 -0.25 1.05 +1.55 0.25 0.95 +1.55 -0.25 0.95 +1.75 0.25 0.75 +1.75 -0.25 0.75 +1.85 0.25 0.45 +1.85 -0.25 0.45 +1.85 0.25 0.15 +1.85 -0.25 0.15 +1.75 0.25 -0.15 +1.75 -0.25 -0.15 +1.55 0.25 -0.35 +1.55 -0.25 -0.35 +1.25 0.25 -0.45 +1.25 -0.25 -0.45 +-1.85 -0.25 -1.05 +-1.45 -0.25 -1.05 +-0.95 -0.25 -0.75 +-1.15 -0.25 -0.95 +-1.85 0.25 -1.05 +-1.45 0.25 -1.05 +-1.15 0.25 -0.95 +-0.95 0.25 -0.75 +-0.35 -0.25 -1.05 +-0.75 -0.25 -1.05 +-0.75 0.25 -1.05 +-0.35 0.25 -1.05 +0.65 -0.25 -1.05 +0.25 -0.25 -1.05 +0.65 0.25 -1.05 +0.25 0.25 -1.05 +1.25 -0.25 -1.05 +0.85 -0.25 -1.05 +1.25 0.25 -1.05 +0.85 0.25 -1.05 +numsurf 80 +SURF 0x20 +mat 0 +refs 3 +0 0 -5.67663e-08 +1 0.4 0.285714 +2 0.4 -5.67663e-08 +SURF 0x20 +mat 0 +refs 3 +3 0.7 0.333333 +4 1 0.571429 +5 0.9 0.428571 +SURF 0x20 +mat 0 +refs 3 +1 0.4 0.285714 +4 1 0.571429 +3 0.7 0.333333 +SURF 0x20 +mat 0 +refs 3 +1 0.4 0.285714 +6 1 0.714286 +4 1 0.571429 +SURF 0x20 +mat 0 +refs 3 +7 0 0 +8 0 0 +9 0 0 +SURF 0x20 +mat 0 +refs 3 +10 0 0 +11 0 0 +12 0 0 +SURF 0x20 +mat 0 +refs 3 +12 0 0 +11 0 0 +8 0 0 +SURF 0x20 +mat 0 +refs 3 +11 0 0 +13 0 0 +8 0 0 +SURF 0x20 +mat 0 +refs 4 +1 0 0 +8 0 0 +7 0 0 +2 0 0 +SURF 0x20 +mat 0 +refs 4 +2 0 0 +7 0 0 +9 0 0 +0 0 0 +SURF 0x20 +mat 0 +refs 4 +4 0 0 +11 0 0 +10 0 0 +5 0 0 +SURF 0x20 +mat 0 +refs 4 +5 0 0 +10 0 0 +12 0 0 +3 0 0 +SURF 0x20 +mat 0 +refs 4 +3 0 0 +12 0 0 +8 0 0 +1 0 0 +SURF 0x20 +mat 0 +refs 4 +6 0 0 +13 0 0 +11 0 0 +4 0 0 +SURF 0x20 +mat 1 +refs 3 +14 1 0.142857 +15 0.5 0.142857 +16 1 -5.67663e-08 +SURF 0x20 +mat 1 +refs 3 +16 1 -5.67663e-08 +15 0.5 0.142857 +17 0 -5.67663e-08 +SURF 0x20 +mat 1 +refs 3 +18 0 0 +19 0 0 +20 0 0 +SURF 0x20 +mat 1 +refs 3 +21 0 0 +19 0 0 +18 0 0 +SURF 0x20 +mat 1 +refs 4 +14 0 0 +20 0 0 +19 0 0 +15 0 0 +SURF 0x20 +mat 1 +refs 4 +16 0 0 +18 0 0 +20 0 0 +14 0 0 +SURF 0x20 +mat 1 +refs 4 +17 0 0 +21 0 0 +18 0 0 +16 0 0 +SURF 0x20 +mat 2 +refs 4 +22 0 0 +23 0 0 +24 0 0 +25 0 0 +SURF 0x20 +mat 3 +refs 4 +26 0 0 +27 0 0 +28 0 0 +29 0 0 +SURF 0x20 +mat 3 +refs 4 +29 0 0 +28 0 0 +30 0 0 +31 0 0 +SURF 0x20 +mat 3 +refs 4 +31 0 0 +30 0 0 +32 0 0 +33 0 0 +SURF 0x20 +mat 3 +refs 4 +33 0 0 +32 0 0 +34 0 0 +35 0 0 +SURF 0x20 +mat 3 +refs 4 +35 0 0 +34 0 0 +36 0 0 +37 0 0 +SURF 0x20 +mat 3 +refs 4 +37 0 0 +36 0 0 +38 0 0 +39 0 0 +SURF 0x20 +mat 3 +refs 4 +39 0 0 +38 0 0 +40 0 0 +41 0 0 +SURF 0x20 +mat 3 +refs 4 +41 0 0 +40 0 0 +42 0 0 +43 0 0 +SURF 0x20 +mat 3 +refs 3 +26 0 1 +29 0.4 1 +43 0.4 0.285714 +SURF 0x20 +mat 3 +refs 3 +31 0.7 0.952381 +33 0.9 0.857143 +29 0.4 1 +SURF 0x20 +mat 3 +refs 3 +29 0.4 1 +33 0.9 0.857143 +43 0.4 0.285714 +SURF 0x20 +mat 3 +refs 3 +41 0.7 0.333333 +43 0.4 0.285714 +39 0.9 0.428571 +SURF 0x20 +mat 3 +refs 3 +39 0.9 0.428571 +43 0.4 0.285714 +37 1 0.571429 +SURF 0x20 +mat 3 +refs 3 +37 1 0.571429 +43 0.4 0.285714 +35 1 0.714286 +SURF 0x20 +mat 3 +refs 3 +33 0.9 0.857143 +35 1 0.714286 +43 0.4 0.285714 +SURF 0x20 +mat 3 +refs 3 +42 0 0 +28 0 0 +27 0 0 +SURF 0x20 +mat 3 +refs 3 +28 0 0 +32 0 0 +30 0 0 +SURF 0x20 +mat 3 +refs 3 +42 0 0 +32 0 0 +28 0 0 +SURF 0x20 +mat 3 +refs 3 +38 0 0 +42 0 0 +40 0 0 +SURF 0x20 +mat 3 +refs 3 +36 0 0 +42 0 0 +38 0 0 +SURF 0x20 +mat 3 +refs 3 +34 0 0 +42 0 0 +36 0 0 +SURF 0x20 +mat 3 +refs 3 +42 0 0 +34 0 0 +32 0 0 +SURF 0x20 +mat 0 +refs 3 +44 0 1 +1 0.4 0.285714 +0 0 -5.67663e-08 +SURF 0x20 +mat 0 +refs 3 +45 0.4 1 +1 0.4 0.285714 +44 0 1 +SURF 0x20 +mat 0 +refs 3 +46 0.9 0.857143 +45 0.4 1 +47 0.7 0.952381 +SURF 0x20 +mat 0 +refs 3 +46 0.9 0.857143 +1 0.4 0.285714 +45 0.4 1 +SURF 0x20 +mat 0 +refs 3 +46 0.9 0.857143 +6 1 0.714286 +1 0.4 0.285714 +SURF 0x20 +mat 0 +refs 3 +9 0 0 +8 0 0 +48 0 0 +SURF 0x20 +mat 0 +refs 3 +48 0 0 +8 0 0 +49 0 0 +SURF 0x20 +mat 0 +refs 3 +50 0 0 +49 0 0 +51 0 0 +SURF 0x20 +mat 0 +refs 3 +49 0 0 +8 0 0 +51 0 0 +SURF 0x20 +mat 0 +refs 3 +8 0 0 +13 0 0 +51 0 0 +SURF 0x20 +mat 0 +refs 4 +0 0 0 +9 0 0 +48 0 0 +44 0 0 +SURF 0x20 +mat 0 +refs 4 +44 0 0 +48 0 0 +49 0 0 +45 0 0 +SURF 0x20 +mat 0 +refs 4 +45 0 0 +49 0 0 +50 0 0 +47 0 0 +SURF 0x20 +mat 0 +refs 4 +47 0 0 +50 0 0 +51 0 0 +46 0 0 +SURF 0x20 +mat 0 +refs 4 +46 0 0 +51 0 0 +13 0 0 +6 0 0 +SURF 0x20 +mat 1 +refs 3 +52 0.5 1 +53 0 1 +15 0.5 0.142857 +SURF 0x20 +mat 1 +refs 3 +15 0.5 0.142857 +53 0 1 +17 0 -5.67663e-08 +SURF 0x20 +mat 1 +refs 3 +19 0 0 +54 0 0 +55 0 0 +SURF 0x20 +mat 1 +refs 3 +21 0 0 +54 0 0 +19 0 0 +SURF 0x20 +mat 1 +refs 4 +52 0 0 +55 0 0 +54 0 0 +53 0 0 +SURF 0x20 +mat 1 +refs 4 +15 0 0 +19 0 0 +55 0 0 +52 0 0 +SURF 0x20 +mat 1 +refs 4 +53 0 0 +54 0 0 +21 0 0 +17 0 0 +SURF 0x20 +mat 2 +refs 3 +25 1 -5.67663e-08 +56 1 1 +22 0 -5.67663e-08 +SURF 0x20 +mat 2 +refs 3 +56 1 1 +57 0 1 +22 0 -5.67663e-08 +SURF 0x20 +mat 2 +refs 3 +23 0 0 +58 0 0 +24 0 0 +SURF 0x20 +mat 2 +refs 3 +23 0 0 +59 0 0 +58 0 0 +SURF 0x20 +mat 2 +refs 4 +25 0 0 +24 0 0 +58 0 0 +56 0 0 +SURF 0x20 +mat 2 +refs 4 +56 0 0 +58 0 0 +59 0 0 +57 0 0 +SURF 0x20 +mat 2 +refs 4 +57 0 0 +59 0 0 +23 0 0 +22 0 0 +SURF 0x20 +mat 3 +refs 3 +60 0.4 -5.67663e-08 +61 0 -5.67663e-08 +43 0.4 0.285714 +SURF 0x20 +mat 3 +refs 3 +61 0 -5.67663e-08 +26 0 1 +43 0.4 0.285714 +SURF 0x20 +mat 3 +refs 3 +62 0 0 +63 0 0 +42 0 0 +SURF 0x20 +mat 3 +refs 3 +63 0 0 +27 0 0 +42 0 0 +SURF 0x20 +mat 3 +refs 4 +61 0 0 +63 0 0 +27 0 0 +26 0 0 +SURF 0x20 +mat 3 +refs 4 +43 0 0 +42 0 0 +62 0 0 +60 0 0 +SURF 0x20 +mat 3 +refs 4 +60 0 0 +62 0 0 +63 0 0 +61 0 0 +kids 0 diff --git a/examples/src/ssg/viewer/data/sorority.txf b/examples/src/ssg/viewer/data/sorority.txf new file mode 100644 index 0000000000000000000000000000000000000000..8b457aafb72a70f703aaf9c754eb64da85709072 GIT binary patch literal 9304 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 Awg3PC literal 0 HcmV?d00001 diff --git a/examples/src/ssg/viewer/pview.cxx b/examples/src/ssg/viewer/pview.cxx new file mode 100644 index 0000000..899bab3 --- /dev/null +++ b/examples/src/ssg/viewer/pview.cxx @@ -0,0 +1,191 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pview.cxx 2048 2005-09-30 18:14:09Z fayjf $ +*/ + +/* + * pview.cxx + * + * By Bram Stolk. + * Minimalistic plib viewer with a proper 'virtual ball' interface. + * LMB for orbiting model, MMB for translation, RMB for approaching model. + */ + +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950f +#endif + +static float aspectratio=1.0; +static int winw, winh; +static ssgRoot *scene=0; +static ssgContext *context=0; +static sgMat4 camtrf; + + +static void idle(void) +{ + context->setCamera(camtrf); + glutPostRedisplay () ; +} + + +static void redraw(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + glMatrixMode(GL_MODELVIEW); + if (scene) ssgCullAndDraw(scene); + glutSwapBuffers () ; +} + + +static int prevx=0; +static int prevy=0; +static int prevbutton=-1; + +static void mouse(int button, int state, int x, int y) +{ + prevx=x; + prevy=y; + prevbutton=button; +} + +static void motion(int x, int y) +{ + const float vel=220.0; + float dx = vel * (x-prevx) / winw; + float dy = vel * (y-prevy) / winh; + prevx = x; prevy = y; + + if (prevbutton==0) + { + sgQuat hq, vq; + sgAngleAxisToQuat(hq, -dx, camtrf[2]); + sgAngleAxisToQuat(vq, -dy, camtrf[0]); + sgQuat totalq; + sgMultQuat(totalq, vq, hq); + sgNormalizeQuat(totalq); + sgMat4 m; + sgQuatToMatrix(m, totalq); + sgPostMultMat4(camtrf, m); + return; + } + if (prevbutton==1) + { + float curdist = sgLengthVec3(camtrf[3]); + sgAddScaledVec3(camtrf[3], camtrf[2], dy*curdist/200.0f); + sgAddScaledVec3(camtrf[3], camtrf[0], -dx*curdist/200.0f); + return; + } + if (prevbutton==2) + { + float curdist = sgLengthVec3(camtrf[3]); + sgAddScaledVec3(camtrf[3], camtrf[1], -dy*curdist/80.0f); + return; + } +} + + +static void reshape(int w, int h) +{ + glViewport ( 0, 0, w, h ) ; + + aspectratio = w / (float) h; + winw=w; winh=h; + float fovy = 60.0f * M_PI / 180.0f; + float nearPlaneDistance = 0.4f; + float farPlaneDistance = 2500.0; + float y = (float)tan(0.5 * fovy) * nearPlaneDistance; + float x = aspectratio * y; + context->setFrustum(-x,x,-y,y,nearPlaneDistance,farPlaneDistance); +} + + +static void keyboard(unsigned char k, int, int) +{ + if (k==27) + { + exit(0); + } +} + + +int main(int argc, char *argv[]) +{ + if (argc<2) + { + fprintf(stderr,"Usage: %s model0 [model1] .. [modeln]\n", argv[0]); + return -1; + } + glutInitWindowSize(512,512); + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE); + glutCreateWindow(argv[1]); + glutDisplayFunc(redraw); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyboard); + glutMouseFunc(mouse); + glutIdleFunc(idle); + glutMotionFunc(motion); + + ssgInit(); + + glClearColor(0.3f,0.3f,0.55f,1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + scene = new ssgRoot(); + + float amb[4]={0.2f, 0.2f, 0.2f, 1.0f}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb); + + ssgTransform *trf = new ssgTransform(); + scene->addKid(trf); + + for (int i=1; iaddKid(ssgLoad(argv[i])); + + context = new ssgContext(); + context->makeCurrent(); + + sgVec3 off; + sgZeroVec3(off); + sgSubVec3(off, scene->getBSphere()->getCenter()); + + trf->setTransform(off); + SGfloat radius = scene->getBSphere()->getRadius(); + float d = 1.8f*radius; + if (d<0.5) d=0.5; // avoid placing near-plane beyond model + sgMakeTransMat4(camtrf, 0, -d, 0); + + ssgLight *light=ssgGetLight(0); + light->setColour(GL_AMBIENT,0,0,0); + light->setPosition(0,0,radius); + light->on(); + + glutMainLoop(); + + return 0; +} + diff --git a/examples/src/ssg/viewer/pview.dsp b/examples/src/ssg/viewer/pview.dsp new file mode 100755 index 0000000..1888441 --- /dev/null +++ b/examples/src/ssg/viewer/pview.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="pview" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pview - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pview.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pview.mak" CFG="pview - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pview - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pview - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pview - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /I "..\..\..\..\..\\" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib fnt_d.lib pui_d.lib ul_d.lib opengl32.lib glu32.lib /nologo /subsystem:console /machine:I386 /out:"pview.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "pview - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /I "..\..\..\..\..\\" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "FREEGLUT_STATIC" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib fnt_d.lib pui_d.lib ul_d.lib opengl32.lib glu32.lib /nologo /subsystem:console /debug /machine:I386 /out:"pview.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "pview - Win32 Release" +# Name "pview - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\pview.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/examples/src/ssg/viewer/viewer.cxx b/examples/src/ssg/viewer/viewer.cxx new file mode 100644 index 0000000..e4a3f27 --- /dev/null +++ b/examples/src/ssg/viewer/viewer.cxx @@ -0,0 +1,859 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: viewer.cxx 2099 2006-11-03 20:04:40Z fayjf $ +*/ + +/**** +* NAME +* viewer - SSG model viewer +* +* DESCRIPTION +* using a PUI interface, allow user to load a model, +* animate the model, and manipulate the camera. +* several models can be loaded at the same time. +* +* MODIFICATION HISTORY +* Sep-2000 Dave McClurg Created +* Sep-2001 Dave McClurg Added wireframe toggle +****/ + +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include + +#ifdef FREEGLUT_IS_PRESENT +# include +#else +# ifdef __APPLE__ +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include + +#define ARROWS_USED 1 +/* +!!!FIXME!!! +Defines what type of arrows to uses with puFileSelector +0 - No arrows +1 - Single move arrows +2 - Single move and Jump 10 moves +*/ + + + +/* +scene graph +*/ +static ssgRoot *scene = NULL ; +static ssgContext *context = NULL ; +static ssgEntity* camera_object = NULL ; + +/* +font vars +*/ +static fntRenderer *text ; +static fntTexFont *font ; + +static puaFileSelector* file_selector = 0 ; + +/* +frame rate vars +*/ +static int frame_counter = 0; +static float fps = 0; + +/* +animation vars +*/ +#define MAX_SPEED 60 +static const int speed_tab [] = +{ 0, 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 }; + +static int speed_index = 1 ; +//static int anim_frame ; +static int num_anim_frames ; + +/* +wire vars +*/ +static int wire_flag = 0 ; +static sgVec4 wire_colour = { 1.0f, 1.0f, 1.0f, 1.0f } ; + +/* +spinner vars +*/ +static int downx, downy; /* for tracking mouse position */ +static int downb = -1; /* and button status */ + +static GLfloat downDist, downEl, downAz, /* for saving state of things */ +downEx, downEy, downEz; /* when button is pressed */ + +static GLfloat dAz, dEl, lastAz, lastEl; /* to calculate spinning w/ polar motion */ +static GLfloat AzSpin = 0.0f, ElSpin = 0.0f; +static int AdjustingAzEl = 0; + +/* Minimum spin to allow in polar (lower forced to zero) */ +#define MIN_AZSPIN 0.1f +#define MIN_ELSPIN 0.1f + +/* Factors used in computing dAz and dEl (which determine AzSpin, ElSpin) */ +#define SLOW_DAZ 0.90f +#define SLOW_DEL 0.90f +#define PREV_DAZ 0.80f +#define PREV_DEL 0.80f +#define CUR_DAZ 0.20f +#define CUR_DEL 0.20f + +/* +* polar movement parameters +*/ +static GLfloat EyeDist= 100.0f; +static GLfloat EyeAz = 0.0f; +static GLfloat EyeEl = 30.0f; +static GLfloat Ex = 0.0f; +static GLfloat Ey = 0.0f; +static GLfloat Ez = 0.0f; + +#define FOVY 45.0f +#define FOV_NEAR 0.1f +#define FOV_FAR 10000.0f + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950f +#endif + +static int getWindowHeight () { return glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ; } +static int getWindowWidth () { return glutGet ( (GLenum) GLUT_WINDOW_WIDTH ) ; } + +static void begin2d ( void ) +{ + int w = getWindowWidth () ; + int h = getWindowHeight () ; + + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + glEnable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + //create an projection that acts like a 2D screen + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + glOrtho ( 0, w, 0, h, 0, 1 ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; +} + +static void end2d ( void ) +{ + glMatrixMode ( GL_PROJECTION ) ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + + glDisable ( GL_ALPHA_TEST ) ; + glDisable ( GL_BLEND ) ; + glAlphaFunc ( GL_ALWAYS, 0.0 ) ; + glBlendFunc ( GL_ONE, GL_ZERO ) ; +} + + +static void count_anim_frames( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + count_anim_frames ( br -> getKid ( i ) ) ; + + if ( e -> isAKindOf ( ssgTypeSelector() ) ) + { + ssgSelector* p = (ssgSelector*) e ; + int num = p -> getMaxKids () ; + if ( num > num_anim_frames ) + num_anim_frames = num ; + } + else if ( e -> isAKindOf ( ssgTypeTransform() ) ) + { + ssgBase* data = e -> getUserData () ; + if ( data != NULL && data -> isAKindOf ( ssgTypeTransformArray() ) ) + { + ssgTransformArray* ta = (ssgTransformArray*) data ; + int num = ta -> getNum () ; + if ( num > num_anim_frames ) + num_anim_frames = num ; + } + } + } + else if ( e -> isAKindOf ( ssgTypeLeaf() ) ) + { + ssgLeaf* leaf = (ssgLeaf *) e ; + ssgState* st = leaf -> getState () ; + + if ( st && st -> isAKindOf ( ssgTypeStateSelector() ) ) + { + ssgStateSelector* ss = (ssgStateSelector*) st ; + + int num = ss -> getNumSteps () ; + if ( num > num_anim_frames ) + num_anim_frames = num ; + } + } +} + + + +// You need to have a object with animation loaded into memory for the following function to make sense. +// Animation can be discreet (in steps) or "smooth" (fluid). +// If you loaded an animation made of several meshes, it will use a selector and therefore always be "discreet" +// If you loaded some transformation matrices like some rotations to rotate the flap of a plane, +// the define DISCREET_STEPS will determine whether the animation is smooth (new code, WK) or +// in steps ("old" code, Dave McClurg). +// +// The anim_frame is for discreet animation and an index determining which matrix / which selector will be used. +// +// typically, you will call set_anim_frame directly before you call ssgCullAndDraw +static void set_anim_frame( ssgEntity *e, int anim_frame ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + set_anim_frame ( br -> getKid ( i ), anim_frame ) ; + + if ( e -> isAKindOf ( ssgTypeSelector() ) ) + { + ssgSelector* p = (ssgSelector*) e ; + int num = p -> getMaxKids () ; + if ( num > 0 ) + { + int frame = anim_frame ; + frame %= num ; + p -> selectStep ( frame ) ; + } + } + } + else if ( e -> isAKindOf ( ssgTypeLeaf() ) ) + { + ssgLeaf* leaf = (ssgLeaf *) e ; + ssgState* st = leaf -> getState () ; + + if ( st && st -> isAKindOf ( ssgTypeStateSelector() ) ) + { + ssgStateSelector* ss = (ssgStateSelector*) st ; + int num = ss -> getNumSteps () ; + if ( num > 0 ) + { + int frame = anim_frame ; + frame %= num ; + ss -> selectStep ( frame ) ; + } + } + } +} + + + +static int wire_draw ( ssgEntity* e ) +{ + if ( e -> isAKindOf ( ssgTypeLeaf() ) ) + { + ssgLeaf* leaf = (ssgLeaf*) e ; + leaf -> drawHighlight ( wire_colour ) ; + } + return 1 ; +} + + +static void wire_update ( ssgEntity *e, int flag ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + wire_update ( br -> getKid ( i ), flag ) ; + } + else if ( e -> isAKindOf ( ssgTypeLeaf() ) ) + { + ssgLeaf* leaf = (ssgLeaf *) e ; + leaf -> setCallback ( SSG_CALLBACK_POSTDRAW, flag? wire_draw: NULL ) ; + } +} + + +static void make_matrix( sgMat4 mat ) +{ + SGfloat angle = -EyeAz * SG_DEGREES_TO_RADIANS ; + sgVec3 eye ; + eye[0] = (SGfloat) cos (angle) * EyeDist + Ex ; + eye[1] = (SGfloat) sin (angle) * EyeDist + Ey ; + angle = EyeEl * SG_DEGREES_TO_RADIANS ; + eye[2] = (SGfloat) sin (angle) * EyeDist + Ez ; + + sgVec3 center ; + sgSetVec3 ( center, Ex, Ey, Ez ) ; + + sgVec3 up ; + sgSetVec3 ( up, 0.0f, 0.0f, 0.1f ) ; + + sgMakeLookAtMat4 ( mat, eye, center, up ) ; +} + + +static bool get_camera_dir ( ssgEntity* e, sgVec3 eye, sgVec3 target ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + { + if ( get_camera_dir ( br -> getKid ( i ), eye, target ) ) + return true ; + } + } + else if ( e -> isAKindOf ( ssgTypeLeaf() ) ) + { + ssgLeaf* leaf = (ssgLeaf *) e ; + if ( leaf -> getNumVertices () >= 2 ) + { + sgCopyVec3 ( eye, leaf -> getVertex ( 0 ) ) ; + sgCopyVec3 ( target, leaf -> getVertex ( 1 ) ) ; + return true ; + } + } + return false ; +} + + +static void follow_camera ( sgMat4 mat ) +{ + if ( camera_object != NULL ) + { + sgVec3 v1, v2 ; + if ( camera_object -> isAKindOf ( ssgTypeTransform() ) && + get_camera_dir ( camera_object, v1, v2 ) ) + { + ssgTransform* tr = (ssgTransform*) camera_object ; + sgMat4 m ; + tr -> getTransform ( m ) ; + + sgVec3 up = { 0.0f, 0.0f, 1.0f } ; + sgVec3 eye, target ; + sgXformPnt3 ( eye, v1, m ) ; + sgXformPnt3 ( target, v2, m ) ; + sgMakeLookAtMat4 ( mat, eye, target, up ) ; + } + } +} + + +/* + The GLUT window reshape event + Fixed to properly maintain aspect ratio. +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; + + float aspect = w / (float)h; + float angle = 0.5f * FOVY * M_PI / 180.0f; + float y = FOV_NEAR * (float)tan(angle); + float x = aspect * y; + + context->setFrustum(-x,x,-y,y,FOV_NEAR,FOV_FAR); +} + + + +/* + The GLUT display event +*/ + +static void display(void) +{ + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + sgMat4 mat ; + make_matrix ( mat ) ; + follow_camera ( mat ) ; + + context->setCamera ( mat ); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glEnable( GL_DEPTH_TEST ) ; + + /* + update virtual time, from "real" time and speed. + */ + static int last_time = 0; + int curr_time = glutGet((GLenum)GLUT_ELAPSED_TIME); + if (last_time != 0) + // calculate the "virtual time" + _ssgGlobTime += 0.001f*(curr_time-last_time)*speed_tab[ speed_index ]; + + last_time = curr_time; + + //set_anim_frame ( scene, _ssgGlobTime); // currently not needed by our sample data + + /* + figure out how fast this bus is going + */ + static int last_time2 = 0; + if (curr_time >= last_time2 + 2000) { + fps = frame_counter * 1000.0f / (curr_time - last_time2); + frame_counter = 0; + last_time2 = curr_time; + } + + ssgCullAndDraw ( scene ) ; + + /* + do the interface stuff + */ + + int h = getWindowHeight () ; + + begin2d () ; + + text -> setFont ( font ) ; + text -> setPointSize ( 12 ) ; + + if ( scene -> getNumKids() == 0 ) + { + text -> begin () ; + glColor3f ( 0.0f, 0.0f, 0.0f ) ; + text -> start2f ( 4.0f, (float)(h-16) ) ; + text -> puts ( "controls\n" ); + text -> puts ( " : exit\n" ) ; + text -> puts ( " c : clear\n" ) ; + text -> puts ( " l : load a shape or sequence\n" ) ; + text -> puts ( " w : toggle wireframe\n" ) ; + text -> puts ( " : faster animation\n" ) ; + text -> puts ( " : slower animation\n" ) ; + text -> puts ( " : rotate\n" ) ; + text -> puts ( " : zoom\n" ) ; + text -> end () ; + } + else if ( fps > 0 ) + { + + char buffer [ PUSTRING_MAX ] ; + text -> begin () ; + glColor3f ( 0.0f, 0.0f, 0.0f ) ; + text -> start2f ( 4.0f, (float)(h-16) ) ; + sprintf ( buffer, "fps : %.02f\n", fps ) ; + text -> puts ( buffer ) ; + sprintf ( buffer, "dist : %.02f\n", EyeDist ) ; + text -> puts ( buffer ) ; + sprintf ( buffer, "anim_speed : %d fps\n", speed_tab[ speed_index ] ) ; + text -> puts ( buffer ) ; + sprintf ( buffer, "time : %-4d\n", curr_time ) ; + text -> puts ( buffer ) ; + text -> end () ; + + } + + glColor3f ( 1.0f, 1.0f, 1.0f ) ; + end2d () ; + + frame_counter ++; + + puDisplay () ; + + glutPostRedisplay () ; + glutSwapBuffers () ; +} + +/* +* keep them vertical; I think this makes a lot of things easier, +* but maybe it wouldn't be too hard to adapt things to let you go +* upside down +*/ +static int ConstrainEl(void) +{ + if (EyeEl <= -90) { + EyeEl = -89.99f; + return 1; + } else if (EyeEl >= 90) { + EyeEl = 89.99f; + return 1; + } + return 0; +} + +/* What to multiply number of pixels mouse moved by to get rotation amount */ +#define EL_SENS 0.5f +#define AZ_SENS 0.5f + +static void motion(int x, int y) +{ + puMouse ( x, y ) ; + + int deltax = x - downx, deltay = y - downy; + + // if ( scene -> getNumKids() > 0 && !file_selector ) + if ( !file_selector ) + + switch (downb) { + case GLUT_LEFT_BUTTON: + EyeEl = downEl + EL_SENS * deltay; + ConstrainEl(); + EyeAz = downAz + AZ_SENS * deltax; + dAz = PREV_DAZ*dAz + CUR_DAZ*(lastAz - EyeAz); + dEl = PREV_DEL*dEl + CUR_DEL*(lastEl - EyeEl); + lastAz = EyeAz; + lastEl = EyeEl; + break; + case GLUT_RIGHT_BUTTON: + { + float mult = 0.01f; + if ( fabs(downDist) > 1.0f ) + mult = float( 0.001 * fabs(downDist) ); + EyeDist = downDist + mult*deltay; + break; + } + case GLUT_MIDDLE_BUTTON: + { + float mult = 0.01f; + if ( fabs(EyeDist) > 1.0f ) + mult = float( 0.0005f * fabs(EyeDist) ); + + sgMat4 mat ; + make_matrix( mat ) ; + + sgVec3 off; + sgSetVec3( off, mult*deltax, 0, -mult*deltay ); + sgXformVec3( off, mat ); + + Ex -= off[0]; + Ey -= off[1]; + Ez -= off[2]; + } + break; + } +} + +static void mouse(int button, int state, int x, int y) +{ + puMouse ( button, state, x, y ) ; + + if (state == GLUT_DOWN && downb == -1) { + downx = x; + downy = y; + downb = button; + + // if ( scene -> getNumKids() > 0 && !file_selector ) + if (!file_selector ) + switch ( button ) + { + case GLUT_LEFT_BUTTON: + lastEl = downEl = EyeEl; + lastAz = downAz = EyeAz; + AzSpin = ElSpin = dAz = dEl = 0; + AdjustingAzEl = 1; + break; + case GLUT_RIGHT_BUTTON: + downDist = EyeDist; + break; + case GLUT_MIDDLE_BUTTON: + downEx = Ex; + downEy = Ey; + downEz = Ez; + break; + } + } else if (state == GLUT_UP && button == downb) { + downb = -1; + + // if ( scene -> getNumKids() > 0 && !file_selector ) + if ( !file_selector ) + switch ( button ) + { + case GLUT_LEFT_BUTTON: + AzSpin = -dAz; + if (AzSpin < MIN_AZSPIN && AzSpin > -MIN_AZSPIN) + AzSpin = 0; + ElSpin = -dEl; + if (ElSpin < MIN_ELSPIN && ElSpin > -MIN_ELSPIN) + ElSpin = 0; + AdjustingAzEl = 0; + break; + } + } +} + +static void special(int key, int x, int y) +{ + puKeyboard ( key + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN ) ; + + switch ( key ) + { + case GLUT_KEY_LEFT: + case GLUT_KEY_UP: + case GLUT_KEY_RIGHT: + case GLUT_KEY_DOWN: + case GLUT_KEY_PAGE_UP: + case GLUT_KEY_PAGE_DOWN: + case GLUT_KEY_HOME: + case GLUT_KEY_END: + break; + } +} + +static void pick_cb ( puObject * ) +{ + char* str ; + file_selector -> getValue ( &str ) ; + + char fname [PUSTRING_MAX]; + strcpy ( fname, str ) ; + + puDeleteObject ( file_selector ) ; + file_selector = 0 ; + + ssgEntity *obj = ssgLoad ( (char*)fname ); //"test_Med.ase" ) ; + +#if 0 + ssgSaveSSG ( "temp.ssg", obj ) ; + delete obj ; + obj = ssgLoadSSG ( "temp.ssg" ) ; +#endif + +#if 0 + ssgSaveDXF ( "temp.dxf", obj ) ; + delete obj ; + obj = ssgLoadDXF ( "temp.dxf" ) ; +#endif + + if ( !obj ) + return; + + scene -> addKid ( obj ) ; + + num_anim_frames = 0 ; + count_anim_frames ( scene ) ; + wire_update ( scene, wire_flag ) ; + + SGfloat radius = scene->getBSphere()->getRadius(); + EyeDist = float( radius * 1.5f / tan( float( FOVY/2 * SG_DEGREES_TO_RADIANS ) ) ); + if (EyeDist < FOV_NEAR*2) + EyeDist = FOV_NEAR*2; + + sgSphere sp = *( scene -> getBSphere() ) ; + if ( sp.isEmpty() ) + { + Ex = 0.0f ; + Ey = 0.0f ; + Ez = 0.0f ; + } + else + { + Ex = sp.getCenter()[ 0 ] ; + Ey = sp.getCenter()[ 1 ] ; + Ez = sp.getCenter()[ 2 ] ; + } + + /* try to find the animated camera */ + camera_object = scene -> getByName ( "FlyCam" ) ; +} + + + +/* + The GLUT keyboard event +*/ + +static void keyboard(unsigned char key, int, int) +{ + puKeyboard ( key, PU_DOWN ) ; + + switch ( key ) + { + case 0x1b: //escape + exit(0); + break; + case '+': + if ( speed_tab[ speed_index ] != MAX_SPEED ) + speed_index ++ ; + break; + case '-': + if ( speed_tab[ speed_index ] != 0 ) + speed_index -- ; + break; + case ' ': + Ex = 0; + Ey = 0; + Ez = 0; + break; + case 'l': + if ( !file_selector ) + { + file_selector = new puaFileSelector ( ( 640 - 320 ) / 2, ( 480 - 270 ) / 2, 320, 270, ARROWS_USED, "data" ) ; + file_selector -> setCallback ( pick_cb ) ; + } + break; + case 'w': + wire_flag = !wire_flag ; + wire_update ( scene, wire_flag ) ; + break ; + case 'c': + //scene -> removeKid ( obj ); + scene -> removeAllKids () ; + break ; + } +} + +static void idle(void) +{ + EyeEl += ElSpin; + EyeAz += AzSpin; + + /* + * weird spin thing to make things look + * look better when you are kept from going + * upside down while spinning - Isn't great + */ + if (ConstrainEl()) { + ElSpin = -ElSpin; + if (fabs(ElSpin) > fabs(AzSpin)) + AzSpin = float( fabs(ElSpin) * ((AzSpin > 0) ? 1 : -1) ); + } + + if (AdjustingAzEl) { + dAz *= SLOW_DAZ; + dEl *= SLOW_DEL; + } +} + +static void init_graphics () +{ + int fake_argc = 1 ; + char *fake_argv[3] ; + fake_argv[0] = "Viewer" ; + fake_argv[1] = "Simple Scene Graph : Viewer Program." ; + fake_argv[2] = NULL ; + + /* + Initialise GLUT + */ + + glutInit ( &fake_argc, fake_argv ) ; + glutInitWindowSize ( 640, 480 ) ; + glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ; + glutCreateWindow ( fake_argv[1] ) ; + glutDisplayFunc ( display ) ; + glutReshapeFunc ( reshape ) ; + glutKeyboardFunc ( keyboard ) ; + glutMouseFunc ( mouse ) ; + glutMotionFunc ( motion ) ; + glutSpecialFunc ( special ) ; + glutIdleFunc ( idle ) ; + + /* + Some basic OpenGL setup + */ + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + /* + Initialise PLIB + */ + + ssgInit () ; + puInit () ; + + /* + Set up the Sun. + */ + + sgVec3 sunposn ; + sgSetVec3 ( sunposn, 200.0f, -500.0f, 500.0f ) ; + ssgGetLight ( 0 ) -> setPosition ( sunposn ) ; + + + + /* + Set up the path to the data files + */ + + ssgModelPath ( "." ) ; + ssgTexturePath ( "." ) ; + + /* + Create a root node + */ + + scene = new ssgRoot ; + context = new ssgContext ; + context->setCullface( true ) ; + context->makeCurrent ( ) ; + + /* + ** Set up PU + */ + text = new fntRenderer () ; + font = new fntTexFont ( "data/default.txf" ) ; + + puFont font1 ( font, 12 ) ; + puFont font2 ( font, 15 ) ; + puSetDefaultFonts ( font1, font2 ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.7f, 0.7f, 0.7f, 1.0f ) ; +} + + +int main(int argc, char** argv) +{ + init_graphics (); + glutMainLoop(); + return 0; /* ANSI C requires main to return int. */ +} + diff --git a/examples/src/ssg/viewer/viewer.dsp b/examples/src/ssg/viewer/viewer.dsp new file mode 100644 index 0000000..88102d7 --- /dev/null +++ b/examples/src/ssg/viewer/viewer.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="viewer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=viewer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "viewer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "viewer.mak" CFG="viewer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "viewer - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "viewer - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "viewer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib fnt.lib pui.lib puAux.lib ssg.lib sg.lib ul.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /machine:I386 /out:"viewer.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "viewer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ssg_d.lib sg_d.lib fnt_d.lib pui_d.lib puAux_d.lib ul_d.lib opengl32.lib glu32.lib glut32.lib /nologo /subsystem:console /debug /machine:I386 /out:"viewer.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "viewer - Win32 Release" +# Name "viewer - Win32 Debug" +# Begin Source File + +SOURCE=.\viewer.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/ssg/water/Makefile.am b/examples/src/ssg/water/Makefile.am new file mode 100644 index 0000000..56a16de --- /dev/null +++ b/examples/src/ssg/water/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_SSG + +noinst_PROGRAMS = water + +water_SOURCES = water.cxx + +water_LDADD = -lplibpw -lplibpuaux -lplibpu -lplibfnt -lplibssgaux -lplibssg -lplibsg -lplibul $(OGL_LIBS) + +endif + +EXTRA_DIST = water.dsp README + diff --git a/examples/src/ssg/water/README b/examples/src/ssg/water/README new file mode 100644 index 0000000..28a1478 --- /dev/null +++ b/examples/src/ssg/water/README @@ -0,0 +1,56 @@ + +Designing Waves. +~~~~~~~~~~~~~~~~ + +The PLIB ssgAux library contains a class for handling +water waves. + + class ssgaWaveSystem ; + +This generates a large polygonal mesh which it distorts in +realtime to simulate water waves. + +The simulation uses between one and sixteen "Wave Trains" - each +using a class: + + class ssgaWaveTrain ; + +The program in this directory demonstrates this system and allows +you to play with the wave train parameters. + +Use the 'Edit Wave Train Number' widget to select which of the +16 wave trains you want to play with. There is a button just +below that to enable or disable that particular wave train - and +for convenience, there is a button to disable ALL wave trains. + +It's often convenient to turn on just one wave train at a time +and set it's parameters - then combine two, three or more to +produce the final effect. + +Each wave train has a direction in which the waves are travelling, +a speed, a wave length (which also affects the shape of the wave), +the height of the wave and a 'curl' term (which controls how much +the tops of the waves bend over). + +The appearance of waves varies greatly with the depth of the +water - and this simulation produces the correct effects. There +are a number of preset water depths that you can select between, +notice that the effect of varying the depth is more pronounced +for long Wave Lengths. + +There are also controls to set the eyepoint pan/tilt/range and to +toggle the system into wireframe. + +You can determine the number of polygons used to render +the patch of waves, the size of the wave patch and the amount +of texture repetition with the three sliders in the bottom right. + +Hitting the space bar toggles the GUI on and off so you can see +your creation without any clutter on the screen. + +Finally, when you have your waves exactly how you like them, hit +the 'Write C++' button and a C++ subroutine to recreate the +exact same-looking waves will be written to stdout. + + + diff --git a/examples/src/ssg/water/data/droplet.rgb b/examples/src/ssg/water/data/droplet.rgb new file mode 100644 index 0000000000000000000000000000000000000000..2ac00550f8e34018b7de1b4aab857b8cfb015579 GIT binary patch literal 10601 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

Q2ZCj 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�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 HRV}{(#ku1) literal 0 HcmV?d00001 diff --git a/examples/src/ssg/water/data/pattern.rgb b/examples/src/ssg/water/data/pattern.rgb new file mode 100644 index 0000000000000000000000000000000000000000..13885fc792dbc169a5c8e2215a4f40b6a809415f GIT binary patch literal 54031 zcmeFZcXZ@yS|;|DO08Hq=d7GnilqutDaXn=D(BqlZgneFP72-k-kE#3OpeR|%fJ9b z8p1N9S$2s&uz0<@UL4k5IEy2AjT=AJL;LjM4A@@#FCX7NeXCmaOZ7e9o1XW1Mfnp7 zg;2>2{!q#H{`;Hq%fX@QV0nN2Z-4avzx;o91sM2h%v(a(B0@M{B81mX2>%xd5zY`I z`W-?fAwr~oN{D=q5XJvYh$=ydrj8Kp2ZS`J2xpv$XaZX4Ic9MP|Br75$hxIC8y^2_`+ZjUceut0;Jj+X5 z|Gia0-v2W~K5;_GC;ys|kMe|k%q8RpKSRi;TL}5gPZ9FDQ9?fdPYC%U*6U0Envkzx zjlcSrgnSL({fF`0e+1wCA2ky4W8Wa;$43bHiC-t=Cmn?R)bA1UtpFiE^IsD3bH{}I z{QpMCw+n>)60ZNtpCaTRHxu%!KS9W^nF#rHob#J@LVoK%CFGxM67t*s10nzHnvmbE zC*=1&B!rPBgvA2>IFF0-ct1&q0M8}-E+JwU@c&nY$Z(z<>#BTCi24!mzb8b8@85v$ z-_!;Cf1Z%mN#Ot6gmf$d|9?(M_cHLu_t!rmq@Mu)TSA6#&M?jy=>Y!UB*bh4{=Y%U z7`~JBUl1|@{3ic9LZ&YWnFW4x?-Q~h1^#$88?do|mJnwX@W(YgxUU!Y^-lr+-zOv# z1^)jHA<leG-xUG= z*qgma!2dr0|1@kLdu#~j7_ep|KMva;1pdDa+sA#z{u9`K9QgmYu>CXGJ^}tE*uD_> zo|2JU! zM&SROuzff1|JSg69DDdzz#q?cien$+`cE2x|Bu1;M}Yrt!1i6h|6juPvCcPG=j1hP zzaIEwopUPSkM$~Iy-HZGJ3H{lK78o`{-~fH9-sOOY`+Ki ze;c-sFZzW)fbCR|38K89|HgX75L|1`&{7v z87wru|M&fy{5wC#nttEo-aY^Cp7ZW!|FQ3HaNqKEvB%%s{~x-?yU+OU`~N%letVy{ z=e&EJx9{J+zCGscYx&vU{p{UmefQ^YkN=N(efOGw_x!i__=k>p_nf!Kzx%Vd@84aA z^82^PzI`np_xAnU``zGYZ_odK>$QAu*d4j}+iTza?d$iu{@dsIexLnAKYRQ9f8Q~0 zulx4(?>p|@H7g(Ue|Fu!?>cXP{`P+5*SGJ>-~aEs?%QL^=a=8VeV%uJ{`T7cS6|Eb zdwXp8I^}zRzx$W(b@{i)-r)7`JGOk>75?H+ym{BNK4-DBT=_V!uJ|1N+2_PO7_zWaQC_x!i( z@$NC@_wQck-Ot`V|J|RL-ygsBI2?AH&pF!N(bn1B(K)c4DcxmqxwXZutDEb5DU*LF zy?Xib{=l<*d7Ze;77yo)Q}Z*{p_$Wjq?5VPuCdk6a`$2&X6ht#t_e(W}khy1>0pBNn(rVLQJNnYR9AQwpZkErCMnwsh=QqL7h_=+|@W56Bsg@U#vkB=Jv zOrVEOmvWiBdYQ7G%28<4;uh!G)1CF*lhfx%N7SuvEi?%<8Zp1Ns+z@<@(?^BU zZ8n!#wryWsU*>KLh5OuXsZ`kTu3X(D@3O@w!8z;L)Oeru@S`t$`tx7@(wBeu%U}7z z=RR|3v&=7yjT&b66X{|)nOF(#)!7Eeqhlq5KF{7uCi@55Ds|*5lg>&Z-4jrC?l5^}YSQ z?K$&kDCCLweXbD|hc6NH)%r2B$w)C!ddU}cDuskElhzZ;qbfowD_KI3SfFU(um;@$ zPcY=Nx!u(8AG=!^3>lBbr^{6Q+8Vl8&eklPJ~`Mu*gHKxKHA>ioK%(rQR0TOt-IDH@7`yk3~MJD0q`qEp|~ z(lHT#aaSy5bLpsK=lc42qEL8vzx3+W`wzRmwVOl=c5!nQn>9{O_F0dA;4`24(hq(9 zi(miZmp=RHkI!d~OG^_|v%&LRx{yiTMBSVDV)28QpZxH{+gJCu#XVp2GI?`X$fb^h z)}FSu_MXv2hto%KQ*7jQPxp|)FgDu36Ul^ZUh9}E5(;_)VdJ1@e}8ND@Zj;Y<0sFa zpE--cQ0K-FzDn=KS0rJsC<%Mpmgnnqz&Jp))g;s~Td zkwDSRoE< z8T4AZ2uKKIIwq^z8}>&%eqX>FjYa&uT$V;5R7#|3DXoH3RM#@Z3WZcokx+Q#_3WI@ zvEW)VwRd#(^jX7asr!5>Ur0w?yEn;;>ufGtxXI=UxmEA#bux9ADP%8>BXe`pgHvZ8 zefa9rU;gapzVhkMeEiXePZvk$7iN9&<76qF&*qX*`zoyKzJPt3$Ys;14R7QsmAESw z^2yVXwYOE*VOn(AZ4?`2fxPbS86Fw7jI;>(0)e=}>zAEMK^M!0GxiZ+K@98p5IK%NkC=iO62W=;( zM~`<8_x9FzcDJ|Z4dzh96Y=@NVY@*qU@^pPBNo#LWtcKZURT$!1fs@P1&2*T!cP-P zWPFiG)y(7#d&6$0$F=0L2f|TL3x}%`^F>UdLN2N&b?h4H!ro4N`(Wqb$^PE@+Um5D zE#}eKJgHR11D1_UW~VRc3D}%VcAG!wcXo1^TBSfP5=rF}dL^M!8SMW$B8%36 ziN_y(`0ye1v#BqC`ZLszzxCX1o|<#*K1roZ=|ZlMj(Ik6`OHJ%Azw-t^SRuvCwh}j z-j{CE+4SS+Ot-wvxa3-#rOZ%f$#=SXhKC0XgAF`-y{K)}84X9gfj~H78TK4LJ~}u$ zJUrPsID(HdPQ{~TED#J_*8VPC|9Y`-lY6)=JQRx5+_yFijqM$MbKZC;>I($JF|*Ne z`uOx<0iFzacfG-lZnOX!@{AS~*agfqq#)4YS6!0}I3T8E_sDZB+ zNjXBXx`o3V^2LL`fY0G`N1}0GJBO!{2_#IOj3*Y;s~-`;%>G_%WB=gz+0o%9@Yc}S z^cta5C6(gks=QE|1;gjz+@1J~m6M;=|^+Ql*qrM@XHryGzBT)KhB7p971z ziP`BPwW?`y^&(fwmeL4==@t9x)9b55HeY|ejWx+>xQZKjyZX7?SD>s*q0f1fQD zlaE)%8rp|v7N;x}3&l!acXkgA>W7CKnKd-+m?yRz2zf%mV0d(3X?=fhcW>|H>Cws2 z(ZP~oG8zrVy@9wd9JQOe)%r-Pm`Y{SnNlW~$>uf(v>m-hZ*(OP_WH4j&1U<--rm{X z;ql|c)04v^_mDXj^F;t;FdB4?H_?R+10((2lrD;%Bxww)@PE-XNT`HV^JEAnVzyYK zZ053i76V?l$L);-;*p5Alf_X<1yT-=&k!iZ^hbmFymc7zQf_$emKt+S=6Fqv7&(Mkh7==l+l{f}J?pWm$5$oDK&v3zyyD9vrkrB0wS- z^kYq&<1L+&@tw`J_4U=w)s2m~l0bFg!jsYLw7uY6VBc6-iY} zuWM<10Z*>x$(qfsaKP*HhXT=n+iPy=9UdO+>+R_@7|ce?R7c-fC>#b5o?yfm*a!{i zTJ#3P(6G60xPNHaXjbVOr=0%Pu+I|?1ifDWNL!C}c*vsf)ej9IJWsUhjghFQ{7Jy? ziNu{FDiL3(P*UU+DS1tyR96rxqC~B@u|YzkbHsd^Sgc`J^3@G$xm3*4G<3Fgb~5Uj zN|{K;<<~O!d}V(ZuZktr=`@-qb(35rQ|hW}Yjn+OHM@?hlqkfCMs^LaMWbvK3q(p) zM@MISeO;YO&K7dS+Ge>xC>Ov%RneJDN)@Goyk>}+I&|ue@xa#eJW`z^a`aN>>DEdh z7G92qSEh`!!T8GRN_;u8c5!=u|4_(e0a)s0ZK8W*(%9RhA2gaxX3KbJ=c4%X;kI;F zDBR|rZH5BN(RehrHf5PzSzCu;tgWn_-W1X|xqPuy%qL#>rh6MyT1q3OnfzoeTPA1M zaI^zfCvqQ;GvM_G{Yw)TgT**zG?*G1^cJhdVm6K$#@umiYM;;RrTV_%wP<*{zV4RR z&Ys?`R;id z45koRsLOS14V=xtr_lH^rBKnryQ-2_OQ*3V$jNxjN42D?vZ|J+Q1Mw3 zg-kAy2!xMFf1$p!;#R!^u)ByA8=O%elr-fTe>>?3?qg?lXM+QZLO+%(z2gU%(#>2K-*P+uR_LX((!njQkp*(ioMbh9ed5xO@>d zw^Siz*Hkl9jk=b`h88YM-Q28JDpgE6jn0*bc;YuOh!sNmqdIXzOOr&QQ;VqlpH_;6 zLcT~U69{E037bxr>y$cOa~Gc@>u6GHrCKhdp23$0Q9X*qLJ^Ln5kjF;m=rqsMnhwB zyM8=|qzM^Lwt&c&ezCo>^ZYV#jWR8>?OuD9NMn{qq%It^vLOz##zP>a* zyEH$surxQhxOR@yso8Jji}`%^a&LYA#fwxTok-<2yzv*;2&#G5*xK&1L@u31R-DhK zOHTrmLuDMANJ2yvro{~=CpG%jkl%|GHW+k_8>dj=1+a!8Yu_Zwh+sJE^DUUhTp>h> z%D{V}P|!2g+c{`54w=mZZQWK6HTun<&*u+@LIFEYcYC5i(tuL@z81VCWY!Di!FQe{CIo&SuT-H zT+8z>kB2A^S*sYgbQSe_v0pX$k9& zFan5{1NQ0BF?$?Ub&%@&gki{v<&Sv7QQxA~GHEX#7NQ1oA%DPWX%seC4W}K zSL#sebGUrISgR5-D=Qgtz8D#hNK#4QKC$>>rCQFc<0~{`p@7ff^27?AhF8xLA=l!u z>)A4$h{s{GS!_N$8>_xfpkWE<9G*x~Rl!h6d18)8B@;60*kV+PBGWi^}@a;_c>LT%YXZ!dWD(q4gWX0VhXj?9crPLEGI)}AEud1~<|vYG3v?9<)t;|ruu55@bthljn$W-^<9nJc7HmuK7C zCl`q{{1-678PCF2ik{L*iVOy?(c%t=g8^#r=ds8U$JFG!BMA3`SQUzl4a^2(p&*b5 z!w}3PTAnvmP1i2y2&^bi-wl{ z;b6$WIA)qy^oNkz!^*tTxXai;5uwKYH3}l9K%;|u!EWcVxmqdwJ4+}NiuqEBSYAcp zYcwK0Um{bW!meY>vGgJ?o5P1SXau#jY$3ar&ZWwKS|sH2Q6Dmu{F)lJNT%Qb34v5y zUCn7w38gHNNTC)pXdIzLERpcJJdse>-pHp`5b|2rJrQ}5zb)LC)1GuHakjI1@Ek5A zS4ieF>CC!k`??%G3i(trc^Z!$T&9p*VIgnfu^xvOW+x{W<4>{$P*mAeI+?tFwzsUl& zu*#lEvvix4HOP}jhu-q*UGT$ zBC!O?k%U!U&w~@;@KEuI#1z6+E4XwTcE6@cQblD*Vy)RtbD#yuF$DCK;nyyme}L# zTrq!li(Q_0@pN1m&uju!E8Re=3h@{ORw^!EH(39;^K7g=;=)k$lgBW z5{cv!N{BK|{zbjcVuv5{Vj=yZkl$?`opJ;MWl+jX7BctEMq(jfAdF%y;+q>Y&f3d| z1yFDV5fdz34c(*mU?kx6xLj_mziVP_!d6bNLd(H0HS*b!!EwaFIRX?n>Q zgh>UXez!m501wyzBx*$p5uZgDh-50YT)?ZR!64ok0WuwpuKtmvStnvMv5S>D1+TW6 zBT=a&e7G{cSgw-NDl3^h6oYIwtVXGn@>o^X^-fzok(kC2NYpx& zgi*^DDV6YqT>O=2WRz;yWUj-0mIkjug)h0!JX!U{b}x#hT#kDCUz7uEBEGP4Szfw( zaDzAL!o~Lb;qx>CbSZn62m5;F8}pu~5g%S5zrD{qUG;^xo@MjtG{S!(l}ufSoUzMP z{vlV)-dv`O#jE4ZqZjE6E{Fsym${-GQ?|&5Y`r@c4q$El!En$v)<0x*L_pV3LkV|f z;17*mlZf7KKkA5(KOA+MMqDTikM$eI93h+@MA;ES z1Rv@#B6P>VI3aYeL~K^ml#}Yq`-8Y6u%#?fCIJP%rI}w}$Kc6yEm~d$YA%^hE#!$r zVy;}%*g&I-8?<}|oy`);SsxHy^Dm;4M&DT`lg!;c+#z{QU0rU6S1(FgP=mMWLh|N%)wK>QD1zU+ zdVckED;i&YoPqbczlXz0C!Pk!O#ZWU>Lznrx-I1r*O!+Y(Uo(Q;E3ojnj9rVNs-s8 z`PFy~-U9jNGFEzG%2uuz0=|&f9a{Dp)UpBhN)&+sLRsoqE{267k|QB(bd;A~LJ3bM zRiTCxsuV5G_*tDs|(GgR7gfUc>o;i#I$m84Ft;n5gkxmKqVvFaGq>XNikD?z{k z6)d5MkvIU0^A|{!5Gl&>`rz?35+Q8ba(u`a;WpxjM50OZ#TMeenObsj+ES zAQW_=i2zc_sGsz}yHUO0jK*A(qce7YBpUGg!y$J#=3g{6@Gn)nC1Fgp%k(hEWT9A0bfq#O8HDWgC~;9MLaf# zR$IxCX_QhXr=G(V$g3+j3K@^h;_y`sQd&(t2idGxDid-Z(YSmbt6Twcq#^-`MU`45 z6R>zRdUZWtfkH~ag`1*qP~H8z;Y}>_?cIGQbH28Ga($gbX;HkpFT$lGztMLMjxMde z$lZe4yS+s*kbZ$IYUc(>-GTVd7Y~9**_W?gzIv4@Ty2K8U!ZzJIdKdB^YZR_WJ2HG zJ~$J5mO;^2DpE@?v#E>o{ng#nZSfY#)%_jij&e(0%cf)4pztt3hk0by<3<4y2u9$P zB60VKmd_C>yT(1yc*p}EkE$c=cTO3`z>Nf=p>lpT-aiwLBRdNF8#&K^Pw}}8CN(5H%X(DRVNF;J_p(-X#s+Eg` zLXku+71Z-=uF&BjeqTDB=g_R-dI%`@Ij#xh>|>&v&<^hdJNs00f?prDoqYqI-vei7a@dQ-tA*rX!MOS$Je@x2$wF$)j(xtl~Xg&1t^>FOCA8t5IIj-F-m*<>n*J_Xt`&kr^aFH$+A zMybq}bMu;7{Fy@jdONcDESb(+qmP6}cq(`5pB(CLY3l6i8k$=)Bc?PLX2>ezE8#%J+F*HadDs6*ODsMG8qA{=-aOu8q z&}SPrPB_46M*}{;rE}aLK{N>ZCJYlEa2@U-DnTrG+&0t<>Y69w3FPfo?<%qZuh-{A zv&L;3H;mcPNJ3)|iCdWbAYrJ%|A>TeKQ#0Wg-C%oG^Rk&pMWj6(xhb8R8>8ys;{l6sNtd% zr2PHM0vL{~^YEZX-Q3oO z7Fg#%*~4ejfC5znHTPYR7W%J@KE zPkVDiV`Fpg)biO)xje}hibW6|&$rf&E)!|o2GJo4pEMo_##X}qY4f;m6(Jd#fJiVL zjRr>*G`c`0*J%_IMduVaq;m1>bCm}wH#16p$MN8WIJ zIWXENQ>x@*KC%-@+k_{IbinWNz=#4NyU8@;2}A%Iu!xY42}>ncs3gK_LKP}SbflwF z9)l~_gxVcN8;ITEV>$AiO1Jx%rk-LD)O17 z=xGx1`Qh#!!8ntA?jCJ#ZfQ`-6k1(FL-)9A>lAJ+n=3us-`-`<_cj8N<>gqEvO?J) zzsu2&nXFUhfxb~kB#Ky!jUSAy#(c&mP7Ol>hQ;kQs%kfgdgx-X6Ugxgv@Ip%{6hq-}T(!3G2^Y$6yA*r&#Z zjn;7sT2z!N@-NYlW^>^2ktv}HW^%bgl+I#>n$G|^S6j~WD=28=(LlUP(SlauLj)25 zvz}cKvQSl9!$YymuB#*rMT=6%ma59!6)34HtbbKGI#gFxqoIlb!eN1uLg!Sik@0zT zOfH*NW}J|`_AGeq=y%Ugu3sgQ>EFM1n??8QI6B(j+SJnC($v)6-PzjKJ2Df0noitg zp=o)Q&6b`77wrpfR8S_}~zF5p&gKi*v^pj#Z5o;v)m+riN?kc6YScH-1)a$Ob|Ak@{=GuaIEJy_Tw$`%EqvYy4FzCmIW`ReA*iQTb$ae0-yDZc-z zkV!sv>(zs^)8h+v8#*eEg{6tEu7TLoYZTGRLOPd9CE}wCYw_iMoV&NaxxTfvuoSsT zT%bRS7EnI-%+uFuU0ie_emd>0#i`lxo}R(LS@HecZ7O}4PTsr-&CCT|o>fYmvPxbr z+U@QIn^)g&MT;&176YYo%x~86+C~O@1`Goh; zyc5zCzr(q-Wb5fRMMAEK&jD^E826jh61{1}Y(%UZFdF&|{c4rc=v-b!9uNU_9t@B5 z8WtueZImg>43s{DhVc=znNR07s!#|hL|m?{shLkf08mNQT1|UfOOv))RbNxn*rAot zz*oTkDBFY;(&3>2lYXGzG;A?jhIw_2202&8XYx^oH#IOZL7;7DX=`bQd_y9Xi>j-r zlI9k5Escv*OCZ&K!HIW0kxZf)SG+GI&f;T@UDMRSf4;V} zbF{a=y|x*d>1df*K0!pkN?)gLZZ3k}4Q%aK_wN<&q53Ek4}9xT5WKoAlpb!=PgW*c z^h>eTt=-L?oz0z%*lKj4t8-@MMG+Nw5!!-GV%fj);^|qEa)U7ObH}IX_@6xU3{Qec z0upFY1RN7>0@d)`?DUe`;enmF{f=Qdx5Kg+352|1AFL)YVsucCe&yL=Sx0lSe{ke5 zOs=j3RwAe~LO#cMD^FuyTyi-Vmu!nJ*OJ?AR0!HE?x;5!1-~0X&%)~5+gv}OY*F^f zYlN27NGv|cYSF-adWzpk9M43*|YD#VaShvPQ1B$F{WK zp)8cOMsKjV|KK#dg)H_Ce~P*D*mS3&XAS{w_w;0cXJ>nTD>m1m=$?*TqK1DBm{@g)uEe+j9F^A&fJUZ2_?sKm19iQ$WuCK1I#8x8nT@Cu#wTm0* zJdoR_60xP&<%_2Y%2hdJzro`8+yiY{&<~al>5tRWDy&x-mKGO1A)nKRX1?EHkkx=V zN6R9*0_Hms8`aMrmGyV0$4?HA_Vx~JhH+4cv7pBniP(+J0-Ac%>5GKC-np5j#RbQb zK~5Jnjlvd#5pO8q3t9#j4t6*9DI1hM^7-#!5!JI;YKc(76>}tNDGPi>m39!F?{TY% zYWx#}Wl)Raj7pb-Y?W|@B4sm&*6xc$*WmLaL1fO}ZZ<iQc+-$&D`fox9=5-2JwMprA2@ZPvv#KS*1U9+u&yqrJ^t z${KjDG#Y)&)X;qggDykQjV)1uAEi*qs~X*4U-!V^z=&aF#4t9}QcopctKbXeJaFAy zsg4FL!l9VQ2#3Zj0&93(~nuq1L*Dc^!9YYE~=>Hy$TLb$K%m? z9D$n2>TtOh?UZR~jb5W`a|f~CVSEGK&j)B6pT*|}+uPdvZELIXErf%;gTvjUO=oXo zuUx<`U*hPYT=lJ;GF?^K6Vg<(<^+cB4mR1RNjn-&e zoU;Q9J0fw|JE~$MaqqN(m4&V@7&Z>sPEU@`V1Cb_P2K;NZPXf#1y`bx6_>eLghWMY zvN=4cMEsDbLd|EA*0Y(yR-+%qIBLfDwAFQlCxl&Wkq^K}#S!zncnmp29Rf{BuNxH;NXKSnqP#Jr#2>icGSQDL!n6<4>S%y39O{ zPxQC9wRZL{t}HKa?rp;?fX13l-#pOM)ISwP8kIrPy0Q>QFQN!G<0Yg`+1z$G^7Qg~ z=H})&Xz6KbZffhcuW#)g9zH$T-QHT?S)S|Cb+$I^M`KSCSLvI}if3UxO zv~zs)0sSL4* z8|w%b>y%AGb-oNilG`a@p$O54o128Nh$5w$(K0;Hqwnvf4t{>9-#FYvuVljORC3gf z3azTCTgvVB`W?O{=aS2TTD{aIW@%Is6-sILBl1BtqZ1m4;UV*Yz8fv~9(fHxR{Ow_ ztHdgqs#zdtU09fPR7{cAcct8Y@#TBFHv7u<&i?A2f3gq$thu>!CKijXZLc36o}3;$ z-t+1kdO8|A^`o9u$ck1s=jWE8#(8-7@Z|@$cj#5@EG?{{-rL&rj6y!#(%jmyw6?Xo zw|jiFzq`A0w6WCP)ZO0F);<%h+^h|Kcg0ZU4!Wi?g%C!(;nE z_srbn?8M|yqku!@eN`Yf*qt8a#U7j8?(jK=#7s0FxZEbwgmq@zI@>dBJ3QDuq3lwQ z$a^UG;^CE*K6V`+Yz9-#tD%w-g+i^<4)nHlbhlGS3Pyv~)Jmt&tEuFR6uN-JXYv#@ zK_5ayz~OPAKLr73Kc_}0VuBzO)mH&U&CrMm;oi_s?ad7jXrU3o$qyB5Bv)*noF?jZ zyKFAX9A$~TzAdEDAiduhGuhB#nR8kPyZib&8Z>>j^)-0;&7G~C-L3sKTfe3SWkq|( zgv%RnyFz0VVdPo`fcTKfCKJh(2{Sfq$n6>HX=&?hZ)s_>ZSL%Ct#0jYZ*FZLZQA=< z^*xlZ0XvP^dz?rt6Iot!PTHVuy$hdT$F6=In}z^IcA&Cky) zITqpLoer;USVa>E1q!$(gTdH8U}^4|J3iVu*w}|o`xqL*#g0a!anPXeYm#u$!WRfc zMkl`L?4o^sVbSih4a!ma3i)(NH(dU}fT^)>@$vEI9?ao@{J6vB42PCuy)~o`Vk82N zgH+U_JYcmN2fMnv+xzLx1g}>?Cxq*cG!1!*CEq}Z`#?|j83;SxAk^3qlLK=-`ZGPotj^}NTNqn zmh~6F;qFXNMV7-GD-e4OcD6J(xA(v%wogxXcOf0zSY4mTghpRii+(z~wZ6BtzBauS zy}G*0QNVYQ*9T|tEPF@prVguRbjoP%Z$wEa6)@!%pKE^6Hoq`$bJ#tLLlU$Zg$!Zi zkZEMhWE^YJJ9Ys9Ozz3S;okm{qr2T|HCj!Ujs^*r1+r9ab~Omj3Cqo^GI+1P`_S3 zI5Ipo+)(|f64vrN)wQ*ib=9?O!q(e-w#9{cCj{Z3c=Z^*fJmc?RY$L@tEbWGtL0_c z_q2IUsqjGQ&qzv+)k(4?yW$uP8O1xWIDg+UwQWYDtDdS@=f-4 zG(!_Ox4atP-os45*4FxF3-2PQ=MWKpV{@^qy}i3jKN-7%Of#2CtU8vjFQ2C<*FfTHTL)V^ zo7;O!#&M7x5s!ajs7cC)=2SYgI5!3FG(QWOwZ}HBpo)K=$7>k&LAvkvg~s}vNVxV6 zk06`hh3zg(O+@0Mcqp`FP=gBPFjWQ=6CU^coP81cDWAElYr**=NMX_Th)fJGA%Yzs zSZ|XLrf~;U=Y2w9gHoZDqQ0v~-t#`CTG!v#q3;EisUI4&4C$&XD#_1Pay43oR;_Il zar-gP;c&W~koCL0&Q1xZu~FWjYE}!XDb-~nL($&ZW3s|hyY%gydL^~;{YMl+6Kgdx ztrF++JKy-H8Ol7u^<5t7jr+sk`qfQYlmGlEJlUyjXzoD{8-;h;-rU|?-(FtOL(0-` z4LrWgCehftirRL{_4fVC_dh^y?&bTZ{>VipQAnq<#~#G%299~P2Virzn8;ayfKQ?{o}*q6Nh;!5{|5em%TPiGmp;V%Lk^Xrxxt< zwgsot;d7a^&{sylqsM0+qZwKbgzHtukVe2^(o`ch zh(NuLd56v8a(WEl<~TyF895Y$2fongu+h*4FCI>XN>rV+g{5JTy$DeC8&;w3RNTp}~RvBM1HE zQ80FygCg%EX8C(CD$9$ptBWUBs3wx+ z7q?+JdwV<6{iCQRBAyUfCzFoHsF(N8%udhSQ6Rfr&Vb9TkqDKY7UweN5s>(JN4l4G z54Sg|+uuIe*gZHtu^EgA>uE?609y8;`|3Z8%HBK?SVPDA*|MsDe(@a92lbOLq^(&@2XBRVDcW9dgvtU*)%b-y=NGOGuwaraL6 z>>X|T8MJz^rNOzDvKxQ`4Y!B8>}~!5&ANl-bwG1hdbmhuANxi-wOzK|!?oq@gF|RJ z;NX_JJNu?ro+b;oC?@U;+0~`^_0{tv<)YldJ~%sie0;b+Zn3FIe7f|?9@JH#dt$F7JAr;VESgQ2Y>R5bPL#7l)$}^F$0D zTdb7uK;bc)O=gs2u!zCo;n9(XO7bB~u2ArWOp#dG#HZ*H9sh0s$yP<6F_pV|2QGm^qv;^Bsv2+SSG4 zzHcLy0o!q#DdcZX;udw6)d!+&b9aAhV>Q0H>S*i5z)>cfN#>KqLN*y&T02id-k;Ci zm0mvFm-d2~-@3{{?GLra$+AV&zqGZB!tikSaBFvcbz`YbKM^}Gz@Q2#bnLFe_SpH; zv$9oOlW*=nef;#~=)`H5ilG7I4F_ZKfOEX5UeYx&J2MX`+#ZMB>$C`^jYem=Iqh>& z{l6J7^)9UMW2$2B@aft4+3B%u#1sfQyx?mi5x;w~8GMP`x3mEDX29)pJKV!OQG?M9 znROrt&AvY{GPJO}g`mBLa^myOXl!F;IoQYIqUk1LfQ^tUl+s6}S_S@az+f^~4yUZ< z7ApAzxo@_aZ7FJYX!oxfb_!Fl*37;&O!Ga51Yw2)%25$lS}Saizf?!Ezo(Ku!@j~K?sI%Tb987y>~s;95Wf$!>QI87$Ei zABaS>lmhYam|EL~%y`&hHJFic8Lcgn7PBJ~2inlMK>%gxH96;Jo!F8~B(3Wmwona! zDXyVop+RLZ%8h>w786>VrgmiE4NWbrD9$Qr0;NjIX9*-2b5S+2s?~iXMvG~%4|-~= zc?4|>wOSx9&(8^XQU#sVsQLzak?*u4d(vo{S)^Xt(4^$j1yZgA)g70iF&c(ODMOTD z@@GRcSgR%fxGjDI8ZiGJ)P0a^Y}&ipnwmR$y4!o&!F>!4Hkv#~;HHW&p=2?ayzpBl zrp6~QUSVBaSejk(Pt3VrWS|eZEj^(BeZA}H>gw$1?C$O9?rZPq)*G9Q-qQ><_mEAc zUVvM_2#i^66B90qgW@Hxn}-bM!J*NX-cd-B!k$O~s>@K+J|dS2h2@SUW~HRErUq`4 z!HG$h5XQeSpcC*7xAu(;4Vw)5UbE3WI5OVRGZb2ZFA8GfD73ud9h1qV;<8u|!flmG z*UoD+xFNnl4+A5`(2EZ@wi`M-1}Qxld;F*pGm1)eV@(YmD-A6l2YL%B1}UiIPan~R zXq{Hq)YB#6N{+l8(i$%0-iQ`bRUP3-;c6s&aa%i@GM$W?DwReA(pCX|jz}S7l6o}3 z#bt#WjZyUo6R!N`Zk?D8#+C^kb0e#U-=tEuU<6f1{<>kxAKpZ!zY^QJ$YKy1R5!$! z>B~KME+q zubSe|F4E8dJl$K{c~K}r)tAGL&)z(Zn%guAxl)A&s;*}$aC`+q1GNLB8=3R{jqUTx z=ZTx^)Ouj&lA11MGG|-y%@>7IB1c7(fYR(q%-XF}HBdWC9bN5xQ{mI|LPix*c{H#ZeY^0~$tB=#|o*fV2OZuHxr{r35S0ngHql10P!r*i)hgCT4)+R?E_ z;p7B=>a$N;(Vg=}(d`*E%t3z)*|KxSGJ)w92ekZOg+OY_pyM+cXry7-ULa{7^}w5j z1LbyLAn2SJpGD^`R2fG1TGp)7Xq75Hqn0MtU|j!Pv!Rl`8Dt@aVMioq!> zks)lA3Iswir>=&sX;e4sv|1WN+tHy_sk9|p616KzTeJ*ve6WdJ|lSxcWKo<*5?CMw(G$2ilT5bDi zz(2FkKljH*u?CO~6CG+CDG`~9AAWQH6z zpU+}&8!Xttm^6hf2E>zN!UT<9*b9Yt$lN=D#sdOn*fV9A3dP(t!7qpHmL^UuPpg)q z8`d)BjY56^p$%*&XtP?TU68OLz44PDBV4T-GH-=S!F*J&&cd<$fi^3U>XD`*mhMy^xH1ytVGFr%eba?4^G zF1_5mt`(}30v39#3K`DqGBhvEpP=x>^&*2wX@LVyGy4ZrE2}9D5K_YTyvQIfcck3Fef?W3U^6^?^=OUkn=m~5GKsxaa zXp~LutxY`>;p0T6TysMwo=QI7+kl`Z1H|(M$}PCQ-;-D&!hkfx6Y`83tY|Z!&jp7K z)v#wo!(%ap95zokyck5G484;#6bm@##wYAiWT9_{D0(L0nV=a%djhz4Vlm&; z2zMG(OeQ3e98t5$9)Y6955+5xc0=IrLO6k}(?{`9{E#&~ zU_teTiup-oth2SFQ$H0wy@7NujdjfCQ;)Z|j-MxxbY`GUD(;5&6R4)|?rv_b&Nica zAf0aSiq!n)bNR%v58kY`t!Dy+H>&Kiq#Om`v;B?zt8C$xdY627sCY^KgM4~99>Fw9 zxda*;0JQ=2cX|2;>UpQRfyc)3v|2D?hskxA2htnI#Q2;ObB_^lIv8ddo(sVcAOH!5 zJrl!|Fd`&a<>{bU%xP>AGNI*Y9`%&NIR=P)!AKc~NoNp3+R9KxkbHvh+q5zPS_*Ql zypGgB3L_P9sr(Ft-YTd8e(n)Vrh-CBAeKv{ng%HyT_u?YQ$TXeIaE+Mav2x=Fb8D@ zt%4>OLL10pFxUd6Mn|OD$h=*p6#q)>H+ImNa6QAq}Sc+=KuwhqRFe9 z7rX0w&lBk9K{$)0PM*fb+uHlagO4-0Bqrs{di>(E-PNt9u(}N9VG?;tmXaYKh%vR` zaXQ@i)fHiv)imMo$HLU$pZYNA7WJ4KrA$Gy*@AT}_MQYy>6pT_1Da~^1dOjrGNqy1_bxex4LIc({{Lg#8hZfgJCD?=!Q`zRxyM&GWK@RJ~1|l zaYnRu;=wo)RcqgL01h7&CVqKD$YC9t#4u1f?s_oA8*&=8!j@56XgTbH8W0!uVj^tH z0a#&D2qMrNQ+!YzOWPW-GML>FE9AUKgf3AlvGQy_*1n-E=&r$U8(`8HSWpbjiJ7%E zHCzxw_*9}%D%JIsBAr$Yq~tPLL!**W#TKj8uq_T93MK`VX@nt?iA9WRHiqY9A~uU& zUn`Kowej#fVWi^k>A&^i^8|h|(0lmh56Me_v3hcq$pDM|L;mVIbK;o@|L;7#XKbVE zx}HZ4)f-7kBqfSs5!HJkC8|)Q3e_c6NmOB%MD?y2jYhrqDXXtFtM0S+IX-sm#71BP z@i}n}0|p#8aAE_o{bwhD|0IY5Bry;pMh3aQ*+8(d%M=QGeJ{WFectD}@9W;D(L_t` z?KAd?gQe)ofnZpCytzNe1a}xgNB$dJ&IK(k&_+OT{t9IO|NjKOgU}%7+E#p<~HdMg&143`hwvQ zl`m|KX}_z#x5picjE{%Go=ngBc`e+c|0K#QGtg$2ry(xRFOVgrDzT6#74qx?=s^|g zqy$YxnKCH}-cS+V#+(Fr9`Wro-xtFKx)flKjKAi6_1qBHh zKGORu=s%Z>uDLh-Dc^d@&Hr$5uo1_O$b_2aEIo~j-LSjIv#`(c^7!-)C)f4)@p5=> zpE8xEA064Vqrmjs(FwH|%_?@SgXPH5{sq!Cej;-6+f%xnqoPk`rhYIkAs*+rmxI?k zXzRv?MY920Da|8?X+RLTqErFxFwGUg!I#?Wo#3y|&dvLKo0%g_h6E)hzs=K~?6cb4 zL6r7vQ1mSVogH*Y1gkv4WAyv|wmvW7SbSi@Ej=_Pwu`|J$l;Z!WSLToS8$}VGc!_> zlm@LTU!_qSw7Mec3nh*f_NRPpS!HRVG%+#TU_`yjrk1WQE7hUJFccN?;FW0jA*Pz!0>cypkcdUQN7u!4In@*(z zJnM9GX>Jk8F)HG7%Fv4i&-@`Sb$SqYH>Z2+s~ZPMk%hDP>hk<(eP$H&fH#Vk5|;`L zCA=#;tMS!+a4J`~s0U8ZV&jqH)3bZph>s7Kuhy5@!8k`iLh@J8s5IO!{OP8}fA6(eX3NQ&V#g)#s>kg*!MxCX5eKnL?#6Goootmn*df19#dCG_q`6Ik~x6 zQW-KnWr4P^0KtBaJT)yxE2uznIM53vSX3yN(sObP1&c~%dPX+xIjtH`YA)L%$3>Fp zqkCk~xA^7;{T^C*Ttf#hSHnvfAMT%TpCylfT$|lMy7%D&w5p4jOW3aXeuGF6j*hGS z(8ws`@?l^6;2Ld>&~jp%d%YH2J_m5~eEopIYa_TyTkqW+I;i`D<=EQ6Imi9Y)8ozc z^V4yB0wvF&=woYRy=7oB7!*`xn5ky{6GI(60Agq#1thb7?6M3Am8VeAPLFkU_T$T9 z*@D!r;n|HR$*?`56gPU9mbX1+0fgAcEzy1VT%R&-0E^uBzdfr5m^#$_iA55_NiV z7PXZk7sY>$oB~*y3YH^X^uy4AV?cN_F6ek)a>Sk-?Jh2CAMwCW6K?((_b;E`KRw;w zyuEt0ytr|I)bHkkhU~?~;dad3@0^)%4-R^wFF7Gl>Vp=zI(@acz~( zc*d=@oioBO?>A{Y*^71w9-se#Hg0V}CiqkzhllTAN031NX+ zk;kucl&C#4s?4lZoO5bfVrq_FrNBO?p#O~wA}K+fLJjr9AY!4>i6zWexO{H!&M!|- z-fUnGMYJcm`=1`yW>;8=XWT#gapd@5ve;V$B*FDg*ynUl1l>N*aKCHe%_S>w!A0Zx z{N#X1{u{c0tl!;gV2KvTEvA%%wZ+v}$JcloStDfrFC(7OQM`n1(MNL|YHoAiL@>at z0ki-$r?=nQrJyQ%dcM89xIBFwa}AEU-QEGW zZ?vn|hg*f~f=~sYU*5cq$JXDR3!~JF#p&1~bhMMJJp^?^A$$FV)tb-0nRoULj?i-+ z5&f>tVzX3Nw)9QT2pbfJ-U%i;oz{NDc--5VDi2^Y{nrTU9oB9SUwdxYoadpyR9{nF zbGyB}rPFRIEj2rQf@y*ig9&KRYqt%uC<0EU(+uM#FgFu%_uG2iSbbRs&JbR%zJ{uL z)90S^CwXBhRFeWjWpRN*ko4#AzZFRmnW5BJDWEyh)Wu~mX$GPr}Y6$#Dn0%N&W zq0|70 zIeGdLwKOX`k0L5h^v=}QY47N;_eHibz)?#j-2X#Pg~&u`HW~`fkJ?6NqjT|vNFWs7 z;_{8qm5C0!lNXFe>gNB4adiN2WDr2@jx^* zzq)^Rg$3-H^ZoL8cf22xk5$|*wu(NLU;`-D8_Vp@&y+q89Be*Lhs|QQJM6Z;`if?I zPj^qZ-3nQQdpBQwdf&lmPjh9Fv65bMbxlpFL0eSTIffbN^LKuRTFYT;>+0-9tx;88 ziweka0PA zNiXPfXUnrVCOA!WDkUQ!70z{CmJ~-da&Pu4*eBWP3SJqyfts9LAap9ZGzGmfwrhzv zH7`f1K-d8gt*{W_gjSV{xhP+fb|KG|zsM4O8tUsCcKgPL24bi5;~qXdKht@=-kurq zdHrtJl%vfdRNu2xqjdeKLZ7)HTwY!5&I4~69qxAwjP&*N4UG-<4Mw@j-90?xc&D$i zH9hKcQ}l!f`uir_zL_!iSa|oG9wEgT-KNWz3;h5eoc&M;5cIS+H<{a78>`E^r$c@p zn+ol@nUK4q#sVj()ehB?MYDFaHUK;e@eqYpQ6T8+uc)XoHKTIrXs)koY^*LTx6}L( zE_YlJ17SBl3UfPyjq3WQo=&@?wWZPSWvOPk=J~jhdmR-O^{uTY_;T&omo!C{rp8i? z&-wXMX&y6BRh|L9fS8&}F3U?zrP(BxW~RN68!K?#(c2cdHY!D`vbesXx}?OYE2^lk zE6z!jq1NQ2;R2CYRIbfPlx5P4QYXu@3v#k@5)wpGZ7C&Gjz*IsW5^++7gAGYgw#S6 z_hx_R<<`sBUVD4+_3f8}5`ZDX*=eM=kC%Er6kk}GM+CU;at7IQu8vuXyL*^qZFN&S zJDVF@`^Tq8M_XHaTyvw_kI(O(Xrc=<)QE!$G`NTWFgzQdTUsCS%%7i}q8IrJeeB6z zz}gmg{d)I6d?bD;dX9vH^U=kXo@#^DGaZpk{QKESs;KF%8q@Ibh`S#>_lR@M?P+YX zp;<)*yOP%Mt5~D!G{`QNR`!RVY&w z4Q6v&U2}`Y(%0S7gE;!JG{#W z@ZUgTJ<(u_a63CXI6vk1y?uIm`)+F-&G-2&6iX_tjmSvjz}}bN{qXUpKm7jBfB%Q? zzx&~3pLslXd?OgOm|(+#2*&Z5K3+XP-JtEbdsy<$U!0vkS1Pq8=I;UOx9RDH zP-Lb~sw^qUfisjYEkqt96PLQT5RHnFx{3bDPd|VB!%x5d@@lNTZ({Dv)w_FWe{3@$ zZ|wXG7!wE;=0WeCH)iAbNbj%kK3?qyo!xb|^$*|u_|va{{@WjZ_vbJ$cZbbG?v?%f zch|fZH%Gy*5WJZa9?ij?(S@(-xs{tUe+SuwKY{0)C9N>{b?<} zz(F?OR#;!>7-f&6wM&&`YjX=6!i~LGTPq0B)&`q<*}{TsP*c%KYhAU$GWYo9`}g9v z=&ODQ5qNKN@6}jo1|?8|0>y}w5sp#N&yPF3qZ3$=C9!`!-H$V#u3XQYc% zgDc=U*Z1~bzdAtjHlUYCQw1szIuBHfMmgF{{473t_!CjOW7X1JWFl%=c6NTTK8t~% zZgl5hZ(F=Bj*5Qw{rBJe^4-TD_eNWL2Hf$z+Z*_ytVPf>2Lj^y>gn?STJrd>A68}; z06IP2y}LcyTW}5bSZwR>zWMh1?|=U7w~xPk@zpn5PRqz(VDXTi2Qu^XQvd}wSl}Pu z!yUp{^t{6S=;HbKs3CL%ztgDy_@?TZiv|lH|0}paUG0P!L1J?0nX`dxYO8B*#_@{J_+il zDVuq83pOk)(&dHC<<<4U)?Rw(p>eh;Z&!6?W36TW>5IpEdc@d9ZF3L0eG9M77+=sfS@17$Bm4@+_s!M2$L9|lfrXQct7k5Q z8-bDTf#I&PgI|94@dxp@!dgHsy1#jR8#fm*UNUuK&lRK`0egpcdvhIM%IePg&gSOY zP+J!@PzdMLh}~S(&{WaB#Cp?ve9Jj>b@vqSC~d7ZnfiRes4(dOF5*P|^m1i&Z8^TZ zwy`~G?ZtW&oeG4Vo%Q7nEp?sCZ$I1#U<%e^X)(IQ6CKk@mD!23%nf>4;kr^;Zmr-2 z>jxz1W6&{PC6@xa*5u$OWf~{XlNPzw)>jxn?(MCuqre_2%EF17&RJ9VCo_b~1WGb8 z?m#+=s^bWgXZ!S-` zs~v}3QS==5kZS~0;0-JxMyw}Shv(bD(H@(l*X-DN&wKI1ufP8A<#+GC{%(7)%{?^S z=bYI+dVO;E`c1?$cXE9Gp7z!yw(Q%xh3W9igTt$%^UdiYNB3}_X>jkmAHMop{GIqq z)dNh6QY9+&9(LHnJR#Z!a#g7F(>yKxdcWfAImIXsoGeHW#(d z(+9o4EI`A4VJ;90#n*=mv-O2C#tmTSiu6V0iriW^PD*qN!^0?LebsWA0TP|kSfolz zO378F7Y@xY%tb>>8*8zJ*}0`YoeUXodZr9xicYPoQ0Fz`FA8|Zpo(JgpRCST7!Bw< z%Z#vVvI+{)ipJL0=VDl(1EPQTTS1r#m_yUtd@qAL!}r8gQ6~_ka5J<1gahi+@9;etYxw;qCjS z<}!=bVyQQE5A+ZBb`Lf+yLZ+Wch)#lH+Hsmc0Enz!G8W^xU(K#MpILnR;+k@@xozWI^fB(Qx_fR{+`TFLjqV`3M zH&5dC;%DBAIO4{YZC6oVMYX;#D??d;drqOvQnk!tj&`{Qoi4$9)FREyQ^=L_!aP|@ zT1I|;TEXBV-kiwtI;z_E+WLTAT8?ieEkUlx;x?|yP`AR6iZJ4G`|tvVOkl_r*-9Bi z9J-0xf{bFg&MRx27b~JqUw!@6Pv8FV!}e$gzG_#uy?-2bw$s~gpFMuPeae1CcaM3` zYHyEcY;@c`GuUPC>K<@3+qb^{<+opd`Q5KS|M=&>{r-3Rqs^m3o<5sp(Btt>c>Ok8 z;F9sg+b;0Z@1gyp&#Vvw0GP320Q!rKK}B<&wpAAs1%$j_u-DJ)~+sFT}742 z)KXW?jy1Bi99do25uA5h+dICRn)YUMo2j!7Ze(psOL=1iZt%^e06h5ei?6m?c<-;g0}iX zG`>1eUQpFoRf4x6J(=#3R3bOS3-q}>eZzy;k^^m-Nz#?MI zXWH`W=e79D_aA@!?Ux_Ff9>vp?>^SuJv|PW2@`g{P6LYpMLu7= z%(=S0TX778BeP5MvB|;So}Qth*1_E`fBfZF@z3HvaCSdEz5{qS*HLS=TWxq0Iy<{N zy9VnUMz>ZLwpTay_gB`@fO}dRdTi|udxr(BZ&`I!p(%RtNCAFF1OLmXr^i@FRa1Ls zb4gVV;8(yTj3CZK&&MC@IzJv*E?1fOaiRku{((_l}IZy(3;M;wI?}II^ip2`|tGsEdo`y8b9e z%dMTc`9u(3cc8CSN9iH`L>~kk4VgE96ornAP zZ@&=4f)A@Rk>m4Yh<`g%13mq2XGibW`)_~zo%lELpWzvJ`|u85Q?$9-YHez1YGDdN zM=IoK99>^p;A!q3tZ#1Z?z+2rW@o(2Fua|$Wo30W>ZZAyw;$d=a{Z(m|M0xfT-DNU zswp-NF=Fy~r|q_hmpf}atGjz|c6K+nw%pwguI=Es-0js>O?G>2Tm1U|`Q3}RqJOlo zx%QGu$Y}&;CaCI-FcGKb~^id`{xcIFaj=(jxVC4y1aaTgaY{R z^mctbaCC8UkM?F|Y6QJ;TkqbNUw`+D_($+4%vuGa3@9G$<}n6sm{q_SF9H^=+NxCO$|GefKeQ+2Dkrn1U05e_iC z2zFb2dt1xvi>xA_-`sRstoSN$2Zx4Q(ZV%Xv@BABy%Rr+??s>HR~ObccQ>7SWeJ}3 zY>+_+4AA9EYh8H!aOzF@W`dEaMtM$ou?lTqNxn2KO_`sSH$)x-%=l$$4BTlhox-5b zN>0kx6czHX>iqmxCij!B35A3lEf%U9oizl%$8Xn1_)^#S^|^OM8r(a0GtI-2H;!|@1jPlit}&nRzU zoW_LD_Z)ou_8Xz1rHZ<_JL7V29pziySk>I^n`Hch%HG~Gv@}1rzPO6vXbn!jyQzbz z5cSn8u*UMD^5UA<#o<1oI{D}IXYFUTy0b`!d4WNX3MLcWJb7s zESv;x^f+g)uBvJs90{P}0S_Cnn??vo*j>dsyFzDT$7}5gg+Z@in~wQAs*3Zf<{zIv zw?8PM=9fYX_%NIXX?A9+LW^ZxnyV-*&5>6+$EPAd`Gd1Dj`kL17DKr_6t+wp1k!Gf z_to~&!s5yjGv~eiH|~n;^rXyuRZ+H54#~Pyo>hxZE(8K8Fip2C)B-$D4+m3`sVD(y zot37aI@;e{r5!dW`t;3rj|6i(u6CRIJ%QcB>+?qjqF3jEQQykO>u;{nO){Uw-?&_@nq2(Z9WaJUeIE0*!{cW~+Mw%<*SL zVn=gNFuH(Qh%#s$Ym>XXm*J^EUIcIH>8;JG4qe_}o*Z2;ad`&f6>BT2tatbczro-& z6A0K^h9t|sZZEBDut6;^ua36bkTf$3rvW_e>aLa7%pM~Zr4_}oy|@}%r2RQmkd&E~ zK>!FShiv$m(%gz+&qR29(#@9x6G4+&#^{1CHPhs^6Jgb65fU7)z^Ek58Oo{ zR&tGbTAsH$UthsX}autBFJe`9Qvl9 z^%`sYrozihv>uj4pGaTETWC~a}3w5YY!($!)%*=!CdELy3o zq)5rZugHSDT%-_XRojNg1&JJZr4EO^$JRYi-E0ZG-6rLFdX#!SEp0d-N~r^k49xG~da?dTrvM+DO?`rm7WEVb4SU1@ReL|~3vb(l*x z-iG=*YqQBxXRc~7TTHFJH4PLb9;SH{-kC6MA*-_U?) z7MdzcMNL~nV@G3CT}OwtrM0V~)-oG{#WneaX4CKY_Ld_-?QA8Er2+AWL0?KSkdY}V zFlr0a({eRAf_+OS%d09ctnR;dBuXg->qT*8QQQ?a=qGSq3b+VbM+ zM%04F($XT7un0LdhB9fgyr@8JyQVpQNhGdtuc_{lE81|qs&p|ra_qai&Qz|rF_U~$ZP;&CW-_pnvfcvo1T=E zl98ODEzu#2&X*|elqw;KN0Ot2Y+fY#D{Jr2NVjb`xO+eygWeLI{-qEJsL4e!Af_3Iq-R z=lF>r^2@-iwQYzQAJG)vzRuq9Xc!4TLn4$SZhKuNDPGJ2n;R?3n!1p$2|9m%Okk4t zbpVNH7#mkt$LQyoZD4r7(MJXsBDI96TXck-WyQ5EVw1p_m#XvzRTi?2Jc;tpFlQkL z{2V}GC@m^2tE|k(EUc&~(i@Bg;Ko%3dbE<yH9Sy_d;e7&)>CO4z7v{VD=Ix`LMyDC?XVu1-P`sggt3q>(sha%CZo?(}JXbi&K z1s7|0O_Y^K=#>wTC}hDxgr=8KadMPjZiRy@m`gF+f!yV9znU9$;9VaW8XV{y`TY5F zu*kHo1>*Jko5M3&?H5>#=P^gJ#3u)90YOBFo{-KhOMVb=So_f7^oahCCX2PTp`m9o z`k8czUW3@W?luxAg2-iNr!CEWs5n90gaUXL80dt+CskjC}L5Rm8;WJn4cn~kz{`dU`TIJrl#c?^@wudFXtDRs4;;k zC3*ju)_~3f36wfltJRgHiZXebWO97_shC<39Ds@CtmuCsXxU!SsvRDtDT}l?u*u{U z3MNng;{45Ke0l%s;`ZW^4%X_#B4Bbx3tY8h;q~JiL3WUE!w-Ks+iey1V!Zr2W|Nt$ zN0+b=Y-+%gCfoyEU9Qj!9&5xPZOA$V)GIoH@j-@slE5R%Kc?@2S+~t>mbCn#uCl7B z(-}Z7ix$Bzh-4k@wo$sYBy~+rv^DhbTYh|!*FJ}RWG0j_buZWhz-d~`OF1iA5N*mc zGt#r!)zKpZutYwi%92PjiA;k{noped=f}_SQKZdDPRZ4=6xs4@rgl)dk|DP%Whv}k zno{)8*-8~oUo9zVm@Jf#U-)Usq6}3&q@!H!6Uh=OU|9_U6NO5hS5zdIB*7KeivIcV zK%XnNcY1ODIrRpG%&WE4t^IR$eYT~W`{ls;`NQ+O=SQv=yRl&W6&F4x1+3Zi$*XyR zuF>1&LPC2DP5k~!a{jkZU#>1~ytx2+djIz6>D^9n^BVg5)8p02!A?B9dUA>ij*ajh z75YKYA?`vb_|(!^*X$T)Qi05SN;3K76z#Bn=R^n{AYPY9w`I^Dkj(s#P=mtb-R-@g zG($*0gcHT@>u+spZmB3~=p%OpWx0S}oSJs_*n37Nrn!`H_CS{qc(ej98QpBZZGe-8 zItpn5e`CT{Aubcn?c7XBB3_jO0jZe>Y_^DRf4UU)SV6Av^ktL{#jp{sN5_CpwZU%EC z+)YYkh6-(Iv0N-pK%e&ah_svDqSQpt3Uu-Mb{|I&G+*vwrwG=r=O*HZ=xZ2UU!ETB ztu4&IKDkP``=?L$7#LrM-A?DgNcc5xgJ4r4%iwx%J-&XxTlDaB3AXocZE6)4Hx4BD zPbb^q@W$c!4M2H%Mpw8wLXdUrqEB`8CYvXO8-k)AY*onFYwh*$3-C<54AUJ=J%Wyw zGeFP=4s_cDk*uJeMG6?0=x#POR#e-@LpWU+kF!8XpX@!jDNsH6{NU@Xb=_Wpq%#5c zBRJO9){kyYkf9?rfe&g!vuPCFF~ds(dXue?6#Pvdxh`LpoQ7hMuC_FXPKa3W6!9kH z;Px`8Cr|^FjK&sduGSEI^2qo*BO0zj&2!G5@pr?PJC&Lgh8g-^w*hMEKRRnen#)Z z>pgq3vl8FNSi{-=h(zuD;x&}aVbRyRhF%vEZyJ3BMoo-+y1V*4)9mMehogdFcS{Sg zEW&+&7Xi~sfBT?YQ0g$vq2l$sddyYzb~i%8Y03EC<3O44^g4$48{BcjF#7OEHetpP z1poM5$*{1KVIde`(12j_oArr1DI`CMv+{T_JnDRS5e14rWl*5-S89}6G|Q4i1;R8m zmYlQ(qb5zr4d%Sa<@MqGP-iCbyw!Pf`mI9t@ug3`!k@A7Ee$%!nK zsCU<-TAuH(&#kii+&#U2{`wsU{m%4gm>%HS^*i>U$J@h&$=Q`dA|A zS<{4Q6YCbw8Kvd;NUwd6y`Qz9b{!Y`Ae9~C(cr`sPK_CEaeXbVj?d-`7K1j3-``%} zGZBzXe>1~%g!jVJi4zi+B^r=$#6KOuh}SLp??kG~$~?I|KTlgkAeTh+LRwf-l&8#> z<>%>*MviTTUMFDok);`p8fi)bCNMRJf{K?!TV9xvrYWybCa17tVba6I`uk+ga!sBx zQ<{N>tvFpI&CkcNokUIxOt&mif|LupOp;sE*74QEB$i2PdbaLY>QzGKu|nGKRc~#vxCCvdcUmAcv-iB$}L^ z;BM)}tSY1;vS9w;WV=q=GCmjO9bmf?%y=`sU45=Vgj@v9l|T@*wO=qH)s-<|W0S$z z0=gqH3qK>kfy~q-nGxfQNL5vnpPre=Zc|KNPIg90in_$00&fGrw^);&m2s>_Ph@>J|(>A56;$$2mEv=XeR7X2>`)31o> zx+O&7>ScU>?d9PaIwPUq^!RYT7aMCg^@sN$f}?wVV0A7o*5Y%^cUMQ1}Kf5u6 z8wu_VYNW-T1FU{Dww}@29<9O1sqG5vVM4mQlRSL50Zz2FOxhYU0S*^j0-pmxF2RiZ zC6mw7AWgXF_mAOlq2%;`F&T`^xVsn&m)h8~{3x3F-G31XA_yBm^~cG}qn-8H8Xc5f ze9r|CbFY1X{R!<9w-3^!#zzRDP*wE!BM}6h+(xjJO*#9!F~0z!<%%*r^VtygZ$)yv zkpQU`c&$?tQ)SW|wO)s^G962pN+>}Sb2T_)I1zMO@})pO3buU>4pZRUy1XP&HXIe1 z6!fK_;7_Ohsx=lFG64kF801Fl8bL|9t4%_+ov~yz7{w~q*HOB;QIJEhT;?h0Zyj3%(Vrx z@SXTp^vTf6GZIuCA%BkrfId%6Q2I~!1VoKd1=*yg+aKe0hdKu~S7c&*KyXuVXs~9X zDM!l?giCxCAmJ~t=`~hKZb{3OVbs;zjeSTU(YBq&HE?%1)u>$h` z-*1~cX6vUoJzNeh9bI3eD+33P&=u@JZ&Q6!LxXuB_L{-bB`Pqw=jTT|3$d*u$;Dqm zH@Lf4nOr(%N_Ts8eGs2rM260bg4ab*M7<0{{-p^!+Ge?ZVg0m#j~{5&pnTDk_5cBV)6-qPOZE zOh7dL{El|Ub|n7&8-7KQc&K8k9LIf-f&e38vHz72tsA?RD?wtwb0xomc4-c1nt4phI zj!sX{PB9B#i2glhVQefD{w^U6WCE?(YzS>2IZqv0iWF5I6c&x9vX`Pru=@%Cjv0X0 zwjn%?#7#`}v<{-NWe>A=_WL1=Q$De@1h>reltY0iE!GfYkXO~i>5m~pGX6Zl0B^Xv z+d;$vr2zZUl<0pJscWh@Sc}q9q*_o9`FW(kWHY*}P|>3k@Mnnxf@Eka%d|qu2k;tl zO)*d8l$2Ce7|V*XGqvRv#YUYjHASu`^8*)HzM%3iu284ENJvc*2!h`s#T9Z3+0S%& zg`^v$q-GTAii-3)u~w`T{cmN2_Qm31&{u-w{`UP7-PW7){cXRklnPp}Cq=?&sB9lv z-oX$iNcuskV7>rs9u8xf7`AfvMN1NNBiVre5#T z9^p7>2sk$GA*sxT!DlYTF{mzax2^BAc6FoPunF;iTpx%6q0;nql*kEgpd*lFlxBSG06>+ili1;ss$7&^P~VmY?)B8)VWyd9?7t=a-uY z@!>OrWy8Z}<|HDxcMy&|67GJ{&mue!xr#J$Sqe+EIb>fHU=+?(YI8x3CL}=0{D%^% zeLz{{6Djg=Zj&&9`9g*#PnAt5R6EVxpvB(&>8Q@?Wz{pj#OWbgQbmiXIy$@SCO)?BZttgND}q`b63 zQvGjg8(VsV#N6OzynljAdbKq@=Jes7m>`tKMLdwtGdlP3==_O%O$w=-i~Y6!)|%Re z>Wa#WnufZnn#ML~bYD0h9=~{|Npi6j85Tk)JTKg2Ot!Q&TkWQ*>K+X8AtpV@0pS%4 zR%^MW78L3UI5z6F24hXv%v_Kwwh&P<4f?5}$!cx0TWhPD`R^8UV{I>`DLFfn9{2dn zjAyV^jn>srRBR|J0z#nHH*`<1x-2q7oxt>HS9N1sN2`#M{%xkVAXkwKqgrkt2OA-5 z9!^uN50a#>va+RVsADjnO8N6d1zV85NTU32F{u`2Bw{v&B?rkylP67Q1A@-Nwk6YP z*!q>ZDoJ9JG+WBf_9Bxc)U4D@A-0(%E>J=wFV2^VlCp?t{k+9)*(iGB!9wH)tgmkCUnsU05D< z&z#(_Sqa9m+sj?Qy{;2S1YGvf;nCqi*RZ9kIk3;<|K0TiBhQPI>8_ClvIoRIs+4~_ zG&;2LrxH$BXI>c5iAK;Yq6>v{&Z`bsj0zivDmHUDorPrNjI&I&v8ZVRULj5 zkYXEsmcOnuwb)FSw(Mkisa`?2Sz=PQp~m=lIlX+nY3QdjpvkM`^G0_E$&sxx?W!m|X#`*^Vz>MpAvX6uQ?;&X72qrq8w z9gOkU?C{t3g2(qPFuOpr@b-dQmVw6o)22U27(C}UbBnWGvc8&!1JP(4Q0)A|d^8>& zt*sr3y}7%>cl@W`i7&?B;ts7aHS5Oa#wKi8caxohat7`L`;W_Bo~`Q`8g`Bij}E}a zb&rkJYt;>Q4Ycvwb{p3*Vi`YRyJ`=p#Jc*S*L^aUFqqON!wAHr)?e@9d$ZK z1l+89(qq*qYuLJc!X%vHAYik)wzjwSQ5b?YL6kx1nVKb6b3raBl@&DlXqPzQ#rjAmoVDc2083!7OUqE{jrnP! zgo5gN*2vt{(AZ?QwiIJFg1jbGC<+z8bSkoQ>*+7gid~pOC71u{`sm_mJ-l>zegEYH zl2JhW19ke^;V`_{_}a$S(kc&hq`A6zAa;EE`1T8w+Rt}$&hYv51!zSW$`223-)==B zhZlkXlhTPkcU`GzY;j|CeRBig?4l4~(NxwnxNv;>eDm(%Ep_gkZyrYcBUiZx(Wl*= z&8_(Ij@v#E3LQ*G4O3a&mlZ#O?J=IxU8*yt1xwUx-tLvN&LCckS$K z?=#Qe6@7wfFbjvafxBEmzBX5pDxhmPACinF9;B(Y-O41ZrQN`dTP!WW-K&;zf|SY9 zE4(+cXs4 zB9|5%(cML0G>p^k0mvC!)+1{Xjv`Nc$93=GRkW|6)Y#y}3=v;iMkKSw&loWmSD8lQ zTld%Ruru9W$B_A7p54*n;I-Js_^`aPInrXM%%5?2XQy4(GDT*pys59R-{BY;a}9&w zo$4&f$yJnhkFeK!gsP{tad2~Idv9fPgTMs7;4WLIA4eEGkDw4gk(F84j$s?k5=uOj zPM$Vnrd(OsJrWsbEnrJ@c6j!7H}+t>uZzBd20uHFw3`_lBOEEcj(mA?g6N&LmfV3F z6Q+S$P==+_&*>8iJub<#OjTiWa+!dSbd5v)jfA694ay8=+rs2jtz$aK*fy3|V+->OD;`r-Lrvr0)SC<8@7!O9eDfUK?>QQt@8R9N-3-K_^1h=Bz8mOk zs=%=8URfi5Vq<$_bA6emXez6&ZtR~rKF42idwmu4E^zVqz=h%~(O=`ug44b|L{<@l zP486L(@rF#RF+Rd4vK_v)TtiN#B4XZh#XawmCc2P2n4L{Zqy}jBwK&=a(fRelDpj& zh|VChA-&>r`h?N$_m7PbWdx0dU9Cf(t;kkYSZAg}IO+Z9+s5`@;sV?jzY_f`ESq!8 zE^5i?*25jp6c-4fcd;b7xUsIb&SY&eBXKs{DzXwmqa|hmOB19xC7Idv@RMV-ey67+ zvAKXrAuTRe7lJw{$fR)nLuP4PYjqW$?`Ugl?X;9gx#5W8ZB zMEw8Z(oJ}rM|*L1b^~~;siqps<|w%@@zuTc&Be9FrDa!rRda2HxjS@tb3ya` zBH|1l9vso~z68et!)s*%s@frK+0E%rY?{SJvpc@Bu(bV>4=+aJp`nJd`pS}e8zt1) zh2;1qI22+E^$ZOSDIlWBJ^tO-J`jt{h9-mKoh3|Qva<^;{ev9+6s8c^C#E_J^9*^p z+A0U>O&0}PoJ9~BM@EJhe6O0uQWYU#a_pe?gB%~SZcpX~1TiY9& zO+fmYhw?CA(6l2^t+Y;^+u$7^5BkPN(KVsTYRZw86muXKsXxCzNr|~t9j*2C2-&$@ z)is-nq)8c6!(ugzE}hy?sgl-lzy(m+U~XXu3MBk~zMl}5$Wmn9-&Ms3z1UFUnDB?9 zkwpw}EAfSe;hM6>+N!!*n~&{kZhoO}D13T+^uVngl@#!ywXxCIVvI*aXF&%}P+K$m-a#zdRoshm z1ygUmDWB-e~)9Zw%N6_&RVQ)>~3shxbQYNkU}v$FlZ^w zX4)pp&{~}C;ZYbv-U**?X0lsP=}#Z8vWw}vqi?ja#q|=KrFdDqA^KaS4$e`iG|RHo z@(fv8wlKMQK}$!e{YC1vqL-zLhrIcDb>GS!x7bSA(K#bI%A zc5w3g^eWuzaE**kxja2}#)qQdI_$*FLdTuOW!@%X|}jj@_Nuc*XM{Exs)Z=XIr z+9y+xDF*fH?c$(ybZo@$^L92Ew1yI0esw<()1|f5HAFg*rFl=Iq1aejt|{#t8TGon zQyumxQlOvF&+}TWEXLx|rH$dbx^{DOr`gh=&jA)HOD|~Y8yFoO=ywba5-2fl(ST_% z7;}|XE$G8}EchkXP#G_+;I3O;-*7dSH8-0(EtWEMW=fV^np$8Ub`A|=F&=e|jZOIM zdMR1rs!U~-+1$ccytul5eFM&WoZP7QUXOQXIuf-oeHM$7Maf*}zE7uRm#VLl&nttKKfEp64=iD}7P*hwu+Oi4^rzK}NvP$jWbI4n?EzIt_d9PM$%7f5}aX98AP zq}Axkh(Vr-hG$~YNML3$He@MjYOX4;>_lI>Fc+Wh>hv0__}SHJzcUsk zn`LRjUcrb<>evg!r}SJrTAW`p|&?- z%Nq!VOKZ9~+*<8T6dN>eN%Fh zOe)H5ot>L>dM8Fa-0}VX*0dC+8M0Jx2Gn2DjO0{R74Jl^1Fm&*OQ!|dW+FKjPs!KrxN)$;WG;nC^c`T5$UwVZxfVPy}>$=K5B%G^Bj z*w{!-vCddrY3Cw{0`2M|=A3(Ve0uvilllKzU5hO(Eyf23KLFR{4|uEuG0QX43hKMM z9b+RlChY_LUQfFbHjqkN(Mbp}3jN8hx*=YP7?N-d7aOaqE~|rfH#5AkPLjjXZfi_K zBSU1fa8}R<@wTg>Y9M*FP5Ql(soOwLvkPy~4$9DN(fwq*OWE2)HXMpYAVc4$jwqxMH0{j42N`;73!=8=Zx3s@=!3NOPMeiXey0GWrD(t zi+MtFveZy-YP59qc5u>|t!-r)iIiojd8Ne##m4gbLRE_gGzP6mjtby%=N!Ouo+xsQ zhvT!$t?`b!QiG_ zhOezIti(BOR>v&BP)V_vj!xK%rHLt71ueq^-F@s&>``MA?sknF#XUI`OfYDy_^r*O zb1UoHyc8RvzYB^Jew4Q^OU4*mpQkBNbE}XfypWev6_+$N8Uct@Ry4O&%gK`$C1f(^ zktqL5ogp``#fv}`?$qcQ{AXXiEXRnKRiQ4{Wz))ik(N{0QeRQszy<}}yUjxG6eaEp zB~mo}F`Ba6T+=8+L-8y#!+&ymwtsZ6e-QT0A*8=QM!@}YroGZ=tQsKBa&BR24!A!D z+GwM;s--8mj*^eA`0Y{5727AO==}8cn^)(OtG~Z69z8mx7`r_`-kxeJ(HklT!Z@aZ zNkqf&T4wv}%1Y|GCfCk|i{aJj(JajVlj95V4NiuC7H5XHwz@RLS`g2SPAojpp(ndN zzh%JQs_-wrf-5n({6;b5=UI(-U2$;nOuS7z{#tT8OnH65#_DB*v^+NIMK!vL>REW~gS_p>A)Emt%*MK~5j zQ1QvRV_GK{cgN@FJN}MxL*>w1JQ9rtmKPV};km$IMOj@>aOVW;7}0rG7tyi!>Gc^m zJmIxi-(23_+g=~B_AzIR%tR(4QFlvGc504!u(zAAB5nu6BfzfJDS1^L-YGm=3?f3) zj>ZvEk;n;K+gRErl41-vAZc||)WacfM-hhU7V?0c&LQ#?2?iN$QOSwP8DpuUvxqfA zJ1IaAB${V)-pIk5gYy$mfp=G%6K&;<{h=jdHYkS| zT{%x?r=yl^Vj2kn!Yoh8kthd9Ye${SGIO`TASO(fo2&1@a;V5D~wqBS}&I%uJK+Pips_xAqtdvP8a_DiB?i}W29 zMfZJ9*SK@oX=%1grvG+WAd;XCn6c@Ls+vq@{vqtG#HF>i*Xi}$9&TtTFVT+$Cc7%z zh9$0lh(Ys10o5~v#Hb8XKQIj8p9k)^q0A^3WfmB9 zSmqUlN=<>TLMg3ix7qASuPmZZouf{-qsP-X7J=;d_M8;?2L^H*eoJFbEpZZcRPpAr z((202;aAre_YWWLm_c7&y`1hF7#tdQxkhY`9vb4)j{fO`lauSS`!DZNJ)f;in7L+} zDr+njv$?*y$yCzf-4Q67%$?|6ogK{%3=cZ`oz&F*qK`$5mQGV+XHB(*E(MxE-z44c z$apu0U_nCR_XPz-;J~$bs3*u9^|I5$nxm~$Ym&78HysvJWkapm)MBryY(q4_xFa;- znv6v~HX!9)j0`i@?5wPsVYL{Ux*~4%-kkRu}+B?Q>WLI6v;F4k(L{^YH5B!S$S1m zZi>9rNT8#xqLA5mky;^1Ba<&RF;ki)_yMv+Y1$gP=i(#==lqD$95NG%ip#`$)E$?G z!Dvjf@P9Ait=dPfEQrrYr9C}fuxd00jY?CFLA^v*-afkv-S>{c5hK3qgEe?(F)rfE zu5PlkXTwvGP-uy;9ERP3ist_6e7>imvQnomDk!cjH-hU}+ymv0BJ&B)#nqdYP#}T@ zDGbp9FJ^juUzg3=VQ=pm21NRKLN~*O(Q8-JvO#IeXft1$DAn14(A5C_1tbN7*Q zXC;EdTCA<;u~~_u>1gTl0u)3TC1`4<{B}d8fYef?%X69R=N0!0@YE?x*$Ju8Xs?&vWg6W$1^kk9N$}XT#Y`cU90~Opu$|s)M*tpfiBgRj4Z3 zy`lLax=YtMvDw4G=SUF=A=>Y1?G>V@`~eoHi(Ct6`+k4I)E~yXs(56Y0@NB1&T4J$ zsSw02A=-R)(%IkJ3wID?B=yS-*%Sp;rOf9Hij3rR0%3skY6{T0Vvi%6B2g+&PE4je zO<_n{kR!(=gbzY3OH0Wxl$BsYs>qdV>nn9d`34o)6l$Y}xwu5}XR5qHjhyN?EhCfL zNRl{7M$8u$3pr{{et33PT1K&??A)M3eg+=R6nuSrhTr-6Wh}aJf&_pjAKII%v)AFy zveMdy#uih3V}E$(2q7|FLu}Z$pWPA|>j{LvSoB8^-yEDBTwEQkN7pa!Pf<)!jNzC# ziuE%ysU(lHy0I_vij_cv^GLSg#Xek_mk7uSw*dQdBf8M`0`L7Z0z8~{>i`fr)B>NE zW6IZ6l%2~!mFR-J${z1D!3V!*!URrp%+}uL1)?UP>45nn^`Ng7@b$FxP6uH~CHVhh zio)1YZ74DbI&is?VFfWHg7}OP;Eb!s*6-onV$o2aO^ZH=a!T-JskzUmq(}*rEl^98 z*RcOMnI)osoQm*|l}IZfZdO6|4cc(Mjz553r&a5DL6Q@)OZ3S7;caHTNYfe=N<}VG z=Krs!^N4LT&(H8DQC*bSdy%L@s!LWcR-s5y#cHH_Aw{Zp$&zKsRxea{8GFWMygOS4 zK@LGKd&wo()8??p06FBeyTC4z+_EW$AP9^+e+3BUW?N(9EC26T-uHcUrYwghhW9C6 zM%)$1A{ROmCF2((2y=COOsd{uwIM-!*lhoO<qw|H4$kEZkIVExQXl-u&@bW@-`4>VW zdwKSB!q-q(Vg29eBLm7Hk1JAce_K859j-6h zNwhSq3JIbG>%7U( zq`$Ew*KS0pYH~I7A^;HqcvNT;gPpByep&*~Lhb~w#XCt!>?j#pR=Z7=kZN?IO@eor%(!jJ@@(E+^NH+XBV7%<)%7c}!e7b9+uty)lMZWs_0^tu=%9h^$SrGBs3L zBFAyvd^q~BLZj^G{K+@leDJ~Op;#8b`F zD`es4FQ(hdp)xhLPrVd2EnE=185b|s7B@)pzq&nV2?U>c5!ubn*)^}i@k(Un53Pw7dBmU~5r!~bN^Pl#s~0ieH0WEY65 zP3J=|Hta1h>8y-_TE?a)A0BDO+hhISmbU(}DR`Tt4TjuxZLltA?Ksu~t+k{UVXF|5 zKkm|CTZ!FdHrR_jA~b${i~;QU*kr)h+8H2@87Ej=5DB0n+pgCt4cbhXm5`bYI9xo_ zC^h&nvy1!NSSd!8;{-W@8HEJvEUO&Nrok+7PidKDF=>LelY%AEVUfqC@_FG9O5+gX z%S-!6{jP6hw|^iB z;2`9!DyeQ8-$vnz|BJ6j^bCi^@-C~2=+!XHXtR+Jc#zx_T;^MU z`Q!L_e}l{7dQ7ncctSLD;UETy&OW}jDERpzOpHH%)Xbv~m^#lCol|OOKOdHo+W+DN7(k z5EmyXEheH!6-1cYvy7?~;f3HdGFh}4e7;f=wE5XabDEA%Om>bvO{UOUS;vxWpfjQk z6yjtRC>Vv3x&zU(CM`)W*K)T|81TMWpjGIluf=08&k!fSBNa1x@?v9oY7_r7CI8Kv ztHa~Vo$-#L^@}LWPa=cPukQC}Cl_~5IkxU@IFooqrUPMe_FrGVqRSxEW_xDt=^-94 z(YsxrUf%DGxkt89bPD7xxf7TB8`Eq1Bxs_KC57|yf*{j9=~h(_GhFC|$7P{^-0IqoKsBjq^{gky`FA`2YmkN9qbX&+qVpq&}y#C zuRRc`E~t-=4-TdWW&x+7B&Tl&F1Rr(a2ME?Ug4*RMh{}9VxZWoa@`|gtf6C2lE=nF zp*9>|G}$49Oz2ue9)O%8sF6pS?z<1-&Tx1nAkypbOw0^>YVymy15;sAuEyY95Io^+ z!h4651&%_et}PkUNI}nj0qsoV3pafOd6#UbqG~GXSgmdNQE%wr*?d7G77g5>GZ#j{;vL~C946VCI=AWP7wtMsD zjxhM+7wg>2`#Ti1M}%w~Obq}Y#CwNf?DFLK=KBBRF0SrwPj_ZOB-BX%q%s$|2N?*F zca01mAW?gxI}n~igNdPtpk5Nh$CxxBew`fa^|ZG8S?zGf3=eWd`I{@tiXYVx*Du5y z*ob)C1MN-xd?K0aFrL=5|T9*!&hhJYw5&_RaBtAi*kQOUC@A%Wg&By0 z37-7f+5T!|{qpXNBj=7!_4?S-_5IEJ*B`Od?Ji8OqV10o8h?9DlXo=N*X_srII^${ z$QCj4_1*R9*~{h7Y7{&t;k5XM7stU}ynXlnJu{NsrTNW6)~?(){A0IB8QK{&6iQ!d zl_>Uw4I2dqQ-7dob!{j{!BvDpQ`5d$AL7>F*c??)ptYfmnT5FIoI>HbP-k^TU0qeR zc-E)HeGn1j;{&bL9+pSJDU{?BK_ZJD`iLL^h5pvYjscYAoVNYmn$ zNSCKm0h9j$3(Tm^0J0NrwP^+Jf)+7W;4Z*Q;Y4y6lH*C&L=n&LrKf1kdU-qx^e+W@ z`4t_JJ#dWl1As$LKyJ>i9%67mj=TK3#o-0?n78Nm7l%8ma|_QdxmWxG-noPA`9WW& zzqh-+y>IFnKX!qg=oT5rJ|26<9+yY_%K6;j@+lUUkN0Slm*+5SW5Vap0T8~}jkH%n ziYSo2@RAw}(XjI6ekgFjpnB_D1z|G~H}-qJ%M&C01AfG@e)0*)dmkO*8-XB}r@yYG z{Bd1nX<=DY|0JPrTwm%|T8i4%0AB~c;M&y{)6r3$@c|-FqP71)?k`F#Kqc-%GjsyA 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

gLu!WkqLuONY0$u{7VA zS5jasiqJe>^Bp4=Rd)9`kqT#ZReqkctl8`K3V2?5VEq|~+dAs<^)-IJqTJK!?PzSR z%2p*)y+X?@dQ>jXs7KPhNk_eDK-xt8DwF;gL&hZ$1P+b9Hh`XRbO?ScSA3=kFrh9* z4*@+jF-2i9CENYtjGCQ|%r9=^KI^h7tfthY*i^nZWPLbP=H}2Wv&-QCn@L*QdD12@(37XZ9|o_evNSDcdze1eE#(Fj~~BV@s>4!H7u_vsH(t}TkEJC zMR4#E1o!1W)!5Ng2?m+c+LH3ZOr43yZ&OZbgWK1EUA>ioBq~%_aXxD=EJ9dY1Ory5 zu_QZvc=3TiEUwMY6JgwA(`1_Q)@o2=IJ3!X&6^~29L+W8^|wr9CaN5TWyQsgOubMp z=`&JupR8{$&Mgo{jkafg>4`<3VYXz_P^QKvD0Ql2jlH1kQ91Q2F|KrALOK&0!x@z# zeNK=}%}|ShsSgmOADWr6LzlsopJu@rdGOhL$wLz(r#U@Da hVPlTXbdHEj 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,#GRT3M(X5^w|! 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 +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include + +#ifndef GL_VERSION_1_3 +// ARB_multitexture +#define GL_TEXTURE0 GL_TEXTURE0_ARB +#define GL_TEXTURE1 GL_TEXTURE1_ARB +#define glActiveTexture glActiveTextureARB +#endif + +#define GUI_BASE 90 +#define VIEW_GUI_BASE 30 +#define FONT_COLOUR 1,1,1,1 + +static puSlider *trainLengthSlider = (puSlider *) NULL ; +static puSlider *trainSpeedSlider = (puSlider *) NULL ; +static puSlider *trainLambdaSlider = (puSlider *) NULL ; +static puSlider *trainHeightSlider = (puSlider *) NULL ; +static puButton *trainEnableButton = (puButton *) NULL ; +static puOneShot *trainDisableButton = (puOneShot *) NULL ; +static puDial *trainHeadingDial = (puDial *) NULL ; +static puaSelectBox *trainSelectBox = (puaSelectBox *) NULL ; +static puText *timeText = (puText *) NULL ; + +static puaSelectBox *depthSelectBox = (puaSelectBox *) NULL ; + +static puDial *viewHeadingDial = (puDial *) NULL ; +static puDial *viewPitchDial = (puDial *) NULL ; +static puSlider *viewRangeSlider = (puSlider *) NULL ; +static puButton *viewWireframeButton= (puButton *) NULL ; +static puButton *viewEnvMapButton = (puButton *) NULL ; + +static puOneShot *writeButton = (puOneShot *) NULL ; + +static puSlider *waveTextureSlider = (puSlider *) NULL ; +static puSlider *waveSizeSlider = (puSlider *) NULL ; +static puSlider *wavePolycountSlider= (puSlider *) NULL ; + +static ssgRoot *scene = NULL ; +static ssgaLensFlare *sun_obj = NULL ; +static ssgTransform *sun = NULL ; +static ssgTransform *teapot = NULL ; +static ssgTransform *fire = NULL ; +static ssgTransform *pedestal = NULL ; +static ssgaWaveSystem *ocean = NULL ; +static ssgaParticleSystem *fountain = NULL ; +static ssgaCube *ped_obj = NULL ; +static ssgaTeapot *tpt_obj = NULL ; +static ssgaFire *fire_obj = NULL ; + +static ssgSimpleState *sea_state = NULL ; +static ssgSimpleState *splash_state = NULL ; +static ssgSimpleState *teapot_state = NULL ; +static ssgSimpleState *plinth_state = NULL ; + +static ssgContext *context = NULL ; + +static ssgaWaveTrain trains [ 16 ] ; + +static char *trainNameList[] = +{ + "Train 0" , "Train 1" , "Train 2" , "Train 3" , "Train 4" , + "Train 5" , "Train 6" , "Train 7" , "Train 8" , "Train 9" , + "Train 10", "Train 11", "Train 12", "Train 13", "Train 14", + "Train 15", + NULL +} ; + +static int quit = FALSE; + +static int curr_train = 0 ; +static int curr_depthfunc = 0 ; +static int wireframe = FALSE ; +static int displayGUI = TRUE ; +static float cam_range = 25.0f ; + +static sgCoord campos = { { 0, -20, 8 }, { 0, -30, 0 } } ; +static sgVec3 sunpos = { 400, 300, 50 } ; + +static int enableTexGen ( ssgEntity * ) +{ +#ifdef GL_ARB_multitexture + int tx ; + glGetIntegerv ( GL_TEXTURE_BINDING_2D, &tx ) ; + glActiveTexture ( GL_TEXTURE1 ) ; +#endif + glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ) ; + glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ) ; + glEnable ( GL_TEXTURE_GEN_S ) ; + glEnable ( GL_TEXTURE_GEN_T ) ; +#ifdef GL_ARB_multitexture + glEnable ( GL_TEXTURE_2D ) ; /* Enables the second texture map. */ + glBindTexture ( GL_TEXTURE_2D, tx ) ; + glActiveTexture ( GL_TEXTURE0 ) ; +#endif + return TRUE ; +} + +static int disableTexGen ( ssgEntity * ) +{ +#ifdef GL_ARB_multitexture + glActiveTexture ( GL_TEXTURE1 ) ; +#endif + glDisable ( GL_TEXTURE_GEN_S ) ; + glDisable ( GL_TEXTURE_GEN_T ) ; +#ifdef GL_ARB_multitexture + glDisable ( GL_TEXTURE_2D ) ; /* Disables the second texture map */ + glActiveTexture ( GL_TEXTURE0 ) ; +#endif + return TRUE ; +} + +static void writeCplusplusCode () +{ + if ( viewEnvMapButton -> getIntegerValue () ) + { + printf ( "\n" ) ; + printf ( "static int enableTexGen ( ssgEntity * )\n" ) ; + printf ( "{\n" ) ; + printf ( "#ifdef GL_ARB_multitexture\n" ) ; + printf ( " int tx ;\n" ) ; + printf ( " glGetIntegerv ( GL_TEXTURE_BINDING_2D, &tx ) ;\n" ) ; + printf ( " glActiveTextureARB ( GL_TEXTURE1_ARB ) ;\n" ) ; + printf ( "#endif\n" ) ; + printf ( " glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ) ;\n" ) ; + printf ( " glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ) ;\n" ) ; + printf ( " glEnable ( GL_TEXTURE_GEN_S ) ;\n" ) ; + printf ( " glEnable ( GL_TEXTURE_GEN_T ) ;\n" ) ; + printf ( " glEnable ( GL_TEXTURE_2D ) ;\n" ) ; + printf ( "#ifdef GL_ARB_multitexture\n" ) ; + printf ( " glBindTexture ( GL_TEXTURE_2D, tx ) ;\n" ) ; + printf ( " glActiveTextureARB ( GL_TEXTURE0_ARB ) ;\n" ) ; + printf ( "#endif\n" ) ; + printf ( " return TRUE ;\n" ) ; + printf ( "} \n" ) ; + printf ( "\n" ) ; + printf ( "static int disableTexGen ( ssgEntity * )\n" ) ; + printf ( "{\n" ) ; + printf ( "#ifdef GL_ARB_multitexture\n" ) ; + printf ( " glActiveTextureARB ( GL_TEXTURE1_ARB ) ;\n" ) ; + printf ( "#endif\n" ) ; + printf ( " glDisable ( GL_TEXTURE_GEN_S ) ;\n" ) ; + printf ( " glDisable ( GL_TEXTURE_GEN_T ) ;\n" ) ; + printf ( " glDisable ( GL_TEXTURE_2D ) ;\n" ) ; + printf ( "#ifdef GL_ARB_multitexture\n" ) ; + printf ( " glActiveTextureARB ( GL_TEXTURE0_ARB ) ;\n" ) ; + printf ( "#endif\n" ) ; + printf ( " return TRUE ;\n" ) ; + printf ( "}\n" ) ; + printf ( "\n" ) ; + } + + printf ( "\n" ) ; + printf ( "ssgaWaveSystem *makeWaveSystem ( ssgState *state )\n" ) ; + printf ( "{\n" ) ; + printf ( " ssgaWaveTrain *wavetrain ;\n" ) ; + printf ( " ssgaWaveSystem *ocean ;\n" ) ; + printf ( " ocean = new ssgaWaveSystem ( %d ) ;\n", ocean->getNumTris() ) ; + printf ( " ocean -> setSize ( %g ) ;\n", ocean->getSize()[0] ) ; + printf ( " ocean -> setKidState ( state ) ;\n" ) ; + printf ( " ocean -> setTexScale ( %g, %g ) ;\n", + ocean->getTexScaleU (), + ocean->getTexScaleV () ) ; + printf ( " ocean -> setWindSpeed ( 10.0f ) ;\n\n" ) ; + + for ( int i = 0 ; i < SSGA_MAX_WAVETRAIN ; i++ ) + { + if ( ocean -> getWaveTrain ( i ) != NULL ) + { + printf ( " wavetrain = new ssgaWaveTrain () ;\n" ) ; + + printf ( " wavetrain->setSpeed ( %g ) ;\n",trains[i].getSpeed ()); + printf ( " wavetrain->setLength ( %g ) ;\n",trains[i].getLength ()); + printf ( " wavetrain->setLambda ( %g ) ;\n",trains[i].getLambda ()); + printf ( " wavetrain->setHeading ( %g ) ;\n",trains[i].getHeading ()); + printf ( " wavetrain->setWaveHeight ( %g ) ;\n",trains[i].getWaveHeight()); + + printf ( " ocean -> setWaveTrain ( %d, wavetrain ) ;\n\n", i ) ; + } + } + + if ( viewEnvMapButton -> getIntegerValue () ) + { + printf ( " ocean -> setKidCallback ( SSG_CALLBACK_PREDRAW , enableTexGen ) ;\n" ) ; + printf ( " ocean -> setKidCallback ( SSG_CALLBACK_POSTDRAW, disableTexGen ) ;\n" ) ; + } + printf ( " ocean -> regenerate () ;\n" ) ; + printf ( " return ocean ;\n" ) ; + printf ( "}\n" ) ; + printf ( "\n" ) ; +} + + +static void waveTextureSlider_cb ( puObject *ob ) +{ + ocean -> setTexScale ( ob -> getFloatValue (), + ob -> getFloatValue () ) ; +} + + +static void waveSizeSlider_cb ( puObject *ob ) +{ + cam_range = ob -> getFloatValue () ; + ocean -> setSize ( ob -> getFloatValue () ) ; +} + + +static void wavePolycountSlider_cb ( puObject *ob ) +{ + ocean -> setNumTris ( ob -> getIntegerValue () ) ; +} + + +static void viewHeadingDial_cb ( puObject *ob ) +{ + campos.hpr[0] = ob -> getFloatValue () ; + + sgVec3 r = { 0, -cam_range, 0 } ; + sgMat4 mat ; + + sgMakeRotMat4 ( mat, campos.hpr ) ; + sgXformVec3 ( campos.xyz, r, mat ) ; +} + + +static void viewPitchDial_cb ( puObject *ob ) +{ + campos . hpr [ 1 ] = ob -> getFloatValue () ; + + sgVec3 r = { 0, -cam_range, 0 } ; + sgMat4 mat ; + + sgMakeRotMat4 ( mat, campos.hpr ) ; + sgXformVec3 ( campos.xyz, r, mat ) ; +} + + +static void viewRangeSlider_cb ( puObject *ob ) +{ + cam_range = ob -> getFloatValue () ; + + sgVec3 r = { 0, -cam_range, 0 } ; + sgMat4 mat ; + + sgMakeRotMat4 ( mat, campos.hpr ) ; + sgXformVec3 ( campos.xyz, r, mat ) ; +} + + +static void viewWireframeButton_cb ( puObject *ob ) +{ + wireframe = ob -> getIntegerValue () ; +} + + +static void viewEnvMapButton_cb ( puObject *ob ) +{ + if ( ob -> getIntegerValue () ) + { + ocean -> setKidCallback ( SSG_CALLBACK_PREDRAW , enableTexGen ) ; + ocean -> setKidCallback ( SSG_CALLBACK_POSTDRAW, disableTexGen ) ; + ocean -> regenerate () ; + } + else + { + ocean -> setKidCallback ( SSG_CALLBACK_PREDRAW , NULL ) ; + ocean -> setKidCallback ( SSG_CALLBACK_POSTDRAW, NULL ) ; + ocean -> regenerate () ; + } +} + + +static void trainLengthSlider_cb ( puObject *ob ) +{ + trains[curr_train].setLength ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + + +static void trainSpeedSlider_cb ( puObject *ob ) +{ + trains[curr_train].setSpeed ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + + +static void trainLambdaSlider_cb ( puObject *ob ) +{ + trains[curr_train].setLambda ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + + +static void trainHeightSlider_cb ( puObject *ob ) +{ + trains[curr_train].setWaveHeight ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + +static void trainDisableButton_cb ( puObject *ob ) +{ + for ( int i = 0 ; i < SSGA_MAX_WAVETRAIN ; i++ ) + ocean -> setWaveTrain ( i, NULL ) ; + + trainEnableButton -> setValue ( 0 ) ; +} + +static void writeButton_cb ( puObject *ob ) +{ + if ( ob -> getIntegerValue () ) + writeCplusplusCode () ; +} + +static void trainEnableButton_cb ( puObject *ob ) +{ + if ( ob -> getIntegerValue () ) + ocean -> setWaveTrain ( curr_train, & trains [ curr_train ] ) ; + else + ocean -> setWaveTrain ( curr_train, NULL ) ; +} + +static void trainHeadingDial_cb ( puObject *ob ) +{ + trains[curr_train].setHeading ( ob -> getFloatValue () ) ; + ob -> setLegend ( ob -> getStringValue () ) ; +} + +static void trainSelectBox_cb ( puObject *ob ) +{ + curr_train = ((puaSelectBox *) ob) -> getCurrentItem () ; + + if ( curr_train < 0 ) + curr_train = 0 ; + + if ( curr_train >= SSGA_MAX_WAVETRAIN ) + curr_train = SSGA_MAX_WAVETRAIN - 1 ; + + trainEnableButton -> setValue ( + ocean -> getWaveTrain ( curr_train ) != NULL ) ; + + trainLengthSlider -> setValue ( trains[curr_train].getLength () ) ; + trainSpeedSlider -> setValue ( trains[curr_train].getSpeed () ) ; + trainLambdaSlider -> setValue ( trains[curr_train].getLambda () ) ; + trainHeightSlider -> setValue ( trains[curr_train].getWaveHeight () ) ; + trainHeadingDial -> setValue ( trains[curr_train].getHeading () ) ; +} + + +static float halfMeterEverywhere ( float x, float y ) +{ + return 0.5f ; +} + + +static float oneMeterEverywhere ( float x, float y ) +{ + return 1.0f ; +} + + +static float twoMeterEverywhere ( float x, float y ) +{ + return 1.5f ; +} + + +static float gentleSlope ( float x, float y ) +{ + return (1.0f + x / (ocean -> getSize ()[0] / 2.0f)) * 2.0f ; +} + + +static float steepSlope ( float x, float y ) +{ + return (1.0f + x / (ocean -> getSize ()[0] / 2.0f)) * 10.0f ; +} + + +static float stepFunction ( float x, float y ) +{ + return (x < 0.0f ) ? 0.5f : 20000.0f ; +} + + +static float twoBeaches ( float x, float y ) +{ + return (float) fabs ( sin( x / ocean->getSize()[0]) * + sin(2.0f * y / ocean->getSize()[1]) * 1.5 + 0.5 ) ; +} + +static const ssgaWSDepthCallback depthFuncs [] = +{ + NULL, /* Infinite depth */ + halfMeterEverywhere, + oneMeterEverywhere, + twoMeterEverywhere, + gentleSlope, + steepSlope, + stepFunction, + twoBeaches, + NULL, +} ; + +static char *depthNames [] = +{ + "Infinite Depth", + "Half Meter Deep", + "One Meter Deep", + "Two Meters Deep", + "Gentle Slope", + "Steep Slope", + "Step Function", + "Two Curved Beaches", + NULL, +} ; + +static void depthSelectBox_cb ( puObject *ob ) +{ + curr_depthfunc = ((puaSelectBox *) ob) -> getCurrentItem () ; + + if ( curr_depthfunc < 0 ) + curr_depthfunc = 0 ; + + if ( curr_depthfunc >= (int)(sizeof(depthFuncs)/sizeof(ssgaWSDepthCallback))) + curr_depthfunc = (int)(sizeof(depthFuncs)/sizeof(ssgaWSDepthCallback)) - 1 ; + + ocean -> setDepthCallback ( depthFuncs [ curr_depthfunc ] ) ; +} + + + +static void update_motion () +{ + static ulClock ck ; + static char s [ 128 ] ; + + ck . update () ; + + double t = ck . getAbsTime () ; + float dt = (float) ck . getDeltaTime () ; + + ocean -> setWindDirn ( (float)( 25.0 * sin ( t / 100.0 ) ) ) ; + ocean -> updateAnimation ( (float) t ) ; + + fountain -> update ( dt ) ; + fire_obj -> update ( dt ) ; + + dt = (float) ck . getDeltaTime () ; + + sprintf ( s, "CalcTime=%1.1fms", dt * 1000.0 ) ; + timeText->setLabel ( s ) ; + sgCoord tptpos ; + + context -> setCamera ( & campos ) ; + + sgSetCoord ( & tptpos, 0.0f, 0.0f, 0.6f, (float)(t * 60), 0.0f, 0.0f ) ; + teapot -> setTransform ( & tptpos ) ; + sgSetCoord ( & tptpos, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f ) ; + fire -> setTransform ( & tptpos ) ; +} + + + +/* + The PW window reshape event +*/ + +static void reshape ( int w, int h ) +{ + glViewport ( 0, 0, w, h ) ; +} + + + +/* + The PW keyboard/mouse events +*/ + +static void keyboard ( int key, int updown, int, int ) +{ + if ( ! puKeyboard ( key, updown ) && ( updown == PU_DOWN ) ) + { + switch ( key ) + { + case 0x03 : + case 0x1B : + case 'q' : + exit ( 0 ) ; + + case ' ' : + default : + displayGUI = ! displayGUI ; + break ; + } + } +} + +static void motionfn ( int x, int y ) +{ + if ( displayGUI ) + puMouse ( x, y ) ; +} + +static void mousefn ( int button, int updown, int x, int y ) +{ + if ( displayGUI ) + puMouse ( button, updown, x, y ) ; +} + + + +/* + The redraw function +*/ + +static void main_loop () +{ + while ( !quit ) + { + update_motion () ; + + glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ; + + if ( wireframe ) + glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE ) ; + else + glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ) ; + + ssgCullAndDraw ( scene ) ; + + glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ) ; + + if ( displayGUI ) + puDisplay () ; + + pwSwapBuffers () ; + } + + pwCleanup () ; +} + + + +static void init_graphics () +{ + /* + Initialise PW + */ + pwInit ( 50, 50, 640, 480, FALSE, "Simple Scene Graph : Water Example Program.", TRUE, 0) ; + pwSetCallbacks ( keyboard, mousefn, motionfn, reshape ) ; + + puInit () ; + ssgInit () ; + + /* + Some basic OpenGL setup + */ + + glClearColor ( 0.2f, 0.7f, 1.0f, 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; + + /* + Set up the viewing parameters + */ + context = new ssgContext () ; + context -> setFOV ( 60.0f, 0.0f ) ; + context -> setNearFar ( 1.0f, 700.0f ) ; + context -> makeCurrent () ; + + /* + Set up the Sun. + */ + + ssgGetLight ( 0 ) -> setPosition ( sunpos ) ; +} + + +/* + Particle system definitions for the fountain. +*/ + +#define SPS ssgaParticleSystem /* Too much typing! */ + +static void droplet_create ( SPS *, int, ssgaParticle *p ) +{ + float c = ((float)(rand()%100)/100.0f) * (256.0f-163.0f)/255.0f ; + + sgSetVec4 ( p -> col, 96.0f/255.0f+c, 147.0f/255.0f+c, 163.0f/255.0f+c, 0.5); + sgSetVec3 ( p -> pos, -2.4f, -0.1f, 1.9f ) ; + sgSetVec3 ( p -> vel, + -(float)(rand()%1000)/200.0f, + (float)(rand()%1000 - 500)/400.0f, + (float)(rand()%1000)/1000.0f + 3.0f ) ; + sgAddScaledVec3 ( p -> pos, p -> vel, (float)(rand()%1000)/20000.0f ) ; + sgSetVec3 ( p -> acc, 0, 0, -9.8f ) ; + p -> time_to_live = 1 ; +} + + +static void init_states () +{ + plinth_state = new ssgSimpleState () ; + plinth_state -> setTexture ( "data/pavement.rgb" ) ; + plinth_state -> enable ( GL_TEXTURE_2D ) ; + plinth_state -> setShadeModel ( GL_SMOOTH ) ; + plinth_state -> enable ( GL_CULL_FACE ) ; + plinth_state -> enable ( GL_BLEND ) ; + plinth_state -> enable ( GL_LIGHTING ) ; + plinth_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + plinth_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + plinth_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + plinth_state -> setShininess ( 2 ) ; + + teapot_state = new ssgSimpleState () ; + teapot_state -> setTexture ( "data/pattern.rgb" ) ; + teapot_state -> enable ( GL_TEXTURE_2D ) ; + teapot_state -> setShadeModel ( GL_SMOOTH ) ; + teapot_state -> enable ( GL_CULL_FACE ) ; + teapot_state -> enable ( GL_BLEND ) ; + teapot_state -> enable ( GL_LIGHTING ) ; + teapot_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + teapot_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + teapot_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + teapot_state -> setShininess ( 2 ) ; + + sea_state = new ssgSimpleState () ; + sea_state -> setTexture ( "data/ocean.rgb" ) ; + sea_state -> setTranslucent () ; + sea_state -> enable ( GL_TEXTURE_2D ) ; + sea_state -> setShadeModel ( GL_SMOOTH ) ; + sea_state -> enable ( GL_CULL_FACE ) ; + sea_state -> enable ( GL_BLEND ) ; + sea_state -> enable ( GL_LIGHTING ) ; + sea_state -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + sea_state -> setMaterial ( GL_EMISSION, 0, 0, 0, 1 ) ; + sea_state -> setMaterial ( GL_SPECULAR, 1, 1, 1, 1 ) ; + sea_state -> setShininess ( 5 ) ; + + splash_state = new ssgSimpleState () ; + splash_state -> setTexture ( "data/droplet.rgb" ) ; + splash_state -> setTranslucent () ; + splash_state -> enable ( GL_TEXTURE_2D ) ; + splash_state -> setShadeModel ( GL_SMOOTH ) ; + splash_state -> enable ( GL_CULL_FACE ) ; + splash_state -> enable ( GL_BLEND ) ; + splash_state -> enable ( GL_LIGHTING ) ; + splash_state -> setColourMaterial ( GL_EMISSION ) ; + splash_state -> setMaterial ( GL_AMBIENT, 0, 0, 0, 1 ) ; + splash_state -> setMaterial ( GL_DIFFUSE, 0, 0, 0, 1 ) ; + splash_state -> setMaterial ( GL_SPECULAR, 0, 0, 0, 1 ) ; + splash_state -> setShininess ( 0 ) ; +} + + + + +static void load_database () +{ + /* Set up the path to the data files */ + + ssgModelPath ( "data" ) ; + ssgTexturePath ( "data" ) ; + + /* Load the states */ + + init_states () ; + + sgVec4 TRANSLUCENT_WHITE = { 1.0f, 1.0f, 1.0f, 0.8f } ; + sgVec3 pos = { 0, 0, 0 } ; + sgCoord pedpos = { { 0, 0, -1.5 }, { 0, 0, 0 } } ; + + /* Create a the scene content. */ + + fountain = new ssgaParticleSystem ( 1000, 100, 500, TRUE, + 0.2f, 1000, + droplet_create ) ; + fountain -> setState ( splash_state ) ; + + /* Set up some interesting defaults. */ + + trains[0] . setSpeed ( 3.1f ) ; + trains[0] . setLength ( 0.8f ) ; + trains[0] . setLambda ( 0.6f ) ; + trains[0] . setHeading ( 47.0f ) ; + trains[0] . setWaveHeight ( 0.2f ) ; + + trains[1] . setSpeed ( 4.6f ) ; + trains[1] . setLength ( 0.8f ) ; + trains[1] . setLambda ( 1.0f ) ; + trains[1] . setHeading ( 36.0f ) ; + trains[1] . setWaveHeight ( 0.1f ) ; + + trains[2] . setSpeed ( 8.5f ) ; + trains[2] . setLength ( 0.6f ) ; + trains[2] . setLambda ( 1.0f ) ; + trains[2] . setHeading ( 65.0f ) ; + trains[2] . setWaveHeight ( 0.1f ) ; + + ocean = new ssgaWaveSystem ( 10000 ) ; + ocean -> setColour ( TRANSLUCENT_WHITE ) ; + ocean -> setSize ( 100 ) ; + ocean -> setTexScale ( 3, 3 ) ; + ocean -> setCenter ( pos ) ; + ocean -> setDepthCallback ( depthFuncs [ curr_depthfunc ] ) ; + ocean -> setKidState ( sea_state ) ; + ocean -> setWindSpeed ( 10.0f ) ; + ocean -> setWaveTrain ( 0, & trains[0] ) ; + ocean -> setWaveTrain ( 1, & trains[1] ) ; + ocean -> setWaveTrain ( 2, & trains[2] ) ; + + ped_obj = new ssgaCube () ; + ped_obj -> setSize ( 4 ) ; + ped_obj -> setKidState ( plinth_state ) ; + ped_obj -> regenerate () ; + + tpt_obj = new ssgaTeapot ( 1000 ) ; + tpt_obj -> setSize ( 2 ) ; + tpt_obj -> setKidState ( teapot_state ) ; + tpt_obj -> regenerate () ; + + fire_obj = new ssgaFire ( 200 ) ; + + /* Build the scene graph */ + + teapot = new ssgTransform ; + teapot -> addKid ( tpt_obj ) ; + teapot -> addKid ( fountain ) ; + + pedestal = new ssgTransform ; + pedestal -> setTransform ( & pedpos ) ; + pedestal -> addKid ( ped_obj ) ; + + sun_obj = new ssgaLensFlare () ; + + sun = new ssgTransform ; + sun -> setTransform ( sunpos ) ; + sun -> addKid ( sun_obj ) ; + + fire = new ssgTransform ; + fire -> addKid ( fire_obj ) ; + + scene = new ssgRoot ; + scene -> addKid ( ocean ) ; + scene -> addKid ( fire ) ; + scene -> addKid ( pedestal ) ; + scene -> addKid ( teapot ) ; + scene -> addKid ( sun ) ; +} + + +static void init_gui () +{ + static puFont *sorority ; + static fntTexFont *fnt ; + + fnt = new fntTexFont () ; + fnt -> load ( "data/sorority.txf" ) ; + sorority = new puFont ( fnt, 12 ) ; + + puSetDefaultFonts ( *sorority, *sorority ) ; + puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ) ; + puSetDefaultColourScheme ( 0.2f, 0.5f, 0.2f, 0.5f ) ; + + puGroup *window_group = new puGroup ( 0, 0 ) ; + + trainLengthSlider = new puSlider ( 200, GUI_BASE+28, 90, false, 20 ) ; + trainLengthSlider->setMaxValue ( 20.0f ) ; + trainLengthSlider->setMinValue ( 0.1f ) ; + trainLengthSlider->setStepSize ( 0.1f ) ; + trainLengthSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + trainLengthSlider->setCallback ( trainLengthSlider_cb ) ; + trainLengthSlider->setLabel ( "Wave Length" ) ; + trainLengthSlider->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + trainLengthSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + trainSpeedSlider = new puSlider ( 200, GUI_BASE+56, 90, false, 20 ) ; + trainSpeedSlider->setMaxValue ( 20.0f ) ; + trainSpeedSlider->setMinValue ( 0.0f ) ; + trainSpeedSlider->setStepSize ( 0.1f ) ; + trainSpeedSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + trainSpeedSlider->setCallback ( trainSpeedSlider_cb ) ; + trainSpeedSlider->setLabel ( "Wave Speed" ) ; + trainSpeedSlider->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + trainSpeedSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + trainLambdaSlider = new puSlider ( 300, GUI_BASE+28, 90, false, 20 ) ; + trainLambdaSlider->setMaxValue ( 2.0f ) ; + trainLambdaSlider->setMinValue ( 0.0f ) ; + trainLambdaSlider->setStepSize ( 0.1f ) ; + trainLambdaSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + trainLambdaSlider->setCallback ( trainLambdaSlider_cb ) ; + trainLambdaSlider->setLabel ( "Wave Curl" ) ; + trainLambdaSlider->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + trainLambdaSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + trainHeightSlider = new puSlider ( 300, GUI_BASE+56, 90, false, 20 ) ; + trainHeightSlider->setMaxValue ( 5.0f ) ; + trainHeightSlider->setMinValue ( 0.0f ) ; + trainHeightSlider->setStepSize ( 0.1f ) ; + trainHeightSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + trainHeightSlider->setCallback ( trainHeightSlider_cb ) ; + trainHeightSlider->setLabel ( "Wave Height (meters)" ) ; + trainHeightSlider->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + trainHeightSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + trainEnableButton = new puButton ( 200, GUI_BASE+84, " " ) ; + trainEnableButton->setStyle ( PUSTYLE_RADIO ) ; + trainEnableButton->setCallback ( trainEnableButton_cb ) ; + trainEnableButton->setLabel ( "Enable this Wave Train" ) ; + trainEnableButton->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + trainEnableButton->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + trainDisableButton = new puOneShot( 450, GUI_BASE+120, + "Disable All WaveTrains" ) ; + trainDisableButton->setCallback ( trainDisableButton_cb ) ; + + trainHeadingDial = new puDial ( 300, GUI_BASE+82, 50 ) ; + trainHeadingDial->setWrap ( 1 ) ; + trainHeadingDial->setMaxValue ( 360 ) ; + trainHeadingDial->setMinValue ( 0 ) ; + trainHeadingDial->setStepSize ( 1 ) ; + trainHeadingDial->setCBMode ( PUSLIDER_ALWAYS ) ; + trainHeadingDial->setCallback ( trainHeadingDial_cb ) ; + trainHeadingDial->setLabel ( "Wave Direction" ) ; + trainHeadingDial->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + trainHeadingDial->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + trainHeadingDial->setLegendPlace ( PUPLACE_BOTTOM_CENTERED ) ; + + trainSelectBox = new puaSelectBox ( 200, GUI_BASE+109, 300, GUI_BASE+139, + trainNameList ) ; + trainSelectBox->setCallback ( trainSelectBox_cb ) ; + trainSelectBox->setCurrentItem ( 0 ) ; + trainSelectBox->setLabel ( "Edit Wave Train Number" ) ; + trainSelectBox->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + trainSelectBox->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + /* Set everything up on the first time around */ + trainSelectBox_cb ( trainSelectBox ) ; + + depthSelectBox = new puaSelectBox ( 200, GUI_BASE, 400, GUI_BASE+20, + depthNames ) ; + depthSelectBox->setCallback ( depthSelectBox_cb ) ; + depthSelectBox->setCurrentItem ( 0 ) ; + depthSelectBox->setLabel ( "Water Depth" ) ; + depthSelectBox->setLabelPlace ( PUPLACE_CENTERED_LEFT ) ; + depthSelectBox->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + /* Set everything up on the first time around */ + depthSelectBox_cb ( depthSelectBox ) ; + + viewHeadingDial = new puDial ( 50, VIEW_GUI_BASE, 50 ) ; + viewHeadingDial->setValue ( 0.0f ) ; + viewHeadingDial->setWrap ( 1 ) ; + viewHeadingDial->setMaxValue ( 360 ) ; + viewHeadingDial->setMinValue ( 0 ) ; + viewHeadingDial->setStepSize ( 0 ) ; + viewHeadingDial->setCBMode ( PUSLIDER_ALWAYS ) ; + viewHeadingDial->setCallback ( viewHeadingDial_cb ) ; + viewHeadingDial->setLabel ( "Pan" ) ; + viewHeadingDial->setLabelPlace ( PUPLACE_BOTTOM_CENTERED ) ; + viewHeadingDial->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + viewPitchDial = new puDial ( 100, VIEW_GUI_BASE, 50 ) ; + viewPitchDial ->setValue ( -45.0f ) ; + viewPitchDial ->setWrap ( 1 ) ; + viewPitchDial ->setMaxValue ( 360 ) ; + viewPitchDial ->setMinValue ( 0 ) ; + viewPitchDial ->setStepSize ( 0 ) ; + viewPitchDial ->setCBMode ( PUSLIDER_ALWAYS ) ; + viewPitchDial ->setCallback ( viewPitchDial_cb ) ; + viewPitchDial ->setLabel ( "Tilt" ) ; + viewPitchDial ->setLabelPlace ( PUPLACE_BOTTOM_CENTERED ) ; + viewPitchDial ->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + viewRangeSlider = new puSlider ( 150, VIEW_GUI_BASE, 90, false, 20 ) ; + viewRangeSlider->setValue ( 25.0f ) ; + viewRangeSlider->setMaxValue ( 150.0f ) ; + viewRangeSlider->setMinValue ( 0.0f ) ; + viewRangeSlider->setStepSize ( 0 ) ; + viewRangeSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + viewRangeSlider->setCallback ( viewRangeSlider_cb ) ; + viewRangeSlider->setLabel ( "Range" ) ; + viewRangeSlider->setLabelPlace ( PUPLACE_BOTTOM_CENTERED ) ; + viewRangeSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + viewWireframeButton= new puButton ( 400, VIEW_GUI_BASE, "Wireframe" ) ; + viewWireframeButton->setCallback ( viewWireframeButton_cb ) ; + viewWireframeButton->setValue ( FALSE ) ; + + viewEnvMapButton= new puButton ( 300, VIEW_GUI_BASE, "Env.Map" ) ; + viewEnvMapButton->setCallback ( viewEnvMapButton_cb ) ; + viewEnvMapButton->setValue ( FALSE ) ; + + writeButton = new puOneShot ( 400, VIEW_GUI_BASE + 30, "Write C++" ) ; + writeButton->setCallback ( writeButton_cb ) ; + writeButton->setValue ( FALSE ) ; + + waveTextureSlider = new puSlider ( 500, VIEW_GUI_BASE , 90, false, 20 ) ; + waveTextureSlider->setValue ( 1.0f ) ; + waveTextureSlider->setMaxValue ( 50.0f ) ; + waveTextureSlider->setMinValue ( 0.01f ) ; + waveTextureSlider->setStepSize ( 0 ) ; + waveTextureSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + waveTextureSlider->setCallback ( waveTextureSlider_cb ) ; + waveTextureSlider->setLabel ( "Texture" ) ; + waveTextureSlider->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + waveTextureSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + waveSizeSlider = new puSlider ( 500, VIEW_GUI_BASE+30, 90, false, 20 ) ; + waveSizeSlider->setValue ( 25.0f ) ; + waveSizeSlider->setMaxValue ( 500.0f ) ; + waveSizeSlider->setMinValue ( 10.0f ) ; + waveSizeSlider->setStepSize ( 0 ) ; + waveSizeSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + waveSizeSlider->setCallback ( waveSizeSlider_cb ) ; + waveSizeSlider->setLabel ( "Size" ) ; + waveSizeSlider->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + waveSizeSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + wavePolycountSlider= new puSlider ( 500, VIEW_GUI_BASE+60, 90, false, 20 ) ; + wavePolycountSlider->setValue ( 10000 ) ; + wavePolycountSlider->setMaxValue ( 50000 ) ; + wavePolycountSlider->setMinValue ( 4000 ) ; + wavePolycountSlider->setStepSize ( 0 ) ; + wavePolycountSlider->setCBMode ( PUSLIDER_ALWAYS ) ; + wavePolycountSlider->setCallback ( wavePolycountSlider_cb ) ; + wavePolycountSlider->setLabel ( "Polygons" ) ; + wavePolycountSlider->setLabelPlace ( PUPLACE_CENTERED_RIGHT ) ; + wavePolycountSlider->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + timeText = new puText ( 500, VIEW_GUI_BASE+80 ) ; + timeText->setColour( PUCOL_LABEL, FONT_COLOUR ) ; + + window_group->close () ; +} + +/* + The works. +*/ + +int main ( int, char ** ) +{ + init_graphics () ; + load_database () ; + init_gui () ; + main_loop () ; + return 0 ; +} + diff --git a/examples/src/ssg/water/water.dsp b/examples/src/ssg/water/water.dsp new file mode 100644 index 0000000..068188d --- /dev/null +++ b/examples/src/ssg/water/water.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="water" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=water - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "water.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "water.mak" CFG="water - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "water - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "water - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "water - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pw.lib pui.lib puAux.lib fnt.lib ssgAux.lib ssg.lib sg.lib ul.lib winmm.lib opengl32.lib glu32.lib /nologo /subsystem:console /machine:I386 /out:"water.exe" /libpath:"..\..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "water - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pw_d.lib pui_d.lib puAux_d.lib fnt_d.lib ssgAux_d.lib ssg_d.lib sg_d.lib ul_d.lib winmm.lib opengl32.lib glu32.lib /nologo /subsystem:console /debug /machine:I386 /out:"water.exe" /pdbtype:sept /libpath:"..\..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "water - Win32 Release" +# Name "water - Win32 Debug" +# Begin Source File + +SOURCE=.\water.cxx +# End Source File +# End Target +# End Project diff --git a/examples/src/util/Makefile.am b/examples/src/util/Makefile.am new file mode 100644 index 0000000..45ae8b8 --- /dev/null +++ b/examples/src/util/Makefile.am @@ -0,0 +1,12 @@ +if BUILD_UL + +noinst_PROGRAMS = test_dir + +test_dir_SOURCES = test_dir.cxx + +test_dir_LDADD = -lplibul + +endif + +EXTRA_DIST = test_dir.dsp + diff --git a/examples/src/util/test_dir.cxx b/examples/src/util/test_dir.cxx new file mode 100644 index 0000000..a863737 --- /dev/null +++ b/examples/src/util/test_dir.cxx @@ -0,0 +1,65 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: test_dir.cxx 1551 2002-09-01 12:04:53Z ude $ +*/ + + +#include + +void list_files( const char* dirname ) +{ + static int level=-1; + level++; + + ulDir* dirp = ulOpenDir(dirname); + if ( dirp != NULL ) + { + ulDirEnt* dp; + while ( (dp = ulReadDir(dirp)) != NULL ) + { + for ( int i=0; id_name ); + if ( dp->d_isdir ) + printf("/"); + printf("\n"); + + if ( dp->d_isdir && + strcmp (dp->d_name, ".") && + strcmp (dp->d_name, "..")) + { + char path[ UL_NAME_MAX+1 ]; + strcpy (path, dirname); + strcat (path, "/"); + strcat (path, dp->d_name); + + list_files( path ); + } + } + ulCloseDir(dirp); + } + level--; +} + +int main(int argc, char* argv[]) +{ + list_files("."); + return 0; +} diff --git a/examples/src/util/test_dir.dsp b/examples/src/util/test_dir.dsp new file mode 100644 index 0000000..fad9195 --- /dev/null +++ b/examples/src/util/test_dir.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="test_dir" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_dir - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_dir.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_dir.mak" CFG="test_dir - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_dir - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_dir - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_dir - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul.lib /nologo /subsystem:console /machine:I386 /out:"test_dir.exe" /libpath:"..\..\..\..\plib" + +!ELSEIF "$(CFG)" == "test_dir - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ul_d.lib /nologo /subsystem:console /debug /machine:I386 /out:"test_dir.exe" /pdbtype:sept /libpath:"..\..\..\..\plib" + +!ENDIF + +# Begin Target + +# Name "test_dir - Win32 Release" +# Name "test_dir - Win32 Debug" +# Begin Source File + +SOURCE=.\test_dir.cxx +# End Source File +# End Target +# End Project diff --git a/plib.dsp b/plib.dsp new file mode 100644 index 0000000..1261733 --- /dev/null +++ b/plib.dsp @@ -0,0 +1,61 @@ +# Microsoft Developer Studio Project File - Name="plib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=plib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "plib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "plib.mak" CFG="plib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "plib - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "plib - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +MTL=midl.exe + +!IF "$(CFG)" == "plib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "plib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "plib___Win32_Debug" +# PROP BASE Intermediate_Dir "plib___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "plib - Win32 Release" +# Name "plib - Win32 Debug" +# End Target +# End Project diff --git a/plib.dsw b/plib.dsw new file mode 100644 index 0000000..797e524 --- /dev/null +++ b/plib.dsw @@ -0,0 +1,209 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "fnt"=".\src\fnt\fnt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "js"=".\src\js\js.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=".\src\net\net.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "plib"=".\plib.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name fnt + End Project Dependency + Begin Project Dependency + Project_Dep_Name js + End Project Dependency + Begin Project Dependency + Project_Dep_Name sg + End Project Dependency + Begin Project Dependency + Project_Dep_Name sl + End Project Dependency + Begin Project Dependency + Project_Dep_Name ssg + End Project Dependency + Begin Project Dependency + Project_Dep_Name psl + End Project Dependency + Begin Project Dependency + Project_Dep_Name pui + End Project Dependency + Begin Project Dependency + Project_Dep_Name ul + End Project Dependency + Begin Project Dependency + Project_Dep_Name ssgAux + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name pw + End Project Dependency + Begin Project Dependency + Project_Dep_Name puAux + End Project Dependency +}}} + +############################################################################### + +Project: "psl"=".\src\psl\psl.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "puAux"=".\src\puAux\puAux.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pui"=".\src\pui\pui.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pw"=".\src\pw\pw.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "sg"=".\src\sg\sg.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "sl"=".\src\sl\sl.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "ssg"=".\src\ssg\ssg.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "ssgAux"=".\src\ssgAux\ssgAux.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "ul"=".\src\util\ul.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ad49fb4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = util js sl pui puAux sg ssg fnt ssgAux net psl pw diff --git a/src/fnt/Makefile.am b/src/fnt/Makefile.am new file mode 100644 index 0000000..d0ef3ca --- /dev/null +++ b/src/fnt/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_FNT + +lib_LIBRARIES = libplibfnt.a + +include_HEADERS = fnt.h + +libplibfnt_a_SOURCES = fnt.cxx fntTXF.cxx fntLocal.h fntBitmap.cxx + +INCLUDES = -I$(top_srcdir)/src/sg -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = fnt.dsp + diff --git a/src/fnt/fnt.cxx b/src/fnt/fnt.cxx new file mode 100644 index 0000000..3faaccd --- /dev/null +++ b/src/fnt/fnt.cxx @@ -0,0 +1,252 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.cxx 1939 2004-08-05 00:41:53Z puggles $ +*/ + +#include "fntLocal.h" + +fntFont:: fntFont () {} +fntFont::~fntFont () {} + +int fntTexFont::load ( const char *fname, GLenum mag, GLenum min ) +{ + const char *p ; + + for ( p = & fname [ strlen ( fname ) -1 ] ; + p != fname && *p != '.' && *p != '/' ; p-- ) + /* Do nothing */ ; + + if ( strcmp ( p, ".txf" ) == 0 ) { + return loadTXF ( fname, mag, min ) ; + } + else + { + ulSetError ( UL_WARNING, + "fnt::load: Error - Unrecognised file format for '%s'", fname ) ; + return FNT_FALSE ; + } +} + + + +float fntTexFont::low_putch ( sgVec3 curpos, float pointsize, + float italic, char c ) +{ + unsigned int cc = (unsigned char) c ; + + /* Auto case-convert if character is absent from font. */ + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + curpos [ 0 ] += pointsize / 2.0f ; + return pointsize / 2.0f ; + } + } + + /* + We might want to consider making some absent characters from + others (if they exist): lowercase 'l' could be made into digit '1' + or letter 'O' into digit '0'...or vice versa. We could also + make 'b', 'd', 'p' and 'q' by mirror-imaging - this would + save a little more texture memory in some fonts. + */ + + if ( ! exists [ cc ] ) + return 0.0f ; + + glBegin ( GL_TRIANGLE_STRIP ) ; + glTexCoord2f ( t_left [cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_left [cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_left [cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_left [cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_right[cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_right[cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + glEnd () ; + + float ww = ( gap + ( fixed_pitch ? width : widths[cc] ) ) * pointsize ; + curpos[0] += ww ; + return ww ; +} + + + +void fntTexFont::setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + exists[cc] = FNT_TRUE ; + + widths[cc] = wid; + + t_left[cc] = tex_left ; t_right[cc] = tex_right ; + t_bot [cc] = tex_bot ; t_top [cc] = tex_top ; + + v_left[cc] = vtx_left ; v_right[cc] = vtx_right ; + v_bot [cc] = vtx_bot ; v_top [cc] = vtx_top ; +} + + +int fntTexFont::getGlyph ( char c, float* wid, + float *tex_left, float *tex_right, + float *tex_bot , float *tex_top , + float *vtx_left, float *vtx_right, + float *vtx_bot , float *vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + if ( ! exists[cc] ) return FNT_FALSE ; + + if ( wid != NULL ) *wid = widths [cc] ; + + if ( tex_left != NULL ) *tex_left = t_left [cc] ; + if ( tex_right != NULL ) *tex_right = t_right[cc] ; + if ( tex_bot != NULL ) *tex_bot = t_bot [cc] ; + if ( tex_top != NULL ) *tex_top = t_top [cc] ; + + if ( vtx_left != NULL ) *vtx_left = v_left [cc] ; + if ( vtx_right != NULL ) *vtx_right = v_right[cc] ; + if ( vtx_bot != NULL ) *vtx_bot = v_bot [cc] ; + if ( vtx_top != NULL ) *vtx_top = v_top [cc] ; + + return FNT_TRUE ; +} + + +void fntTexFont::getBBox ( const char *s, + float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float h_pos = 0.0f ; + float v_pos = 0.0f ; + float l, r, b, t ; + + l = r = b = t = 0.0f ; + + while ( *s != '\0' ) + { + if ( *s == '\n' ) + { + h_pos = 0.0f ; + v_pos -= 1.333f ; + s++ ; + continue ; + } + + unsigned int cc = (unsigned char) *(s++) ; + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + r += 0.5f ; + h_pos += 0.5f ; + + continue ; + } + } + + if ( ! exists [ cc ] ) + continue ; + + if ( italic >= 0 ) + { + if ( l > h_pos + v_left [cc] ) l = h_pos + v_left [cc] ; + if ( r < gap + h_pos + v_right[cc]+italic ) r = gap + h_pos + v_right[cc] + italic ; + } + else + { + if ( l > h_pos + v_left [cc]+italic ) l = h_pos + v_left [cc] + italic ; + if ( r < gap + h_pos + v_right[cc] ) r = gap + h_pos + v_right[cc] ; + } + + + if ( b > v_pos + v_bot [cc] ) b = v_pos + v_bot [cc] ; + if ( t < v_pos + v_top [cc] ) t = v_pos + v_top [cc] ; + + h_pos += gap + ( fixed_pitch ? width : widths[cc] ) ; + } + + if ( left != NULL ) *left = l * pointsize ; + if ( right != NULL ) *right = r * pointsize ; + if ( top != NULL ) *top = t * pointsize ; + if ( bot != NULL ) *bot = b * pointsize ; +} + + +void fntTexFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + SGfloat origx = curpos[0] ; + + if ( ! bound ) + bind_texture () ; + + while ( *s != '\0' ) + { + if (*s == '\n') + { + curpos[0] = origx ; + curpos[1] -= pointsize ; + } + else + low_putch ( curpos, pointsize, italic, *s ) ; + + s++ ; + } +} + +void fntInit () +{ + /* Empty right now */ +} + + diff --git a/src/fnt/fnt.dsp b/src/fnt/fnt.dsp new file mode 100644 index 0000000..470d346 --- /dev/null +++ b/src/fnt/fnt.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="fnt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=fnt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "fnt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "fnt.mak" CFG="fnt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "fnt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "fnt - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "fnt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\sg" /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy fnt.h ..\..\fnt.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "fnt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\sg" /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\fnt_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy fnt.h ..\..\fnt.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "fnt - Win32 Release" +# Name "fnt - Win32 Debug" +# Begin Source File + +SOURCE=.\fnt.cxx +# End Source File +# Begin Source File + +SOURCE=.\fnt.h +# End Source File +# Begin Source File + +SOURCE=.\fntLocal.h +# End Source File +# Begin Source File + +SOURCE=.\fntTXF.cxx +# End Source File +# Begin Source File + +SOURCE=.\fntBitmap.cxx +# End Source File +# End Target +# End Project diff --git a/src/fnt/fnt.h b/src/fnt/fnt.h new file mode 100644 index 0000000..22da92b --- /dev/null +++ b/src/fnt/fnt.h @@ -0,0 +1,361 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.h 1923 2004-04-06 12:53:17Z sjbaker $ +*/ + + +#ifndef _FNT_H_ +#define _FNT_H_ 1 + +#include +#include "sg.h" + +#ifdef UL_MAC_OSX +# include +#else +# include +#endif + + +#define FNTMAX_CHAR 256 +#define FNT_TRUE 1 +#define FNT_FALSE 0 + + +class fntFont +{ +public: + fntFont () ; + + virtual ~fntFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) = 0 ; + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) = 0 ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) = 0 ; + virtual void begin () = 0 ; + virtual void end () = 0 ; + + virtual int load ( const char *fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) = 0 ; + + virtual void setFixedPitch ( int fix ) = 0 ; + virtual int isFixedPitch () const = 0 ; + + virtual void setWidth ( float w ) = 0 ; + virtual void setGap ( float g ) = 0 ; + + virtual float getWidth () const = 0 ; + virtual float getGap () const = 0 ; + + virtual int hasGlyph ( char c ) const = 0 ; +} ; + + +class fntTexFont : public fntFont +{ +private: + GLuint texture ; + int bound ; + int fixed_pitch ; + + float width ; /* The width of a character in fixed-width mode */ + float gap ; /* Set the gap between characters */ + + int exists [ FNTMAX_CHAR ] ; /* TRUE if character exists in tex-map */ + + /* + The quadrilaterals that describe the characters + in the font are drawn with the following texture + and spatial coordinates. The texture coordinates + are in (S,T) space, with (0,0) at the bottom left + of the image and (1,1) at the top-right. + + The spatial coordinates are relative to the current + 'cursor' position. They should be scaled such that + 1.0 represent the height of a capital letter. Hence, + characters like 'y' which have a descender will be + given a negative v_bot. Most capitals will have + v_bot==0.0 and v_top==1.0. + */ + + /* Nominal baseline widths */ + + float widths [ FNTMAX_CHAR ] ; + + /* Texture coordinates */ + + float t_top [ FNTMAX_CHAR ] ; /* Top edge of each character [0..1] */ + float t_bot [ FNTMAX_CHAR ] ; /* Bottom edge of each character [0..1] */ + float t_left [ FNTMAX_CHAR ] ; /* Left edge of each character [0..1] */ + float t_right [ FNTMAX_CHAR ] ; /* Right edge of each character [0..1] */ + + /* Vertex coordinates. */ + + float v_top [ FNTMAX_CHAR ] ; + float v_bot [ FNTMAX_CHAR ] ; + float v_left [ FNTMAX_CHAR ] ; + float v_right [ FNTMAX_CHAR ] ; + + void bind_texture () + { + glEnable ( GL_TEXTURE_2D ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* For ancient SGI machines */ + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + } + + float low_putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + + int loadTXF ( const char *fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ; +public: + + fntTexFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + } + + fntTexFont ( const char *fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) : fntFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + load ( fname, mag, min ) ; + } + + ~fntTexFont () + { + if ( texture != 0 ) + { +#ifdef GL_VERSION_1_1 + glDeleteTextures ( 1, &texture ) ; +#else + /* For ancient SGI machines */ + glDeleteTexturesEXT ( 1, &texture ) ; +#endif + } + } + + int load ( const char *fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ; + + void setFixedPitch ( int fix ) { fixed_pitch = fix ; } + int isFixedPitch () const { return fixed_pitch ; } + + void setWidth ( float w ) { width = w ; } + void setGap ( float g ) { gap = g ; } + + float getWidth () const { return width ; } + float getGap () const { return gap ; } + + + void setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) ; + void setGlyph ( char c, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) /* deprecated */ + { + setGlyph ( c, vtx_right, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int getGlyph ( char c, float* wid, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) ; + int getGlyph ( char c, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) /* deprecated */ + { + return getGlyph ( c, NULL, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int hasGlyph ( char c ) const { return exists[ (GLubyte) c ] ; } + + void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + void begin () + { + bind_texture () ; + bound = FNT_TRUE ; + } + + void end () + { + bound = FNT_FALSE ; + } + + void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + void putch ( sgVec3 curpos, float pointsize, float italic, char c ) + { + if ( ! bound ) + bind_texture () ; + + low_putch ( curpos, pointsize, italic, c ) ; + } + +} ; + + + +class fntBitmapFont : public fntFont +{ + protected: + + const GLubyte **data; + int first; + int count; + int height; + float xorig, yorig; + int fix; + float wid, gap; + + public: + + // data is a null-terminated list of glyph images: + // data[i][0] - image width + // data[i][1..n] - packed bitmap + + fntBitmapFont ( const GLubyte **data, int first, int height, + float xorig, float yorig ) ; + + virtual ~fntBitmapFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + virtual void begin () ; + virtual void end () ; + + virtual int load ( const char *fname, GLenum mag, GLenum min ) { return -1; } + + virtual void setFixedPitch ( int f ) { fix = f; } + virtual int isFixedPitch () const { return fix; } + + virtual void setWidth ( float w ) { wid = w; } + virtual void setGap ( float g ) { gap = g; } + + virtual float getWidth () const { return wid; } + virtual float getGap () const { return gap; } + + virtual int hasGlyph ( char c ) const ; +}; + + +/* Builtin Bitmap Fonts */ + +#define FNT_BITMAP_8_BY_13 0 +#define FNT_BITMAP_9_BY_15 1 +#define FNT_BITMAP_HELVETICA_10 2 +#define FNT_BITMAP_HELVETICA_12 3 +#define FNT_BITMAP_HELVETICA_18 4 +#define FNT_BITMAP_TIMES_ROMAN_10 5 +#define FNT_BITMAP_TIMES_ROMAN_24 6 + +fntBitmapFont *fntGetBitmapFont(int id); + + + +class fntRenderer +{ + fntFont *font ; + + sgVec3 curpos ; + + float pointsize ; + float italic ; + +public: + fntRenderer () + { + start2f ( 0.0f, 0.0f ) ; + font = NULL ; + pointsize = 10 ; + italic = 0 ; + } + + void start3fv ( sgVec3 pos ) { sgCopyVec3 ( curpos, pos ) ; } + void start2fv ( sgVec2 pos ) { sgCopyVec2 ( curpos, pos ) ; curpos[2]=0.0f ; } + void start2f ( float x, float y ) { sgSetVec3 ( curpos, x, y, 0.0f ) ; } + void start3f ( float x, float y, float z ) { sgSetVec3 ( curpos, x, y, z ) ; } + + void getCursor ( float *x, float *y, float *z ) const + { + if ( x != NULL ) *x = curpos [ 0 ] ; + if ( y != NULL ) *y = curpos [ 1 ] ; + if ( z != NULL ) *z = curpos [ 2 ] ; + } + + void setFont ( fntFont *f ) { font = f ; } + fntFont *getFont () const { return font ; } + + void setSlant ( float i ) { italic = i ; } + void setPointSize ( float p ) { pointsize = p ; } + + float getSlant () const { return italic ; } + float getPointSize () const { return pointsize ; } + + void begin () { font->begin () ; } + void end () { font->end () ; } + + void putch ( char c ) { font->putch ( curpos, pointsize, italic, c ) ; } + void puts ( const char *s ) { font->puts ( curpos, pointsize, italic, s ) ; } +} ; + + +void fntInit () ; + +#endif + diff --git a/src/fnt/fntBitmap.cxx b/src/fnt/fntBitmap.cxx new file mode 100644 index 0000000..1976d6f --- /dev/null +++ b/src/fnt/fntBitmap.cxx @@ -0,0 +1,1017 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntBitmap.cxx 2036 2005-07-14 16:37:55Z fayjf $ +*/ + + +#include "fnt.h" + + +fntBitmapFont::fntBitmapFont( const GLubyte **_data, int _first, int _height, + float _xorig, float _yorig ) +{ + data = _data; + first = _first; + count = 0; + height = _height; + xorig = _xorig; + yorig = _yorig; + fix = 0; + wid = 0; + gap = 0; + + while (data[count] != NULL) + count++; +} + + +fntBitmapFont::~fntBitmapFont() +{ +} + + +void fntBitmapFont::begin () +{ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + + +void fntBitmapFont::end () +{ + glPopClientAttrib(); +} + + +void fntBitmapFont::putch ( sgVec3 curpos, float pointsize, float italic, char c ) +{ + if (c == '\n') { + curpos[1] -= height; + } else { + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + int i = (GLubyte) c - first; + if (i >= 0 && i < count) { + glBitmap(data[i][0], height, xorig, yorig, (float) data[i][0], 0, data[i] + 1); + curpos[0] += data[i][0]; + } + } +} + + +void fntBitmapFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + float x0 = curpos[0]; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + curpos[0] = x0; + curpos[1] -= height; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count) { + glBitmap(data[j][0], height, xorig, yorig, (float) data[j][0], 0, data[j] + 1); + curpos[0] += data[j][0]; + } + } + } +} + + +int fntBitmapFont::hasGlyph ( char c ) const +{ + int i = (GLubyte) c - first; + return i >= 0 && i < count && data[i][0] > 0; +} + + +void fntBitmapFont::getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float l, r, b, t; + + if (count > 0) { + int x1 = 0, y1 = 0, x = 0, y = 0; + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + if (x > x1) x1 = x; + y -= height; + x = 0; + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count && data[j][0] > 0) { + x += data[j][0]; + y1 = y; + } + } + } + if (x > x1) x1 = x; + l = - xorig; + r = (float) x1 - xorig; + b = (float) y1 - yorig; + t = (float) height - yorig; + } else { + l = r = b = t = 0.0f; + } + + if (left != NULL) *left = l; + if (right != NULL) *right = r; + if (bot != NULL) *bot = b; + if (top != NULL) *top = t; +} + + + +// This is to match the FreeGLUT definitions below + +struct SFG_Font { + char* Name; /* The source font name */ + int Quantity; /* Number of chars in font */ + int Height; /* Height of the characters */ + const GLubyte** Characters; /* The characters mapping */ + + float xorig, yorig; /* Relative origin of the character */ +}; + + +/* + * Below is the font data taken from FreeGLUT. Nothing is changed except + * the structures are made "static" to avoid namespace pollution. + */ + + +/* + * freeglut_font_data.c + * + * This file has been automatically generated by the genfonts utility. + * + * Copyright (c) 1999-2000 by Pawel W. Olszta + * Written by Pawel W. Olszta, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Sotware. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* + * Following fonts are defined in this file: + * + * 1. fgFontFixed8x13 <-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1> + * 2. fgFontFixed9x15 <-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1> + * 3. fgFontHelvetica10 <-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1> + * 4. fgFontHelvetica12 <-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1> + * 5. fgFontHelvetica18 <-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1> + * 6. fgFontTimesRoman10 <-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1> + * 7. fgFontTimesRoman24 <-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1> + */ + +static const GLubyte Fixed8x13_Character_032[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed8x13_Character_097[] = { 8, 0, 0,116,140,132,124, 4,120, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed8x13_Character_098[] = { 8, 0, 0,184,196,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_099[] = { 8, 0, 0,120,132,128,128,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_100[] = { 8, 0, 0,116,140,132,132,140,116, 4, 4, 4, 0, 0}; +static const GLubyte Fixed8x13_Character_101[] = { 8, 0, 0,120,132,128,252,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_102[] = { 8, 0, 0, 64, 64, 64, 64,248, 64, 64, 68, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_103[] = { 8,120,132,120,128,112,136,136,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_104[] = { 8, 0, 0,132,132,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_105[] = { 8, 0, 0,248, 32, 32, 32, 32, 96, 0, 32, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_106[] = { 8,112,136,136, 8, 8, 8, 8, 24, 0, 8, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_107[] = { 8, 0, 0,132,136,144,224,144,136,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_108[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32, 96, 0, 0}; +static const GLubyte Fixed8x13_Character_109[] = { 8, 0, 0,130,146,146,146,146,236, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_110[] = { 8, 0, 0,132,132,132,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_111[] = { 8, 0, 0,120,132,132,132,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_112[] = { 8,128,128,128,184,196,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_113[] = { 8, 4, 4, 4,116,140,132,140,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_114[] = { 8, 0, 0, 64, 64, 64, 64, 68,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_115[] = { 8, 0, 0,120,132, 24, 96,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_116[] = { 8, 0, 0, 56, 68, 64, 64, 64,248, 64, 64, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_117[] = { 8, 0, 0,116,136,136,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_119[] = { 8, 0, 0, 68,170,146,146,130,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_118[] = { 8, 0, 0, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_120[] = { 8, 0, 0,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_121[] = { 8,120,132, 4,116,140,132,132,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_122[] = { 8, 0, 0,252, 64, 32, 16, 8,252, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed8x13_Character_065[] = { 8, 0, 0,132,132,132,252,132,132,132, 72, 48, 0, 0}; /* "A" */ +static const GLubyte Fixed8x13_Character_066[] = { 8, 0, 0,252, 66, 66, 66,124, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_068[] = { 8, 0, 0,252, 66, 66, 66, 66, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_069[] = { 8, 0, 0,252,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_070[] = { 8, 0, 0,128,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_071[] = { 8, 0, 0,116,140,132,156,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_073[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32,248, 0, 0}; +static const GLubyte Fixed8x13_Character_074[] = { 8, 0, 0,112,136, 8, 8, 8, 8, 8, 8, 60, 0, 0}; +static const GLubyte Fixed8x13_Character_075[] = { 8, 0, 0,132,136,144,160,192,160,144,136,132, 0, 0}; +static const GLubyte Fixed8x13_Character_076[] = { 8, 0, 0,252,128,128,128,128,128,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_077[] = { 8, 0, 0,130,130,130,146,146,170,198,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_078[] = { 8, 0, 0,132,132,132,140,148,164,196,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_080[] = { 8, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_081[] = { 8, 0, 4,120,148,164,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_082[] = { 8, 0, 0,132,136,144,160,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_083[] = { 8, 0, 0,120,132, 4, 4,120,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_084[] = { 8, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0}; +static const GLubyte Fixed8x13_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_087[] = { 8, 0, 0, 68,170,146,146,146,130,130,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_086[] = { 8, 0, 0, 16, 40, 40, 40, 68, 68, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_088[] = { 8, 0, 0,130,130, 68, 40, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_089[] = { 8, 0, 0, 16, 16, 16, 16, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_090[] = { 8, 0, 0,252,128,128, 64, 32, 16, 8, 4,252, 0, 0}; /* "Z" */ +static const GLubyte Fixed8x13_Character_048[] = { 8, 0, 0, 48, 72,132,132,132,132,132, 72, 48, 0, 0}; /* "0" */ +static const GLubyte Fixed8x13_Character_049[] = { 8, 0, 0,248, 32, 32, 32, 32, 32,160, 96, 32, 0, 0}; +static const GLubyte Fixed8x13_Character_050[] = { 8, 0, 0,252,128, 64, 48, 8, 4,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_051[] = { 8, 0, 0,120,132, 4, 4, 56, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_052[] = { 8, 0, 0, 8, 8,252,136,136, 72, 40, 24, 8, 0, 0}; +static const GLubyte Fixed8x13_Character_053[] = { 8, 0, 0,120,132, 4, 4,196,184,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_054[] = { 8, 0, 0,120,132,132,196,184,128,128, 64, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_055[] = { 8, 0, 0, 64, 64, 32, 32, 16, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_056[] = { 8, 0, 0,120,132,132,132,120,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_057[] = { 8, 0, 0,112, 8, 4, 4,116,140,132,132,120, 0, 0}; /* "9" */ +static const GLubyte Fixed8x13_Character_096[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 16, 96,224, 0, 0}; /* "`" */ +static const GLubyte Fixed8x13_Character_126[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,168, 72, 0, 0}; /* "~" */ +static const GLubyte Fixed8x13_Character_033[] = { 8, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0}; /* "!" */ +static const GLubyte Fixed8x13_Character_064[] = { 8, 0, 0,120,128,148,172,164,156,132,132,120, 0, 0}; /* "@" */ +static const GLubyte Fixed8x13_Character_035[] = { 8, 0, 0, 0, 72, 72,252, 72,252, 72, 72, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed8x13_Character_036[] = { 8, 0, 0, 0, 32,240, 40,112,160,120, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed8x13_Character_037[] = { 8, 0, 0,136, 84, 72, 32, 16, 16, 72,164, 68, 0, 0}; /* "%" */ +static const GLubyte Fixed8x13_Character_094[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0}; /* "^" */ +static const GLubyte Fixed8x13_Character_038[] = { 8, 0, 0,116,136,148, 96,144,144, 96, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed8x13_Character_042[] = { 8, 0, 0, 0, 0, 72, 48,252, 48, 72, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed8x13_Character_040[] = { 8, 0, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0}; /* "(" */ +static const GLubyte Fixed8x13_Character_041[] = { 8, 0, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0}; /* ")" */ +static const GLubyte Fixed8x13_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,252, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed8x13_Character_095[] = { 8, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed8x13_Character_061[] = { 8, 0, 0, 0, 0,252, 0, 0,252, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed8x13_Character_043[] = { 8, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed8x13_Character_091[] = { 8, 0, 0,240,128,128,128,128,128,128,128,240, 0, 0}; /* "[" */ +static const GLubyte Fixed8x13_Character_123[] = { 8, 0, 0, 56, 64, 64, 32,192, 32, 64, 64, 56, 0, 0}; /* "{" */ +static const GLubyte Fixed8x13_Character_125[] = { 8, 0, 0,224, 16, 16, 32, 24, 32, 16, 16,224, 0, 0}; /* "}" */ +static const GLubyte Fixed8x13_Character_093[] = { 8, 0, 0,240, 16, 16, 16, 16, 16, 16, 16,240, 0, 0}; /* "]" */ +static const GLubyte Fixed8x13_Character_059[] = { 8, 0,128, 96,112, 0, 0, 32,112, 32, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed8x13_Character_058[] = { 8, 0, 64,224, 64, 0, 0, 64,224, 64, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed8x13_Character_044[] = { 8, 0,128, 96,112, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed8x13_Character_046[] = { 8, 0, 64,224, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed8x13_Character_060[] = { 8, 0, 0, 8, 16, 32, 64,128, 64, 32, 16, 8, 0, 0}; /* "<" */ +static const GLubyte Fixed8x13_Character_062[] = { 8, 0, 0,128, 64, 32, 16, 8, 16, 32, 64,128, 0, 0}; /* ">" */ +static const GLubyte Fixed8x13_Character_047[] = { 8, 0, 0,128,128, 64, 32, 16, 8, 4, 2, 2, 0, 0}; /* "/" */ +static const GLubyte Fixed8x13_Character_063[] = { 8, 0, 0, 16, 0, 16, 16, 8, 4,132,132,120, 0, 0}; /* "?" */ +static const GLubyte Fixed8x13_Character_092[] = { 8, 0, 0, 2, 2, 4, 8, 16, 32, 64,128,128, 0, 0}; /* "\" */ +static const GLubyte Fixed8x13_Character_034[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,144,144, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed8x13_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 0, 0}; /* """ */ +static const GLubyte Fixed8x13_Character_124[] = { 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* """ */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed8x13_Character_Map[] = {Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_032,Fixed8x13_Character_033,Fixed8x13_Character_034,Fixed8x13_Character_035,Fixed8x13_Character_036,Fixed8x13_Character_037,Fixed8x13_Character_038,Fixed8x13_Character_039,Fixed8x13_Character_040, + Fixed8x13_Character_041,Fixed8x13_Character_042,Fixed8x13_Character_043,Fixed8x13_Character_044,Fixed8x13_Character_045,Fixed8x13_Character_046,Fixed8x13_Character_047,Fixed8x13_Character_048,Fixed8x13_Character_049,Fixed8x13_Character_050,Fixed8x13_Character_051,Fixed8x13_Character_052,Fixed8x13_Character_053,Fixed8x13_Character_054,Fixed8x13_Character_055,Fixed8x13_Character_056,Fixed8x13_Character_057,Fixed8x13_Character_058,Fixed8x13_Character_059,Fixed8x13_Character_060,Fixed8x13_Character_061,Fixed8x13_Character_062,Fixed8x13_Character_063,Fixed8x13_Character_064,Fixed8x13_Character_065,Fixed8x13_Character_066,Fixed8x13_Character_067,Fixed8x13_Character_068,Fixed8x13_Character_069,Fixed8x13_Character_070,Fixed8x13_Character_071,Fixed8x13_Character_072,Fixed8x13_Character_073,Fixed8x13_Character_074,Fixed8x13_Character_075,Fixed8x13_Character_076,Fixed8x13_Character_077,Fixed8x13_Character_078,Fixed8x13_Character_079,Fixed8x13_Character_080,Fixed8x13_Character_081,Fixed8x13_Character_082, + Fixed8x13_Character_083,Fixed8x13_Character_084,Fixed8x13_Character_085,Fixed8x13_Character_086,Fixed8x13_Character_087,Fixed8x13_Character_088,Fixed8x13_Character_089,Fixed8x13_Character_090,Fixed8x13_Character_091,Fixed8x13_Character_092,Fixed8x13_Character_093,Fixed8x13_Character_094,Fixed8x13_Character_095,Fixed8x13_Character_096,Fixed8x13_Character_097,Fixed8x13_Character_098,Fixed8x13_Character_099,Fixed8x13_Character_100,Fixed8x13_Character_101,Fixed8x13_Character_102,Fixed8x13_Character_103,Fixed8x13_Character_104,Fixed8x13_Character_105,Fixed8x13_Character_106,Fixed8x13_Character_107,Fixed8x13_Character_108,Fixed8x13_Character_109,Fixed8x13_Character_110,Fixed8x13_Character_111,Fixed8x13_Character_112,Fixed8x13_Character_113,Fixed8x13_Character_114,Fixed8x13_Character_115,Fixed8x13_Character_116,Fixed8x13_Character_117,Fixed8x13_Character_118,Fixed8x13_Character_119,Fixed8x13_Character_120,Fixed8x13_Character_121,Fixed8x13_Character_122,Fixed8x13_Character_123,Fixed8x13_Character_124, + Fixed8x13_Character_125,Fixed8x13_Character_126,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed8x13 = { "-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1", 93, 13, Fixed8x13_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Fixed9x15_Character_032[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed9x15_Character_097[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,126, 0, 2, 0, 2, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed9x15_Character_098[] = { 9, 0, 0, 0, 0, 0, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_099[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_100[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_101[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,254, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_102[] = { 9, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 32, 0, 32, 0, 34, 0, 34, 0, 28, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_103[] = { 9,124, 0,130, 0,130, 0,124, 0,128, 0,120, 0,132, 0,132, 0,132, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_104[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_105[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_106[] = { 9,120, 0,132, 0,132, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 28, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_107[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,140, 0,176, 0,192, 0,176, 0,140, 0,130, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_108[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,146, 0,146, 0,146, 0,146, 0,146, 0,236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_110[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_111[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_112[] = { 9,128, 0,128, 0,128, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_113[] = { 9, 2, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_114[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 66, 0, 98, 0,156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_115[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0,124, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_116[] = { 9, 0, 0, 0, 0, 0, 0, 28, 0, 34, 0, 32, 0, 32, 0, 32, 0, 32, 0,252, 0, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_117[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,132, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_119[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_118[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_120[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_121[] = { 9,120, 0,132, 0, 4, 0,116, 0,140, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_122[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed9x15_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Fixed9x15_Character_066[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0,124, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_067[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_069[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_070[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_071[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,142, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_073[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_074[] = { 9, 0, 0, 0, 0, 0, 0,120, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 30, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_075[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,132, 0,136, 0,144, 0,160, 0,224, 0,144, 0,136, 0,132, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_076[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_077[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,146, 0,146, 0,170, 0,170, 0,198, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,134, 0,138, 0,146, 0,162, 0,194, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_079[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_080[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_081[] = { 9, 0, 0, 6, 0, 8, 0,124, 0,146, 0,162, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_082[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,132, 0,136, 0,144, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_083[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0, 2, 0, 12, 0,112, 0,128, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_084[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_085[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_087[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,146, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Fixed9x15_Character_048[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Fixed9x15_Character_049[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,144, 0, 80, 0, 48, 0, 16, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_050[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 48, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_051[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0, 28, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_052[] = { 9, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 4, 0,254, 0,132, 0, 68, 0, 36, 0, 20, 0, 12, 0, 4, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_053[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0,194, 0,188, 0,128, 0,128, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_054[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0, 64, 0, 60, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_055[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 32, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_056[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0, 68, 0, 56, 0, 68, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_057[] = { 9, 0, 0, 0, 0, 0, 0,120, 0, 4, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Fixed9x15_Character_096[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 64, 0,192, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Fixed9x15_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,140, 0,146, 0, 98, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Fixed9x15_Character_033[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0}; /* "!" */ +static const GLubyte Fixed9x15_Character_064[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,154, 0,166, 0,162, 0,158, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Fixed9x15_Character_035[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed9x15_Character_036[] = { 9, 0, 0, 0, 0, 16, 0,124, 0,146, 0, 18, 0, 18, 0, 20, 0, 56, 0, 80, 0,144, 0,146, 0,124, 0, 16, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed9x15_Character_037[] = { 9, 0, 0, 0, 0, 0, 0,132, 0, 74, 0, 74, 0, 36, 0, 16, 0, 16, 0, 72, 0,164, 0,164, 0, 66, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Fixed9x15_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Fixed9x15_Character_038[] = { 9, 0, 0, 0, 0, 0, 0, 98, 0,148, 0,136, 0,148, 0, 98, 0, 96, 0,144, 0,144, 0,144, 0, 96, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed9x15_Character_042[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,146, 0, 84, 0, 56, 0, 84, 0,146, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed9x15_Character_040[] = { 9, 0, 0, 0, 0, 32, 0, 64, 0, 64, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 64, 0, 64, 0, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Fixed9x15_Character_041[] = { 9, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0}; /* ")" */ +static const GLubyte Fixed9x15_Character_045[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed9x15_Character_095[] = { 9, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed9x15_Character_061[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed9x15_Character_043[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0,254, 0, 16, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed9x15_Character_091[] = { 9, 0, 0, 0, 0,240, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,240, 0, 0, 0}; /* "[" */ +static const GLubyte Fixed9x15_Character_123[] = { 9, 0, 0, 0, 0, 56, 0, 64, 0, 64, 0, 64, 0, 32, 0,192, 0,192, 0, 32, 0, 64, 0, 64, 0, 64, 0, 56, 0, 0, 0}; /* "{" */ +static const GLubyte Fixed9x15_Character_125[] = { 9, 0, 0, 0, 0,224, 0, 16, 0, 16, 0, 16, 0, 32, 0, 24, 0, 24, 0, 32, 0, 16, 0, 16, 0, 16, 0,224, 0, 0, 0}; /* "}" */ +static const GLubyte Fixed9x15_Character_093[] = { 9, 0, 0, 0, 0,240, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,240, 0, 0, 0}; /* "]" */ +static const GLubyte Fixed9x15_Character_059[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed9x15_Character_058[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed9x15_Character_044[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed9x15_Character_046[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed9x15_Character_060[] = { 9, 0, 0, 0, 0, 0, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Fixed9x15_Character_062[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Fixed9x15_Character_047[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 16, 0, 16, 0, 8, 0, 4, 0, 4, 0, 2, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Fixed9x15_Character_063[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 16, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Fixed9x15_Character_092[] = { 9, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 4, 0, 8, 0, 16, 0, 16, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Fixed9x15_Character_034[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0,144, 0,144, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed9x15_Character_039[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Fixed9x15_Character_124[] = { 9, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed9x15_Character_Map[] = {Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_032,Fixed9x15_Character_033,Fixed9x15_Character_034,Fixed9x15_Character_035,Fixed9x15_Character_036,Fixed9x15_Character_037,Fixed9x15_Character_038,Fixed9x15_Character_039,Fixed9x15_Character_040, + Fixed9x15_Character_041,Fixed9x15_Character_042,Fixed9x15_Character_043,Fixed9x15_Character_044,Fixed9x15_Character_045,Fixed9x15_Character_046,Fixed9x15_Character_047,Fixed9x15_Character_048,Fixed9x15_Character_049,Fixed9x15_Character_050,Fixed9x15_Character_051,Fixed9x15_Character_052,Fixed9x15_Character_053,Fixed9x15_Character_054,Fixed9x15_Character_055,Fixed9x15_Character_056,Fixed9x15_Character_057,Fixed9x15_Character_058,Fixed9x15_Character_059,Fixed9x15_Character_060,Fixed9x15_Character_061,Fixed9x15_Character_062,Fixed9x15_Character_063,Fixed9x15_Character_064,Fixed9x15_Character_065,Fixed9x15_Character_066,Fixed9x15_Character_067,Fixed9x15_Character_068,Fixed9x15_Character_069,Fixed9x15_Character_070,Fixed9x15_Character_071,Fixed9x15_Character_072,Fixed9x15_Character_073,Fixed9x15_Character_074,Fixed9x15_Character_075,Fixed9x15_Character_076,Fixed9x15_Character_077,Fixed9x15_Character_078,Fixed9x15_Character_079,Fixed9x15_Character_080,Fixed9x15_Character_081,Fixed9x15_Character_082, + Fixed9x15_Character_083,Fixed9x15_Character_084,Fixed9x15_Character_085,Fixed9x15_Character_086,Fixed9x15_Character_087,Fixed9x15_Character_088,Fixed9x15_Character_089,Fixed9x15_Character_090,Fixed9x15_Character_091,Fixed9x15_Character_092,Fixed9x15_Character_093,Fixed9x15_Character_094,Fixed9x15_Character_095,Fixed9x15_Character_096,Fixed9x15_Character_097,Fixed9x15_Character_098,Fixed9x15_Character_099,Fixed9x15_Character_100,Fixed9x15_Character_101,Fixed9x15_Character_102,Fixed9x15_Character_103,Fixed9x15_Character_104,Fixed9x15_Character_105,Fixed9x15_Character_106,Fixed9x15_Character_107,Fixed9x15_Character_108,Fixed9x15_Character_109,Fixed9x15_Character_110,Fixed9x15_Character_111,Fixed9x15_Character_112,Fixed9x15_Character_113,Fixed9x15_Character_114,Fixed9x15_Character_115,Fixed9x15_Character_116,Fixed9x15_Character_117,Fixed9x15_Character_118,Fixed9x15_Character_119,Fixed9x15_Character_120,Fixed9x15_Character_121,Fixed9x15_Character_122,Fixed9x15_Character_123,Fixed9x15_Character_124, + Fixed9x15_Character_125,Fixed9x15_Character_126,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed9x15 = { "-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1", 93, 15, Fixed9x15_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica10_Character_032[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica10_Character_097[] = { 5, 0, 0,104,144,144,112, 16,224, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica10_Character_098[] = { 6, 0, 0,176,200,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica10_Character_099[] = { 5, 0, 0, 96,144,128,128,144, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica10_Character_100[] = { 6, 0, 0,104,152,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica10_Character_101[] = { 5, 0, 0, 96,144,128,240,144, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica10_Character_102[] = { 4, 0, 0, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica10_Character_103[] = { 6,112, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica10_Character_104[] = { 6, 0, 0,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica10_Character_105[] = { 2, 0, 0,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica10_Character_106[] = { 2, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica10_Character_107[] = { 5, 0, 0,144,144,160,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica10_Character_108[] = { 2, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica10_Character_109[] = { 8, 0, 0,146,146,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica10_Character_110[] = { 6, 0, 0,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica10_Character_111[] = { 6, 0, 0,112,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica10_Character_112[] = { 6,128,128,176,200,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica10_Character_113[] = { 6, 8, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica10_Character_114[] = { 4, 0, 0,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica10_Character_115[] = { 5, 0, 0, 96,144, 16, 96,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica10_Character_116[] = { 4, 0, 0, 96, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica10_Character_117[] = { 5, 0, 0,112,144,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica10_Character_118[] = { 6, 0, 0, 32, 32, 80, 80,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica10_Character_119[] = { 8, 0, 0, 40, 40, 84, 84,146,146, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica10_Character_120[] = { 6, 0, 0,136,136, 80, 32, 80,136, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica10_Character_121[] = { 5,128, 64, 64, 96,160,160,144,144, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica10_Character_122[] = { 5, 0, 0,240,128, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica10_Character_065[] = { 7, 0, 0,130,130,124, 68, 40, 40, 16, 16, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica10_Character_066[] = { 7, 0, 0,240,136,136,136,240,136,136,240, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica10_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,132,120, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica10_Character_068[] = { 8, 0, 0,240,136,132,132,132,132,136,240, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica10_Character_069[] = { 7, 0, 0,248,128,128,128,248,128,128,248, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica10_Character_070[] = { 6, 0, 0,128,128,128,128,240,128,128,248, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica10_Character_071[] = { 8, 0, 0,116,140,132,140,128,128,132,120, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica10_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica10_Character_073[] = { 3, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica10_Character_074[] = { 5, 0, 0, 96,144, 16, 16, 16, 16, 16, 16, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica10_Character_075[] = { 7, 0, 0,136,136,144,144,224,160,144,136, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica10_Character_076[] = { 6, 0, 0,240,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica10_Character_077[] = { 9, 0, 0, 0, 0,146, 0,146, 0,146, 0,170, 0,170, 0,198, 0,198, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica10_Character_078[] = { 8, 0, 0,140,140,148,148,164,164,196,196, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica10_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,120, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica10_Character_080[] = { 7, 0, 0,128,128,128,128,240,136,136,240, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica10_Character_081[] = { 8, 0, 2,124,140,148,132,132,132,132,120, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica10_Character_082[] = { 7, 0, 0,136,136,136,136,240,136,136,240, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica10_Character_083[] = { 7, 0, 0,112,136,136, 8,112,128,136,112, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica10_Character_084[] = { 5, 0, 0, 32, 32, 32, 32, 32, 32, 32,248, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica10_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica10_Character_086[] = { 7, 0, 0, 16, 40, 40, 68, 68, 68,130,130, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica10_Character_087[] = { 9, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica10_Character_088[] = { 7, 0, 0,136,136, 80, 80, 32, 80,136,136, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica10_Character_089[] = { 7, 0, 0, 16, 16, 16, 40, 40, 68, 68,130, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica10_Character_090[] = { 7, 0, 0,248,128, 64, 32, 32, 16, 8,248, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica10_Character_048[] = { 6, 0, 0,112,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica10_Character_049[] = { 6, 0, 0, 64, 64, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica10_Character_050[] = { 6, 0, 0,248,128, 64, 48, 8, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica10_Character_051[] = { 6, 0, 0,112,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica10_Character_052[] = { 6, 0, 0, 16, 16,248,144, 80, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica10_Character_053[] = { 6, 0, 0,112,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica10_Character_054[] = { 6, 0, 0,112,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica10_Character_055[] = { 6, 0, 0, 64, 64, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica10_Character_056[] = { 6, 0, 0,112,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica10_Character_057[] = { 6, 0, 0,112,136, 8,104,152,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0,128,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica10_Character_033[] = { 3, 0, 0,128, 0,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica10_Character_064[] = { 11, 62, 0, 64, 0,155, 0,164,128,164,128,162, 64,146, 64, 77, 64, 32,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica10_Character_035[] = { 6, 0, 0, 80, 80,248, 40,124, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica10_Character_036[] = { 6, 0, 32,112,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica10_Character_037[] = { 9, 0, 0, 0, 0, 38, 0, 41, 0, 22, 0, 16, 0, 8, 0,104, 0,148, 0,100, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica10_Character_094[] = { 6, 0, 0, 0, 0, 0,136, 80, 80, 32, 32, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica10_Character_038[] = { 8, 0, 0,100,152,152,164, 96, 80, 80, 32, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica10_Character_042[] = { 4, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica10_Character_040[] = { 4, 32, 64, 64,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica10_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica10_Character_045[] = { 7, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica10_Character_095[] = { 6,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica10_Character_061[] = { 5, 0, 0, 0, 0,240, 0,240, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica10_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica10_Character_123[] = { 3, 32, 64, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica10_Character_125[] = { 3,128, 64, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica10_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica10_Character_059[] = { 3,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica10_Character_058[] = { 3, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica10_Character_044[] = { 3,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica10_Character_046[] = { 3, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica10_Character_060[] = { 6, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica10_Character_062[] = { 6, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica10_Character_047[] = { 3, 0, 0,128,128, 64, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica10_Character_063[] = { 6, 0, 0, 64, 0, 64, 64, 32, 16,144, 96, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica10_Character_092[] = { 3, 0, 0, 32, 32, 64, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica10_Character_124[] = { 3, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica10_Character_Map[] = {Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_032,Helvetica10_Character_033,Helvetica10_Character_034,Helvetica10_Character_035,Helvetica10_Character_036,Helvetica10_Character_037, + Helvetica10_Character_038,Helvetica10_Character_039,Helvetica10_Character_040,Helvetica10_Character_041,Helvetica10_Character_042,Helvetica10_Character_043,Helvetica10_Character_044,Helvetica10_Character_045,Helvetica10_Character_046,Helvetica10_Character_047,Helvetica10_Character_048,Helvetica10_Character_049,Helvetica10_Character_050,Helvetica10_Character_051,Helvetica10_Character_052,Helvetica10_Character_053,Helvetica10_Character_054,Helvetica10_Character_055,Helvetica10_Character_056,Helvetica10_Character_057,Helvetica10_Character_058,Helvetica10_Character_059,Helvetica10_Character_060,Helvetica10_Character_061,Helvetica10_Character_062,Helvetica10_Character_063,Helvetica10_Character_064,Helvetica10_Character_065,Helvetica10_Character_066,Helvetica10_Character_067,Helvetica10_Character_068,Helvetica10_Character_069,Helvetica10_Character_070,Helvetica10_Character_071,Helvetica10_Character_072,Helvetica10_Character_073,Helvetica10_Character_074,Helvetica10_Character_075,Helvetica10_Character_076, + Helvetica10_Character_077,Helvetica10_Character_078,Helvetica10_Character_079,Helvetica10_Character_080,Helvetica10_Character_081,Helvetica10_Character_082,Helvetica10_Character_083,Helvetica10_Character_084,Helvetica10_Character_085,Helvetica10_Character_086,Helvetica10_Character_087,Helvetica10_Character_088,Helvetica10_Character_089,Helvetica10_Character_090,Helvetica10_Character_091,Helvetica10_Character_092,Helvetica10_Character_093,Helvetica10_Character_094,Helvetica10_Character_095,Helvetica10_Character_096,Helvetica10_Character_097,Helvetica10_Character_098,Helvetica10_Character_099,Helvetica10_Character_100,Helvetica10_Character_101,Helvetica10_Character_102,Helvetica10_Character_103,Helvetica10_Character_104,Helvetica10_Character_105,Helvetica10_Character_106,Helvetica10_Character_107,Helvetica10_Character_108,Helvetica10_Character_109,Helvetica10_Character_110,Helvetica10_Character_111,Helvetica10_Character_112,Helvetica10_Character_113,Helvetica10_Character_114,Helvetica10_Character_115, + Helvetica10_Character_116,Helvetica10_Character_117,Helvetica10_Character_118,Helvetica10_Character_119,Helvetica10_Character_120,Helvetica10_Character_121,Helvetica10_Character_122,Helvetica10_Character_123,Helvetica10_Character_124,Helvetica10_Character_125,Helvetica10_Character_126,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica10 = { "-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1", 93, 13, Helvetica10_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Helvetica12_Character_032[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica12_Character_097[] = { 7, 0, 0, 0,116,136,136,120, 8,136,112, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica12_Character_098[] = { 7, 0, 0, 0,176,200,136,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica12_Character_099[] = { 7, 0, 0, 0,112,136,128,128,128,136,112, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica12_Character_100[] = { 7, 0, 0, 0,104,152,136,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica12_Character_101[] = { 7, 0, 0, 0,112,136,128,248,136,136,112, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica12_Character_102[] = { 4, 0, 0, 0, 64, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica12_Character_103[] = { 7,112,136, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica12_Character_104[] = { 7, 0, 0, 0,136,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica12_Character_105[] = { 3, 0, 0, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica12_Character_106[] = { 4,128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica12_Character_107[] = { 6, 0, 0, 0,136,144,160,192,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica12_Character_108[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica12_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,146, 0,146, 0,146, 0,146, 0,146, 0,218, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica12_Character_110[] = { 7, 0, 0, 0,136,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica12_Character_111[] = { 7, 0, 0, 0,112,136,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica12_Character_112[] = { 7,128,128,128,176,200,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica12_Character_113[] = { 7, 8, 8, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica12_Character_114[] = { 4, 0, 0, 0,128,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica12_Character_115[] = { 6, 0, 0, 0, 96,144, 16, 96,128,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica12_Character_116[] = { 4, 0, 0, 0, 96, 64, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica12_Character_117[] = { 7, 0, 0, 0,104,152,136,136,136,136,136, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica12_Character_118[] = { 7, 0, 0, 0, 32, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica12_Character_119[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica12_Character_120[] = { 6, 0, 0, 0,132,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica12_Character_121[] = { 7,128, 64, 32, 32, 80, 80,144,136,136,136, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica12_Character_122[] = { 6, 0, 0, 0,240,128, 64, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica12_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,124, 0, 68, 0, 68, 0, 40, 0, 40, 0, 16, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica12_Character_066[] = { 8, 0, 0, 0,248,132,132,132,248,132,132,132,248, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica12_Character_067[] = { 9, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica12_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,248, 0,132, 0,130, 0,130, 0,130, 0,130, 0,130, 0,132, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica12_Character_069[] = { 8, 0, 0, 0,252,128,128,128,252,128,128,128,252, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica12_Character_070[] = { 8, 0, 0, 0,128,128,128,128,248,128,128,128,252, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica12_Character_071[] = { 9, 0, 0, 0, 0, 0, 0, 58, 0, 70, 0,130, 0,130, 0,142, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica12_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica12_Character_073[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica12_Character_074[] = { 7, 0, 0, 0,112,136,136, 8, 8, 8, 8, 8, 8, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica12_Character_075[] = { 8, 0, 0, 0,130,132,136,144,224,160,144,136,132, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica12_Character_076[] = { 7, 0, 0, 0,248,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica12_Character_077[] = { 11, 0, 0, 0, 0, 0, 0,136,128,136,128,148,128,148,128,162,128,162,128,193,128,193,128,128,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica12_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,134, 0,138, 0,138, 0,146, 0,162, 0,162, 0,194, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica12_Character_079[] = { 10, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,129, 0,129, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica12_Character_080[] = { 8, 0, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica12_Character_081[] = { 10, 0, 0, 0, 0, 0, 0, 61, 0, 66, 0,133, 0,137, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica12_Character_082[] = { 8, 0, 0, 0,132,132,132,136,248,132,132,132,248, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica12_Character_083[] = { 8, 0, 0, 0,120,132,132, 4, 24, 96,128,132,120, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica12_Character_084[] = { 7, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica12_Character_085[] = { 8, 0, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica12_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica12_Character_087[] = { 11, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 85, 0, 73, 0,136,128,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica12_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica12_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica12_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica12_Character_048[] = { 7, 0, 0, 0,112,136,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica12_Character_049[] = { 7, 0, 0, 0, 32, 32, 32, 32, 32, 32, 32,224, 32, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica12_Character_050[] = { 7, 0, 0, 0,248,128,128, 64, 32, 16, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica12_Character_051[] = { 7, 0, 0, 0,112,136,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica12_Character_052[] = { 7, 0, 0, 0, 8, 8,252,136, 72, 40, 40, 24, 8, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica12_Character_053[] = { 7, 0, 0, 0,112,136,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica12_Character_054[] = { 7, 0, 0, 0,112,136,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica12_Character_055[] = { 7, 0, 0, 0, 64, 64, 32, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica12_Character_056[] = { 7, 0, 0, 0,112,136,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica12_Character_057[] = { 7, 0, 0, 0,112,136, 8, 8,120,136,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica12_Character_096[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica12_Character_126[] = { 8, 0, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica12_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica12_Character_064[] = { 12, 0, 0, 0, 0, 62, 0, 64, 0,155, 0,166,128,162, 64,162, 64,146, 64, 77, 64, 96,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica12_Character_035[] = { 7, 0, 0, 0, 80, 80, 80,252, 40,252, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica12_Character_036[] = { 7, 0, 0, 32,112,168,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica12_Character_037[] = { 11, 0, 0, 0, 0, 0, 0, 35, 0, 20,128, 20,128, 19, 0, 8, 0,104, 0,148, 0,148, 0, 98, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica12_Character_094[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica12_Character_038[] = { 9, 0, 0, 0, 0, 0, 0,114, 0,140, 0,132, 0,138, 0, 80, 0, 48, 0, 72, 0, 72, 0, 48, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica12_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica12_Character_040[] = { 4, 32, 64, 64,128,128,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica12_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica12_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica12_Character_095[] = { 7, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica12_Character_061[] = { 7, 0, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica12_Character_043[] = { 7, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica12_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica12_Character_123[] = { 4, 48, 64, 64, 64, 64, 64,128, 64, 64, 64, 64, 48, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica12_Character_125[] = { 4,192, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32,192, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica12_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica12_Character_059[] = { 3, 0,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica12_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica12_Character_044[] = { 4, 0,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica12_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica12_Character_060[] = { 7, 0, 0, 0, 0, 12, 48,192, 48, 12, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica12_Character_062[] = { 7, 0, 0, 0, 0,192, 48, 12, 48,192, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica12_Character_047[] = { 4, 0, 0, 0,128,128,128, 64, 64, 64, 32, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica12_Character_063[] = { 7, 0, 0, 0, 32, 0, 32, 32, 16, 16,136,136,112, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica12_Character_092[] = { 4, 0, 0, 0, 32, 32, 32, 64, 64, 64,128,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica12_Character_034[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica12_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0}; /* "'" */ +static const GLubyte Helvetica12_Character_124[] = { 3, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica12_Character_Map[] = {Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_032,Helvetica12_Character_033,Helvetica12_Character_034,Helvetica12_Character_035,Helvetica12_Character_036,Helvetica12_Character_037, + Helvetica12_Character_038,Helvetica12_Character_039,Helvetica12_Character_040,Helvetica12_Character_041,Helvetica12_Character_042,Helvetica12_Character_043,Helvetica12_Character_044,Helvetica12_Character_045,Helvetica12_Character_046,Helvetica12_Character_047,Helvetica12_Character_048,Helvetica12_Character_049,Helvetica12_Character_050,Helvetica12_Character_051,Helvetica12_Character_052,Helvetica12_Character_053,Helvetica12_Character_054,Helvetica12_Character_055,Helvetica12_Character_056,Helvetica12_Character_057,Helvetica12_Character_058,Helvetica12_Character_059,Helvetica12_Character_060,Helvetica12_Character_061,Helvetica12_Character_062,Helvetica12_Character_063,Helvetica12_Character_064,Helvetica12_Character_065,Helvetica12_Character_066,Helvetica12_Character_067,Helvetica12_Character_068,Helvetica12_Character_069,Helvetica12_Character_070,Helvetica12_Character_071,Helvetica12_Character_072,Helvetica12_Character_073,Helvetica12_Character_074,Helvetica12_Character_075,Helvetica12_Character_076, + Helvetica12_Character_077,Helvetica12_Character_078,Helvetica12_Character_079,Helvetica12_Character_080,Helvetica12_Character_081,Helvetica12_Character_082,Helvetica12_Character_083,Helvetica12_Character_084,Helvetica12_Character_085,Helvetica12_Character_086,Helvetica12_Character_087,Helvetica12_Character_088,Helvetica12_Character_089,Helvetica12_Character_090,Helvetica12_Character_091,Helvetica12_Character_092,Helvetica12_Character_093,Helvetica12_Character_094,Helvetica12_Character_095,Helvetica12_Character_096,Helvetica12_Character_097,Helvetica12_Character_098,Helvetica12_Character_099,Helvetica12_Character_100,Helvetica12_Character_101,Helvetica12_Character_102,Helvetica12_Character_103,Helvetica12_Character_104,Helvetica12_Character_105,Helvetica12_Character_106,Helvetica12_Character_107,Helvetica12_Character_108,Helvetica12_Character_109,Helvetica12_Character_110,Helvetica12_Character_111,Helvetica12_Character_112,Helvetica12_Character_113,Helvetica12_Character_114,Helvetica12_Character_115, + Helvetica12_Character_116,Helvetica12_Character_117,Helvetica12_Character_118,Helvetica12_Character_119,Helvetica12_Character_120,Helvetica12_Character_121,Helvetica12_Character_122,Helvetica12_Character_123,Helvetica12_Character_124,Helvetica12_Character_125,Helvetica12_Character_126,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica12 = { "-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1", 93, 15, Helvetica12_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica18_Character_032[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica18_Character_097[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,118, 0,238, 0,198, 0,198, 0,230, 0,126, 0, 14, 0,198, 0,238, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica18_Character_098[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica18_Character_099[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,192, 0,192, 0,192, 0,192, 0, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica18_Character_100[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 1,128, 1,128, 1,128, 1,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica18_Character_101[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,127, 0,227, 0,192, 0,192, 0,255, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica18_Character_102[] = { 6, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 60, 28, 0, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica18_Character_103[] = { 11, 28, 0,127, 0, 99, 0, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 97,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica18_Character_104[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica18_Character_105[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192, 0, 0,192,192, 0, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica18_Character_106[] = { 4,224,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica18_Character_107[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,199, 0,198, 0,206, 0,204, 0,216, 0,248, 0,240, 0,216, 0,204, 0,198, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica18_Character_108[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica18_Character_109[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,231, 48,222,240,204, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica18_Character_110[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica18_Character_111[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,193,128,193,128,193,128,193,128, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica18_Character_112[] = { 11,192, 0,192, 0,192, 0,192, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica18_Character_113[] = { 11, 1,128, 1,128, 1,128, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica18_Character_114[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,224,216,216, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica18_Character_115[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,252, 0,198, 0, 6, 0, 62, 0,252, 0,192, 0,198, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica18_Character_116[] = { 6, 0, 0, 0, 0, 24, 56, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 48, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica18_Character_117[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,115, 0,251, 0,199, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica18_Character_118[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica18_Character_119[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 25,128, 25,128, 57,192, 41, 64,105, 96,102, 96,102, 96,198, 48,198, 48,198, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica18_Character_120[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,231, 0,102, 0, 60, 0, 24, 0, 24, 0, 60, 0,102, 0,231, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica18_Character_121[] = { 10,112, 0,112, 0, 24, 0, 24, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica18_Character_122[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0,192, 0, 96, 0, 48, 0, 24, 0, 12, 0, 6, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica18_Character_065[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 48,192, 48, 96, 96, 96, 96,127,224, 63,192, 48,192, 48,192, 25,128, 25,128, 15, 0, 15, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica18_Character_066[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192,192,224,192, 96,192, 96,192,224,255,192,255,128,193,128,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica18_Character_067[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 0,192, 0,192, 0,192, 0,192, 0,224, 0, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica18_Character_068[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,128,193,192,192,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica18_Character_069[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,128,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica18_Character_070[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica18_Character_071[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,176, 63,240,112,112, 96, 48,224, 48,193,240,193,240,192, 0,192, 0,224, 48, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica18_Character_072[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,255,224,255,224,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica18_Character_073[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica18_Character_074[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica18_Character_075[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192,112,192,224,193,192,195,128,199, 0,206, 0,252, 0,248, 0,220, 0,206, 0,199, 0,195,128,193,192,192,224, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica18_Character_076[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica18_Character_077[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0,195, 12,195, 12,199,140,196,140,204,204,204,204,216,108,216,108,240, 60,240, 60,224, 28,224, 28,192, 12,192, 12, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica18_Character_078[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192,224,193,224,193,224,195, 96,198, 96,198, 96,204, 96,204, 96,216, 96,240, 96,240, 96,224, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica18_Character_079[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 56,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica18_Character_080[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica18_Character_081[] = { 15, 0, 0, 0, 0, 0, 0, 0, 48, 15,176, 63,224,112,240, 97,176,225,184,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica18_Character_082[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192,192,192,192,192,192,192,192,193,128,193,128,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica18_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0,127,192,224,224,192, 96, 0, 96, 0,224, 3,192, 31, 0,124, 0,224, 0,192, 96,224,224,127,192, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica18_Character_084[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica18_Character_085[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0,127,192, 96,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica18_Character_086[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 15, 0, 15, 0, 25,128, 25,128, 25,128, 48,192, 48,192, 48,192, 96, 96, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica18_Character_087[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 24, 24, 0, 28, 56, 0, 52, 44, 0, 54,108, 0, 54,108, 0,102,102, 0,102,102, 0, 98, 70, 0, 99,198, 0,195,195, 0,193,131, 0,193,131, 0,193,131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica18_Character_088[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,224,224, 96,192,113,192, 49,128, 27, 0, 14, 0, 14, 0, 27, 0, 49,128,113,192, 96,192,224,224,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica18_Character_089[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 15, 0, 25,128, 48,192, 48,192, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica18_Character_090[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,255,192,255,192,192, 0, 96, 0, 48, 0, 24, 0, 28, 0, 12, 0, 6, 0, 3, 0, 1,128, 0,192,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica18_Character_048[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,102, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica18_Character_049[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,248, 0,248, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica18_Character_050[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,224, 0,112, 0, 56, 0, 28, 0, 14, 0, 7, 0, 3, 0,195, 0,254, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica18_Character_051[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,199, 0,195, 0, 3, 0, 7, 0, 30, 0, 28, 0, 6, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica18_Character_052[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0,255,128,255,128,195, 0, 99, 0, 51, 0, 51, 0, 27, 0, 15, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica18_Character_053[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,199, 0,195, 0, 3, 0, 3, 0,199, 0,254, 0,252, 0,192, 0,192, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica18_Character_054[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,227, 0,195, 0,195, 0,195, 0,254, 0,220, 0,192, 0,192, 0, 99, 0,127, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica18_Character_055[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 96, 0, 48, 0, 48, 0, 48, 0, 24, 0, 24, 0, 12, 0, 12, 0, 6, 0, 3, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica18_Character_056[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0,102, 0,126, 0,102, 0,195, 0,195, 0,231, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica18_Character_057[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,198, 0, 3, 0, 3, 0, 59, 0,127, 0,195, 0,195, 0,195, 0,199, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica18_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,192,128,128, 64, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica18_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 0,126, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica18_Character_033[] = { 6, 0, 0, 0, 0,192,192, 0, 0,128,128,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica18_Character_064[] = { 18, 0, 0, 0, 7,224, 0, 31,240, 0, 56, 0, 0,112, 0, 0,103,112, 0,207,248, 0,204,204, 0,204,102, 0,204,102, 0,204, 99, 0,198, 51, 0,103,115, 0, 99,179, 0, 48, 6, 0, 28, 14, 0, 15,252, 0, 3,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica18_Character_035[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 36, 0, 36, 0,255,128,255,128, 18, 0, 18, 0, 18, 0,127,192,127,192, 9, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica18_Character_036[] = { 10, 0, 0, 0, 0, 8, 0, 8, 0, 62, 0,127, 0,235,128,201,128, 9,128, 15, 0, 62, 0,120, 0,232, 0,200, 0,203, 0,127, 0, 62, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica18_Character_037[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 24,120, 24,252, 12,204, 12,204, 6,252, 6,120, 3, 0,123, 0,253,128,205,128,204,192,252,192,120, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica18_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0,198, 0,108, 0, 56, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica18_Character_038[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 60,112,126,224,231,192,195,128,195,192,198,192,238,192,124, 0, 60, 0,102, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica18_Character_042[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136,112,112,248, 32, 32, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica18_Character_040[] = { 6, 16, 48, 96, 96,192,192,192,192,192,192,192,192,192,192, 96, 96, 48, 16, 0, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica18_Character_041[] = { 6,128,192, 96, 96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 96, 96,192,128, 0, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica18_Character_045[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica18_Character_095[] = { 10,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica18_Character_061[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica18_Character_043[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0,255, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica18_Character_091[] = { 5,240,240,192,192,192,192,192,192,192,192,192,192,192,192,192,192,240,240, 0, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica18_Character_123[] = { 7, 12, 24, 48, 48, 48, 48, 48, 48, 96,192, 96, 48, 48, 48, 48, 48, 24, 12, 0, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica18_Character_125[] = { 7,192, 96, 48, 48, 48, 48, 48, 48, 24, 12, 24, 48, 48, 48, 48, 48, 96,192, 0, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica18_Character_093[] = { 5,240,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,240,240, 0, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica18_Character_059[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica18_Character_058[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica18_Character_044[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica18_Character_046[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica18_Character_060[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 15, 0, 60, 0,112, 0,192, 0,112, 0, 60, 0, 15, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica18_Character_062[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,240, 0, 60, 0, 14, 0, 3, 0, 14, 0, 60, 0,240, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica18_Character_047[] = { 5, 0, 0, 0, 0,192,192, 64, 64, 96, 96, 32, 32, 48, 48, 16, 16, 24, 24, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica18_Character_063[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 48, 0, 48, 0, 48, 0, 56, 0, 28, 0, 14, 0,198, 0,198, 0,254, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica18_Character_092[] = { 5, 0, 0, 0, 0, 24, 24, 16, 16, 48, 48, 32, 32, 96, 96, 64, 64,192,192, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica18_Character_034[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144,144,216,216,216, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica18_Character_039[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64,192,192, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica18_Character_124[] = { 4, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica18_Character_Map[] = {Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_032,Helvetica18_Character_033,Helvetica18_Character_034,Helvetica18_Character_035,Helvetica18_Character_036,Helvetica18_Character_037, + Helvetica18_Character_038,Helvetica18_Character_039,Helvetica18_Character_040,Helvetica18_Character_041,Helvetica18_Character_042,Helvetica18_Character_043,Helvetica18_Character_044,Helvetica18_Character_045,Helvetica18_Character_046,Helvetica18_Character_047,Helvetica18_Character_048,Helvetica18_Character_049,Helvetica18_Character_050,Helvetica18_Character_051,Helvetica18_Character_052,Helvetica18_Character_053,Helvetica18_Character_054,Helvetica18_Character_055,Helvetica18_Character_056,Helvetica18_Character_057,Helvetica18_Character_058,Helvetica18_Character_059,Helvetica18_Character_060,Helvetica18_Character_061,Helvetica18_Character_062,Helvetica18_Character_063,Helvetica18_Character_064,Helvetica18_Character_065,Helvetica18_Character_066,Helvetica18_Character_067,Helvetica18_Character_068,Helvetica18_Character_069,Helvetica18_Character_070,Helvetica18_Character_071,Helvetica18_Character_072,Helvetica18_Character_073,Helvetica18_Character_074,Helvetica18_Character_075,Helvetica18_Character_076, + Helvetica18_Character_077,Helvetica18_Character_078,Helvetica18_Character_079,Helvetica18_Character_080,Helvetica18_Character_081,Helvetica18_Character_082,Helvetica18_Character_083,Helvetica18_Character_084,Helvetica18_Character_085,Helvetica18_Character_086,Helvetica18_Character_087,Helvetica18_Character_088,Helvetica18_Character_089,Helvetica18_Character_090,Helvetica18_Character_091,Helvetica18_Character_092,Helvetica18_Character_093,Helvetica18_Character_094,Helvetica18_Character_095,Helvetica18_Character_096,Helvetica18_Character_097,Helvetica18_Character_098,Helvetica18_Character_099,Helvetica18_Character_100,Helvetica18_Character_101,Helvetica18_Character_102,Helvetica18_Character_103,Helvetica18_Character_104,Helvetica18_Character_105,Helvetica18_Character_106,Helvetica18_Character_107,Helvetica18_Character_108,Helvetica18_Character_109,Helvetica18_Character_110,Helvetica18_Character_111,Helvetica18_Character_112,Helvetica18_Character_113,Helvetica18_Character_114,Helvetica18_Character_115, + Helvetica18_Character_116,Helvetica18_Character_117,Helvetica18_Character_118,Helvetica18_Character_119,Helvetica18_Character_120,Helvetica18_Character_121,Helvetica18_Character_122,Helvetica18_Character_123,Helvetica18_Character_124,Helvetica18_Character_125,Helvetica18_Character_126,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica18 = { "-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1", 93, 22, Helvetica18_Character_Map, -1.0f, 4.0f }; + +static const GLubyte TimesRoman10_Character_032[] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman10_Character_097[] = { 4, 0, 0, 0,224,160, 96, 32,192, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman10_Character_098[] = { 5, 0, 0, 0,224,144,144,144,224,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman10_Character_099[] = { 4, 0, 0, 0, 96,128,128,128, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman10_Character_100[] = { 5, 0, 0, 0,104,144,144,144,112, 16, 48, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman10_Character_101[] = { 4, 0, 0, 0, 96,128,192,160, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman10_Character_102[] = { 4, 0, 0, 0,224, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman10_Character_103[] = { 5, 0,224,144, 96, 64,160,160,112, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman10_Character_104[] = { 5, 0, 0, 0,216,144,144,144,224,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman10_Character_105[] = { 3, 0, 0, 0, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman10_Character_106[] = { 3, 0,128, 64, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman10_Character_107[] = { 5, 0, 0, 0,152,144,224,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman10_Character_108[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman10_Character_109[] = { 8, 0, 0, 0,219,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman10_Character_110[] = { 5, 0, 0, 0,216,144,144,144,224, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman10_Character_111[] = { 5, 0, 0, 0, 96,144,144,144, 96, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman10_Character_112[] = { 5, 0,192,128,224,144,144,144,224, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman10_Character_113[] = { 5, 0, 56, 16,112,144,144,144,112, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman10_Character_114[] = { 4, 0, 0, 0,224, 64, 64, 96,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman10_Character_115[] = { 4, 0, 0, 0,224, 32, 96,128,224, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman10_Character_116[] = { 4, 0, 0, 0, 48, 64, 64, 64,224, 64, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman10_Character_117[] = { 5, 0, 0, 0,104,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman10_Character_118[] = { 5, 0, 0, 0, 32, 96, 80,144,216, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman10_Character_119[] = { 8, 0, 0, 0, 40,108, 84,146,219, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman10_Character_120[] = { 6, 0, 0, 0,216, 80, 32, 80,216, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman10_Character_121[] = { 5, 0, 64, 64, 32, 48, 80, 72,220, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman10_Character_122[] = { 5, 0, 0, 0,240,144, 64, 32,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman10_Character_065[] = { 8, 0, 0, 0,238, 68,124, 40, 40, 56, 16, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman10_Character_066[] = { 6, 0, 0, 0,240, 72, 72,112, 72, 72,240, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman10_Character_067[] = { 7, 0, 0, 0,120,196,128,128,128,196,124, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman10_Character_068[] = { 7, 0, 0, 0,248, 76, 68, 68, 68, 76,248, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman10_Character_069[] = { 6, 0, 0, 0,248, 72, 64,112, 64, 72,248, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman10_Character_070[] = { 6, 0, 0, 0,224, 64, 64,112, 64, 72,248, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman10_Character_071[] = { 7, 0, 0, 0,120,196,132,156,128,196,124, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman10_Character_072[] = { 8, 0, 0, 0,238, 68, 68,124, 68, 68,238, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman10_Character_073[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,224, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman10_Character_074[] = { 4, 0, 0, 0,192,160, 32, 32, 32, 32,112, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman10_Character_075[] = { 7, 0, 0, 0,236, 72, 80, 96, 80, 72,236, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman10_Character_076[] = { 6, 0, 0, 0,248, 72, 64, 64, 64, 64,224, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman10_Character_077[] = { 10, 0, 0, 0, 0, 0, 0,235,128, 73, 0, 85, 0, 85, 0, 99, 0, 99, 0,227,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman10_Character_078[] = { 8, 0, 0, 0,228, 76, 76, 84, 84,100,238, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman10_Character_079[] = { 7, 0, 0, 0,120,204,132,132,132,204,120, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman10_Character_080[] = { 6, 0, 0, 0,224, 64, 64,112, 72, 72,240, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman10_Character_081[] = { 7, 0, 12, 24,112,204,132,132,132,204,120, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman10_Character_082[] = { 7, 0, 0, 0,236, 72, 80,112, 72, 72,240, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman10_Character_083[] = { 5, 0, 0, 0,224,144, 16, 96,192,144,112, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman10_Character_084[] = { 6, 0, 0, 0,112, 32, 32, 32, 32,168,248, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman10_Character_085[] = { 8, 0, 0, 0, 56,108, 68, 68, 68, 68,238, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman10_Character_086[] = { 8, 0, 0, 0, 16, 16, 40, 40,108, 68,238, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman10_Character_087[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 85, 0,201,128,136,128,221,192, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman10_Character_088[] = { 8, 0, 0, 0,238, 68, 40, 16, 40, 68,238, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman10_Character_089[] = { 8, 0, 0, 0, 56, 16, 16, 40, 40, 68,238, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman10_Character_090[] = { 6, 0, 0, 0,248,136, 64, 32, 16,136,248, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman10_Character_048[] = { 5, 0, 0, 0, 96,144,144,144,144,144, 96, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman10_Character_049[] = { 5, 0, 0, 0,224, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman10_Character_050[] = { 5, 0, 0, 0,240, 64, 32, 32, 16,144, 96, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman10_Character_051[] = { 5, 0, 0, 0,224, 16, 16, 96, 16,144, 96, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman10_Character_052[] = { 5, 0, 0, 0, 16, 16,248,144, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman10_Character_053[] = { 5, 0, 0, 0,224,144, 16, 16,224, 64,112, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman10_Character_054[] = { 5, 0, 0, 0, 96,144,144,144,224, 64, 48, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman10_Character_055[] = { 5, 0, 0, 0, 64, 64, 64, 32, 32,144,240, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman10_Character_056[] = { 5, 0, 0, 0, 96,144,144, 96,144,144, 96, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman10_Character_057[] = { 5, 0, 0, 0,192, 32,112,144,144,144, 96, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman10_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman10_Character_064[] = { 9, 0, 0, 62, 0, 64, 0,146, 0,173, 0,165, 0,165, 0,157, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman10_Character_035[] = { 5, 0, 0, 0, 80, 80,248, 80,248, 80, 80, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman10_Character_036[] = { 5, 0, 0, 32,224,144, 16, 96,128,144,112, 32, 0, 0}; /* "$" */ +static const GLubyte TimesRoman10_Character_037[] = { 8, 0, 0, 0, 68, 42, 42, 86,168,164,126, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman10_Character_094[] = { 5, 0, 0, 0, 0, 0, 0, 0,160,160, 64, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman10_Character_038[] = { 8, 0, 0, 0,118,141,152,116,110, 80, 48, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman10_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman10_Character_040[] = { 4, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman10_Character_041[] = { 4, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman10_Character_045[] = { 7, 0, 0, 0, 0, 0,240, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman10_Character_095[] = { 5,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman10_Character_061[] = { 6, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman10_Character_091[] = { 3, 0,192,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman10_Character_123[] = { 4, 0, 32, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman10_Character_125[] = { 4, 0,128, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman10_Character_093[] = { 3, 0,192, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman10_Character_059[] = { 3, 0,128,128,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman10_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman10_Character_044[] = { 3, 0,128,128,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman10_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman10_Character_060[] = { 5, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman10_Character_062[] = { 5, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman10_Character_047[] = { 3, 0, 0, 0,128,128, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman10_Character_063[] = { 4, 0, 0, 0, 64, 0, 64, 64, 32,160,224, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman10_Character_092[] = { 3, 0, 0, 0, 32, 32, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 64, 64,192, 0, 0}; /* "'" */ +static const GLubyte TimesRoman10_Character_124[] = { 2,128,128,128,128,128,128,128,128,128,128,128,128,128}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman10_Character_Map[] = {TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_032,TimesRoman10_Character_033,TimesRoman10_Character_034,TimesRoman10_Character_035, +TimesRoman10_Character_036,TimesRoman10_Character_037,TimesRoman10_Character_038,TimesRoman10_Character_039,TimesRoman10_Character_040,TimesRoman10_Character_041,TimesRoman10_Character_042,TimesRoman10_Character_043,TimesRoman10_Character_044,TimesRoman10_Character_045,TimesRoman10_Character_046,TimesRoman10_Character_047,TimesRoman10_Character_048,TimesRoman10_Character_049,TimesRoman10_Character_050,TimesRoman10_Character_051,TimesRoman10_Character_052,TimesRoman10_Character_053,TimesRoman10_Character_054,TimesRoman10_Character_055,TimesRoman10_Character_056,TimesRoman10_Character_057,TimesRoman10_Character_058,TimesRoman10_Character_059,TimesRoman10_Character_060,TimesRoman10_Character_061,TimesRoman10_Character_062,TimesRoman10_Character_063,TimesRoman10_Character_064,TimesRoman10_Character_065,TimesRoman10_Character_066,TimesRoman10_Character_067,TimesRoman10_Character_068,TimesRoman10_Character_069,TimesRoman10_Character_070,TimesRoman10_Character_071,TimesRoman10_Character_072, +TimesRoman10_Character_073,TimesRoman10_Character_074,TimesRoman10_Character_075,TimesRoman10_Character_076,TimesRoman10_Character_077,TimesRoman10_Character_078,TimesRoman10_Character_079,TimesRoman10_Character_080,TimesRoman10_Character_081,TimesRoman10_Character_082,TimesRoman10_Character_083,TimesRoman10_Character_084,TimesRoman10_Character_085,TimesRoman10_Character_086,TimesRoman10_Character_087,TimesRoman10_Character_088,TimesRoman10_Character_089,TimesRoman10_Character_090,TimesRoman10_Character_091,TimesRoman10_Character_092,TimesRoman10_Character_093,TimesRoman10_Character_094,TimesRoman10_Character_095,TimesRoman10_Character_096,TimesRoman10_Character_097,TimesRoman10_Character_098,TimesRoman10_Character_099,TimesRoman10_Character_100,TimesRoman10_Character_101,TimesRoman10_Character_102,TimesRoman10_Character_103,TimesRoman10_Character_104,TimesRoman10_Character_105,TimesRoman10_Character_106,TimesRoman10_Character_107,TimesRoman10_Character_108,TimesRoman10_Character_109, +TimesRoman10_Character_110,TimesRoman10_Character_111,TimesRoman10_Character_112,TimesRoman10_Character_113,TimesRoman10_Character_114,TimesRoman10_Character_115,TimesRoman10_Character_116,TimesRoman10_Character_117,TimesRoman10_Character_118,TimesRoman10_Character_119,TimesRoman10_Character_120,TimesRoman10_Character_121,TimesRoman10_Character_122,TimesRoman10_Character_123,TimesRoman10_Character_124,TimesRoman10_Character_125,TimesRoman10_Character_126,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman10 = { "-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1", 93, 13, TimesRoman10_Character_Map, 0.0f, 3.0f }; + +static const GLubyte TimesRoman24_Character_032[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman24_Character_097[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,128,251, 0,199, 0,195, 0,195, 0, 99, 0, 59, 0, 15, 0, 3, 0, 99, 0,103, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman24_Character_098[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,110, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman24_Character_099[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 65,128, 99,128, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman24_Character_100[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,192,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 1,128, 1,128, 1,128, 1,128, 3,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman24_Character_101[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,255,128,193,128, 65,128, 99, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman24_Character_102[] = { 7, 0, 0, 0, 0, 0, 0,120, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,254, 48, 48, 48, 22, 14, 0, 0, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman24_Character_103[] = { 12, 0, 0, 63, 0,241,192,192, 96,192, 32, 96, 96, 63,192,127, 0, 96, 0, 48, 0, 62, 0, 51, 0, 97,128, 97,128, 97,128, 97,128, 51, 0, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman24_Character_104[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,103, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman24_Character_105[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 96, 96, 0, 0, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman24_Character_106[] = { 6, 0,192,224, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,112, 0, 0, 0, 48, 48, 0, 0, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman24_Character_107[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,243,224, 97,192, 99,128,103, 0,110, 0,108, 0,120, 0,104, 0,100, 0,102, 0, 99, 0,103,192, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman24_Character_108[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman24_Character_109[] = { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,227,192, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128,113,227,128,111,159, 0,231, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman24_Character_110[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman24_Character_111[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128, 97,128,192,192,192,192,192,192,192,192,192,192,192,192, 97,128,115,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman24_Character_112[] = { 12, 0, 0,240, 0, 96, 0, 96, 0, 96, 0, 96, 0,110, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman24_Character_113[] = { 12, 0, 0, 3,192, 1,128, 1,128, 1,128, 1,128, 29,128,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman24_Character_114[] = { 8, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96,118,110,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman24_Character_115[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 0,198, 0,131, 0, 3, 0, 7, 0, 30, 0,124, 0,112, 0,224, 0,194, 0,102, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman24_Character_116[] = { 7, 0, 0, 0, 0, 0, 0, 28, 50, 48, 48, 48, 48, 48, 48, 48, 48, 48,254,112, 48, 16, 0, 0, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman24_Character_117[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,224, 62,192,113,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,225,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman24_Character_118[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman24_Character_119[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 16, 0, 14, 56, 0, 14, 56, 0, 26, 40, 0, 26,100, 0, 25,100, 0, 49,100, 0, 48,194, 0, 48,194, 0, 96,194, 0, 96,195, 0,241,231,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman24_Character_120[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 33,128, 51,128, 27, 0, 14, 0, 12, 0, 26, 0, 57, 0, 49,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman24_Character_121[] = { 11, 0, 0,224, 0,240, 0, 24, 0, 8, 0, 12, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman24_Character_122[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,195, 0, 97, 0,112, 0, 48, 0, 56, 0, 24, 0, 28, 0, 14, 0,134, 0,195, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman24_Character_065[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 16, 6, 0, 16, 12, 0, 24, 12, 0, 8, 12, 0, 15,248, 0, 12, 24, 0, 4, 24, 0, 4, 48, 0, 6, 48, 0, 2, 48, 0, 2, 96, 0, 1, 96, 0, 1,192, 0, 1,192, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman24_Character_066[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,224, 48,120, 48, 24, 48, 12, 48, 12, 48, 12, 48, 24, 48, 56, 63,224, 48, 64, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman24_Character_067[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 30, 56, 56, 8, 96, 4, 96, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 96, 4, 96, 4, 56, 12, 28, 60, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman24_Character_068[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,192, 0, 48,112, 0, 48, 56, 0, 48, 12, 0, 48, 12, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 12, 0, 48, 12, 0, 48, 56, 0, 48,112, 0,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman24_Character_069[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 64, 48, 64, 63,192, 48, 64, 48, 64, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman24_Character_070[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 32, 48, 32, 63,224, 48, 32, 48, 32, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman24_Character_071[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 30, 56, 0, 56, 28, 0, 96, 12, 0, 96, 12, 0,192, 12, 0,192, 12, 0,192, 63, 0,192, 0, 0,192, 0, 0,192, 0, 0,192, 0, 0, 96, 4, 0, 96, 4, 0, 56, 12, 0, 28, 60, 0, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman24_Character_072[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 63,254, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman24_Character_073[] = { 8, 0, 0, 0, 0, 0, 0,252, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,252, 0, 0, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman24_Character_074[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,204, 0,198, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman24_Character_075[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31, 0, 48, 14, 0, 48, 28, 0, 48, 56, 0, 48,112, 0, 48,224, 0, 49,192, 0, 51,128, 0, 63, 0, 0, 62, 0, 0, 51, 0, 0, 49,128, 0, 48,192, 0, 48, 96, 0, 48, 48, 0, 48, 24, 0,252,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman24_Character_076[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman24_Character_077[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 33,248, 32, 96, 96, 32, 96, 96, 32,208, 96, 32,208, 96, 33,136, 96, 33,136, 96, 35, 8, 96, 35, 4, 96, 38, 4, 96, 38, 2, 96, 44, 2, 96, 44, 2, 96, 56, 1, 96, 56, 1, 96, 48, 0,224,240, 0,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman24_Character_078[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 12, 0, 32, 28, 0, 32, 28, 0, 32, 52, 0, 32,100, 0, 32,100, 0, 32,196, 0, 33,132, 0, 33,132, 0, 35, 4, 0, 38, 4, 0, 38, 4, 0, 44, 4, 0, 56, 4, 0, 56, 4, 0, 48, 4, 0,240, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman24_Character_079[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman24_Character_080[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 63,192, 48,112, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman24_Character_081[] = { 18, 0, 0, 0, 0, 15, 0, 0, 56, 0, 0,112, 0, 0,224, 0, 1,192, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman24_Character_082[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 30, 48, 28, 48, 56, 48,112, 48, 96, 48,192, 49,192, 51,128, 63,192, 48,112, 48, 48, 48, 56, 48, 24, 48, 56, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman24_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,158, 0,241,128,192,192,128, 96,128, 96, 0, 96, 0,224, 3,192, 15,128, 30, 0,120, 0,224, 0,192, 64,192, 64,192,192, 99,192, 30, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman24_Character_084[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,192, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0,131, 4,131, 4,195, 12,255,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman24_Character_085[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 48, 0, 24, 8, 0, 48, 8, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0,252, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman24_Character_086[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,128, 0, 1,128, 0, 1,128, 0, 3,192, 0, 3, 64, 0, 3, 96, 0, 6, 32, 0, 6, 32, 0, 6, 48, 0, 12, 16, 0, 12, 24, 0, 24, 8, 0, 24, 8, 0, 24, 12, 0, 48, 4, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman24_Character_087[] = { 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,131, 0, 1,131, 0, 1,131,128, 3,135,128, 3, 70,128, 3, 70,192, 6, 70, 64, 6, 76, 64, 6, 76, 96, 12, 44, 96, 12, 44, 32, 24, 44, 32, 24, 24, 48, 24, 24, 16, 48, 24, 16, 48, 24, 24,252,126,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman24_Character_088[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 15,192, 48, 3,128, 24, 7, 0, 8, 14, 0, 4, 12, 0, 6, 24, 0, 2, 56, 0, 1,112, 0, 0,224, 0, 0,192, 0, 1,192, 0, 3,160, 0, 3, 16, 0, 6, 8, 0, 14, 12, 0, 28, 6, 0,126, 15,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman24_Character_089[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 3,192, 3, 64, 6, 96, 6, 32, 12, 48, 28, 16, 24, 24, 56, 8, 48, 12,252, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman24_Character_090[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248,224, 24,112, 8, 48, 8, 56, 0, 24, 0, 28, 0, 14, 0, 6, 0, 7, 0, 3, 0, 3,128, 1,192,128,192,128,224,192,112,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman24_Character_048[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 51, 0, 97,128, 97,128,225,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman24_Character_049[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,120, 0, 24, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman24_Character_050[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192, 96, 64, 48, 0, 24, 0, 12, 0, 4, 0, 6, 0, 3, 0, 3, 0, 1,128, 1,128,129,128,129,128, 67,128,127, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman24_Character_051[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,230, 0,195, 0, 1, 0, 1,128, 1,128, 1,128, 3,128, 7, 0, 30, 0, 12, 0, 6, 0,131, 0,131, 0, 71, 0,126, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman24_Character_052[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 3, 0,255,192,255,192,195, 0, 67, 0, 99, 0, 35, 0, 51, 0, 19, 0, 27, 0, 11, 0, 7, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman24_Character_053[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0,227,128,193,128, 0,192, 0,192, 0,192, 0,192, 1,192, 3,128, 15,128,126, 0,120, 0, 96, 0, 32, 0, 32, 0, 31,128, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman24_Character_054[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,123,128, 97,128,224,192,192,192,192,192,192,192,192,192,193,128,243,128,238, 0, 96, 0,112, 0, 48, 0, 24, 0, 14, 0, 3,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman24_Character_055[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 12, 0, 12, 0, 12, 0, 4, 0, 6, 0, 6, 0, 2, 0, 3, 0, 3, 0, 1, 0, 1,128,129,128,192,192,255,192,127,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman24_Character_056[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128,225,128,192,192,192,192,192,192, 65,192, 97,128, 55, 0, 30, 0, 30, 0, 51, 0, 97,128, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman24_Character_057[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 0, 28, 0, 6, 0, 3, 0, 3,128, 1,128, 29,128,115,192, 97,192,192,192,192,192,192,192,192,192,193,192, 97,128,119,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman24_Character_096[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,224,128,192, 96, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman24_Character_126[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131,128,199,192,124, 96, 56, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman24_Character_033[] = { 8, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman24_Character_064[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,240, 0, 14, 12, 0, 24, 0, 0, 48, 0, 0, 97,222, 0, 99,123, 0,198, 57,128,198, 24,128,198, 24,192,198, 24, 64,198, 12, 64,195, 12, 64,195,140, 64,225,252, 64, 96,236,192,112, 0,128, 56, 1,128, 28, 3, 0, 15, 14, 0, 3,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman24_Character_035[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 34, 0, 34, 0,255,192,255,192, 17, 0, 17, 0, 17, 0,127,224,127,224, 8,128, 8,128, 8,128, 8,128, 8,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman24_Character_036[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 63, 0,229,192,196,192,132, 96,132, 96, 4, 96, 4,224, 7,192, 7,128, 30, 0, 60, 0,116, 0,100, 0,100, 32,100, 96, 52,224, 31,128, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte TimesRoman24_Character_037[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 60, 0, 24,114, 0, 12, 97, 0, 4, 96,128, 6, 96,128, 3, 48,128, 1, 25,128, 1,143, 0,120,192, 0,228, 64, 0,194, 96, 0,193, 48, 0,193, 16, 0, 97, 24, 0, 51,252, 0, 30, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman24_Character_094[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128,193,128, 65, 0, 99, 0, 34, 0, 54, 0, 20, 0, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman24_Character_038[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 60, 0,127,126, 0,225,225, 0,192,192, 0,193,192, 0,193,160, 0, 99, 32, 0, 55, 16, 0, 30, 24, 0, 14, 62, 0, 15, 0, 0, 29,128, 0, 24,192, 0, 24, 64, 0, 24, 64, 0, 12,192, 0, 7,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman24_Character_042[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 28, 0,201,128,235,128, 28, 0,235,128,201,128, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman24_Character_040[] = { 8, 0, 4, 8, 16, 48, 32, 96, 96,192,192,192,192,192,192,192,192, 96, 96, 32, 48, 16, 8, 4, 0, 0, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman24_Character_041[] = { 8, 0,128, 64, 32, 48, 16, 24, 24, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 16, 48, 32, 64,128, 0, 0, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman24_Character_045[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman24_Character_095[] = { 13, 0, 0,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman24_Character_061[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman24_Character_043[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0,255,240,255,240, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman24_Character_091[] = { 8, 0, 0,248,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,248, 0, 0, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman24_Character_123[] = { 10, 0, 0, 7, 0, 12, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 16, 0, 48, 0, 32, 0,192, 0, 32, 0, 48, 0, 16, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 12, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman24_Character_125[] = { 10, 0, 0,224, 0, 48, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 8, 0, 12, 0, 4, 0, 3, 0, 4, 0, 12, 0, 8, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 48, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman24_Character_093[] = { 8, 0, 0,248, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,248, 0, 0, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman24_Character_059[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman24_Character_058[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman24_Character_044[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman24_Character_046[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman24_Character_060[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman24_Character_062[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman24_Character_047[] = { 7, 0, 0, 0,192,192,192, 96, 96, 32, 48, 48, 16, 24, 24, 8, 12, 12, 4, 6, 6, 3, 3, 3, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman24_Character_063[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 24, 0, 24, 0, 12, 0, 14, 0, 7, 0,195, 0,195, 0,131, 0,198, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman24_Character_092[] = { 7, 0, 0, 0, 0, 0, 0, 6, 6, 4, 12, 12, 8, 24, 24, 16, 48, 48, 32, 96, 96, 64,192,192, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman24_Character_034[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0,204, 0,204, 0,204, 0,204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman24_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte TimesRoman24_Character_124[] = { 6, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman24_Character_Map[] = {TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_032,TimesRoman24_Character_033,TimesRoman24_Character_034,TimesRoman24_Character_035, +TimesRoman24_Character_036,TimesRoman24_Character_037,TimesRoman24_Character_038,TimesRoman24_Character_039,TimesRoman24_Character_040,TimesRoman24_Character_041,TimesRoman24_Character_042,TimesRoman24_Character_043,TimesRoman24_Character_044,TimesRoman24_Character_045,TimesRoman24_Character_046,TimesRoman24_Character_047,TimesRoman24_Character_048,TimesRoman24_Character_049,TimesRoman24_Character_050,TimesRoman24_Character_051,TimesRoman24_Character_052,TimesRoman24_Character_053,TimesRoman24_Character_054,TimesRoman24_Character_055,TimesRoman24_Character_056,TimesRoman24_Character_057,TimesRoman24_Character_058,TimesRoman24_Character_059,TimesRoman24_Character_060,TimesRoman24_Character_061,TimesRoman24_Character_062,TimesRoman24_Character_063,TimesRoman24_Character_064,TimesRoman24_Character_065,TimesRoman24_Character_066,TimesRoman24_Character_067,TimesRoman24_Character_068,TimesRoman24_Character_069,TimesRoman24_Character_070,TimesRoman24_Character_071,TimesRoman24_Character_072, +TimesRoman24_Character_073,TimesRoman24_Character_074,TimesRoman24_Character_075,TimesRoman24_Character_076,TimesRoman24_Character_077,TimesRoman24_Character_078,TimesRoman24_Character_079,TimesRoman24_Character_080,TimesRoman24_Character_081,TimesRoman24_Character_082,TimesRoman24_Character_083,TimesRoman24_Character_084,TimesRoman24_Character_085,TimesRoman24_Character_086,TimesRoman24_Character_087,TimesRoman24_Character_088,TimesRoman24_Character_089,TimesRoman24_Character_090,TimesRoman24_Character_091,TimesRoman24_Character_092,TimesRoman24_Character_093,TimesRoman24_Character_094,TimesRoman24_Character_095,TimesRoman24_Character_096,TimesRoman24_Character_097,TimesRoman24_Character_098,TimesRoman24_Character_099,TimesRoman24_Character_100,TimesRoman24_Character_101,TimesRoman24_Character_102,TimesRoman24_Character_103,TimesRoman24_Character_104,TimesRoman24_Character_105,TimesRoman24_Character_106,TimesRoman24_Character_107,TimesRoman24_Character_108,TimesRoman24_Character_109, +TimesRoman24_Character_110,TimesRoman24_Character_111,TimesRoman24_Character_112,TimesRoman24_Character_113,TimesRoman24_Character_114,TimesRoman24_Character_115,TimesRoman24_Character_116,TimesRoman24_Character_117,TimesRoman24_Character_118,TimesRoman24_Character_119,TimesRoman24_Character_120,TimesRoman24_Character_121,TimesRoman24_Character_122,TimesRoman24_Character_123,TimesRoman24_Character_124,TimesRoman24_Character_125,TimesRoman24_Character_126,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman24 = { "-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1", 93, 28, TimesRoman24_Character_Map, -1.0f, 6.0f }; + + + +/* + * End of code from FreeGLUT. + */ + + +#define ARGS(X) (X.Characters, 1, X.Height, X.xorig, X.yorig) + +static fntBitmapFont fntFixed8x13 ARGS( fgFontFixed8x13 ); +static fntBitmapFont fntFixed9x15 ARGS( fgFontFixed9x15 ); +static fntBitmapFont fntHelvetica10 ARGS( fgFontHelvetica10 ); +static fntBitmapFont fntHelvetica12 ARGS( fgFontHelvetica12 ); +static fntBitmapFont fntHelvetica18 ARGS( fgFontHelvetica18 ); +static fntBitmapFont fntTimesRoman10 ARGS( fgFontTimesRoman10 ); +static fntBitmapFont fntTimesRoman24 ARGS( fgFontTimesRoman24 ); + + +fntBitmapFont *fntGetBitmapFont(int id) +{ + fntBitmapFont *fnt = NULL; + switch (id) { + case FNT_BITMAP_8_BY_13: fnt = &fntFixed8x13; break; + case FNT_BITMAP_9_BY_15: fnt = &fntFixed9x15; break; + case FNT_BITMAP_HELVETICA_10: fnt = &fntHelvetica10; break; + case FNT_BITMAP_HELVETICA_12: fnt = &fntHelvetica12; break; + case FNT_BITMAP_HELVETICA_18: fnt = &fntHelvetica18; break; + case FNT_BITMAP_TIMES_ROMAN_10: fnt = &fntTimesRoman10; break; + case FNT_BITMAP_TIMES_ROMAN_24: fnt = &fntTimesRoman24; break; + } + return fnt; +} diff --git a/src/fnt/fntLocal.h b/src/fnt/fntLocal.h new file mode 100644 index 0000000..6d48679 --- /dev/null +++ b/src/fnt/fntLocal.h @@ -0,0 +1,111 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntLocal.h 1727 2002-11-09 21:27:21Z ude $ +*/ + + +#include "fnt.h" + + +extern int _fntIsSwapped ; +extern FILE *_fntCurrImageFd ; + + +inline void _fnt_swab_short ( unsigned short *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + + +inline void _fnt_swab_int ( unsigned int *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + + +inline void _fnt_swab_int_array ( int *x, int leng ) +{ + if ( ! _fntIsSwapped ) + return ; + + for ( int i = 0 ; i < leng ; i++ ) + { + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; + x++ ; + } +} + + +inline unsigned char _fnt_readByte () +{ + unsigned char x ; + fread ( & x, sizeof(unsigned char), 1, _fntCurrImageFd ) ; + return x ; +} + +inline unsigned short _fnt_readShort () +{ + unsigned short x ; + fread ( & x, sizeof(unsigned short), 1, _fntCurrImageFd ) ; + _fnt_swab_short ( & x ) ; + return x ; +} + +inline unsigned int _fnt_readInt () +{ + unsigned int x ; + fread ( & x, sizeof(unsigned int), 1, _fntCurrImageFd ) ; + _fnt_swab_int ( & x ) ; + return x ; +} + + +#define FNT_BYTE_FORMAT 0 +#define FNT_BITMAP_FORMAT 1 + +struct TXF_Glyph +{ + unsigned short ch ; + unsigned char w ; + unsigned char h ; + signed char x_off ; + signed char y_off ; + signed char step ; + signed char unknown ; + short x ; + short y ; + + sgVec2 tx0 ; sgVec2 vx0 ; + sgVec2 tx1 ; sgVec2 vx1 ; + sgVec2 tx2 ; sgVec2 vx2 ; + sgVec2 tx3 ; sgVec2 vx3 ; +} ; + + diff --git a/src/fnt/fntTXF.cxx b/src/fnt/fntTXF.cxx new file mode 100644 index 0000000..e664c90 --- /dev/null +++ b/src/fnt/fntTXF.cxx @@ -0,0 +1,363 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntTXF.cxx 1735 2002-12-01 18:21:48Z sjbaker $ +*/ + + +#include "fntLocal.h" + +#ifdef UL_MACINTOSH +# include +#elif defined(UL_MAC_OSX) +# include +#elif defined(UL_WIN32) + /* Nothing */ +#else +# include +#endif + +static bool glIsValidContext ( void ) +{ +#if defined(CONSOLE) + return true ; +#elif defined(UL_WIN32) + return ( wglGetCurrentContext () != NULL ) ; +#elif defined(UL_MACINTOSH) + return ( aglGetCurrentContext () != NULL ) ; +#elif defined(UL_MAC_OSX) + return ( CGLGetCurrentContext () != NULL ) ; +#else + return ( glXGetCurrentContext () != NULL ) ; +#endif +} + +FILE *_fntCurrImageFd ; +int _fntIsSwapped = FALSE ; + +static void tex_make_mip_maps ( GLubyte *image, int xsize, + int ysize, int zsize ) +{ + GLubyte *texels [ 20 ] ; /* One element per level of MIPmap */ + + for ( int l = 0 ; l < 20 ; l++ ) + texels [ l ] = NULL ; + + texels [ 0 ] = image ; + + int lev ; + + for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 || + ( ysize >> (lev+1) ) != 0 ) ; lev++ ) + { + /* Suffix '1' is the higher level map, suffix '2' is the lower level. */ + + int l1 = lev ; + int l2 = lev+1 ; + int w1 = xsize >> l1 ; + int h1 = ysize >> l1 ; + int w2 = xsize >> l2 ; + int h2 = ysize >> l2 ; + + if ( w1 <= 0 ) w1 = 1 ; + if ( h1 <= 0 ) h1 = 1 ; + if ( w2 <= 0 ) w2 = 1 ; + if ( h2 <= 0 ) h2 = 1 ; + + texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ; + + for ( int x2 = 0 ; x2 < w2 ; x2++ ) + for ( int y2 = 0 ; y2 < h2 ; y2++ ) + for ( int c = 0 ; c < zsize ; c++ ) + { + int x1 = x2 + x2 ; + int x1_1 = ( x1 + 1 ) % w1 ; + int y1 = y2 + y2 ; + int y1_1 = ( y1 + 1 ) % h1 ; + + int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * zsize + c ] ; + int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * zsize + c ] ; + int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * zsize + c ] ; + int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ; + + texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] = + ( t1 + t2 + t3 + t4 ) / 4 ; + } + } + + texels [ lev+1 ] = NULL ; + + if ( ! ((xsize & (xsize-1))==0) || + ! ((ysize & (ysize-1))==0) ) + { + ulSetError ( UL_FATAL, + "TXFloader: TXF Map is not a power-of-two in size!" ) ; + } + + glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ; + + int map_level = 0 ; + +#ifdef PROXY_TEXTURES_ARE_NOT_BROKEN + int ww ; + + do + { + glTexImage2D ( GL_PROXY_TEXTURE_2D, + map_level, zsize, xsize, ysize, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, NULL ) ; + + glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ; + + if ( ww == 0 ) + { + delete [] texels [ 0 ] ; + xsize >>= 1 ; + ysize >>= 1 ; + + for ( int l = 0 ; texels [ l ] != NULL ; l++ ) + texels [ l ] = texels [ l+1 ] ; + + if ( xsize < 64 && ysize < 64 ) + { + ulSetError ( UL_FATAL, + "FNT: OpenGL will not accept a font texture?!?" ) ; + } + } + } while ( ww == 0 ) ; +#endif + + for ( int i = 0 ; texels [ i ] != NULL ; i++ ) + { + int w = xsize>>i ; + int h = ysize>>i ; + + if ( w <= 0 ) w = 1 ; + if ( h <= 0 ) h = 1 ; + + glTexImage2D ( GL_TEXTURE_2D, + map_level, zsize, w, h, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ; + map_level++ ; + delete [] texels [ i ] ; + } +} + + + +int fntTexFont::loadTXF ( const char *fname, GLenum mag, GLenum min ) +{ + if ( ! glIsValidContext () ) + { + ulSetError ( UL_FATAL, + "FNT font loader called without a valid OpenGL context."); + } + + FILE *fd ; + + if ( (fd = fopen ( fname, "rb" )) == NULL ) + { + ulSetError ( UL_WARNING, + "fntLoadTXF: Failed to open '%s' for reading.", fname ) ; + return FNT_FALSE ; + } + + _fntCurrImageFd = fd ; + + unsigned char magic [ 4 ] ; + + if ( (int)fread ( &magic, sizeof (unsigned int), 1, fd ) != 1 ) + { + ulSetError ( UL_WARNING, + "fntLoadTXF: '%s' an empty file!", fname ) ; + return FNT_FALSE ; + } + + if ( magic [ 0 ] != 0xFF || magic [ 1 ] != 't' || + magic [ 2 ] != 'x' || magic [ 3 ] != 'f' ) + { + ulSetError ( UL_WARNING, + "fntLoadTXF: '%s' is not a 'txf' font file.", fname ) ; + return FNT_FALSE ; + } + + _fntIsSwapped = FALSE ; + int endianness = _fnt_readInt () ; + + _fntIsSwapped = ( endianness != 0x12345678 ) ; + + int format = _fnt_readInt () ; + int tex_width = _fnt_readInt () ; + int tex_height = _fnt_readInt () ; + int max_height = _fnt_readInt () ; + _fnt_readInt () ; + int num_glyphs = _fnt_readInt () ; + + int w = tex_width ; + int h = tex_height ; + + float xstep = 0.5f / (float) w ; + float ystep = 0.5f / (float) h ; + + int i, j ; + + /* + Load the TXF_Glyph array + */ + + TXF_Glyph glyph ; + + for ( i = 0 ; i < num_glyphs ; i++ ) + { + glyph . ch = _fnt_readShort () ; + + glyph . w = _fnt_readByte () ; + glyph . h = _fnt_readByte () ; + glyph . x_off = _fnt_readByte () ; + glyph . y_off = _fnt_readByte () ; + glyph . step = _fnt_readByte () ; + glyph . unknown = _fnt_readByte () ; + glyph . x = _fnt_readShort () ; + glyph . y = _fnt_readShort () ; + + setGlyph ( (char) glyph.ch, + (float) glyph.step / (float) max_height, + (float) glyph.x / (float) w + xstep, + (float)( glyph.x + glyph.w ) / (float) w + xstep, + (float) glyph.y / (float) h + ystep, + (float)( glyph.y + glyph.h ) / (float) h + ystep, + (float) glyph.x_off / (float) max_height, + (float)( glyph.x_off + glyph.w ) / (float) max_height, + (float) glyph.y_off / (float) max_height, + (float)( glyph.y_off + glyph.h ) / (float) max_height ) ; + } + + exists [ ' ' ] = FALSE ; + + /* + Load the image part of the file + */ + + int ntexels = w * h ; + + unsigned char *teximage ; + unsigned char *texbitmap ; + + switch ( format ) + { + case FNT_BYTE_FORMAT: + { + unsigned char *orig = new unsigned char [ ntexels ] ; + + if ( (int)fread ( orig, 1, ntexels, fd ) != ntexels ) + { + ulSetError ( UL_WARNING, + "fntLoadTXF: Premature EOF in '%s'.", fname ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < ntexels ; i++ ) + { + teximage [ i*2 ] = orig [ i ] ; + teximage [ i*2 + 1 ] = orig [ i ] ; + } + + delete [] orig ; + } + break ; + + case FNT_BITMAP_FORMAT: + { + int stride = (w + 7) >> 3; + + texbitmap = new unsigned char [ stride * h ] ; + + if ( (int)fread ( texbitmap, 1, stride * h, fd ) != stride * h ) + { + delete [] texbitmap ; + ulSetError ( UL_WARNING, + "fntLoadTXF: Premature EOF in '%s'.", fname ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < 2 * ntexels ; i++ ) + teximage [ i ] = 0 ; + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7))) + { + teximage[(i * w + j) * 2 ] = 255; + teximage[(i * w + j) * 2 + 1] = 255; + } + + delete [] texbitmap ; + } + break ; + + default: + ulSetError ( UL_WARNING, + "fntLoadTXF: Unrecognised format type in '%s'.", fname ) ; + return FNT_FALSE ; + } + + fclose ( fd ) ; + + fixed_pitch = FALSE ; + +#ifdef GL_VERSION_1_1 + glGenTextures ( 1, & texture ) ; + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* This is only useful on some ancient SGI hardware */ + glGenTexturesEXT ( 1, & texture ) ; + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + + tex_make_mip_maps ( teximage, w, h, 2 ) ; + + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, 0 ) ; +#else + glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ; +#endif + + return FNT_TRUE ; +} + + diff --git a/src/js/Makefile.am b/src/js/Makefile.am new file mode 100644 index 0000000..df48950 --- /dev/null +++ b/src/js/Makefile.am @@ -0,0 +1,17 @@ +if BUILD_JS + +lib_LIBRARIES = libplibjs.a + +include_HEADERS = js.h + +libplibjs_a_SOURCES = js.cxx jsLinux.cxx jsLinuxOld.cxx jsMacOS.cxx \ + jsMacOSX.cxx jsWindows.cxx jsBSD.cxx \ + jsNone.cxx + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = js.dsp + + diff --git a/src/js/README b/src/js/README new file mode 100644 index 0000000..99c9634 --- /dev/null +++ b/src/js/README @@ -0,0 +1,30 @@ + +Joystick interfaces are horribly non-portable. + +Using conditional compilation on a line-by-line basis +became FAR too complex after more than a couple of OS's +were supported so now there is on standard interface header: + + js.h + +...one set of common routines: + + js.cxx + + +...and a bunch of OS-specific code: + + + jsLinux.cxx + jsLinuxOld.cxx + jsWindows.cxx + jsBSD.cxx + jsMacOS.cxx + +...for OS's (eg IRIX and SOLARIS) without standard joystick +functionality, we add: + + jsNone.cxx + +...which safely returns error status with no buttons or axes provided. + diff --git a/src/js/js.cxx b/src/js/js.cxx new file mode 100644 index 0000000..6324e9f --- /dev/null +++ b/src/js/js.cxx @@ -0,0 +1,86 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +#include "js.h" + +float jsJoystick::fudge_axis ( float value, int axis ) const +{ + if ( value < center[axis] ) + { + float xx = ( value - center[ axis ] ) / + ( center [ axis ] - min [ axis ] ) ; + + if ( xx < -saturate [ axis ] ) + return -1.0f ; + + if ( xx > -dead_band [ axis ] ) + return 0.0f ; + + xx = ( xx + dead_band [ axis ] ) / + ( saturate [ axis ] - dead_band [ axis ] ) ; + + return ( xx < -1.0f ) ? -1.0f : xx ; + } + else + { + float xx = ( value - center [ axis ] ) / + ( max [ axis ] - center [ axis ] ) ; + + if ( xx > saturate [ axis ] ) + return 1.0f ; + + if ( xx < dead_band [ axis ] ) + return 0.0f ; + + xx = ( xx - dead_band [ axis ] ) / + ( saturate [ axis ] - dead_band [ axis ] ) ; + + return ( xx > 1.0f ) ? 1.0f : xx ; + } +} + + +void jsJoystick::read ( int *buttons, float *axes ) +{ + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 0.0f ; + + return ; + } + + float raw_axes [ _JS_MAX_AXES ] ; + + rawRead ( buttons, raw_axes ) ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = fudge_axis ( raw_axes[i], i ) ; +} + + + diff --git a/src/js/js.dsp b/src/js/js.dsp new file mode 100644 index 0000000..0d69e87 --- /dev/null +++ b/src/js/js.dsp @@ -0,0 +1,126 @@ +# Microsoft Developer Studio Project File - Name="js" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=js - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "js.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "js.mak" CFG="js - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "js - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "js - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "js - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy js.h ..\..\js.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\js_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy js.h ..\..\js.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "js - Win32 Release" +# Name "js - Win32 Debug" +# Begin Source File + +SOURCE=.\js.cxx +# End Source File +# Begin Source File + +SOURCE=.\js.h +# End Source File +# Begin Source File + +SOURCE=.\jsBSD.cxx +# End Source File +# Begin Source File + +SOURCE=.\jsLinux.cxx +# End Source File +# Begin Source File + +SOURCE=.\jsMacOS.cxx +# End Source File +# Begin Source File + +SOURCE=.\jsMacOSX.cxx +# End Source File +# Begin Source File + +SOURCE=.\jsNone.cxx +# End Source File +# Begin Source File + +SOURCE=.\jsWindows.cxx +# End Source File +# End Target +# End Project diff --git a/src/js/js.h b/src/js/js.h new file mode 100644 index 0000000..2962b24 --- /dev/null +++ b/src/js/js.h @@ -0,0 +1,98 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: js.h 2067 2006-01-30 07:36:01Z bram $ +*/ + +#ifndef __INCLUDED_JS_H__ +#define __INCLUDED_JS_H__ 1 +#define JS_NEW + +#include "ul.h" + +#include +#include +#include // -dw- for memcpy + +#define _JS_MAX_AXES 16 +#define _JS_MAX_BUTTONS 32 +#define _JS_MAX_HATS 4 + +#define JS_TRUE 1 +#define JS_FALSE 0 + + +class jsJoystick +{ + int id ; +protected: + struct os_specific_s *os ; + friend struct os_specific_s ; + int error ; + char name [ 128 ] ; + int num_axes ; + int num_buttons ; + + float dead_band [ _JS_MAX_AXES ] ; + float saturate [ _JS_MAX_AXES ] ; + float center [ _JS_MAX_AXES ] ; + float max [ _JS_MAX_AXES ] ; + float min [ _JS_MAX_AXES ] ; + + void open () ; + void close () ; + + float fudge_axis ( float value, int axis ) const ; + +public: + + jsJoystick ( int ident = 0 ) ; + ~jsJoystick () { close () ; } + + const char* getName () const { return name ; } + int getNumAxes () const { return num_axes ; } + int getNumButtons () const { return num_buttons; } + int notWorking () const { return error ; } + void setError () { error = JS_TRUE ; } + + float getDeadBand ( int axis ) const { return dead_band [ axis ] ; } + void setDeadBand ( int axis, float db ) { dead_band [ axis ] = db ; } + + float getSaturation ( int axis ) const { return saturate [ axis ] ; } + void setSaturation ( int axis, float st ) { saturate [ axis ] = st ; } + + void setMinRange ( float *axes ) { memcpy ( min , axes, num_axes * sizeof(float) ) ; } + void setMaxRange ( float *axes ) { memcpy ( max , axes, num_axes * sizeof(float) ) ; } + void setCenter ( float *axes ) { memcpy ( center, axes, num_axes * sizeof(float) ) ; } + + void getMinRange ( float *axes ) const { memcpy ( axes, min , num_axes * sizeof(float) ) ; } + void getMaxRange ( float *axes ) const { memcpy ( axes, max , num_axes * sizeof(float) ) ; } + void getCenter ( float *axes ) const { memcpy ( axes, center, num_axes * sizeof(float) ) ; } + + void read ( int *buttons, float *axes ) ; + void rawRead ( int *buttons, float *axes ) ; + // bool SetForceFeedBack ( int axe, float force ); +} ; + +extern void jsInit () ; + +#endif + + diff --git a/src/js/jsBSD.cxx b/src/js/jsBSD.cxx new file mode 100644 index 0000000..5c510c4 --- /dev/null +++ b/src/js/jsBSD.cxx @@ -0,0 +1,522 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsBSD.cxx 2133 2008-07-18 14:32:22Z fayjf $ +*/ + +/* + * Inspired by the X-Mame USB HID joystick driver for NetBSD and + * FreeBSD by Krister Walfridsson . + * Incorporates the original analog joystick driver for BSD by + * Stephen Montgomery-Smith , with + * NetBSD mods courtesy of Rene Hexel. + * + * Bert Driehuis + * + * Notes: + * Hats are mapped to two axes for now. A cleaner implementation requires + * an API extension, and to be useful for my devious purposes, FlightGear + * would need to understand that. + */ + +#include "js.h" + +#if defined (UL_BSD) + +#if defined(__NetBSD__) || defined(__FreeBSD__) +#define HAVE_USB_JS 1 +#endif + +#include +#include +#include +#if defined(__FreeBSD__) +# include +#else +# include // For analog joysticks +#endif +#ifdef HAVE_USB_JS +#if defined(__NetBSD__) +#ifdef HAVE_USBHID_H +#include +#else +#include +#endif +#elif defined(__FreeBSD__) +extern "C" { +# if __FreeBSD_version < 500000 +# include +# else +# define HAVE_USBHID_H 1 +# include +# endif +} +#endif +#include +#include + +/* Compatibility with older usb.h revisions */ +#if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES) +#define USB_MAX_DEVNAMES MAXDEVNAMES +#endif +#endif + +static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 }; +static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 }; +struct os_specific_s { + char fname [128 ]; + int fd; + int is_analog; + // The following structure members are specific to analog joysticks + struct joystick ajs; +#ifdef HAVE_USB_JS + // The following structure members are specific to USB joysticks + struct hid_item *hids; + int hid_dlen; + int hid_offset; + char *hid_data_buf; + int axes_usage [ _JS_MAX_AXES ] ; +#endif + // We keep button and axes state ourselves, as they might not be updated + // on every read of a USB device + int cache_buttons ; + float cache_axes [ _JS_MAX_AXES ] ; + float axes_minimum [ _JS_MAX_AXES ] ; + float axes_maximum [ _JS_MAX_AXES ] ; +}; + +// Idents lower than USB_IDENT_OFFSET are for analog joysticks. +#define USB_IDENT_OFFSET 2 + +#define USBDEV "/dev/usb" +#define UHIDDEV "/dev/uhid" +#define AJSDEV "/dev/joy" + +#ifdef HAVE_USB_JS +/* + * findusbdev (and its helper, walkusbdev) try to locate the full name + * of a USB device. If /dev/usbN isn't readable, we punt and return the + * uhidN device name. We warn the user of this situation once. + */ +static char * +walkusbdev(int f, char *dev, char *out, int outlen) +{ + struct usb_device_info di; + int i, a; + char *cp; + + for (a = 1; a < USB_MAX_DEVICES; a++) { + di.udi_addr = a; + if (ioctl(f, USB_DEVICEINFO, &di) != 0) + return NULL; + for (i = 0; i < USB_MAX_DEVNAMES; i++) + if (di.udi_devnames[i][0] && + strcmp(di.udi_devnames[i], dev) == 0) { + cp = new char[strlen(di.udi_vendor) + strlen(di.udi_product) + 2]; + strcpy(cp, di.udi_vendor); + strcat(cp, " "); + strcat(cp, di.udi_product); + strncpy(out, cp, outlen - 1); + out[outlen - 1] = 0; + delete cp; + return out; + } + } + return NULL; +} + +static int +findusbdev(char *name, char *out, int outlen) +{ + int i, f; + char buf[50]; + char *cp; + static int protection_warned = 0; + + for (i = 0; i < 16; i++) { + sprintf(buf, "%s%d", USBDEV, i); + f = open(buf, O_RDONLY); + if (f >= 0) { + cp = walkusbdev(f, name, out, outlen); + close(f); + if (cp) + return 1; + } else if (errno == EACCES) { + if (!protection_warned) { + fprintf(stderr, "Can't open %s for read!\n", + buf); + protection_warned = 1; + } + } + } + return 0; +} + +static int joy_initialize_hid(struct os_specific_s *os, + int *num_axes, int *num_buttons) +{ + int size, is_joystick; +#ifdef HAVE_USBHID_H + int report_id = 0; +#endif + struct hid_data *d; + struct hid_item h; + report_desc_t rd; + + if ((rd = hid_get_report_desc(os->fd)) == 0) + { + fprintf(stderr, "error: %s: %s", os->fname, strerror(errno)); + return FALSE; + } + + os->hids = NULL; + +#ifdef HAVE_USBHID_H + if (ioctl(os->fd, USB_GET_REPORT_ID, &report_id) < 0) + { + fprintf(stderr, "error: %s: %s", os->fname, strerror(errno)); + return FALSE; + } + + size = hid_report_size(rd, hid_input, report_id); +#else + size = hid_report_size(rd, 0, hid_input); +#endif + os->hid_data_buf = new char[size]; + os->hid_dlen = size; + + is_joystick = 0; +#ifdef HAVE_USBHID_H + d = hid_start_parse(rd, 1 << hid_input, report_id); +#else + d = hid_start_parse(rd, 1 << hid_input); +#endif + while (hid_get_item(d, &h)) + { + int usage, page, interesting_hid; + + page = HID_PAGE(h.usage); + usage = HID_USAGE(h.usage); + + /* This test is somewhat too simplistic, but this is how MicroSoft + * does, so I guess it works for all joysticks/game pads. */ + is_joystick = is_joystick || + (h.kind == hid_collection && + page == HUP_GENERIC_DESKTOP && + (usage == HUG_JOYSTICK || usage == HUG_GAME_PAD)); + + if (h.kind != hid_input) + continue; + + if (!is_joystick) + continue; + + interesting_hid = TRUE; + if (page == HUP_GENERIC_DESKTOP) + { + switch(usage) { + case HUG_X: + case HUG_RX: + case HUG_Y: + case HUG_RY: + case HUG_Z: + case HUG_RZ: + case HUG_SLIDER: + if (*num_axes < _JS_MAX_AXES) + { + os->axes_usage[*num_axes] = usage; + os->axes_minimum[*num_axes] = h.logical_minimum; + os->axes_maximum[*num_axes] = h.logical_maximum; + (*num_axes)++; + } + break; + case HUG_HAT_SWITCH: + if (*num_axes + 1 < _JS_MAX_AXES) // Allocate two axes for a hat + { + os->axes_usage[*num_axes] = usage; + (*num_axes)++; + os->axes_usage[*num_axes] = usage; + (*num_axes)++; + } + break; + default: + interesting_hid = FALSE; + } + } + else if (page == HUP_BUTTON) + { + interesting_hid = (usage > 0) && (usage <= _JS_MAX_BUTTONS); + + if (interesting_hid && usage - 1 > *num_buttons) + { + *num_buttons = usage - 1; + } + } + + if (interesting_hid) + { + h.next = os->hids; + os->hids = new struct hid_item; + *os->hids = h; + } + } + hid_end_parse(d); + + return (os->hids != NULL); +} +#endif + +void jsJoystick::open () +{ + char *cp; + + name [0] = '\0' ; + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + os->cache_axes [ i ] = 0.0f ; + + os->cache_buttons = 0 ; + + os->fd = ::open ( os->fname, O_RDONLY | O_NONBLOCK) ; + + if (os->fd < 0 && errno == EACCES) + fprintf(stderr, "%s exists but is not readable by you\n", os->fname); + + error = ( os->fd < 0 ) ; + + if ( error ) + return ; + + num_axes = 0; + num_buttons = 0; + if ( os->is_analog ) + { + num_axes = 2 ; + num_buttons = 32 ; + FILE *joyfile ; + char joyfname [ 1024 ] ; + int noargs, in_no_axes ; + + float axes [ _JS_MAX_AXES ] ; + int buttons [ _JS_MAX_AXES ] ; + + rawRead ( buttons, axes ) ; + error = axes[0] < -1000000000.0f && axes[1] < -1000000000.0f ; + if ( error ) + return ; + + sprintf( joyfname, "%s/.joy%drc", ::getenv ( "HOME" ), id ) ; + + joyfile = fopen ( joyfname, "r" ) ; + error = ( joyfile == NULL ) ; + if ( error ) + { + ulSetError ( UL_WARNING, "unable to open calibration file %s (%s), joystick %i disabled (you can generate the calibration file with the plib-jscal utility)", + joyfname, strerror ( errno ), id + 1 ); + return ; + } + + noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes, + &min [ 0 ], ¢er [ 0 ], &max [ 0 ], + &min [ 1 ], ¢er [ 1 ], &max [ 1 ] ) ; + error = noargs != 7 || in_no_axes != _JS_MAX_AXES ; + fclose ( joyfile ) ; + if ( error ) + return ; + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + { + dead_band [ i ] = 0.0f ; + saturate [ i ] = 1.0f ; + } + + return; // End of analog code + } + +#ifdef HAVE_USB_JS + if ( !joy_initialize_hid(os, &num_axes, &num_buttons ) ) + { + ::close(os->fd); + error = 1; + return; + } + + cp = strrchr(os->fname, '/'); + if (cp) { + if (findusbdev(&cp[1], name, sizeof(name)) == 0) + strcpy(name, &cp[1]); + } + + if ( num_axes > _JS_MAX_AXES ) + num_axes = _JS_MAX_AXES ; + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + { + if ( os->axes_usage [ i ] == HUG_HAT_SWITCH ) + { + max [ i ] = 1.0f ; + center [ i ] = 0.0f ; + min [ i ] = -1.0f ; + } + else + { + max [ i ] = os->axes_maximum [ i ]; + min [ i ] = os->axes_minimum [ i ]; + center [ i ] = (max [ i ] + min [ i ]) / 2; + } + dead_band [ i ] = 0.0f ; + saturate [ i ] = 1.0f ; + } +#endif +} + + + +void jsJoystick::close () +{ + if (os) { + if ( ! error ) + ::close ( os->fd ) ; +#ifdef HAVE_USB_JS + if (os->hids) + delete os->hids; + if (os->hid_data_buf) + delete os->hid_data_buf; +#endif + delete os; + } +} + + +jsJoystick::jsJoystick ( int ident ) +{ + id = ident ; + error = 0; + + os = new struct os_specific_s; + memset(os, 0, sizeof(struct os_specific_s)); + if (ident < USB_IDENT_OFFSET) + os->is_analog = 1; + if (os->is_analog) + sprintf(os->fname, "%s%d", AJSDEV, ident); + else + sprintf(os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET); + open () ; +} + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + int len, usage, page, d; + struct hid_item *h; + + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 1500.0f ; + + return ; + } + + if ( os->is_analog ) + { + int status = ::read ( os->fd, &os->ajs, sizeof(os->ajs) ); + if ( status != sizeof(os->ajs) ) { + perror ( os->fname ) ; + setError () ; + return ; + } + if ( buttons != NULL ) + *buttons = ( os->ajs.b1 ? 1 : 0 ) | ( os->ajs.b2 ? 2 : 0 ) ; + + if ( axes != NULL ) + { + if ( os->ajs.x >= -1000000000 ) + os->cache_axes[0] = os->ajs.x; + if ( os->ajs.y >= -1000000000 ) + os->cache_axes[1] = os->ajs.y; + + axes[0] = os->cache_axes[0]; + axes[1] = os->cache_axes[1]; + } + + return; + } + +#ifdef HAVE_USB_JS + while ((len = ::read(os->fd, os->hid_data_buf, os->hid_dlen)) == os->hid_dlen) + { + for (h = os->hids; h; h = h->next) + { + d = hid_get_data(os->hid_data_buf, h); + + page = HID_PAGE(h->usage); + usage = HID_USAGE(h->usage); + + if (page == HUP_GENERIC_DESKTOP) + { + for (int i = 0; i < num_axes; i++) + if (os->axes_usage[i] == usage) + { + if (usage == HUG_HAT_SWITCH) + { + if (d < 0 || d > 8) + d = 0; // safety + os->cache_axes[i] = (float)hatmap_x[d]; + os->cache_axes[i + 1] = (float)hatmap_y[d]; + } + else + { + os->cache_axes[i] = (float)d; + } + break; + } + } + else if (page == HUP_BUTTON) + { + if (usage > 0 && usage < _JS_MAX_BUTTONS + 1) + { + if (d) + os->cache_buttons |= (1 << usage - 1) ; + else + os->cache_buttons &= ~(1 << usage - 1) ; + } + } + } + } + if (len < 0 && errno != EAGAIN) + { + perror( os->fname ) ; + setError () ; + error = 1; + } + if ( buttons != NULL ) *buttons = os->cache_buttons ; + if ( axes != NULL ) + memcpy ( axes, os->cache_axes, sizeof(float) * num_axes ) ; +#endif +} + +void jsInit () {} + +#endif diff --git a/src/js/jsLinux.cxx b/src/js/jsLinux.cxx new file mode 100644 index 0000000..650f772 --- /dev/null +++ b/src/js/jsLinux.cxx @@ -0,0 +1,201 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsLinux.cxx 2017 2005-02-21 07:37:25Z bram $ +*/ + +#include "js.h" + +#if defined (UL_LINUX) + +#include + +#if defined(JS_VERSION) && JS_VERSION >= 0x010000 + +#include +#include +#include + +struct os_specific_s { + js_event js ; + int tmp_buttons ; + float tmp_axes [ _JS_MAX_AXES ] ; + char fname [ 128 ] ; + int fd ; +}; + +void jsInit () {} + +void jsJoystick::open () +{ + name [0] = '\0' ; + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + os->tmp_axes [ i ] = 0.0f ; + + os->tmp_buttons = 0 ; + + os->fd = ::open ( os->fname, O_RDONLY ) ; + + error = ( os->fd < 0 ) ; + + if ( error ) + return ; + + /* + Set the correct number of axes for the linux driver + */ + + /* Melchior Franz's fixes for big-endian Linuxes since writing + * to the upper byte of an uninitialized word doesn't work. + * 9 April 2003 + */ + unsigned char u ; + ioctl ( os->fd, JSIOCGAXES , &u ) ; + num_axes = u ; + ioctl ( os->fd, JSIOCGBUTTONS, &u ) ; + num_buttons = u ; + ioctl ( os->fd, JSIOCGNAME ( sizeof(name) ), name ) ; + fcntl ( os->fd, F_SETFL , O_NONBLOCK ) ; + + int all_axes = num_axes; + if ( num_axes > _JS_MAX_AXES ) + num_axes = _JS_MAX_AXES ; + + // Remove any deadband value already done in the kernel. + // Since we have our own deadband management this is save to do so. + struct js_corr* corr = new js_corr[ all_axes ] ; + ioctl ( os->fd, JSIOCGCORR, corr ); + for ( int i = 0; i < num_axes ; ++i ) { + if ( corr[ i ] . type == JS_CORR_BROKEN ) { + int nodeadband = ( corr[ i ] . coef[ 0 ] + corr[ i ] . coef[ 1 ] ) / 2 ; + corr[ i ] . coef[ 0 ] = nodeadband ; + corr[ i ] . coef[ 1 ] = nodeadband ; + } + } + ioctl ( os->fd, JSIOCSCORR, corr ); + delete [] corr; + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + { + max [ i ] = 32767.0f ; + center [ i ] = 0.0f ; + min [ i ] = -32767.0f ; + dead_band [ i ] = 0.0f ; + saturate [ i ] = 1.0f ; + } +} + + + +void jsJoystick::close () +{ + if ( ! error ) + ::close ( os->fd ) ; + delete os; +} + + +jsJoystick::jsJoystick ( int ident ) +{ + id = ident ; + os = new struct os_specific_s; + + sprintf ( os->fname, "/dev/input/js%d", ident ) ; + + if ( access ( os->fname, F_OK ) != 0 ) + sprintf ( os->fname, "/dev/js%d", ident ) ; + + open () ; +} + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 1500.0f ; + + return ; + } + + while (1) + { + int status = ::read ( os->fd, &(os->js), sizeof(js_event) ) ; + + if ( status != sizeof(js_event) ) + { + /* use the old values */ + + if ( buttons != NULL ) *buttons = os->tmp_buttons ; + if ( axes != NULL ) + memcpy ( axes, os->tmp_axes, sizeof(float) * num_axes ) ; + + if ( errno == EAGAIN ) + return ; + + perror( os->fname ) ; + setError () ; + return ; + } + + switch ( os->js.type & ~JS_EVENT_INIT ) + { + case JS_EVENT_BUTTON : + if ( os->js.value == 0 ) /* clear the flag */ + os->tmp_buttons &= ~(1 << os->js.number) ; + else + os->tmp_buttons |= (1 << os->js.number) ; + break ; + + case JS_EVENT_AXIS: + if ( os->js.number < num_axes ) + { + os->tmp_axes [ os->js.number ] = (float) os->js.value ; + + if ( axes ) + memcpy ( axes, os->tmp_axes, sizeof(float) * num_axes ) ; + } + break ; + + default: + ulSetError ( UL_WARNING, "PLIB_JS: Unrecognised /dev/js return!?!" ) ; + + /* use the old values */ + + if ( buttons != NULL ) *buttons = os->tmp_buttons ; + if ( axes != NULL ) + memcpy ( axes, os->tmp_axes, sizeof(float) * num_axes ) ; + + return ; + } + + if ( buttons != NULL ) + *buttons = os->tmp_buttons ; + } +} + +#endif +#endif diff --git a/src/js/jsLinuxOld.cxx b/src/js/jsLinuxOld.cxx new file mode 100644 index 0000000..961dd43 --- /dev/null +++ b/src/js/jsLinuxOld.cxx @@ -0,0 +1,146 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsLinuxOld.cxx 2039 2005-07-16 17:27:51Z fayjf $ +*/ + +#include "js.h" + +#if defined (UL_LINUX) + +#include + +#if !defined(JS_VERSION) || JS_VERSION < 0x010000 + +#include +#include + +struct os_specific_s { + JS_DATA_TYPE js ; + char fname [ 128 ] ; + int fd ; +}; + +void jsJoystick::open () +{ + name [0] = '\0' ; + + num_axes = 2 ; /* Default for older Linux systems. */ + num_buttons = 32 ; + + os->fd = ::open ( os->fname, O_RDONLY ) ; + + error = ( os->fd < 0 ) ; + + if ( error ) + return ; + + /* + The Linux driver seems to return 512 for all axes + when no stick is present - but there is a chance + that could happen by accident - so it's gotta happen + on both axes for at least 100 attempts. + */ + + int counter = 0 ; + + do + { + rawRead ( NULL, center ) ; + counter++ ; + } while ( ! error && + counter < 100 && + center[0] == 512.0f && + center[1] == 512.0f ) ; + + if ( counter >= 100 ) + setError() ; + + + for ( int i = 0 ; i < _JS_MAX_AXES ; i++ ) + { + max [ i ] = center [ i ] * 2.0f ; + min [ i ] = 0.0f ; + dead_band [ i ] = 0.0f ; + saturate [ i ] = 1.0f ; + } +} + + + +void jsJoystick::close () +{ + if ( ! error ) + ::close ( os->fd ) ; + delete os; +} + + +jsJoystick::jsJoystick ( int ident ) +{ + id = ident ; + os = new struct os_specific_s; + + sprintf ( os->fname, "/dev/input/js%d", ident ) ; + + if ( access ( os->fname, F_OK ) != 0 ) + sprintf ( os->fname, "/dev/js%d", ident ) ; + + open () ; +} + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 1500.0f ; + + return ; + } + + int status = ::read ( os->fd, &(os->js), JS_RETURN ) ; + + if ( status != JS_RETURN ) + { + perror ( os->fname ) ; + setError () ; + return ; + } + + if ( buttons != NULL ) + *buttons = os->js.buttons ; + + if ( axes != NULL ) + { + axes[0] = (float) os->js.x ; + axes[1] = (float) os->js.y ; + } +} + +void jsInit() {} + +#endif +#endif diff --git a/src/js/jsMacOS.cxx b/src/js/jsMacOS.cxx new file mode 100644 index 0000000..c1b4418 --- /dev/null +++ b/src/js/jsMacOS.cxx @@ -0,0 +1,210 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsMacOS.cxx 1960 2004-09-21 11:45:55Z smokydiamond $ +*/ + +#include "js.h" + +#if defined (UL_MACINTOSH) + +#include + +//#define _JS_MAX_AXES_MAC 9 +#define isp_num_axis 9 +#define isp_num_needs 41 + +struct os_specific_s { + ISpElementReference isp_elem [ isp_num_needs ] ; + ISpNeed isp_needs [ isp_num_needs ] ; +}; + +void jsJoystick::open () +{ + name [0] = '\0' ; + + /* + FIXME: get joystick name in Mac + */ + + OSStatus err ; + + err = ISpStartup () ; + + if ( err == noErr ) + { +#define ISP_CHECK_ERR(x) if ( x != noErr ) { setError () ; return ; } + + setError () ; + + // initialize the needs structure + ISpNeed temp_isp_needs[isp_num_needs] = + { + { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 }, + + { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 }, + } ; + + memcpy ( os->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) ) ; + + + // next two calls allow keyboard and mouse to emulate other input + // devices (gamepads, joysticks, etc) + + /* + err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard ) ; + ISP_CHECK_ERR(err) + + + err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse ) ; + ISP_CHECK_ERR(err) + */ + + err = ISpElement_NewVirtualFromNeeds ( isp_num_needs, os->isp_needs, os->isp_elem, 0 ) ; + ISP_CHECK_ERR(err) + + err = ISpInit ( isp_num_needs, os->isp_needs, os->isp_elem, 'PLIB', nil, 0, 128, 0 ) ; + ISP_CHECK_ERR(err) + + num_buttons = isp_num_needs - isp_num_axis ; + num_axes = isp_num_axis ; + + for ( int i = 0 ; i < num_axes ; i++ ) + { + dead_band [ i ] = 0 ; + saturate [ i ] = 1 ; + center [ i ] = kISpAxisMiddle ; + max [ i ] = kISpAxisMaximum ; + min [ i ] = kISpAxisMinimum ; + } + + error = false ; + } + else + { + setError () ; + num_buttons = num_axes = 0 ; + } +} + + +void jsJoystick::close () +{ + ISpSuspend () ; + ISpStop () ; + ISpShutdown () ; + delete os; +} + + +jsJoystick::jsJoystick ( int ident ) +{ + id = ident ; + os = new struct os_specific_s; + // sprintf ( fname, "/dev/js%d", ident ) ; /* FIXME */ + open () ; +} + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 1500.0f ; + + return ; + } + + int i ; + int err ; + UInt32 state ; + + if ( buttons != NULL ) + { + *buttons = 0; + + for ( i = 0 ; i < num_buttons ; i++ ) + { + err = ISpElement_GetSimpleState ( os->isp_elem [ i + isp_num_axis ], &state) ; + ISP_CHECK_ERR(err) + + *buttons |= state << i ; + } + } + + if ( axes != NULL ) + { + for ( i = 0 ; i < num_axes ; i++ ) + { + err = ISpElement_GetSimpleState ( os->isp_elem [ i ], &state ) ; + ISP_CHECK_ERR(err) + + axes [i] = (float) state ; + } + } +} + +void jsInit() {} + +#endif diff --git a/src/js/jsMacOSX.cxx b/src/js/jsMacOSX.cxx new file mode 100644 index 0000000..0325d80 --- /dev/null +++ b/src/js/jsMacOSX.cxx @@ -0,0 +1,466 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsMacOSX.cxx 2165 2011-01-22 22:56:03Z fayjf $ +*/ + +#include "js.h" + +#if defined (UL_MAC_OSX) + +#include +#include +#include +#include +#include +#include +#include + +#ifdef MACOS_10_0_4 +# include +#else +/* The header was moved here in MacOS X 10.1 */ +# include +#endif + +static const int kNumDevices = 32; +static int numDevices = -1; +static io_object_t ioDevices[kNumDevices]; + +static int NS_hat[8] = {1, 1, 0, -1, -1, -1, 0, 1}; +static int WE_hat[8] = {0, 1, 1, 1, 0, -1, -1, -1}; + +struct os_specific_s { + IOHIDDeviceInterface ** hidDev; + IOHIDElementCookie buttonCookies[41]; + IOHIDElementCookie axisCookies[_JS_MAX_AXES]; + IOHIDElementCookie hatCookies[_JS_MAX_HATS]; + int num_hats; + long hat_min[_JS_MAX_HATS]; + long hat_max[_JS_MAX_HATS]; + + void enumerateElements(jsJoystick* joy, CFTypeRef element); + static void elementEnumerator( const void *element, void* vjs); + /// callback for CFArrayApply + void parseElement(jsJoystick* joy, CFDictionaryRef element); + void addAxisElement(jsJoystick* joy, CFDictionaryRef axis); + void addButtonElement(jsJoystick* joy, CFDictionaryRef button); + void addHatElement(jsJoystick* joy, CFDictionaryRef hat); +}; + +static void findDevices(mach_port_t); +static CFDictionaryRef getCFProperties(io_object_t); + + +void jsInit() +{ + if (numDevices < 0) { + numDevices = 0; + + mach_port_t masterPort; + IOReturn rv = IOMasterPort(bootstrap_port, &masterPort); + if (rv != kIOReturnSuccess) { + ulSetError(UL_WARNING, "error getting master Mach port"); + return; + } + + findDevices(masterPort); + } +} + +/** open the IOKit connection, enumerate all the HID devices, add their +interface references to the static array. We then use the array index +as the device number when we come to open() the joystick. */ +static void findDevices(mach_port_t masterPort) +{ + CFMutableDictionaryRef hidMatch = NULL; + IOReturn rv = kIOReturnSuccess; + io_iterator_t hidIterator; + + // build a dictionary matching HID devices + hidMatch = IOServiceMatching(kIOHIDDeviceKey); + + rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator); + if (rv != kIOReturnSuccess || !hidIterator) { + ulSetError(UL_WARNING, "no joystick (HID) devices found"); + return; + } + + // iterate + io_object_t ioDev; + + while ((ioDev = IOIteratorNext(hidIterator))) { + // filter out keyboard and mouse devices + CFDictionaryRef properties = getCFProperties(ioDev); + long usage, page; + + CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey)); + CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey)); + CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage); + CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page); + + // keep only joystick devices + if ( (page == kHIDPage_GenericDesktop) && + ((usage == kHIDUsage_GD_Joystick) || + (usage == kHIDUsage_GD_GamePad) + // || (usage == kHIDUsage_GD_MultiAxisController) + // || (usage == kHIDUsage_GD_Hatswitch) + ) + ) + { + // add it to the array + ioDevices[numDevices++] = ioDev; + } + } + + IOObjectRelease(hidIterator); +} + + +jsJoystick::jsJoystick(int ident) : + id(ident), + os(NULL), + error(JS_FALSE), + num_axes(0), + num_buttons(0) +{ + if (ident >= numDevices) { + setError(); + return; + } + + os = new struct os_specific_s; + os->num_hats = 0; + + // get the name now too + CFDictionaryRef properties = getCFProperties(ioDevices[id]); + CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey)); + if (!ref) + ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name")); + + if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) { + ulSetError(UL_WARNING, "error getting device name"); + name[0] = '\0'; + } + //printf("Joystick name: %s \n", name); + open(); +} + +void jsJoystick::open() +{ +#if 0 // test already done in the constructor + if (id >= numDevices) { + ulSetError(UL_WARNING, "device index out of range in jsJoystick::open"); + return; + } +#endif + + // create device interface + IOReturn rv; + SInt32 score; + IOCFPlugInInterface **plugin; + + rv = IOCreatePlugInInterfaceForService(ioDevices[id], + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugin, &score); + + if (rv != kIOReturnSuccess) { + ulSetError(UL_WARNING, "error creting plugin for io device"); + return; + } + + HRESULT pluginResult = (*plugin)->QueryInterface(plugin, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID*)&(os->hidDev) ); + + if (pluginResult != S_OK) + ulSetError(UL_WARNING, "QI-ing IO plugin to HID Device interface failed"); + + (*plugin)->Release(plugin); // don't leak a ref + if (os->hidDev == NULL) return; + + // store the interface in this instance + rv = (*(os->hidDev))->open(os->hidDev, 0); + if (rv != kIOReturnSuccess) { + ulSetError(UL_WARNING, "error opening device interface"); + return; + } + + CFDictionaryRef props = getCFProperties(ioDevices[id]); + + // recursively enumerate all the bits (buttons, axes, hats, ...) + CFTypeRef topLevelElement = + CFDictionaryGetValue (props, CFSTR(kIOHIDElementKey)); + os->enumerateElements(this, topLevelElement); + CFRelease(props); + + // for hats to be implemented as axes: must be the last axes: + for (int h = 0; h<2*os->num_hats; h++) + { + int index = num_axes++; + dead_band [ index ] = 0.0f ; + saturate [ index ] = 1.0f ; + center [ index ] = 0.0f; + max [ index ] = 1.0f; + min [ index ] = -1.0f; + } +} + +CFDictionaryRef getCFProperties(io_object_t ioDev) +{ + IOReturn rv; + CFMutableDictionaryRef cfProperties; + +#if 0 + // comment copied from darwin/SDL_sysjoystick.c + /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties + */ + + io_registry_entry_t parent1, parent2; + + rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1); + if (rv != kIOReturnSuccess) { + ulSetError(UL_WARNING, "error getting device entry parent"); + return NULL; + } + + rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2); + if (rv != kIOReturnSuccess) { + ulSetError(UL_WARNING, "error getting device entry parent 2"); + return NULL; + } + +#endif + rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/, + &cfProperties, kCFAllocatorDefault, kNilOptions); + if (rv != kIOReturnSuccess || !cfProperties) { + ulSetError(UL_WARNING, "error getting device properties"); + return NULL; + } + + return cfProperties; +} + +void jsJoystick::close() +{ + if (!os) + return; + if (os->hidDev != NULL) (*(os->hidDev))->close(os->hidDev); + if (os) delete os; +} + +/** element enumerator function : pass NULL for top-level*/ +void os_specific_s::enumerateElements(jsJoystick* joy, CFTypeRef element) +{ + assert(CFGetTypeID(element) == CFArrayGetTypeID()); + + CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)}; + CFArrayApplyFunction((CFArrayRef) element, range, + &elementEnumerator, joy); +} + +void os_specific_s::elementEnumerator( const void *element, void* vjs) +{ + if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) { + ulSetError(UL_WARNING, "element enumerator passed non-dictionary value"); + return; + } + + static_cast(vjs)-> + os->parseElement( static_cast(vjs), (CFDictionaryRef) element); +} + +void os_specific_s::parseElement(jsJoystick* joy, CFDictionaryRef element) +{ + CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey)); + CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey)); + + long type, page, usage; + + CFNumberGetValue((CFNumberRef) + CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)), + kCFNumberLongType, &type); + + switch (type) { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_Button: + //printf("got input element..."); + CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage); + CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page); + + if (page == kHIDPage_GenericDesktop) { + switch (usage) /* look at usage to determine function */ + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: // for throttle / trim controls + case kHIDUsage_GD_Dial: + //printf(" axis\n"); + /*joy->os->*/addAxisElement(joy, (CFDictionaryRef) element); + break; + + case kHIDUsage_GD_Hatswitch: + //printf(" hat\n"); + /*joy->os->*/addHatElement(joy, (CFDictionaryRef) element); + break; + + default: + ulSetError(UL_WARNING, "input type element has weird usage (%lx)\n", usage); + break; + } + } else if (page == kHIDPage_Simulation) { + switch (usage) /* look at usage to determine function */ + { + case kHIDUsage_Sim_Rudder: + case kHIDUsage_Sim_Throttle: + //printf(" axis\n"); + /*joy->os->*/addAxisElement(joy, (CFDictionaryRef) element); + break; + default: + ulSetError(UL_WARNING, "Simulation page input type element has weird usage (%lx)\n", usage); + } + } else if (page == kHIDPage_Button) { + //printf(" button\n"); + /*joy->os->*/addButtonElement(joy, (CFDictionaryRef) element); + } else if (page == kHIDPage_PID) { + ulSetError(UL_WARNING, "Force feedback and related data ignored\n", usage); + } else + ulSetError(UL_WARNING, "input type element has weird usage (%lx)\n", usage); + break; + + case kIOHIDElementTypeCollection: + /*joy->os->*/enumerateElements(joy, + CFDictionaryGetValue(element, CFSTR(kIOHIDElementKey)) + ); + break; + + default: + break; + } +} + +void os_specific_s::addAxisElement(jsJoystick* joy, CFDictionaryRef axis) +{ + long cookie, lmin, lmax; + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (axis, CFSTR(kIOHIDElementCookieKey)), + kCFNumberLongType, &cookie); + + int index = joy->num_axes++; + + /*joy->os->*/axisCookies[index] = (IOHIDElementCookie) cookie; + + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (axis, CFSTR(kIOHIDElementMinKey)), + kCFNumberLongType, &lmin); + + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (axis, CFSTR(kIOHIDElementMaxKey)), + kCFNumberLongType, &lmax); + + joy->min[index] = lmin; + joy->max[index] = lmax; + joy->dead_band[index] = 0.0; + joy->saturate[index] = 1.0; + joy->center[index] = (lmax - lmin) * 0.5 + lmin; +} + +void os_specific_s::addButtonElement(jsJoystick* joy, CFDictionaryRef button) +{ + long cookie; + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (button, CFSTR(kIOHIDElementCookieKey)), + kCFNumberLongType, &cookie); + + /*joy->os->*/buttonCookies[joy->num_buttons++] = (IOHIDElementCookie) cookie; + // anything else for buttons? +} + +void os_specific_s::addHatElement(jsJoystick* joy, CFDictionaryRef hat) +{ + long cookie, lmin, lmax; + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (hat, CFSTR(kIOHIDElementCookieKey)), + kCFNumberLongType, &cookie); + + int index = /*joy->*/num_hats++; + + /*joy->os->*/hatCookies[index] = (IOHIDElementCookie) cookie; + + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (hat, CFSTR(kIOHIDElementMinKey)), + kCFNumberLongType, &lmin); + + CFNumberGetValue ((CFNumberRef) + CFDictionaryGetValue (hat, CFSTR(kIOHIDElementMaxKey)), + kCFNumberLongType, &lmax); + + hat_min[index] = lmin; + hat_max[index] = lmax; + // do we map hats to axes or buttons? + // axes; there is room for that: Buttons are limited to 32. + // (a joystick with 2 hats will use 16 buttons!) +} + +void jsJoystick::rawRead(int *buttons, float *axes) +{ + if (buttons) + *buttons = 0; + + IOHIDEventStruct hidEvent; + + for (int b=0; bhidDev))->getElementValue(os->hidDev, os->buttonCookies[b], &hidEvent); + if (hidEvent.value && buttons) + *buttons |= 1 << b; + } + + // real axes: + int real_num_axes = num_axes - 2*os->num_hats; + for (int a=0; ahidDev))->getElementValue(os->hidDev, os->axisCookies[a], &hidEvent); + axes[a] = hidEvent.value; + } + + // hats: + for (int h=0; h < os->num_hats; ++h) { + (*(os->hidDev))->getElementValue(os->hidDev, os->hatCookies[h], &hidEvent); + long result = ( hidEvent.value - os->hat_min[h] ) * 8; + result /= ( os->hat_max[h] - os->hat_min[h] + 1 ); + if ( (result>=0) && (result<8) ) + { + axes[h+real_num_axes+1] = NS_hat[result]; + axes[h+real_num_axes] = WE_hat[result]; + } + else + { + axes[h+real_num_axes] = 0; + axes[h+real_num_axes+1] = 0; + } + } +} + +#endif diff --git a/src/js/jsNone.cxx b/src/js/jsNone.cxx new file mode 100644 index 0000000..300bdbc --- /dev/null +++ b/src/js/jsNone.cxx @@ -0,0 +1,59 @@ +/* + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsNone.cxx 1960 2004-09-21 11:45:55Z smokydiamond $ +*/ + +#include "js.h" + +#if defined(UL_IRIX) || defined(UL_SOLARIS) || defined (UL_HPUX) + +struct os_specific_s ; + + +void jsJoystick::open () +{ + error = TRUE ; + num_axes = num_buttons = 0 ; +} + + +void jsJoystick::close () +{ + error = TRUE ; +} + + +jsJoystick::jsJoystick ( int ident ) +{ + error = TRUE ; + num_axes = num_buttons = 0 ; + os = NULL; +} + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + if ( buttons != NULL ) *buttons = 0 ; +} + +void jsInit () {} + +#endif + diff --git a/src/js/jsWindows.cxx b/src/js/jsWindows.cxx new file mode 100644 index 0000000..33896ec --- /dev/null +++ b/src/js/jsWindows.cxx @@ -0,0 +1,273 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: jsWindows.cxx 2164 2011-01-22 22:47:30Z fayjf $ +*/ + +#include "js.h" + +#if defined (UL_WIN32) + +#define _JS_MAX_AXES_WIN 8 /* X,Y,Z,R,U,V,POV_X,POV_Y */ + +struct os_specific_s { + JOYCAPS jsCaps ; + JOYINFOEX js ; + UINT js_id ; + static bool getOEMProductName ( jsJoystick* joy, char *buf, int buf_sz ) ; +}; + + + +// Inspired by +// http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp + +bool os_specific_s::getOEMProductName ( jsJoystick* joy, char *buf, int buf_sz ) +{ + if ( joy->error ) return false ; + + union + { + char key [ 256 ] ; + char value [ 256 ] ; + } ; + char OEMKey [ 256 ] ; + + HKEY hKey ; + DWORD dwcb ; + LONG lr ; + int hkcu = 0; + + // Open .. MediaResources\CurrentJoystickSettings + sprintf ( key, "%s\\%s\\%s", + REGSTR_PATH_JOYCONFIG, joy->os->jsCaps.szRegKey, + REGSTR_KEY_JOYCURR ) ; + + lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey) ; + + if ( lr != ERROR_SUCCESS ) + { + hkcu = 1; + // XP/Vista/7 seem to have moved it to "current user" + lr = RegOpenKeyEx ( HKEY_CURRENT_USER, key, 0, KEY_QUERY_VALUE, &hKey) ; + if ( lr != ERROR_SUCCESS ) return false ; + } + + // Get OEM Key name + dwcb = sizeof(OEMKey) ; + + // JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. + sprintf ( value, "Joystick%d%s", joy->os->js_id + 1, REGSTR_VAL_JOYOEMNAME ) ; + + lr = RegQueryValueEx ( hKey, value, 0, 0, (LPBYTE) OEMKey, &dwcb); + RegCloseKey ( hKey ) ; + + if ( lr != ERROR_SUCCESS ) return false ; + + // Open OEM Key from ...MediaProperties + sprintf ( key, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey ) ; + + if (!hkcu) + lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey) ; + else + lr = RegOpenKeyEx ( HKEY_CURRENT_USER, key, 0, KEY_QUERY_VALUE, &hKey) ; + if ( lr != ERROR_SUCCESS ) + { + return false ; + } + + // Get OEM Name + dwcb = buf_sz ; + + lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf, + &dwcb ) ; + RegCloseKey ( hKey ) ; + + if ( lr != ERROR_SUCCESS ) return false ; + + return true ; +} + + +void jsJoystick::open () +{ + name [0] = '\0' ; + + os->js . dwFlags = JOY_RETURNALL ; + os->js . dwSize = sizeof ( os->js ) ; + + memset ( &(os->jsCaps), 0, sizeof(os->jsCaps) ) ; + + error = ( joyGetDevCaps( os->js_id, &(os->jsCaps), sizeof(os->jsCaps) ) + != JOYERR_NOERROR ) ; + num_buttons = os->jsCaps.wNumButtons ; + if ( os->jsCaps.wNumAxes == 0 ) + { + num_axes = 0 ; + setError () ; + } + else + { + // Device name from jsCaps is often "Microsoft PC-joystick driver", + // at least for USB. Try to get the real name from the registry. + if ( ! os->getOEMProductName ( this, name, sizeof(name) ) ) + { + ulSetError ( UL_WARNING, + "JS: Failed to read joystick name from registry" ) ; + + strncpy ( name, os->jsCaps.szPname, sizeof(name) ) ; + } + + // Windows joystick drivers may provide any combination of + // X,Y,Z,R,U,V,POV - not necessarily the first n of these. + if ( os->jsCaps.wCaps & JOYCAPS_HASPOV ) + { + num_axes = _JS_MAX_AXES_WIN ; + min [ 7 ] = -1.0 ; max [ 7 ] = 1.0 ; // POV Y + min [ 6 ] = -1.0 ; max [ 6 ] = 1.0 ; // POV X + } + else + num_axes = 6 ; + + min [ 5 ] = (float) os->jsCaps.wVmin ; max [ 5 ] = (float) os->jsCaps.wVmax ; + min [ 4 ] = (float) os->jsCaps.wUmin ; max [ 4 ] = (float) os->jsCaps.wUmax ; + min [ 3 ] = (float) os->jsCaps.wRmin ; max [ 3 ] = (float) os->jsCaps.wRmax ; + min [ 2 ] = (float) os->jsCaps.wZmin ; max [ 2 ] = (float) os->jsCaps.wZmax ; + min [ 1 ] = (float) os->jsCaps.wYmin ; max [ 1 ] = (float) os->jsCaps.wYmax ; + min [ 0 ] = (float) os->jsCaps.wXmin ; max [ 0 ] = (float) os->jsCaps.wXmax ; + } + + for ( int i = 0 ; i < num_axes ; i++ ) + { + center [ i ] = ( max[i] + min[i] ) / 2.0f ; + dead_band [ i ] = 0.0f ; + saturate [ i ] = 1.0f ; + } +} + + +void jsJoystick::close () +{ + delete os; +} + + + +jsJoystick::jsJoystick ( int ident ) +{ + id = ident ; + os = new struct os_specific_s; + + if (ident >= 0 && ident < (int)joyGetNumDevs()) { + os->js_id = JOYSTICKID1 + ident; + open(); + } + else { + num_axes = 0; + setError(); + } +} + + + +void jsJoystick::rawRead ( int *buttons, float *axes ) +{ + if ( error ) + { + if ( buttons ) + *buttons = 0 ; + + if ( axes ) + for ( int i = 0 ; i < num_axes ; i++ ) + axes[i] = 1500.0f ; + + return ; + } + + MMRESULT status = joyGetPosEx ( os->js_id, &(os->js) ) ; + + if ( status != JOYERR_NOERROR ) + { + setError() ; + return ; + } + + if ( buttons != NULL ) + *buttons = (int) os->js.dwButtons ; + + if ( axes != NULL ) + { + /* WARNING - Fall through case clauses!! */ + + switch ( num_axes ) + { + case 8: + // Generate two POV axes from the POV hat angle. + // Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in + // hundredths of a degree, or 0xFFFF when idle. + + if ( ( os->js.dwPOV & 0xFFFF ) == 0xFFFF ) + { + axes [ 6 ] = 0.0 ; + axes [ 7 ] = 0.0 ; + } + else + { + // This is the contentious bit: how to convert angle to X/Y. + // wk: I know of no define for PI that we could use here: + // SG_PI would pull in sg, M_PI is undefined for MSVC + // But the accuracy of the value of PI is very unimportant at + // this point. + + float s = (float) sin ( ( os->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180 ) ) ; + float c = (float) cos ( ( os->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180 ) ) ; + + // Convert to coordinates on a square so that North-East + // is (1,1) not (.7,.7), etc. + // s and c cannot both be zero so we won't divide by zero. + if ( fabs ( s ) < fabs ( c ) ) + { + axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ; + axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f ; + } + else + { + axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f ; + axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ; + } + } + + case 6: axes[5] = (float) os->js . dwVpos ; + case 5: axes[4] = (float) os->js . dwUpos ; + case 4: axes[3] = (float) os->js . dwRpos ; + case 3: axes[2] = (float) os->js . dwZpos ; + case 2: axes[1] = (float) os->js . dwYpos ; + case 1: axes[0] = (float) os->js . dwXpos ; + break; + + default: + ulSetError ( UL_WARNING, "PLIB_JS: Wrong num_axes. Joystick input is now invalid" ) ; + } + } +} + +void jsInit() {} + +#endif diff --git a/src/net/Makefile.am b/src/net/Makefile.am new file mode 100644 index 0000000..48de5c7 --- /dev/null +++ b/src/net/Makefile.am @@ -0,0 +1,16 @@ +if BUILD_NET + +lib_LIBRARIES = libplibnet.a + +include_HEADERS = netBuffer.h netChannel.h netChat.h netMessage.h \ + netMonitor.h netSocket.h net.h + +libplibnet_a_SOURCES = netBuffer.cxx netChannel.cxx netChat.cxx \ + netMessage.cxx netMonitor.cxx netSocket.cxx + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = net.dsp + diff --git a/src/net/README b/src/net/README new file mode 100644 index 0000000..6382629 --- /dev/null +++ b/src/net/README @@ -0,0 +1,47 @@ +PLIB Network Library +------------------- + +DESCRIPTION: + +This is a C++ library for making networked games. +It includes event-driven channels, support for +buffering and pipelining, a monitor server for remote +telnet admin, and binary messages. + +HISTORY + +Initial work by Dave McClurg +This library is based on from a high-performance +internet server architecture for Python called Medusa. + http://www.nightmare.com/medusa/medusa.html +Added binary messages and PLIB naming conventions + +LICENSING: + +This library is distributed with PLIB under the GNU LGPL. + + +MEDUSA COPYRIGHT: + +--- +Copyright 1996 by Sam Rushing + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that copyright notice and this permission +notice appear in supporting documentation, and that the name of Sam +Rushing not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +--- diff --git a/src/net/net.dsp b/src/net/net.dsp new file mode 100644 index 0000000..256d54d --- /dev/null +++ b/src/net/net.dsp @@ -0,0 +1,144 @@ +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy net.h ..\..\net.h copy netBuffer.h ..\..\netBuffer.h copy netChannel.h ..\..\netChannel.h copy netChat.h ..\..\netChat.h copy netMessage.h ..\..\netMessage.h copy netMonitor.h ..\..\netMonitor.h copy netSocket.h ..\..\netSocket.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "net - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\net_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy net.h ..\..\net.h copy netBuffer.h ..\..\netBuffer.h copy netChannel.h ..\..\netChannel.h copy netChat.h ..\..\netChat.h copy netMessage.h ..\..\netMessage.h copy netMonitor.h ..\..\netMonitor.h copy netSocket.h ..\..\netSocket.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Source File + +SOURCE=.\netBuffer.cxx +# End Source File +# Begin Source File + +SOURCE=.\netBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\netChannel.cxx +# End Source File +# Begin Source File + +SOURCE=.\netChannel.h +# End Source File +# Begin Source File + +SOURCE=.\netChat.cxx +# End Source File +# Begin Source File + +SOURCE=.\netChat.h +# End Source File +# Begin Source File + +SOURCE=.\netMessage.cxx +# End Source File +# Begin Source File + +SOURCE=.\netMessage.h +# End Source File +# Begin Source File + +SOURCE=.\netMonitor.cxx +# End Source File +# Begin Source File + +SOURCE=.\netMonitor.h +# End Source File +# Begin Source File + +SOURCE=.\netSocket.cxx +# End Source File +# Begin Source File + +SOURCE=.\netSocket.h +# End Source File +# End Target +# End Project diff --git a/src/net/net.h b/src/net/net.h new file mode 100644 index 0000000..e7b0c90 --- /dev/null +++ b/src/net/net.h @@ -0,0 +1,12 @@ +#ifndef __PLIB_NET_H__ +#define __PLIB_NET_H__ 1 + +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/src/net/netBuffer.cxx b/src/net/netBuffer.cxx new file mode 100644 index 0000000..01db94a --- /dev/null +++ b/src/net/netBuffer.cxx @@ -0,0 +1,69 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netBuffer.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include "netBuffer.h" + +void +netBufferChannel::handleRead (void) +{ + int max_read = in_buffer.getMaxLength() - in_buffer.getLength() ; + if (max_read) + { + char* data = in_buffer.getData() + in_buffer.getLength() ; + int num_read = recv (data, max_read) ; + if (num_read > 0) + { + in_buffer.append (num_read) ; + //ulSetError ( UL_DEBUG, "netBufferChannel: %d read", num_read ) ; + } + } + if (in_buffer.getLength()) + { + handleBufferRead (in_buffer); + } +} + +void +netBufferChannel::handleWrite (void) +{ + if (out_buffer.getLength()) + { + if (isConnected()) + { + int length = out_buffer.getLength() ; + if (length>512) + length=512; + int num_sent = netChannel::send ( + out_buffer.getData(), length); + if (num_sent > 0) + { + out_buffer.remove (0, num_sent); + //ulSetError ( UL_DEBUG, "netBufferChannel: %d sent", num_sent ) ; + } + } + } + else if (should_close) + { + close(); + } +} diff --git a/src/net/netBuffer.h b/src/net/netBuffer.h new file mode 100644 index 0000000..c5ff59b --- /dev/null +++ b/src/net/netBuffer.h @@ -0,0 +1,234 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netBuffer.h 1901 2004-03-21 18:19:11Z sjbaker $ +*/ + +/**** +* NAME +* netBuffer - network buffer class +* +* DESCRIPTION +* Clients and servers built on top of netBufferChannel +* automatically support pipelining. +* +* Pipelining refers to a protocol capability. Normally, +* a conversation with a server has a back-and-forth +* quality to it. The client sends a command, and +* waits for the response. If a client needs to send +* many commands over a high-latency connection, +* waiting for each response can take a long time. +* +* For example, when sending a mail message to many recipients +* with SMTP, the client will send a series of RCPT commands, one +* for each recipient. For each of these commands, the server will +* send back a reply indicating whether the mailbox specified is +* valid. If you want to send a message to several hundred recipients, +* this can be rather tedious if the round-trip time for each command +* is long. You'd like to be able to send a bunch of RCPT commands +* in one batch, and then count off the responses to them as they come. +* +* I have a favorite visual when explaining the advantages of +* pipelining. Imagine each request to the server is a boxcar on a train. +* The client is in Los Angeles, and the server is in New York. +* Pipelining lets you hook all your cars in one long chain; send +* them to New York, where they are filled and sent back to you. +* Without pipelining you have to send one car at a time. +* +* Not all protocols allow pipelining. Not all servers support it; +* Sendmail, for example, does not support pipelining because it tends +* to fork unpredictably, leaving buffered data in a questionable state. +* A recent extension to the SMTP protocol allows a server to specify +* whether it supports pipelining. HTTP/1.1 explicitly requires that +* a server support pipelining. +* +* NOTES +* When a user passes in a buffer object, it belongs to +* the user. When the library gives a buffer to the user, +* the user should copy it. +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef NET_BUFFER_H +#define NET_BUFFER_H + +#include "netChannel.h" + + +// =========================================================================== +// netName +// =========================================================================== + +enum { NET_MAX_NAME = 31 } ; +//eg. char name [ NET_MAX_NAME+1 ] ; + + +inline char* netCopyName ( char* dst, const char* src ) +{ + if ( src != NULL ) + { + strncpy ( dst, src, NET_MAX_NAME ) ; + dst [ NET_MAX_NAME ] = 0 ; + } + else + *dst = 0 ; + return dst ; +} + + +// =========================================================================== +// netBuffer +// =========================================================================== + +class netBuffer +{ +protected: + int length ; + int max_length ; + char* data ; + +public: + netBuffer ( int _max_length ) + { + length = 0 ; + max_length = _max_length ; + data = new char [ max_length+1 ] ; //for null terminator + } + + ~netBuffer () + { + delete[] data ; + } + + int getLength() const { return length ; } + int getMaxLength() const { return max_length ; } + + /* + ** getData() returns a pointer to the data + ** Note: a zero (0) byte is appended for convenience + ** but the data may have internal zero (0) bytes already + */ + char* getData() { data [length] = 0 ; return data ; } + const char* getData() const { ((char*)data) [length] = 0 ; return data ; } + + void remove () + { + length = 0 ; + } + + void remove (int pos, int n) + { + assert (pos>=0 && pos=0 && pos next_channel ) + { + if (ch == this) + { + ch = ch -> next_channel ; + if ( prev != NULL ) + prev -> next_channel = ch ; + else + channels = ch ; + next_channel = 0 ; + break; + } + prev = ch ; + } +} + +void +netChannel::setHandle (int handle, bool is_connected) +{ + close () ; + netSocket::setHandle ( handle ) ; + connected = is_connected ; + //if ( connected ) this->handleConnect(); + closed = false ; +} + +bool +netChannel::open (void) +{ + close(); + if (netSocket::open(true)) { + closed = false ; + setBlocking ( false ) ; + return true ; + } + return false ; +} + +int +netChannel::listen ( int backlog ) +{ + accepting = true ; + return netSocket::listen ( backlog ) ; +} + +int +netChannel::connect ( const char* host, int port ) +{ + int result = netSocket::connect ( host, port ) ; + if (result == 0) { + connected = true ; + //this->handleConnect(); + return 0; + } else if (isNonBlockingError ()) { + return 0; + } else { + // some other error condition + this->handleError (result); + close(); + return -1; + } +} + +int +netChannel::send (const void * buffer, int size, int flags) +{ + int result = netSocket::send (buffer, size, flags); + + if (result == (int)size) { + // everything was sent + write_blocked = false ; + return result; + } else if (result >= 0) { + // not all of it was sent, but no error + write_blocked = true ; + return result; + } else if (isNonBlockingError ()) { + write_blocked = true ; + return 0; + } else { + this->handleError (result); + close(); + return -1; + } + +} + +int +netChannel::recv (void * buffer, int size, int flags) +{ + int result = netSocket::recv (buffer, size, flags); + + if (result > 0) { + return result; + } else if (result == 0) { + close(); + return 0; + } else if (isNonBlockingError ()) { + return 0; + } else { + this->handleError (result); + close(); + return -1; + } +} + +void +netChannel::close (void) +{ + if ( !closed ) + { + this->handleClose(); + + closed = true ; + connected = false ; + accepting = false ; + write_blocked = false ; + } + + netSocket::close () ; +} + +void +netChannel::handleReadEvent (void) +{ + if (accepting) { + if (!connected) { + connected = true ; + //this->handleConnect(); + } + this->handleAccept(); + } else if (!connected) { + connected = true ; + //this->handleConnect(); + this->handleRead(); + } else { + this->handleRead(); + } +} + +void +netChannel::handleWriteEvent (void) +{ + if (!connected) { + connected = true ; + //this->handleConnect(); + } + write_blocked = false ; + this->handleWrite(); +} + +bool +netChannel::poll (unsigned int timeout) +{ + if (!channels) + return false ; + + enum { MAX_SOCKETS = 256 } ; + netSocket* reads [ MAX_SOCKETS+1 ] ; + netSocket* writes [ MAX_SOCKETS+1 ] ; + netSocket* deletes [ MAX_SOCKETS+1 ] ; + int nreads = 0 ; + int nwrites = 0 ; + int ndeletes = 0 ; + int nopen = 0 ; + netChannel* ch; + for ( ch = channels; ch != NULL; ch = ch -> next_channel ) + { + if ( ch -> should_delete ) + { + assert(ndeletes closed ) + { + nopen++ ; + if (ch -> readable()) { + assert(nreads writable()) { + assert(nwrites closed ) + ch -> handleReadEvent(); + } + + for ( i=0; writes[i]; i++ ) + { + ch = (netChannel*)writes[i]; + if ( ! ch -> closed ) + ch -> handleWriteEvent(); + } + + return true ; +} + +void +netChannel::loop (unsigned int timeout) +{ + while ( poll (timeout) ) ; +} + diff --git a/src/net/netChannel.h b/src/net/netChannel.h new file mode 100644 index 0000000..8da5e1e --- /dev/null +++ b/src/net/netChannel.h @@ -0,0 +1,116 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netChannel.h 1899 2004-03-21 17:41:07Z sjbaker $ +*/ + +/**** +* NAME +* netChannel - network channel class +* +* DESCRIPTION +* netChannel is adds event-handling to the low-level +* netSocket class. Otherwise, it can be treated as +* a normal non-blocking socket object. +* +* The direct interface between the netPoll() loop and +* the channel object are the handleReadEvent and +* handleWriteEvent methods. These are called +* whenever a channel object 'fires' that event. +* +* The firing of these low-level events can tell us whether +* certain higher-level events have taken place, depending on +* the timing and state of the connection. +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef NET_CHANNEL_H +#define NET_CHANNEL_H + +#include "netSocket.h" + +class netChannel : public netSocket +{ + bool closed, connected, accepting, write_blocked, should_delete ; + netChannel* next_channel ; + + friend bool netPoll (unsigned int timeout); + +public: + + netChannel () ; + virtual ~netChannel () ; + + void setHandle (int s, bool is_connected = true); + bool isConnected () const { return connected; } + bool isClosed () const { return closed; } + void shouldDelete () { should_delete = true ; } + + // -------------------------------------------------- + // socket methods + // -------------------------------------------------- + + bool open ( void ) ; + void close ( void ) ; + int listen ( int backlog ) ; + int connect ( const char* host, int port ) ; + int send ( const void * buf, int size, int flags = 0 ) ; + int recv ( void * buf, int size, int flags = 0 ) ; + + // poll() eligibility predicates + virtual bool readable (void) { return (connected || accepting); } + virtual bool writable (void) { return (!connected || write_blocked); } + + // -------------------------------------------------- + // event handlers + // -------------------------------------------------- + + void handleReadEvent (void); + void handleWriteEvent (void); + + // These are meant to be overridden. + virtual void handleClose (void) { + //ulSetError(UL_WARNING,"Network: %d: unhandled close",getHandle()); + } + virtual void handleRead (void) { + ulSetError(UL_WARNING,"Network: %d: unhandled read",getHandle()); + } + virtual void handleWrite (void) { + ulSetError(UL_WARNING,"Network: %d: unhandled write",getHandle()); + } + virtual void handleAccept (void) { + ulSetError(UL_WARNING,"Network: %d: unhandled accept",getHandle()); + } + virtual void handleError (int error) { + ulSetError(UL_WARNING,"Network: %d: errno: %s(%d)",getHandle(),strerror(errno),errno); + } + + static bool poll (unsigned int timeout = 0 ) ; + static void loop (unsigned int timeout = 0 ) ; +}; + +#endif // NET_CHANNEL_H diff --git a/src/net/netChat.cxx b/src/net/netChat.cxx new file mode 100644 index 0000000..865407b --- /dev/null +++ b/src/net/netChat.cxx @@ -0,0 +1,125 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netChat.cxx 2117 2007-09-13 23:21:09Z fayjf $ +*/ + +#include "netChat.h" + +void +netChat::setTerminator (const char* t) +{ + if (terminator) delete[] terminator; + terminator = ulStrDup(t); +} + +const char* +netChat::getTerminator (void) +{ + return terminator; +} + +// return the size of the largest prefix of needle at the end +// of haystack + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +static int +find_prefix_at_end (const netBuffer& haystack, const char* needle) +{ + const char* hd = haystack.getData(); + int hl = haystack.getLength(); + int nl = strlen(needle); + + for (int i = MAX (nl-hl, 0); i < nl; i++) { + //if (haystack.compare (needle, hl-(nl-i), nl-i) == 0) { + if (memcmp(needle, &hd[hl-(nl-i)], nl-i) == 0) { + return (nl-i); + } + } + return 0; +} + +static int +find_terminator (const netBuffer& haystack, const char* needle) +{ + if (needle && *needle) + { + const char* data = haystack.getData(); + const char* ptr = strstr(data,needle); + if (ptr != NULL) + return(ptr-data); + } + return -1; +} + + +void +netChat::handleBufferRead (netBuffer& in_buffer) +{ + // Continue to search for terminator in in_buffer, + // while calling collect_incoming_data. The while loop is + // necessary because we might read several data+terminator combos + // with a single recv(). + + while (in_buffer.getLength()) { + + // special case where we're not using a terminator + if (terminator == 0 || *terminator == 0) { + collectIncomingData (in_buffer.getData(),in_buffer.getLength()); + in_buffer.remove (); + return; + } + + int terminator_len = strlen(terminator); + + int index = find_terminator ( in_buffer, terminator ) ; + + // 3 cases: + // 1) end of buffer matches terminator exactly: + // collect data, transition + // 2) end of buffer matches some prefix: + // collect data to the prefix + // 3) end of buffer does not match any prefix: + // collect data + + if (index != -1) { + // we found the terminator + collectIncomingData ( in_buffer.getData(), index ) ; + in_buffer.remove (0, index + terminator_len); + foundTerminator(); + } else { + // check for a prefix of the terminator + int num = find_prefix_at_end (in_buffer, terminator); + if (num) { + int bl = in_buffer.getLength(); + // we found a prefix, collect up to the prefix + collectIncomingData ( in_buffer.getData(), bl - num ) ; + in_buffer.remove (0, bl - num); + break; + } else { + // no prefix, collect it all + collectIncomingData (in_buffer.getData(), in_buffer.getLength()); + in_buffer.remove(); + } + } + } +} + diff --git a/src/net/netChat.h b/src/net/netChat.h new file mode 100644 index 0000000..db56e47 --- /dev/null +++ b/src/net/netChat.h @@ -0,0 +1,86 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netChat.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +/**** +* NAME +* netChat - network chat class +* +* DESCRIPTION +* This class adds support for 'chat' style protocols - +* where one side sends a 'command', and the other sends +* a response (examples would be the common internet +* protocols - smtp, nntp, ftp, etc..). +* +* The handle_buffer_read() method looks at the input +* stream for the current 'terminator' (usually '\r\n' +* for single-line responses, '\r\n.\r\n' for multi-line +* output), calling found_terminator() on its receipt. +* +* EXAMPLE +* Say you build an nntp client using this class. +* At the start of the connection, you'll have +* terminator set to '\r\n', in order to process +* the single-line greeting. Just before issuing a +* 'LIST' command you'll set it to '\r\n.\r\n'. +* The output of the LIST command will be accumulated +* (using your own 'collect_incoming_data' method) +* up to the terminator, and then control will be +* returned to you - by calling your found_terminator() +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef NET_CHAT_H +#define NET_CHAT_H + +#include "netBuffer.h" + +class netChat : public netBufferChannel +{ + char* terminator; + + virtual void handleBufferRead (netBuffer& buffer) ; + +public: + + netChat () : terminator (0) {} + + void setTerminator (const char* t); + const char* getTerminator (void); + + bool push (const char* s) + { + return bufferSend ( s, strlen(s) ) ; + } + + virtual void collectIncomingData (const char* s, int n) {} + virtual void foundTerminator (void) {} +}; + +#endif // NET_CHAT_H diff --git a/src/net/netMessage.cxx b/src/net/netMessage.cxx new file mode 100644 index 0000000..7bff3af --- /dev/null +++ b/src/net/netMessage.cxx @@ -0,0 +1,50 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netMessage.cxx 1899 2004-03-21 17:41:07Z sjbaker $ +*/ + +#include "netMessage.h" + + +void +netMessageChannel::handleBufferRead (netBuffer& in_buffer) +{ + int n = in_buffer.getLength () ; + while ( n >= 2 ) + { + unsigned short msg_len = *( (unsigned short*)in_buffer.getData() ) ; + if ( n >= msg_len ) + { + //we have a complete message; handle it + netMessage msg(in_buffer.getData(),msg_len); + in_buffer.remove(0,msg_len); + handleMessage ( msg ); + + //ulSetError ( UL_DEBUG, "netMessageChannel: %d read", msg_len ) ; + n -= msg_len ; + } + else + { + //ulSetError ( UL_DEBUG, "netMessageChannel: %d waiting", n ) ; + break ; + } + } +} diff --git a/src/net/netMessage.h b/src/net/netMessage.h new file mode 100644 index 0000000..57a4a89 --- /dev/null +++ b/src/net/netMessage.h @@ -0,0 +1,294 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netMessage.h 2112 2006-12-12 18:45:28Z fayjf $ +*/ + +/**** +* NAME +* netMessage - message buffer and channel classes +* +* DESCRIPTION +* messages are a binary format for sending buffers over a channel. +* message headers contain a type field and length. +* +* AUTHOR +* Dave McClurg +* +* CREATION DATE +* Dec-2000 +****/ + +#ifndef __NET_MESSAGE__ +#define __NET_MESSAGE__ + + +#include "netBuffer.h" + +// ntohs() etc prototypes +#ifdef UL_MSVC +#include +#else +#include +#endif + +#ifdef __FreeBSD__ +#include +#endif + + +class netGuid //Globally Unique IDentifier +{ +public: + unsigned char data [ 16 ] ; + + netGuid () {} + + netGuid ( unsigned int l, + unsigned short w1, unsigned short w2, + unsigned char b1, unsigned char b2, + unsigned char b3, unsigned char b4, + unsigned char b5, unsigned char b6, + unsigned char b7, unsigned char b8 ) + { + //store in network format (big-endian) + + data[ 0] = (unsigned char)(l>>24) ; + data[ 1] = (unsigned char)(l>>16) ; + data[ 2] = (unsigned char)(l>> 8) ; + data[ 3] = (unsigned char)(l ) ; + data[ 4] = (unsigned char)(w1>>8) ; + data[ 5] = (unsigned char)(w1 ) ; + data[ 6] = (unsigned char)(w2>>8) ; + data[ 7] = (unsigned char)(w2 ) ; + + data[ 8] = b1 ; + data[ 9] = b2 ; + data[10] = b3 ; + data[11] = b4 ; + data[12] = b5 ; + data[13] = b6 ; + data[14] = b7 ; + data[15] = b8 ; + } + + bool operator ==( const netGuid& guid ) const + { + return memcmp ( data, guid.data, sizeof(data) ) == 0 ; + } + + bool operator !=( const netGuid& guid ) const + { + return memcmp ( data, guid.data, sizeof(data) ) != 0 ; + } +} ; + + +class netMessage : public netBuffer +{ + int pos ; + + void seek ( int new_pos ) const + { + if ( new_pos < 0 ) + new_pos = 0 ; + else + if ( new_pos > length ) + new_pos = length ; + + //logical const-ness + + ((netMessage*)this) -> pos = new_pos ; + } + + void skip ( int off ) const + { + seek(pos+off); + } + +public: + + // incoming message; header is already there + netMessage ( const char* s, int n ) : netBuffer(n) + { + assert ( n >= 5 ) ; + append(s,n); + pos = 5 ; // seek past header + } + + // outgoing message + netMessage ( int type, int to_id, int from_id=0, int n=256 ) : netBuffer(n) + { + // output header + putw ( 0 ) ; //msg_len + putbyte ( type ) ; + putbyte ( to_id ) ; + putbyte ( from_id ) ; + } + + int getType () const { return ( (unsigned char*)data )[ 2 ] ; } + int getToID () const { return ( (unsigned char*)data )[ 3 ] ; } + int getFromID () const { return ( (unsigned char*)data )[ 4 ] ; } + void setFromID ( int from_id ) { ( (unsigned char*)data )[ 4 ] = (unsigned char)from_id; } + + void geta ( void* a, int n ) const + { + assert (pos>=0 && pos=0 && pospos++; + if (ch==0) + break; + } + *dst = 0 ; + } + void puts ( const char* s ) + { + puta(s,strlen(s)+1); + } + + void print ( FILE *fd = stderr ) const + { + fprintf ( fd, "netMessage: %p, length=%d\n", this, length ) ; + fprintf ( fd, " header (type,to,from) = (%d,%d,%d)\n", + getType(), getToID(), getFromID() ) ; + fprintf ( fd, " data = " ) ; + for ( int i=0; i active = 0 ; + } +} ; + + +// for now, we ignore any telnet option stuff sent to +// us, and we process the backspace key ourselves. +// gee, it would be fun to write a full-blown line-editing +// environment, etc... + +static void clean_line (char* line) +{ + char* dst = line ; + for ( char* src = line ; *src ; src ++ ) + { + char ch = *src ; + if (ch==8 || ch==127) + { + // backspace + if (dst != line) + dst -- ; + } + else if (ch<127) + { + *dst++ = *src ; + } + } + *dst = 0 ; +} + + +netMonitorChannel::netMonitorChannel ( netMonitorServer* _server ) : buffer(512) +{ + server = _server ; + setTerminator("\r\n"); + + if ( server -> password && server -> password [0] != 0 ) + { + authorized = false ; + push ("Enter password: ") ; + } + else + { + authorized = true ; + push ( netFormat("Connected to \"%s\"... Welcome!\r\n", server -> name ) ) ; + prompt(); + } +} + + +void netMonitorChannel::prompt () +{ + push ( server -> prompt ) ; +} + + +void netMonitorChannel::collectIncomingData (const char* s, int n) +{ + if ( !buffer.append(s,n) ) + { + // denial of service. + push ("BCNU\r\n"); + closeWhenDone(); + } +} + +void netMonitorChannel::foundTerminator (void) +{ + char* line = buffer.getData(); + clean_line ( line ) ; + + if (!authorized) + { + if (strcmp(line,server -> password) == 0) + { + authorized = true ; + push ( netFormat("Connected to \"%s\"... Welcome!\r\n",server -> name) ) ; + prompt () ; + } + else + { + close(); + } + } + else if (*line == 0) + { + prompt(); + } + else if (*line == 4 || strcmp(line,"exit") == 0) + { + push ("BCNU\r\n"); //Be seein' you + closeWhenDone(); + } + else + { + if ( server -> cmdfunc ) + { + server -> cmdfunc ( line ) ; + } + else + { + ulSetError(UL_DEBUG,"echo: %s",line); + + push(line); + push(getTerminator()); + } + + prompt(); + } + buffer.remove(); +} + + +void netMonitorServer::handleAccept (void) +{ + if ( !active ) + { + netAddress addr ; + int s = accept ( &addr ) ; + + ulSetError(UL_DEBUG, "%d: Client %s:%d connected",s,addr.getHost(),addr.getPort()); + + active = new netMonitorChannel ( this ) ; + active -> setHandle (s); + } +} + + +bool netMonitorServer::push (const char* s) +{ + if ( active ) + return active -> push ( s ) ; + return false ; +} diff --git a/src/net/netMonitor.h b/src/net/netMonitor.h new file mode 100644 index 0000000..d3cd6f5 --- /dev/null +++ b/src/net/netMonitor.h @@ -0,0 +1,106 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netMonitor.h 1899 2004-03-21 17:41:07Z sjbaker $ +*/ + +/**** +* NAME +* netMonitor - network monitor server +* +* DESCRIPTION +* netMonitor is a telnet command port with +* password authorization. It can be paired +* with and used to remotely admin another server. +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef NET_MONITOR_H +#define NET_MONITOR_H + +#include "netChat.h" + + +class netMonitorServer : private netChannel +{ + char* name ; + char* password ; + char* prompt ; + void (*cmdfunc)(const char*) ; + class netMonitorChannel* active ; + + friend class netMonitorChannel ; + + virtual bool writable (void) { return false ; } + virtual void handleAccept (void) ; + +public: + + netMonitorServer( const char* _name, int port ) + { + name = ulStrDup(_name); + password = ulStrDup("") ; + prompt = ulStrDup(">>> "); + cmdfunc = 0 ; + active = 0 ; + + open () ; + bind ("", port); + listen (1); + + ulSetError(UL_DEBUG, "Monitor \"%s\" started on port %d",name,port); + } + + ~netMonitorServer() + { + delete[] name ; + delete[] password ; + delete[] prompt ; + } + + const char* getPassword () const { return password; } + void setPassword ( const char* string ) + { + delete[] password ; + password = ulStrDup ( string?string:"" ) ; + } + + void setPrompt ( const char* string ) + { + delete[] prompt ; + prompt = ulStrDup ( string?string:"" ) ; + } + + void setCommandFunc ( void (*func)(const char*) ) + { + cmdfunc = func ; + } + + bool push (const char* s) ; +} ; + +#endif // NET_MONITOR_H diff --git a/src/net/netSocket.cxx b/src/net/netSocket.cxx new file mode 100644 index 0000000..ad57ee7 --- /dev/null +++ b/src/net/netSocket.cxx @@ -0,0 +1,499 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netSocket.cxx 2135 2008-11-10 15:40:28Z fayjf $ +*/ + +#include "netSocket.h" + +#if defined(UL_CYGWIN) || !defined (UL_WIN32) + +#include +#include +#include +#include +#include +#include /* Need both for Mandrake 8.0!! */ +#include +#include +#include + +#else + +#include +#include + +#endif + +#if defined(UL_MSVC) && !defined(socklen_t) +#define socklen_t int +#endif + +/* Paul Wiltsey says we need this for Solaris 2.8 */ + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long)-1) +#endif + +netAddress::netAddress ( const char* host, int port ) +{ + set ( host, port ) ; +} + + +void netAddress::set ( const char* host, int port ) +{ + memset(this, 0, sizeof(netAddress)); + + sin_family = AF_INET ; + sin_port = htons (port); + + /* Convert a string specifying a host name or one of a few symbolic + ** names to a numeric IP address. This usually calls gethostbyname() + ** to do the work; the names "" and "" are special. + */ + + if (host[0] == '\0') + sin_addr = INADDR_ANY; + else + if (host[0] == '<' && strcmp(host, "") == 0) + sin_addr = INADDR_BROADCAST; + else + { + sin_addr = inet_addr ( host ) ; + + if ( sin_addr == INADDR_NONE ) + { + struct hostent *hp = gethostbyname ( host ) ; + + if ( hp != NULL ) + memcpy ( (char *) &sin_addr, hp->h_addr, hp->h_length ) ; + else + { + perror ( "netAddress::set" ) ; + sin_addr = INADDR_ANY ; + } + } + } +} + + +/* Create a string object representing an IP address. + This is always a string of the form 'dd.dd.dd.dd' (with variable + size numbers). */ + +const char* netAddress::getHost () const +{ +#if 0 + const char* buf = inet_ntoa ( sin_addr ) ; +#else + static char buf [32]; + long x = ntohl(sin_addr); + sprintf(buf, "%d.%d.%d.%d", + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff ); +#endif + return buf; +} + +unsigned int netAddress::getIP () const +{ + return sin_addr; +} + +unsigned int netAddress::getPort() const +{ + return ntohs(sin_port); +} + +unsigned int netAddress::getFamily () const +{ + return sin_family; +} + +const char* netAddress::getLocalHost () +{ + //gethostbyname(gethostname()) + + char buf[256]; + memset(buf, 0, sizeof(buf)); + gethostname(buf, sizeof(buf)-1); + const hostent *hp = gethostbyname(buf); + + if (hp && *hp->h_addr_list) + { + in_addr addr = *((in_addr*)*hp->h_addr_list); + const char* host = inet_ntoa(addr); + + if ( host ) + return host ; + } + + return "127.0.0.1" ; +} + + +bool netAddress::getBroadcast () const +{ + return sin_addr == INADDR_BROADCAST; +} + + +netSocket::netSocket () +{ + handle = -1 ; +} + + +netSocket::~netSocket () +{ + close () ; +} + + +void netSocket::setHandle (int _handle) +{ + close () ; + handle = _handle ; +} + + +bool netSocket::open ( bool stream ) +{ + close () ; + handle = ::socket ( AF_INET, (stream? SOCK_STREAM: SOCK_DGRAM), 0 ) ; + return (handle != -1); +} + + +void netSocket::setBlocking ( bool blocking ) +{ + assert ( handle != -1 ) ; + +#if defined(UL_CYGWIN) || !defined (UL_WIN32) + + int delay_flag = ::fcntl (handle, F_GETFL, 0); + + if (blocking) + delay_flag &= (~O_NDELAY); + else + delay_flag |= O_NDELAY; + + ::fcntl (handle, F_SETFL, delay_flag); + +#else + + u_long nblocking = blocking? 0: 1; + ::ioctlsocket(handle, FIONBIO, &nblocking); + +#endif +} + + +void netSocket::setBroadcast ( bool broadcast ) +{ + assert ( handle != -1 ) ; + int result; + if ( broadcast ) { + int one = 1; +#ifdef UL_WIN32 + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one) ); +#else + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one) ); +#endif + } else { + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 ); + } + if ( result < 0 ) { + perror("set broadcast:"); + } + assert ( result != -1 ); +} + + +int netSocket::bind ( const char* host, int port ) +{ + assert ( handle != -1 ) ; + netAddress addr ( host, port ) ; + return ::bind(handle,(const sockaddr*)&addr,sizeof(netAddress)); +} + + +int netSocket::listen ( int backlog ) +{ + assert ( handle != -1 ) ; + return ::listen(handle,backlog); +} + + +int netSocket::accept ( netAddress* addr ) +{ + assert ( handle != -1 ) ; + + if ( addr == NULL ) + { + return ::accept(handle,NULL,NULL); + } + else + { + socklen_t addr_len = (socklen_t) sizeof(netAddress) ; + return ::accept(handle,(sockaddr*)addr,&addr_len); + } +} + + +int netSocket::connect ( const char* host, int port ) +{ + assert ( handle != -1 ) ; + netAddress addr ( host, port ) ; + if ( addr.getBroadcast() ) { + setBroadcast( true ); + } + return ::connect(handle,(const sockaddr*)&addr,sizeof(netAddress)); +} + + +int netSocket::send (const void * buffer, int size, int flags) +{ + assert ( handle != -1 ) ; + return ::send (handle, (const char*)buffer, size, flags); +} + + +int netSocket::sendto ( const void * buffer, int size, + int flags, const netAddress* to ) +{ + assert ( handle != -1 ) ; + return ::sendto(handle,(const char*)buffer,size,flags, + (const sockaddr*)to,sizeof(netAddress)); +} + + +int netSocket::recv (void * buffer, int size, int flags) +{ + assert ( handle != -1 ) ; + return ::recv (handle, (char*)buffer, size, flags); +} + + +int netSocket::recvfrom ( void * buffer, int size, + int flags, netAddress* from ) +{ + assert ( handle != -1 ) ; + socklen_t fromlen = (socklen_t) sizeof(netAddress) ; + return ::recvfrom(handle,(char*)buffer,size,flags,(sockaddr*)from,&fromlen); +} + + +void netSocket::close (void) +{ + if ( handle != -1 ) + { +#if defined(UL_CYGWIN) || !defined (UL_WIN32) + ::close( handle ); +#else + ::closesocket( handle ); +#endif + handle = -1 ; + } +} + + +bool netSocket::isNonBlockingError () +{ +#if defined(UL_CYGWIN) || !defined (UL_WIN32) + switch (errno) { + case EWOULDBLOCK: // always == NET_EAGAIN? + case EALREADY: + case EINPROGRESS: + return true; + } + return false; +#else + int wsa_errno = WSAGetLastError(); + if ( wsa_errno != 0 ) + { + WSASetLastError(0); + ulSetError(UL_WARNING,"WSAGetLastError() => %d",wsa_errno); + switch (wsa_errno) { + case WSAEWOULDBLOCK: // always == NET_EAGAIN? + case WSAEALREADY: + case WSAEINPROGRESS: + return true; + } + } + return false; +#endif +} + + +////////////////////////////////////////////////////////////////////// +// +// modified version by os +// +////////////////////////////////////////////////////////////////////// +int netSocket::select ( netSocket** reads, netSocket** writes, int timeout ) +{ + fd_set r,w; + int retval; + + FD_ZERO (&r); + FD_ZERO (&w); + + int i, k ; + int num = 0 ; + + if ( reads ) + { + for ( i=0; reads[i]; i++ ) + { + int fd = reads[i]->getHandle(); + FD_SET (fd, &r); + num++; + } + } + + if ( writes ) + { + for ( i=0; writes[i]; i++ ) + { + int fd = writes[i]->getHandle(); + FD_SET (fd, &w); + num++; + } + } + + if (!num) + return num ; + + /* Set up the timeout */ + struct timeval tv ; + tv.tv_sec = timeout/1000; + tv.tv_usec = (timeout%1000)*1000; + + // It bothers me that select()'s first argument does not appear to + // work as advertised... [it hangs like this if called with + // anything less than FD_SETSIZE, which seems wasteful?] + + // Note: we ignore the 'exception' fd_set - I have never had a + // need to use it. The name is somewhat misleading - the only + // thing I have ever seen it used for is to detect urgent data - + // which is an unportable feature anyway. + + retval = ::select (FD_SETSIZE, &r, &w, 0, &tv); + + //remove sockets that had no activity + + num = 0 ; + + if ( reads ) + { + for ( k=i=0; reads[i]; i++ ) + { + int fd = reads[i]->getHandle(); + if ( FD_ISSET (fd, &r) ) + { + reads[k++] = reads[i]; + num++; + } + } + reads[k] = NULL ; + } + + if ( writes ) + { + for ( k=i=0; writes[i]; i++ ) + { + int fd = writes[i]->getHandle(); + if ( FD_ISSET (fd, &w) ) + { + writes[k++] = writes[i]; + num++; + } + } + writes[k] = NULL ; + } + + if (retval == 0) // timeout + return (-2); + if (retval == -1)// error + return (-1); + + return num ; +} + + +/* Init/Exit functions */ + +static void netExit ( void ) +{ +#if defined(UL_CYGWIN) || !defined (UL_WIN32) +#else + /* Clean up windows networking */ + if ( WSACleanup() == SOCKET_ERROR ) { + if ( WSAGetLastError() == WSAEINPROGRESS ) { + WSACancelBlockingCall(); + WSACleanup(); + } + } +#endif +} + + +int netInit ( int* argc, char** argv ) +{ + /* Legacy */ + + return netInit () ; +} + + +int netInit () +{ + assert ( sizeof(sockaddr_in) == sizeof(netAddress) ) ; + +#if defined(UL_CYGWIN) || !defined (UL_WIN32) +#else + /* Start up the windows networking */ + WORD version_wanted = MAKEWORD(1,1); + WSADATA wsaData; + + if ( WSAStartup(version_wanted, &wsaData) != 0 ) { + ulSetError(UL_WARNING,"Couldn't initialize Winsock 1.1"); + return(-1); + } +#endif + + atexit( netExit ) ; + return(0); +} + + +const char* netFormat ( const char* format, ... ) +{ + static char buffer[ 256 ]; + va_list argptr; + va_start(argptr, format); + vsprintf( buffer, format, argptr ); + va_end(argptr); + return( buffer ); +} + + diff --git a/src/net/netSocket.h b/src/net/netSocket.h new file mode 100644 index 0000000..80e65da --- /dev/null +++ b/src/net/netSocket.h @@ -0,0 +1,128 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: netSocket.h 2135 2008-11-10 15:40:28Z fayjf $ +*/ + +/**** +* NAME +* netSocket - network sockets +* +* DESCRIPTION +* netSocket is a thin C++ wrapper over bsd sockets to +* facilitate porting to other platforms +* +* AUTHOR +* Dave McClurg +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef NET_SOCKET_H +#define NET_SOCKET_H + +#include "ul.h" +#include + +#if defined(UL_MAC_OSX) +# include +#endif + +/* + * Socket address, internet style. + */ +class netAddress +{ + /* DANGER!!! This MUST match 'struct sockaddr_in' exactly! */ +#ifdef UL_MAC_OSX +// This member is added since OS X 10.5.2 ... I'm wondering how to handle this... + __uint8_t sin_len; + __uint8_t sin_family; + in_port_t sin_port; + in_addr_t sin_addr; + char sin_zero[8]; +#else + short sin_family ; + unsigned short sin_port ; + unsigned int sin_addr ; + char sin_zero [ 8 ] ; +#endif + +public: + netAddress () {} + netAddress ( const char* host, int port ) ; + + void set ( const char* host, int port ) ; + const char* getHost () const ; + unsigned int getPort() const ; + unsigned int getIP () const ; + unsigned int getFamily () const ; + static const char* getLocalHost () ; + + bool getBroadcast () const ; +}; + + +/* + * Socket type + */ +class netSocket +{ + int handle ; + +public: + + netSocket () ; + virtual ~netSocket () ; + + int getHandle () const { return handle; } + void setHandle (int handle) ; + + bool open ( bool stream=true ) ; + void close ( void ) ; + int bind ( const char* host, int port ) ; + int listen ( int backlog ) ; + int accept ( netAddress* addr ) ; + int connect ( const char* host, int port ) ; + int send ( const void * buffer, int size, int flags = 0 ) ; + int sendto ( const void * buffer, int size, int flags, const netAddress* to ) ; + int recv ( void * buffer, int size, int flags = 0 ) ; + int recvfrom ( void * buffer, int size, int flags, netAddress* from ) ; + + void setBlocking ( bool blocking ) ; + void setBroadcast ( bool broadcast ) ; + + static bool isNonBlockingError () ; + static int select ( netSocket** reads, netSocket** writes, int timeout ) ; +} ; + + +int netInit ( int* argc, char** argv = NULL ) ; /* Legacy */ + +int netInit () ; + + +const char* netFormat ( const char* fmt, ... ) ; + + +#endif // NET_SOCKET_H + diff --git a/src/psl/Makefile.am b/src/psl/Makefile.am new file mode 100644 index 0000000..6ba1169 --- /dev/null +++ b/src/psl/Makefile.am @@ -0,0 +1,20 @@ + +if BUILD_PSL + +lib_LIBRARIES = libplibpsl.a + +include_HEADERS = psl.h + +libplibpsl_a_SOURCES = psl.cxx pslCodeGen.cxx pslContext.cxx \ + pslCompiler.cxx pslSymbols.cxx pslToken.cxx \ + pslExpression.cxx pslProgram.cxx pslDump.cxx \ + pslError.cxx pslFileIO.cxx pslCompiler.h \ + pslContext.h pslFileIO.h pslLocal.h \ + pslOpcodes.h pslSymbol.h + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = psl.dsp + diff --git a/src/psl/psl.cxx b/src/psl/psl.cxx new file mode 100644 index 0000000..c5a0856 --- /dev/null +++ b/src/psl/psl.cxx @@ -0,0 +1,66 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: psl.cxx 1708 2002-09-27 21:19:59Z ude $ +*/ + + +#include "pslLocal.h" + +int _pslInitialised = FALSE ; +static char *_pslScriptPath = NULL ; + +void pslInit () { _pslInitialised = TRUE ; } + +void pslScriptPath ( const char *path ) +{ + delete [] _pslScriptPath ; + _pslScriptPath = ulStrDup ( path ) ; +} + + +char* _pslMakeScriptPath ( char* path, const char* fname ) +{ + if ( fname != NULL && fname [ 0 ] != '\0' ) + { + if ( ! ulIsAbsolutePathName ( fname ) && + _pslScriptPath != NULL && _pslScriptPath[0] != '\0' ) + { + strcpy ( path, _pslScriptPath ) ; + strcat ( path, "/" ) ; + strcat ( path, fname ) ; + } + else + strcpy ( path, fname ) ; + + /* Convert backward slashes to forward slashes */ + + for ( char* ptr = path ; *ptr ; ptr ++ ) + if ( *ptr == '\\' ) + *ptr = '/' ; + } + else + path [0] = 0 ; + + return path ; +} + + + diff --git a/src/psl/psl.dsp b/src/psl/psl.dsp new file mode 100644 index 0000000..18ad01b --- /dev/null +++ b/src/psl/psl.dsp @@ -0,0 +1,166 @@ +# Microsoft Developer Studio Project File - Name="psl" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=psl - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "psl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "psl.mak" CFG="psl - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "psl - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "psl - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "psl - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy psl.h ..\..\psl.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "psl - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\psl_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy psl.h ..\..\psl.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "psl - Win32 Release" +# Name "psl - Win32 Debug" +# Begin Source File + +SOURCE=.\pslCodeGen.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslCompiler.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslCompiler.h +# End Source File +# Begin Source File + +SOURCE=.\pslContext.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslContext.h +# End Source File +# Begin Source File + +SOURCE=.\psl.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslDump.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslError.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslExpression.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslFileIO.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslFileIO.h +# End Source File +# Begin Source File + +SOURCE=.\psl.h +# End Source File +# Begin Source File + +SOURCE=.\pslLocal.h +# End Source File +# Begin Source File + +SOURCE=.\pslOpcodes.h +# End Source File +# Begin Source File + +SOURCE=.\pslProgram.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslSymbol.h +# End Source File +# Begin Source File + +SOURCE=.\pslSymbols.cxx +# End Source File +# Begin Source File + +SOURCE=.\pslToken.cxx +# End Source File +# End Target +# End Project diff --git a/src/psl/psl.h b/src/psl/psl.h new file mode 100644 index 0000000..67602f6 --- /dev/null +++ b/src/psl/psl.h @@ -0,0 +1,348 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: psl.h 2072 2006-03-25 00:35:57Z bram $ +*/ + +#ifndef _PSL_H +#define _PSL_H 1 + +#include +#include "ul.h" + +enum pslResult +{ + PSL_PROGRAM_END, + PSL_PROGRAM_PAUSE, + PSL_PROGRAM_CONTINUE +} ; + + +#define PSL_COMPILETIME_WARNING 1 +#define PSL_COMPILETIME_ERROR 2 +#define PSL_RUNTIME_WARNING 3 +#define PSL_RUNTIME_ERROR 4 + +typedef unsigned char pslOpcode ; +class pslContext ; +class pslCompiler ; +class pslProgram ; + + +enum pslType +{ + PSL_INT = 0, + PSL_FLOAT = 1, + PSL_STRING = 3, + PSL_VOID = 4 +} ; + + +class pslNumber +{ +protected: + + union + { + int i ; + float f ; + } ; + + char *s ; + + + pslType t ; + +public: + + pslNumber () { t = PSL_VOID ; s = NULL ; } + virtual ~pslNumber () { } + + virtual void set ( int ) = 0 ; + virtual void set ( float ) = 0 ; + virtual void set ( const char * ) = 0 ; + virtual void set ( const pslNumber * ) = 0 ; + + void reset () { t = PSL_VOID ; i = 0 ; delete [] s ; s = NULL ; } + + pslType getType () const { return t ; } + void setType ( pslType _type ) { t = _type ; } + + int getInt () const + { + switch ( t ) + { + case PSL_INT : return i ; + case PSL_FLOAT : return (int) f ; + case PSL_STRING : return (int) strtol(s,NULL,0) ; + case PSL_VOID : return 0 ; + } + return 0 ; + } + + float getFloat () const + { + switch ( t ) + { + case PSL_INT : return (float) i ; + case PSL_FLOAT : return f ; + case PSL_STRING : return (float) atof( s ) ; + case PSL_VOID : return 0.0f ; + } + return 0.0f ; + } + + char *getString () const + { + switch ( t ) + { + case PSL_STRING : return s ; + case PSL_INT : + case PSL_FLOAT : + case PSL_VOID : return NULL ; + } + return NULL ; + } + +} ; + + + +/* + psVariables can change value - but their type is + fixed once set. +*/ + +class pslVariable : public pslNumber +{ + pslVariable *array ; + int array_size ; +public: + + pslVariable () { array = NULL ; array_size = 0 ; } + virtual ~pslVariable () { } + + void setArrayType ( pslType _type, int arraysize ) + { + setType ( _type ) ; + delete [] array ; + array_size = arraysize ; + array = new pslVariable [ array_size ] ; + + for ( int i = 0 ; i < array_size ; i++ ) + { + array [ i ] . setType ( getType () ) ; + array [ i ] . set ( 0 ) ; + } + } + + virtual void set ( int v ) + { + switch ( t ) + { + case PSL_INT : i = v ; return ; + case PSL_FLOAT : f = (float) v ; return ; + case PSL_STRING : + case PSL_VOID : return ; + } + } + + virtual void set ( float v ) + { + switch ( t ) + { + case PSL_INT : i = (int) v ; return ; + case PSL_FLOAT : f = v ; return ; + case PSL_STRING : + case PSL_VOID : return ; + } + } + + virtual void set ( const char *v ) + { + switch ( t ) + { + case PSL_INT : i = (int) strtol ( v, NULL, 0 ) ; return ; + case PSL_FLOAT : f = (float) atof ( v ) ; return ; + case PSL_STRING : delete [] s ; + if ( v == NULL ) + s = ulStrDup ( "" ) ; + else + s = ulStrDup ( v ) ; + return ; + case PSL_VOID : return ; + } + } + + virtual void set ( const pslNumber *v ) + { + switch ( t ) + { + case PSL_INT : set ( v -> getInt () ) ; return ; + case PSL_FLOAT : set ( v -> getFloat () ) ; return ; + case PSL_STRING : set ( v -> getString () ) ; return ; + case PSL_VOID : return ; + } + } + + + pslVariable *getIndex ( int index ) + { + if ( index < 0 || index >= array_size || array == NULL ) + return this ; + + return & ( array [ index ] ) ; + } + + + virtual void set ( int v, int index ) + { + if ( index < 0 || index >= array_size || array == NULL ) + set ( v ) ; + + array [ index ] . set ( v ) ; + } + + virtual void set ( float v, int index ) + { + if ( index < 0 || index >= array_size || array == NULL ) + set ( v ) ; + + array [ index ] . set ( v ) ; + } + + virtual void set ( const char *v, int index ) + { + if ( index < 0 || index >= array_size || array == NULL ) + set ( v ) ; + + array [ index ] . set ( v ) ; + } + + virtual void set ( const pslNumber *v, int index ) + { + if ( index < 0 || index >= array_size || array == NULL ) + set ( v ) ; + + array [ index ] . set ( v ) ; + } + +} ; + + +/* + psValues can change their type as needed. +*/ + +class pslValue : public pslNumber +{ +public: + virtual ~pslValue () { } + virtual void set () { t = PSL_VOID ; } + virtual void set ( int v ) { t = PSL_INT ; i = v ; } + virtual void set ( float v ) { t = PSL_FLOAT ; f = v ; } + virtual void set ( const char *v ) { t = PSL_STRING ; + delete [] s ; + if ( v == NULL ) + s = ulStrDup ( "" ) ; + else + s = ulStrDup ( v ) ; } + + virtual void set ( const pslNumber *v ) + { + t = v -> getType () ; + + switch ( t ) + { + case PSL_INT : set ( v -> getInt () ) ; break ; + case PSL_FLOAT : set ( v -> getFloat () ) ; break ; + case PSL_STRING : set ( v -> getString () ) ; break ; + case PSL_VOID : break ; + } + } + +} ; + + +class pslExtension +{ +public: + const char *symbol ; + int argc ; + pslValue (*func) ( int, pslValue *, pslProgram *p ) ; +} ; + + + +class pslProgram +{ + pslOpcode *code ; + pslContext *context ; + pslCompiler *compiler ; + const pslExtension *extensions ; + + void *userData ; + + char *progName ; + + int force_trace ; + int force_stacktrace ; + +public: + + pslProgram ( const pslExtension *ext, const char *_progName = NULL ) ; + pslProgram ( pslProgram *src, const char *_progName = NULL ) ; + + ~pslProgram () ; + + pslContext *getContext () const { return context ; } + pslOpcode *getCode () const { return code ; } + pslCompiler *getCompiler () const { return compiler ; } + const pslExtension *getExtensions () const { return extensions ; } + + int getStackTraceFlag () const { return force_stacktrace ; } + + char *getProgName () const { return progName ; } + + void setProgName ( const char *nm ) + { + delete [] progName ; + progName = ulStrDup ( nm ) ; + } + + void *getUserData () const { return userData ; } + void setUserData ( void *ud ) { userData = ud ; } + + void dump () const ; + int compile ( const char *fname ) ; + int compile ( const char *memptr, const char *fname ) ; + int compile ( FILE *fd ) ; + void reset () ; + pslResult step () ; + pslResult trace () ; +} ; + + +void pslInit () ; +void pslScriptPath ( const char *path ) ; +void pslSetErrorCallback ( void (*CB) ( pslProgram *, int, char *, int, char * ) ) ; + +#endif + diff --git a/src/psl/pslCodeGen.cxx b/src/psl/pslCodeGen.cxx new file mode 100644 index 0000000..03a67cf --- /dev/null +++ b/src/psl/pslCodeGen.cxx @@ -0,0 +1,394 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslCodeGen.cxx 1891 2004-03-18 22:09:07Z sjbaker $ +*/ + + +#include "pslLocal.h" + + +void pslCompiler::genCodeByte ( pslOpcode op ) +{ + if ( next_code >= MAX_CODE - 1 ) + error ( "Program too big!" ) ; + else + code [ next_code++ ] = op ; +} + + +void pslCompiler::genCodeAddr ( pslAddress a ) +{ + genCodeByte ( a & 0xFF ) ; + genCodeByte ( ( a >> 8 ) & 0xFF ) ; +} + + +void pslCompiler::genLineNumber ( int l ) +{ + genCodeByte ( OPCODE_LINE_NUMBER ) ; + genCodeByte ( l & 0xFF ) ; + genCodeByte ( ( l >> 8 ) & 0xFF ) ; +} + + +void pslCompiler::genCharConstant ( char c ) +{ + /* A bit wasteful but... */ + + genCodeByte ( OPCODE_PUSH_INT_CONSTANT ) ; + genCodeByte ( c ) ; + genCodeByte ( 0 ) ; + genCodeByte ( 0 ) ; + genCodeByte ( 0 ) ; +} + + +void pslCompiler::genConstant ( const char *c ) +{ + int isInteger = TRUE ; + + for ( const char *p = c ; *p != '\0' ; p++ ) + if ( *p == '.' || *p == 'f' || *p == 'F' ) + { + isInteger = FALSE ; + break ; + } + + if ( isInteger ) + genIntConstant ( c ) ; + else + genFloatConstant ( c ) ; +} + + +void pslCompiler::genStringConstant ( const char *c ) +{ + genCodeByte ( OPCODE_PUSH_STRING_CONSTANT ) ; + + for ( int i = 0 ; c [ i ] != '\0' ; i++ ) + genCodeByte ( (unsigned char)( c [ i ]) ) ; + + genCodeByte ( '\0' ) ; +} + + +void pslCompiler::genIntConstant ( int i ) +{ + char *ii = (char *) & i ; + + genCodeByte ( OPCODE_PUSH_INT_CONSTANT ) ; + genCodeByte ( ii [ 0 ] ) ; + genCodeByte ( ii [ 1 ] ) ; + genCodeByte ( ii [ 2 ] ) ; + genCodeByte ( ii [ 3 ] ) ; +} + +void pslCompiler::genIntConstant ( const char *c ) +{ + int i = (int) strtol ( c, NULL, 0 ) ; + genIntConstant ( i ) ; +} + +void pslCompiler::genFloatConstant ( const char *c ) +{ + float f = (float) atof ( c ) ; + char *ff = (char *) & f ; + + genCodeByte ( OPCODE_PUSH_FLOAT_CONSTANT ) ; + genCodeByte ( ff [ 0 ] ) ; + genCodeByte ( ff [ 1 ] ) ; + genCodeByte ( ff [ 2 ] ) ; + genCodeByte ( ff [ 3 ] ) ; +} + +void pslCompiler::genGetParameter ( pslAddress var, int argpos ) +{ + genCodeByte ( OPCODE_GET_PARAMETER ) ; + genCodeByte ( (unsigned char) var ) ; + genCodeByte ( argpos ) ; +} + +int pslCompiler::genMakeIntArray ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_INT_ARRAY ) ; + genCodeByte ( a ) ; + return a ; +} + +int pslCompiler::genMakeFloatArray ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_FLOAT_ARRAY ) ; + genCodeByte ( a ) ; + return a ; +} + +int pslCompiler::genMakeStringArray ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_STRING_ARRAY ) ; + genCodeByte ( a ) ; + return a ; +} + +int pslCompiler::genMakeIntVariable ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_INT_VARIABLE ) ; + genCodeByte ( a ) ; + return a ; +} + +int pslCompiler::genMakeFloatVariable ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_FLOAT_VARIABLE ) ; + genCodeByte ( a ) ; + return a ; +} + +int pslCompiler::genMakeStringVariable ( const char *c ) +{ + int a = getVarSymbol ( c ) ; + + genCodeByte ( OPCODE_SET_STRING_VARIABLE ) ; + genCodeByte ( a ) ; + return a ; +} + + +void pslCompiler::genVariable ( const char *c, int array_ref ) +{ + int a = getVarSymbol ( c ) ; + + genIntConstant ( array_ref ) ; + genIntConstant ( a ) ; +} + +void pslCompiler::genFetch () +{ + genCodeByte ( OPCODE_FETCH ) ; +} + +void pslCompiler::genIncrementFetch () +{ + genCodeByte ( OPCODE_INCREMENT_FETCH ) ; +} + +void pslCompiler::genDecrementFetch () +{ + genCodeByte ( OPCODE_DECREMENT_FETCH ) ; +} + +void pslCompiler::genIncrementLValue () +{ + genCodeByte ( OPCODE_INCREMENT_LVALUE ) ; +} + +void pslCompiler::genDecrementLValue () +{ + genCodeByte ( OPCODE_DECREMENT_LVALUE ) ; +} + +void pslCompiler::genAssignment () +{ + genCodeByte ( OPCODE_POP_VARIABLE ) ; +} + +void pslCompiler::genAddAssignment () +{ + genCodeByte ( OPCODE_POP_ADD_VARIABLE ) ; +} + + +void pslCompiler::genSubAssignment () +{ + genCodeByte ( OPCODE_POP_SUB_VARIABLE ) ; +} + + +void pslCompiler::genMulAssignment () +{ + genCodeByte ( OPCODE_POP_MUL_VARIABLE ) ; +} + + +void pslCompiler::genModAssignment () +{ + genCodeByte ( OPCODE_POP_MOD_VARIABLE ) ; +} + + +void pslCompiler::genDivAssignment () +{ + genCodeByte ( OPCODE_POP_DIV_VARIABLE ) ; +} + + +void pslCompiler::genAndAssignment () +{ + genCodeByte ( OPCODE_POP_AND_VARIABLE ) ; +} + + +void pslCompiler::genOrAssignment () +{ + genCodeByte ( OPCODE_POP_OR_VARIABLE ) ; +} + + +void pslCompiler::genXorAssignment () +{ + genCodeByte ( OPCODE_POP_XOR_VARIABLE ) ; +} + + +void pslCompiler::genSHLAssignment () +{ + genCodeByte ( OPCODE_POP_SHL_VARIABLE ) ; +} + + +void pslCompiler::genSHRAssignment () +{ + genCodeByte ( OPCODE_POP_SHR_VARIABLE ) ; +} + + +void pslCompiler::genCall ( const char *c, int argc ) +{ + int ext = getExtensionSymbol ( c ) ; + + if ( ext < 0 ) + { + genIntConstant ( argc ) ; + genCodeByte ( OPCODE_CALL ) ; + + int a = getCodeSymbol ( c, next_code ) ; + + genCodeAddr ( a ) ; + genCodeByte ( argc ) ; + } + else + { + genCodeByte ( OPCODE_CALLEXT ) ; + genCodeByte ( ext ) ; + genCodeByte ( argc ) ; + } +} + +void pslCompiler::genExchange () { genCodeByte ( OPCODE_EXCHANGE ) ; } +void pslCompiler::genReturn () { genCodeByte ( OPCODE_RETURN ) ; } +void pslCompiler::genPop () { genCodeByte ( OPCODE_POP ) ; } +void pslCompiler::genSubtract () { genCodeByte ( OPCODE_SUB ) ; } +void pslCompiler::genAdd () { genCodeByte ( OPCODE_ADD ) ; } +void pslCompiler::genDivide () { genCodeByte ( OPCODE_DIV ) ; } +void pslCompiler::genMultiply () { genCodeByte ( OPCODE_MULT ) ; } +void pslCompiler::genModulo () { genCodeByte ( OPCODE_MOD ) ; } +void pslCompiler::genNegate () { genCodeByte ( OPCODE_NEG ) ; } +void pslCompiler::genNot () { genCodeByte ( OPCODE_NOT ) ; } +void pslCompiler::genTwiddle () { genCodeByte ( OPCODE_TWIDDLE ) ; } +void pslCompiler::genOrOr () { genCodeByte ( OPCODE_OROR ) ; } +void pslCompiler::genAndAnd () { genCodeByte ( OPCODE_ANDAND ) ; } +void pslCompiler::genOr () { genCodeByte ( OPCODE_OR ) ; } +void pslCompiler::genAnd () { genCodeByte ( OPCODE_AND ) ; } +void pslCompiler::genXor () { genCodeByte ( OPCODE_XOR ) ; } +void pslCompiler::genShiftLeft () { genCodeByte ( OPCODE_SHIFTLEFT ) ; } +void pslCompiler::genShiftRight () { genCodeByte ( OPCODE_SHIFTRIGHT ) ; } + +void pslCompiler::genLess () { genCodeByte ( OPCODE_LESS ) ; } +void pslCompiler::genLessEqual () { genCodeByte ( OPCODE_LESSEQUAL ) ; } +void pslCompiler::genGreater () { genCodeByte ( OPCODE_GREATER ) ; } +void pslCompiler::genGreaterEqual () { genCodeByte ( OPCODE_GREATEREQUAL); } +void pslCompiler::genNotEqual () { genCodeByte ( OPCODE_NOTEQUAL ) ; } +void pslCompiler::genEqual () { genCodeByte ( OPCODE_EQUAL ) ; } + +void pslCompiler::genStackDup () { genCodeByte ( OPCODE_STACK_DUPLICATE ) ; } + +int pslCompiler::genPeekJumpIfTrue ( int l ) +{ + genCodeByte ( OPCODE_PEEK_JUMP_TRUE ) ; + + int res = next_code ; + + genCodeAddr ( l ) ; + + return res ; +} + +int pslCompiler::genPeekJumpIfFalse ( int l ) +{ + genCodeByte ( OPCODE_PEEK_JUMP_FALSE ) ; + + int res = next_code ; + + genCodeAddr ( l ) ; + + return res ; +} + +int pslCompiler::genJumpIfTrue ( int l ) +{ + genCodeByte ( OPCODE_JUMP_TRUE ) ; + + int res = next_code ; + + genCodeAddr ( l ) ; + + return res ; +} + +int pslCompiler::genJumpIfFalse ( int l ) +{ + genCodeByte ( OPCODE_JUMP_FALSE ) ; + + int res = next_code ; + + genCodeAddr ( l ) ; + + return res ; +} + +int pslCompiler::genJump ( int l ) +{ + genCodeByte ( OPCODE_JUMP ) ; + + int res = next_code ; + + genCodeAddr ( l ) ; + + return res ; +} + + +int pslCompiler::genPauseStatement() +{ + genCodeByte ( OPCODE_PAUSE ) ; + return TRUE ; +} + + diff --git a/src/psl/pslCompiler.cxx b/src/psl/pslCompiler.cxx new file mode 100644 index 0000000..1e705f8 --- /dev/null +++ b/src/psl/pslCompiler.cxx @@ -0,0 +1,884 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslCompiler.cxx 1745 2003-01-06 05:10:14Z sjbaker $ +*/ + + +#include "pslLocal.h" +#include "ul.h" + + +int pslCompiler::compile ( const char *fname ) +{ + char filename [ 1024 ] ; + _pslMakeScriptPath ( filename, fname ) ; + + FILE *fd = fopen ( filename, "ra" ) ; + + if ( fd == NULL ) + { + perror ( "PSL:" ) ; + ulSetError ( UL_WARNING, "PSL: Failed while opening '%s' for reading.", + filename ); + return FALSE ; + } + + return compile ( fd, fname ) ; +} + + +int pslCompiler::compile ( const char *memptr, const char *fname ) +{ + init () ; + + _pslPushDefaultFile ( memptr, (fname == NULL) ? progName : fname ) ; + genProgram () ; + _pslPopDefaultFile () ; + + return cleanup () ; +} + + +int pslCompiler::compile ( FILE *fd, const char *fname ) +{ + init () ; + + _pslPushDefaultFile ( fd, (fname == NULL) ? progName : fname ) ; + genProgram () ; + _pslPopDefaultFile () ; + + return cleanup () ; +} + + +int pslCompiler::cleanup () +{ + const char *dump_env = getenv ( "PSL_DUMP" ) ; + + if ( num_errors != 0 || num_warnings != 0 ) + fprintf ( stderr, "PSL: '%s' Compiled with %d Warnings, %d Fatal Errors\n", + progName, num_warnings, num_errors ) ; + + /* If there are errors, prevent the program from running. */ + + if ( num_errors != 0 ) + { + if ( dump_env != NULL && + ulStrEqual ( dump_env, "on_error" ) ) + dump () ; + + next_code = 0 ; + genCodeByte ( OPCODE_HALT ) ; + } + else + if ( dump_env != NULL && + ulStrEqual ( dump_env, "always" ) ) + dump () ; + + return num_errors ; +} + + + +int pslCompiler::genReturnStatement () +{ + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( c [ 0 ] == ';' ) /* Return without data == "return 0" */ + { + ungetToken ( c ) ; + genConstant ( "0.0" ) ; + } + else + { + ungetToken ( c ) ; + genExpression () ; + } + + genReturn () ; + return TRUE ; +} + + + +/* Administer the break/continue jump addresses */ + +void pslCompiler::pushBreakToLabel () +{ + if ( next_break >= MAX_LABEL-1 ) + error ( "Too many nested 'break' contexts" ) ; + else + breakToAddressStack [ next_break++ ] = next_tmp_label++ ; +} + +void pslCompiler::pushNoContinue () +{ + continueToAddressStack [ next_continue++ ] = -1 ; +} + +int pslCompiler::pushContinueToLabel () +{ + if ( next_continue >= MAX_LABEL-1 ) + error ( "Too many nested 'continue' contexts" ) ; + else + continueToAddressStack [ next_continue++ ] = next_tmp_label++ ; + + return next_tmp_label-1 ; +} + +void pslCompiler::setContinueToLabel ( int which ) +{ + char s [ 10 ] ; + sprintf ( s, "L%d", which ) ; + setCodeSymbol ( s, next_code ) ; +} + +void pslCompiler::popBreakToLabel () +{ + char s [ 10 ] ; + sprintf ( s, "L%d", breakToAddressStack[next_break-1] ) ; + setCodeSymbol ( s, next_code ) ; + next_break-- ; +} + +void pslCompiler::popContinueToLabel () +{ + next_continue-- ; +} + + + +/* Implement actual break and continue statements. */ + +int pslCompiler::genBreakStatement () +{ + if ( next_break <= 0 ) + return error ( "'break' statement is not inside a 'switch' or a loop." ) ; + + char s [ 10 ] ; + sprintf ( s, "L%d", breakToAddressStack [ next_break-1 ] ) ; + genJump ( getCodeSymbol ( s, next_code+1 ) ) ; + return TRUE ; +} + + +int pslCompiler::genContinueStatement () +{ + if ( next_break <= 0 ) + return error ( "'continue' statement is not inside a loop." ) ; + + if ( continueToAddressStack [ next_continue-1 ] < 0 ) + return error ( "'continue' statement not allowed inside a 'switch'." ) ; + + char s [ 10 ] ; + sprintf ( s, "L%d", continueToAddressStack [ next_continue-1 ] ) ; + genJump ( getCodeSymbol ( s, next_code+1 ) ) ; + return TRUE ; +} + + + + + +int pslCompiler::genSwitchStatement () +{ + if ( ! genExpression () ) + return error ( "Missing control expression for 'switch'" ) ; + + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; /* Hopefully, the word 'while' */ + + if ( c [ 0 ] != '{' ) + return error ( "Missing '{' after 'switch'" ) ; + + int jumpToNextCase = genJump ( 0 ) ; + int jumpAfterTest = 0 ; + + pushBreakToLabel () ; + pushNoContinue () ; + + while ( TRUE ) + { + getToken ( c ) ; + + if ( strcmp ( c, "case" ) == 0 ) + { + jumpAfterTest = genJump ( 0 ) ; + + code [ jumpToNextCase ] = next_code & 0xFF ; + code [ jumpToNextCase+1 ] = ( next_code >> 8 ) & 0xFF ; + + genStackDup () ; + + if ( ! genExpression () ) + error ( "Missing expression after 'case'." ) ; + + getToken ( c ) ; + + if ( c[0] != ':' ) + error ( "Missing ':' after 'case' expression." ) ; + + genEqual () ; + + jumpToNextCase = genJumpIfFalse ( 0 ) ; + + code [ jumpAfterTest ] = next_code & 0xFF ; + code [ jumpAfterTest+1 ] = ( next_code >> 8 ) & 0xFF ; + } + else + if ( strcmp ( c, "default" ) == 0 ) + { + code [ jumpToNextCase ] = next_code & 0xFF ; + code [ jumpToNextCase+1 ] = ( next_code >> 8 ) & 0xFF ; + + getToken ( c ) ; + + if ( c[0] != ':' ) + error ( "Missing ':' after 'default'." ) ; + } + else + if ( strcmp ( c, "}" ) == 0 ) + { + ungetToken ( ";" ) ; + break ; + } + else + { + ungetToken ( c ) ; + + if ( ! genStatement () ) + error ( "Missing statement within switch." ) ; + + getToken ( c ) ; + + if ( c [ 0 ] != ';' ) + error ( "Missing semicolon." ) ; + } + } + + popBreakToLabel () ; + popContinueToLabel () ; + genPop () ; + return TRUE ; +} + + + +int pslCompiler::genDoWhileStatement () +{ + /* Remember place to jump back to */ + + int start_loc = next_code ; + + pushBreakToLabel () ; + setContinueToLabel ( pushContinueToLabel () ) ; + + if ( ! genStatement () ) + return error ( "Missing statement for 'do/while'" ) ; + + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; /* The final ';' of the action */ + + getToken ( c ) ; /* Hopefully, the word 'while' */ + + if ( strcmp ( c, "while" ) != 0 ) + return error ( "Missing 'while' for 'do/while'" ) ; + + if ( ! genExpression () ) + return error ( "Missing expression for 'while' in a 'do/while'" ) ; + + genJumpIfTrue ( start_loc ) ; + + popBreakToLabel () ; + popContinueToLabel () ; + return TRUE ; +} + + +int pslCompiler::genForStatement () +{ + char c [ MAX_TOKEN ] ; + + pushLocality () ; + pushBreakToLabel () ; + int ct_lab = pushContinueToLabel () ; + + getToken ( c ) ; /* The initial '(' of the action */ + + if ( c [ 0 ] != '(' ) + { + popLocality () ; + return error ( "Missing '(' for 'for' loop" ) ; + } + + if ( ! genStatement () ) + { + popLocality () ; + return error ( "Missing initialiser for 'if'" ) ; + } + + getToken ( c ) ; /* The ';' after the initialiser */ + + if ( c [ 0 ] != ';' ) + { + popLocality () ; + return error ( "Missing ';' after 'for' loop initialisation" ) ; + } + + /* Remember place to jump back to */ + + int start_loc = next_code ; + + /* The test */ + + if ( ! genExpression () ) + return error ( "Missing test for 'for' loop" ) ; + + getToken ( c ) ; /* The ';' after the initialiser */ + + if ( c [ 0 ] != ';' ) + { + popLocality () ; + return error ( "Missing ';' after 'for' loop test" ) ; + } + + char saved [ MAX_UNGET ][ MAX_TOKEN ] ; + int next_saved = 0 ; + int paren_counter = 0 ; + + do + { + getToken ( saved [ next_saved ] ) ; + + if ( saved [ next_saved ][ 0 ] == '(' ) paren_counter++ ; + if ( saved [ next_saved ][ 0 ] == ')' ) paren_counter-- ; + + if ( next_saved >= MAX_UNGET-1 ) + { + popLocality () ; + return error ( "Too many tokens in 'increment' part of 'for' loop" ) ; + } + + next_saved++ ; + + } while ( paren_counter >= 0 ) ; + + next_saved-- ; /* Throw away the ')' */ + + int label_loc = genJumpIfFalse ( 0 ) ; + + if ( ! genStatement () ) + { + popLocality () ; + return error ( "Missing action body for 'for' loop" ) ; + } + + setContinueToLabel ( ct_lab ) ; + + getToken ( c ) ; /* Throw away the ';' */ + + /* Put the increment test back onto the token stream */ + + ungetToken ( ";" ) ; + + for ( int i = next_saved-1 ; i >= 0 ; i-- ) + ungetToken ( saved[i] ) ; + + if ( ! genStatement () ) + { + popLocality () ; + return error ( "Missing 'increment' part of 'for' loop" ) ; + } + + genJump ( start_loc ) ; + + code [ label_loc ] = next_code & 0xFF ; + code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ; + + popBreakToLabel () ; + popContinueToLabel () ; + popLocality () ; + return TRUE ; +} + + +int pslCompiler::genWhileStatement () +{ + /* Remember place to jump back to */ + + pushBreakToLabel () ; + setContinueToLabel ( pushContinueToLabel () ) ; + + int start_loc = next_code ; + + if ( ! genExpression () ) + return error ( "Missing expression for 'while'" ) ; + + int label_loc = genJumpIfFalse ( 0 ) ; + + if ( ! genStatement () ) + return error ( "Missing statement for 'while'" ) ; + + genJump ( start_loc ) ; + + code [ label_loc ] = next_code & 0xFF ; + code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ; + + popBreakToLabel () ; + popContinueToLabel () ; + + return TRUE ; +} + + +int pslCompiler::genIfStatement () +{ + if ( ! genExpression () ) + return error ( "Missing expression for 'if'" ) ; + + int else_loc = genJumpIfFalse ( 0 ) ; + + if ( ! genStatement () ) + return error ( "Missing statement for 'if'" ) ; + + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( c [ 0 ] != ';' ) + { + ungetToken ( c ) ; + return error ( "Missing ';' or 'else' after 'if' statement" ) ; + } + + getToken ( c ) ; + + if ( strcmp ( c, "else" ) != 0 ) + { + code [ else_loc ] = next_code & 0xFF ; + code [ else_loc+1 ] = ( next_code >> 8 ) & 0xFF ; + + ungetToken ( c ) ; + ungetToken ( ";" ) ; + return TRUE ; + } + + int label_loc = genJump ( 0 ) ; + + code [ else_loc ] = next_code & 0xFF ; + code [ else_loc+1 ] = ( next_code >> 8 ) & 0xFF ; + + if ( ! genStatement () ) + return error ( "Missing statement for 'else'" ) ; + + code [ label_loc ] = next_code & 0xFF ; + code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ; + + return TRUE ; +} + + +int pslCompiler::genFunctionCall ( const char *var ) +{ + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + /* + 'var' should be the name of a function, + 'c' should be an '(' + */ + + if ( c[0] != '(' ) + return error ( "Missing '(' in call to '%s'", var ) ; + + getToken ( c ) ; + + int argc = 0 ; + + while ( c[0] != ')' ) + { + ungetToken ( c ) ; + genExpression () ; + argc++ ; + getToken ( c ) ; + + if ( c[0] == ')' ) + break ; + + if ( c[0] != ',' ) + return error ( "Missing ')' or ',' in call to '%s'", var ) ; + + getToken ( c ) ; + } + + genCall ( var, argc ) ; + return TRUE ; +} + + +int pslCompiler::genCompoundStatement () +{ + char c [ MAX_TOKEN ] ; + + pushLocality () ; + + while ( genStatement () ) + { + getToken ( c ) ; + + if ( c[0] != ';' ) + { + popLocality () ; + return warning ( "Unexpected '%s' in Compound statement", c ) ; + } + } + + getToken ( c ) ; + + if ( c[0] == '}' ) + { + popLocality () ; + ungetToken ( ";" ) ; + return TRUE ; + } + + popLocality () ; + ungetToken ( c ) ; + return warning ( "Unexpected '%s' in Compound statement", c ) ; +} + + +int pslCompiler::genStatement () +{ + char c [ MAX_TOKEN ] ; + + if ( generate_line_numbers ) + genLineNumber ( _pslGetLineNo () ) ; + + getToken ( c ) ; + + if ( strcmp ( c, "static" ) == 0 ) return genStaticVarDecl () ; + if ( strcmp ( c, "string" ) == 0 ) return genLocalVarDecl ( PSL_STRING) ; + if ( strcmp ( c, "int" ) == 0 ) return genLocalVarDecl ( PSL_INT ) ; + if ( strcmp ( c, "float" ) == 0 ) return genLocalVarDecl ( PSL_FLOAT ) ; + if ( strcmp ( c, "return" ) == 0 ) return genReturnStatement () ; + if ( strcmp ( c, "break" ) == 0 ) return genBreakStatement () ; + if ( strcmp ( c, "continue" ) == 0 ) return genContinueStatement () ; + if ( strcmp ( c, "pause" ) == 0 ) return genPauseStatement () ; + if ( strcmp ( c, "for" ) == 0 ) return genForStatement () ; + if ( strcmp ( c, "do" ) == 0 ) return genDoWhileStatement () ; + if ( strcmp ( c, "switch" ) == 0 ) return genSwitchStatement () ; + if ( strcmp ( c, "while" ) == 0 ) return genWhileStatement () ; + if ( strcmp ( c, "if" ) == 0 ) return genIfStatement () ; + if ( strcmp ( c, "case" ) == 0 || strcmp ( c, "default" ) == 0 ) + return error ( "'%s' encountered - not inside 'switch' statement", c ) ; + + if ( c [ 0 ] == '{' ) return genCompoundStatement () ; + + ungetToken ( c ) ; + + if ( genExpression () ) + { + genPop () ; /* Discard result */ + return TRUE ; + } + + return FALSE ; +} + + +void pslCompiler::genProgram () +{ + char c [ MAX_TOKEN ] ; + + /* Compile the program */ + + while ( TRUE ) + { + getToken ( c ) ; + + if ( c[0] == '\0' ) + break ; + + ungetToken ( c ) ; + + genGlobalDeclaration () ; + } + + /* Have the program call 'main' and then halt */ + + genIntConstant ( 0 ) ; /* No arguments to main *YET* */ + + genCodeByte ( OPCODE_CALL ) ; + genCodeAddr ( getCodeSymbol ( "main", next_code ) ) ; + genCodeByte ( 0 ) ; /* Argc */ + genCodeByte ( OPCODE_HALT ) ; + + checkUnresolvedSymbols () ; +} + + + +int pslCompiler::genLocalVarDecl ( pslType t ) +{ + char c [ MAX_TOKEN ] ; + char s [ MAX_TOKEN ] ; + + getToken ( s ) ; + + setVarSymbol ( s ) ; + + getToken ( c ) ; + + if ( c[0] == '[' ) + { + genExpression () ; + + switch ( t ) + { + case PSL_FLOAT : genMakeFloatArray ( s ) ; break ; + case PSL_STRING : genMakeStringArray ( s ) ; break ; + default : genMakeIntArray ( s ) ; break ; + } + + getToken ( c ) ; + + if ( c[0] != ']' ) + return error ( "Missing ']' after array declaration" ) ; + + return TRUE ; + } + + switch ( t ) + { + case PSL_FLOAT : genMakeFloatVariable ( s ) ; break ; + case PSL_STRING : genMakeStringVariable ( s ) ; break ; + default : genMakeIntVariable ( s ) ; break ; + } + + if ( strcmp ( c, "=" ) == 0 ) + { + genVariable ( s, FALSE ) ; + genExpression () ; + genAssignment () ; + genPop () ; + return TRUE ; + } + + ungetToken ( c ) ; + return TRUE ; +} + + + +int pslCompiler::genStaticVarDecl () +{ + return error ( "Static Local Variables are Not Supported Yet." ) ; +} + + + +int pslCompiler::genGlobalVarDecl ( const char *s, pslType t ) +{ + char c [ MAX_TOKEN ] ; + + setVarSymbol ( s ) ; + + getToken ( c ) ; + + if ( c[0] == '[' ) + { + genExpression () ; + + switch ( t ) + { + case PSL_FLOAT : genMakeFloatArray ( s ) ; break ; + case PSL_STRING : genMakeStringArray ( s ) ; break ; + default : genMakeIntArray ( s ) ; break ; + } + + getToken ( c ) ; + + if ( c[0] != ']' ) + return error ( "Missing ']' after array declaration" ) ; + } + else + { + switch ( t ) + { + case PSL_FLOAT : genMakeFloatVariable ( s ) ; break ; + case PSL_STRING : genMakeStringVariable ( s ) ; break ; + default : genMakeIntVariable ( s ) ; break ; + } + + if ( strcmp ( c, "=" ) == 0 ) + { + genVariable ( s, FALSE ) ; + genExpression () ; + genAssignment () ; + genPop () ; + getToken ( c ) ; + } + } + + if ( c[0] != ';' ) + return error ( "Missing ';' after declaration of '%s'", s ) ; + + return TRUE ; +} + + + +int pslCompiler::genGlobalDeclaration () +{ + char c [ MAX_TOKEN ] ; + char fn [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "static" ) == 0 || + strcmp ( c, "extern" ) == 0 ) + { + /* Something complicated should probably happen here! */ + getToken ( c ) ; + } + + pslType t ; + + if ( strcmp ( c, "void" ) == 0 ) t = PSL_VOID ; else + if ( strcmp ( c, "int" ) == 0 ) t = PSL_INT ; else + if ( strcmp ( c, "float" ) == 0 ) t = PSL_FLOAT ; else + if ( strcmp ( c, "string" ) == 0 ) t = PSL_STRING ; else + return error ( "Expected declaration - but got '%s'", c ) ; + + getToken ( fn ) ; + + getToken ( c ) ; + + if ( c[0] == '(' ) + { + ungetToken ( c ) ; + return genFunctionDeclaration ( fn ) ; + } + + if ( c[0] == '[' || strcmp ( c, "=" ) == 0 || c[0] == ';' ) + { + ungetToken ( c ) ; + return genGlobalVarDecl ( fn, t ) ; + } + + return error ( "Expected a declaration - but got '%s'", c); +} + + +int pslCompiler::genFunctionDeclaration ( const char *fn ) +{ + char c [ MAX_TOKEN ] ; + + pslAddress jump_target = genJump ( 0 ) ; + + setCodeSymbol ( fn, next_code ) ; + + getToken ( c ) ; + + if ( c[0] != '(' ) + return error ( "Missing '(' in declaration of '%s'", fn ) ; + + pushLocality () ; + + int argpos = 0 ; + + while ( 1 ) + { + getToken ( c ) ; + + if ( c [ 0 ] == ')' || c [ 0 ] == '\0' ) + break ; + + char s [ MAX_TOKEN ] ; + + getToken ( s ) ; + + pslAddress a = setVarSymbol ( s ) ; + + if ( strcmp ( c, "int" ) == 0 ) genMakeIntVariable ( s ) ; else + if ( strcmp ( c, "float" ) == 0 ) genMakeFloatVariable ( s ) ; else + if ( strcmp ( c, "string" ) == 0 ) genMakeStringVariable ( s ) ; else + { + popLocality () ; + return error ( "Missing ')' in declaration of '%s'", fn ) ; + } + + genGetParameter ( a, argpos++ ) ; + + getToken ( c ) ; + + if ( c[0] == ',' ) + continue ; + + if ( c[0] == ')' ) + break ; + + popLocality () ; + return error ( "Missing ',' or ')' in declaration of '%s'", fn ) ; + } + + if ( c[0] != ')' ) + { + popLocality () ; + return error ( "Missing ')' in declaration of '%s'", fn ) ; + } + + getToken ( c ) ; + + if ( c [ 0 ] != '{' ) + { + popLocality () ; + return error ( "Missing '{' in function '%s'", fn ) ; + } + + if ( ! genCompoundStatement () ) + { + popLocality () ; + return error ( "Missing '}' in function '%s'", fn ) ; + } + + getToken ( c ) ; + + /* If we fall off the end of the function, we still need a return value */ + + genConstant ( "0.0" ) ; + genReturn () ; + + code [ jump_target ] = next_code & 0xFF ; + code [ jump_target+1 ] = (next_code >> 8) & 0xFF ; + + popLocality () ; + return TRUE ; +} + + + diff --git a/src/psl/pslCompiler.h b/src/psl/pslCompiler.h new file mode 100644 index 0000000..9658d4b --- /dev/null +++ b/src/psl/pslCompiler.h @@ -0,0 +1,414 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslCompiler.h 1941 2004-08-05 00:57:38Z puggles $ +*/ + + +struct pslFwdRef +{ + char *symbol ; + pslAddress where ; + + void set ( const char *s, pslAddress w ) + { + symbol = ulStrDup ( s ) ; + where = w ; + } + + pslAddress getWhere () const { return where ; } + + int matches ( const char *s ) const + { + return symbol != NULL && strcmp ( s, symbol ) == 0 ; + } + + pslFwdRef () { symbol = NULL ; } + ~pslFwdRef () { delete [] symbol ; } +} ; + + + +class pslProgram ; + + +class pslCompiler +{ + int ref_count; // reference count. Clean up when this reaches zero + + /* File I/O and preprocessor */ + + int getChar () ; + + void doIncludeStatement () ; + void doDefineStatement () ; + void doIfdefStatement () ; + void doUndefStatement () ; + void doIfndefStatement () ; + void doElseStatement () ; + void doEndifStatement () ; + + int doPreProcessorCommand () ; + void getToken ( char *s, int define_sub = TRUE ) ; + void ungetToken ( const char *s ) ; + void skipToEOL () ; + + + /* Write data into Code space */ + + void genCodeByte ( unsigned char b ) ; + void genCodeAddr ( pslAddress a ) ; + int printOpcode ( FILE *fd, int addr ) const ; + + void genLineNumber ( int l ) ; + + /* Write single byte-coded instructions into code space. */ + + void genStackDup () ; + void genPop () ; + void genSubtract () ; + void genAdd () ; + void genDivide () ; + void genMultiply () ; + void genModulo () ; + void genNegate () ; + void genNot () ; + void genTwiddle () ; + void genOrOr () ; + void genAndAnd () ; + void genOr () ; + void genAnd () ; + void genXor () ; + void genShiftLeft () ; + void genShiftRight () ; + void genLess () ; + void genLessEqual () ; + void genGreater () ; + void genGreaterEqual () ; + void genNotEqual () ; + void genEqual () ; + void genExchange () ; + + int genPeekJumpIfFalse ( int l ) ; + int genPeekJumpIfTrue ( int l ) ; + int genJumpIfFalse ( int l ) ; + int genJumpIfTrue ( int l ) ; + int genJump ( int l ) ; + + void genGetParameter ( pslAddress var, int argpos ) ; + + int genMakeIntVariable ( const char *s ) ; + int genMakeFloatVariable ( const char *s ) ; + int genMakeStringVariable ( const char *s ) ; + + int genMakeIntArray ( const char *s ) ; + int genMakeFloatArray ( const char *s ) ; + int genMakeStringArray ( const char *s ) ; + + void genConstant ( const char *s ) ; + void genIntConstant ( const char *s ) ; + void genFloatConstant ( const char *s ) ; + void genStringConstant ( const char *s ) ; + void genCharConstant ( char c ) ; + void genIntConstant ( int i ) ; + + void genVoidConstant () ; + + void genFetch () ; + + void genIncrementLValue () ; + void genDecrementLValue () ; + void genIncrementFetch () ; + void genDecrementFetch () ; + + void genVariable ( const char *s, int dimension ) ; + void genAssignment () ; + void genAddAssignment () ; + void genSubAssignment () ; + void genMulAssignment () ; + void genModAssignment () ; + void genDivAssignment () ; + void genAndAssignment () ; + void genOrAssignment () ; + void genXorAssignment () ; + void genSHLAssignment () ; + void genSHRAssignment () ; + + void genCall ( const char *s, int argc ) ; + void genReturn () ; + + /* Expression parsers & code generators. */ + + int genLValue () ; + int genPrimitive () ; + int genBitwiseExpression () ; + int genMultExpression () ; + int genAddExpression () ; + int genShiftExpression () ; + int genRelExpression () ; + int genBoolExpression () ; + int genExpression () ; + + /* Statement-level parsers & code generators. */ + + int genBreakStatement () ; + int genContinueStatement() ; + int genReturnStatement () ; + int genPauseStatement () ; + int genSwitchStatement () ; + int genWhileStatement () ; + int genDoWhileStatement () ; + int genForStatement () ; + int genIfStatement () ; + int genFunctionCall ( const char *s ) ; + int genCompoundStatement() ; + int genStatement () ; + + int genLocalVarDecl ( pslType t ) ; + int genGlobalVarDecl ( const char *fn, pslType t ) ; + int genStaticVarDecl () ; + + /* Top level constructs */ + + int genFunctionDeclaration ( const char *fn ) ; + int genGlobalDeclaration () ; + void genProgram () ; + + /* The symbol tables for variables, code and define's */ + + int next_label ; + int next_code_symbol ; + int next_define ; + + int generate_line_numbers ; + + char *define_token [ MAX_SYMBOL ] ; + char *define_replacement [ MAX_SYMBOL ] ; + + int searchDefines ( const char *s ) const ; + + int skipOverride ; + int skippingFlag ; + int next_skippingLevel ; + + int skipping () const + { + return ! skipOverride && skippingFlag != 0 ; + } + + pslSymbol symtab [ MAX_SYMBOL ] ; + pslSymbol code_symtab [ MAX_SYMBOL ] ; + + pslAddress getVarSymbol ( const char *s ) ; + pslAddress setVarSymbol ( const char *s ) ; + + pslAddress getCodeSymbol ( const char *s, pslAddress fixupLoc ) ; + void setCodeSymbol ( const char *s, pslAddress v ) ; + + int getExtensionSymbol ( const char *s ) const ; + + const pslExtension *extensions ; + + /* Forward references to code symbols that are not yet defined */ + + int next_fwdref ; + pslFwdRef forward_ref [ MAX_SYMBOL ] ; + + int locality_stack [ MAX_NESTING ] ; + int locality_sp ; + + void fixup ( const char *s, pslAddress v ) ; + void addFwdRef ( const char *s, pslAddress where ) ; + void checkUnresolvedSymbols () ; + + int cleanup () ; /* Report errors, etc after a compile */ + + void pushLocality () + { + if ( locality_sp >= MAX_NESTING-1 ) + error ( "Too many nested {}'s" ) ; + else + locality_stack [ locality_sp++ ] = next_var ; + } + + void popLocality () + { + if ( locality_sp <= 0 ) + error ( "Locality stack underflow !!" ) ; + + /* Delete any local symbols */ + + for ( int i = locality_stack [ locality_sp-1 ] ; + i < next_var ; i++ ) + { + delete [] symtab [ i ] . symbol ; + symtab [ i ] . symbol = NULL ; + } + + /* Put the next_var pointer back where it belongs */ + + next_var = locality_stack [ --locality_sp ] ; + } + + /* Ikky stuff to remember where break and continue should jump */ + + int breakToAddressStack [ MAX_LABEL ] ; + int continueToAddressStack [ MAX_LABEL ] ; + int next_break ; + int next_tmp_label ; + int next_continue ; + + void pushBreakToLabel () ; + int pushContinueToLabel () ; + void setContinueToLabel ( int which ) ; + void pushNoContinue () ; + + void popBreakToLabel () ; + void popContinueToLabel () ; + + /* Error and warning handlers */ + + int num_errors ; + int num_warnings ; + + void bumpErrors () { num_errors++ ; } + void bumpWarnings () { num_warnings++ ; } + + int error ( const char *fmt, ... ) ; + int warning ( const char *fmt, ... ) ; + + /* Remember the name of the program for debug purposes */ + + char *progName ; + + char *getProgName () const { return progName ; } + + /* Major storage for symbols and byte-codes */ + + int next_var ; + int next_code ; + pslOpcode *code ; + pslContext *context ; + pslProgram *program ; + +public: + + pslCompiler ( pslProgram *prog, + pslOpcode *_code, + const pslExtension *_extn, + const char *_progName ) + { + ref_count = 1; + program = prog ; + progName = ulStrDup ( _progName ) ; + + generate_line_numbers = FALSE ; + code = _code ; + extensions = _extn ; + + for ( int i = 0 ; i < MAX_SYMBOL ; i++ ) + { + define_token [ i ] = NULL ; + define_replacement [ i ] = NULL ; + symtab [ i ] . symbol = NULL ; + forward_ref [ i ] . symbol = NULL ; + code_symtab [ i ] . symbol = NULL ; + } + + init () ; + } + + ~pslCompiler () + { + for ( int i = 0 ; i < MAX_SYMBOL ; i++ ) + { + delete [] define_token [ i ] ; + delete [] define_replacement [ i ] ; + delete [] symtab [ i ] . symbol ; symtab [ i ] . symbol = NULL ; + delete [] code_symtab [ i ] . symbol ; code_symtab [ i ] . symbol = NULL ; + delete [] forward_ref [ i ] . symbol ; forward_ref [ i ] . symbol = NULL ; + } + } + + void ref() + { + ref_count++; + } + + void deref() + { + ref_count--; + if(ref_count < 1) + { + delete [] code; + delete this; + } + } + + const pslExtension *getExtensions () const { return extensions ; } + + int printInstruction ( FILE *fd, int addr ) const ; + + + void generateLineNumbers () + { + generate_line_numbers = TRUE ; + } + + + void init () + { + int i ; + + for ( i = 0 ; i < MAX_CODE ; i++ ) code [ i ] = OPCODE_HALT ; + + for ( i = 0 ; i < MAX_SYMBOL ; i++ ) + { + delete [] define_token [ i ] ; define_token [ i ] = NULL ; + delete [] define_replacement [ i ] ; define_replacement [ i ] = NULL ; + delete [] symtab [ i ] . symbol ; symtab [ i ] . symbol = NULL ; + delete [] code_symtab [ i ] . symbol ; code_symtab [ i ] . symbol = NULL ; + delete [] forward_ref [ i ] . symbol ; forward_ref [ i ] . symbol = NULL ; + } + + num_errors = num_warnings = 0 ; + + locality_sp = 0 ; + + next_define = 0 ; + next_continue = 0 ; + next_break = 0 ; + next_tmp_label= 0 ; + next_fwdref = 0 ; + next_label = 0 ; + next_code_symbol = 0 ; + next_code = 0 ; + next_var = 0 ; + skippingFlag = 0 ; + next_skippingLevel = 1 ; + } + + void dump () const ; + int compile ( const char *fname ) ; + int compile ( FILE *fd, const char *fname = NULL ) ; + int compile ( const char *memptr, const char *prog_name ) ; +} ; + + diff --git a/src/psl/pslContext.cxx b/src/psl/pslContext.cxx new file mode 100644 index 0000000..7ef8ebf --- /dev/null +++ b/src/psl/pslContext.cxx @@ -0,0 +1,864 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslContext.cxx 1703 2002-09-22 20:49:05Z ude $ +*/ + + +#include "pslLocal.h" + +pslVariable *pslContext::peekLValue () +{ + return ( stack[sp-2].getInt() == 0 ) ? + & ( variable [ stack[sp-1].getInt() ] ) : + variable [ stack[sp-1].getInt() ] . getIndex ( stack [ sp-3 ].getInt() ) ; +} + + +pslVariable *pslContext::popLValue () +{ + if ( stack[sp-2].getInt() == 0 ) + { + sp -= 2 ; + return & ( variable [ stack[sp+1].getInt() ] ) ; + } + else + { + sp -= 3 ; + return variable [ stack[sp+2].getInt() ] . + getIndex ( stack [ sp ].getInt() ) ; + } +} + + +pslResult pslContext::step () +{ + switch ( code [ pc ] ) + { + case OPCODE_BAD : + error ( "Suspicious opcode 0x00?!", code[pc] ) ; + pc++ ; + return PSL_PROGRAM_END ; + + case OPCODE_LINE_NUMBER : + line_no = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + pc+=3 ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PUSH_INT_CONSTANT : + { + int ii ; + + memcpy ( & ii, & code [ pc+1 ], sizeof(int) ) ; + + pushInt ( ii ) ; + + pc += sizeof(int) + 1 ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PUSH_FLOAT_CONSTANT : + { + float ff ; + + memcpy ( & ff, & code [ pc+1 ], sizeof(float) ) ; + + pushFloat ( ff ) ; + + pc += sizeof(float) + 1 ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PUSH_STRING_CONSTANT : + { + char *ff = (char *) & code [ pc+1 ] ; + + pushString ( ff ) ; + + pc += strlen ( ff ) + 1 + 1 ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_GET_PARAMETER : + { + int var = code [ ++pc ] ; + int nargs = stack [ sp - 2 ] . getInt () ; + int off = sp - ( nargs + 2 ) + code [ ++pc ] ; + + variable [ var ] . set ( & stack [ off ] ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_POP : + popVoid() ; + pc++ ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_CALLEXT : + { + int ext = code [ ++pc ] ; + int argc = code [ ++pc ] ; + int required_argc = extensions [ ext ] . argc ; + + if ( required_argc >= 0 && argc != required_argc ) + { + warning ( "Wrong number of parameters for function %s\n", + extensions [ ext ] . symbol ) ; + } + + pslValue argv [ MAX_ARGS ] ; + + /* Pop args off the stack in reverse order */ + + for ( int i = argc-1 ; i >= 0 ; i-- ) + popNumber ( & argv[i] ) ; + + /* Euwww! */ + + pslValue v = (*(extensions [ ext ] . func)) (argc,argv,program) ; + + pushNumber ( & v ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_CALL : + pushInt ( pc+4 ) ; + memcpy ( & pc, & code [ pc+1 ], sizeof ( int ) ) ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_RETURN : + { + int nargs ; + pslValue result ; + + popNumber ( &result ) ; + pc = popInt () ; + nargs = popInt () ; + popVoid ( nargs ) ; + pushNumber ( &result ) ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_STACK_DUPLICATE : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pushNumber ( v1 ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SUB : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() - v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt() - v1 -> getInt() ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SHIFTLEFT : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () << v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SHIFTRIGHT : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () >> v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_ADD : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() + v1 -> getFloat() ) ; + else + if ( v1->getType() == PSL_STRING && v2->getType() == PSL_STRING ) + { + int v1_len = strlen ( v1 -> getString () ) ; + int v2_len = strlen ( v2 -> getString () ) ; + char *s = new char [ v1_len + v2_len + 1 ] ; + + memcpy ( s, v2 -> getString (), v2_len ) ; + memcpy ( s+v2_len, v1 -> getString (), v1_len+1 ) ; + + v2 -> set ( s ) ; + delete [] s ; + } + else + v2 -> set ( v2 -> getInt () + v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_NOT : + { + pslValue *v1 = & stack [ sp - 1 ] ; + + v1 -> set ( ! v1 -> getInt () ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_TWIDDLE : + { + pslValue *v1 = & stack [ sp - 1 ] ; + + v1 -> set ( ~ v1 -> getInt () ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_OROR : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () || v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_ANDAND : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () && v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_OR : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () | v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_AND : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () & v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_XOR : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + v2 -> set ( v2 -> getInt () ^ v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_DIV : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + { + if ( v1 -> getFloat() != 0.0f ) + v2 -> set ( v2 -> getFloat() / v1 -> getFloat() ) ; + else + warning ( "Floating Point Divide by Zero!" ) ; + } + else + { + if ( v1 -> getInt() != 0 ) + v2 -> set ( v2 -> getInt() / v1 -> getInt() ) ; + else + warning ( "Integer Divide by Zero!" ) ; + } + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_MOD : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + { + warning ( "Floating Point Modulo!" ) ; + } + else + { + if ( v1 -> getInt() != 0 ) + v2 -> set ( v2 -> getInt () % v1 -> getInt () ) ; + else + warning ( "Integer Modulo Zero!" ) ; + } + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_MULT : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() * v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () * v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_NEG : + { + pslValue *v1 = & stack [ sp - 1 ] ; + + if ( v1->getType() == PSL_FLOAT ) + v1 -> set ( - v1 -> getFloat() ) ; + else + v1 -> set ( - v1 -> getInt () ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_LESS : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() < v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () < v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_LESSEQUAL : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() <= v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () <= v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_GREATER : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() > v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () > v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_GREATEREQUAL : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() >= v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () >= v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_NOTEQUAL : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() != v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () != v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_EQUAL : + { + pslValue *v1 = & stack [ sp - 1 ] ; + pslValue *v2 = & stack [ sp - 2 ] ; + + if ( v1->getType() == PSL_FLOAT || v2->getType() == PSL_FLOAT ) + v2 -> set ( v2 -> getFloat() == v1 -> getFloat() ) ; + else + v2 -> set ( v2 -> getInt () == v1 -> getInt () ) ; + + popVoid () ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PAUSE : + pc++ ; + return PSL_PROGRAM_PAUSE ; + + case OPCODE_HALT : + return PSL_PROGRAM_END ; /* Note: PC is *NOT* incremented. */ + + case OPCODE_PEEK_JUMP_TRUE : + if ( peekInt () ) + pc = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + else + { + sp-- ; + pc += 3 ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PEEK_JUMP_FALSE : + if ( peekInt () ) + { + sp-- ; + pc += 3 ; + } + else + pc = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_JUMP_TRUE : + if ( popInt () ) + pc = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + else + pc += 3 ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_JUMP_FALSE : + if ( popInt () ) + pc += 3 ; + else + pc = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_JUMP : + pc = code [ pc + 1 ] + ( code [ pc + 2 ] << 8 ) ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_PUSH_VARIABLE : + pushNumber ( & variable [ code[++pc] ] ) ; + pc++ ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_EXCHANGE : + { + pslValue n = stack [ sp - 1 ] ; + stack [ sp - 1 ] = stack [ sp - 2 ] ; + stack [ sp - 2 ] = n ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_POP_ADD_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt() + s -> getInt() ) ; + else + if ( v -> getType () == PSL_STRING ) + { + int s1_len = strlen ( v -> getString () ) ; + int s2_len = strlen ( s -> getString () ) ; + char *str = new char [ s1_len + s2_len + 1 ] ; + + memcpy ( str, v -> getString (), s1_len ) ; + memcpy ( str+s1_len, s -> getString (), s2_len+1 ) ; + + v -> set ( str ) ; + delete [] str ; + } + else + v -> set ( v -> getFloat() + s -> getFloat() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_POP_SUB_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt() - s -> getInt() ) ; + else + v -> set ( v -> getFloat() - s -> getFloat() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_MUL_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt() * s -> getInt() ) ; + else + v -> set ( v -> getFloat() * s -> getFloat() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_MOD_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + { + if ( s -> getInt () != 0 ) + v -> set ( v -> getInt() % s -> getInt() ) ; + else + warning ( "Integer Modulo by Zero!" ) ; + } + else + warning ( "Floating Point Modulo!" ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_DIV_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + { + if ( s -> getInt () != 0 ) + v -> set ( v -> getInt() / s -> getInt() ) ; + else + warning ( "Integer Divide by Zero!" ) ; + } + else + { + if ( s -> getFloat () != 0.0f ) + v -> set ( v -> getFloat() / s -> getFloat() ) ; + else + warning ( "Floating Point Divide by Zero!" ) ; + } + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_AND_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + v -> set ( v -> getInt() & s -> getInt() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_OR_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + v -> set ( v -> getInt() | s -> getInt() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_XOR_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + v -> set ( v -> getInt() ^ s -> getInt() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_SHL_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + v -> set ( v -> getInt() << s -> getInt() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_SHR_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + v -> set ( v -> getInt() >> s -> getInt() ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_POP_VARIABLE : + { + pslValue *s = & stack[--sp] ; + pslVariable *v = popLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( s -> getInt () ) ; + else + if ( v -> getType () == PSL_FLOAT ) + v -> set ( s -> getFloat () ) ; + else + v -> set ( s -> getString () ) ; + + pc++ ; + pushNumber ( v ) ; + } + return PSL_PROGRAM_CONTINUE ; + + + case OPCODE_SET_INT_ARRAY : + variable [ code[++pc] ] . setArrayType ( PSL_INT, + stack[--sp].getInt() ) ; + pc++ ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SET_FLOAT_ARRAY : + variable [ code[++pc] ] . setArrayType ( PSL_FLOAT, + stack[--sp].getInt() ) ; + pc++ ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SET_STRING_ARRAY : + variable [ code[++pc] ] . setArrayType ( PSL_STRING, + stack[--sp].getInt() ) ; + pc++ ; + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SET_INT_VARIABLE : + { + pslVariable *v = & ( variable [ code[++pc] ] ) ; + v -> setType ( PSL_INT ) ; + v -> set ( 0 ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SET_FLOAT_VARIABLE : + { + pslVariable *v = & ( variable [ code[++pc] ] ) ; + v -> setType ( PSL_FLOAT ) ; + v -> set ( 0.0f ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_SET_STRING_VARIABLE : + { + pslVariable *v = & ( variable [ code[++pc] ] ) ; + v -> setType ( PSL_STRING ) ; + v -> set ( "" ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_FETCH : + { + pslVariable *v = popLValue () ; + pushNumber ( v ) ; + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_INCREMENT_LVALUE : + { + pslVariable *v = peekLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt () + 1 ) ; + else + v -> set ( v -> getFloat () + 1 ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_DECREMENT_LVALUE : + { + pslVariable *v = peekLValue () ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt () - 1 ) ; + else + v -> set ( v -> getFloat () - 1 ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_INCREMENT_FETCH : + { + pslVariable *v = popLValue () ; + pushNumber ( v ) ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt () + 1 ) ; + else + v -> set ( v -> getFloat () + 1 ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + case OPCODE_DECREMENT_FETCH : + { + pslVariable *v = popLValue () ; + pushNumber ( v ) ; + + if ( v -> getType () == PSL_INT ) + v -> set ( v -> getInt () - 1 ) ; + else + v -> set ( v -> getFloat () - 1 ) ; + + pc++ ; + } + return PSL_PROGRAM_CONTINUE ; + + + default : + error ( "Suspicious opcode 0x02x?!", code[pc] ) ; + return PSL_PROGRAM_END ; + } +} + + +void pslContext::printStack ( FILE *fd ) const +{ + fprintf ( fd, "STACK [%d deep] : ", sp ) ; + + if ( sp >= 8 ) fprintf ( stderr, "..." ) ; + + for ( int i = (sp<8)? 0 : (sp-8) ; i < sp ; i++ ) + switch ( stack[i].getType () ) + { + case PSL_INT : fprintf ( fd, "%d " , stack[i].getInt () ) ; break ; + case PSL_FLOAT : fprintf ( fd, "%gf " , stack[i].getFloat () ) ; break ; + case PSL_STRING : fprintf ( fd, "'%s' ", stack[i].getString() ) ; break ; + case PSL_VOID : fprintf ( fd, " " ) ; break ; + } + + fprintf ( fd, "\n" ) ; +} + + diff --git a/src/psl/pslContext.h b/src/psl/pslContext.h new file mode 100644 index 0000000..02bb9c4 --- /dev/null +++ b/src/psl/pslContext.h @@ -0,0 +1,106 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslContext.h 1703 2002-09-22 20:49:05Z ude $ +*/ + + +class pslContext +{ + pslOpcode *code ; + const pslExtension *extensions ; + pslProgram *program ; + + pslVariable variable [ MAX_VARIABLE ] ; + pslValue stack [ MAX_STACK ] ; + int sp ; + pslAddress pc ; + + int num_errors ; + int num_warnings ; + int line_no ; + + void bumpErrors () { num_errors++ ; } + void bumpWarnings () { num_warnings++ ; } + + char *getProgName () const { return program -> getProgName () ; } + + void error ( const char *fmt, ... ) ; + void warning ( const char *fmt, ... ) ; + + pslVariable *popLValue () ; + pslVariable *peekLValue () ; + +public: + + pslContext ( pslProgram *p ) + { + code = p -> getCode () ; + extensions = p -> getExtensions () ; + program = p ; + + reset () ; + } + + ~pslContext () {} ; + + int getLineNo () const { return line_no ; } + + void printStack ( FILE *fd ) const ; + + void pushInt ( int x ) { stack [ sp++ ] . set ( x ) ; } + void pushFloat ( float x ) { stack [ sp++ ] . set ( x ) ; } + void pushString ( const char *x ) { stack [ sp++ ] . set ( x ) ; } + void pushNumber ( const pslNumber *x ) { stack [ sp++ ] . set ( x ) ; } + + int peekInt () const { return stack [ sp-1 ] . getInt () ; } + float peekFloat () const { return stack [ sp-1 ] . getFloat () ; } + char *peekString () const { return stack [ sp-1 ] . getString () ; } + + void popVoid ( int n = 1 ) { sp -= n ; } + int popInt () { return stack [ --sp ] . getInt () ; } + float popFloat () { return stack [ --sp ] . getFloat () ; } + char *popString () { return stack [ --sp ] . getString () ; } + + void popNumber ( pslNumber *v ) { v -> set ( & stack [ --sp ] ) ; } + + pslResult step () ; + + pslResult trace () + { + if ( program -> getStackTraceFlag () ) printStack ( stdout ) ; + program -> getCompiler () -> printInstruction ( stdout, pc ) ; + fflush ( stdout ) ; + return step () ; + } + + void reset () + { + for ( int i = 0 ; i < MAX_VARIABLE ; i++ ) + variable [ i ] . reset () ; + + line_no = -1 ; + + sp = 0 ; + pc = 0 ; + } +} ; + + diff --git a/src/psl/pslDump.cxx b/src/psl/pslDump.cxx new file mode 100644 index 0000000..7f645bd --- /dev/null +++ b/src/psl/pslDump.cxx @@ -0,0 +1,288 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslDump.cxx 1700 2002-09-22 18:25:16Z sjbaker $ +*/ + + +#include "pslLocal.h" + + +struct OpcodeDecode +{ + const char *s ; + unsigned char opcode ; + int numArgBytes ; +} ; + + + +static const OpcodeDecode opcodeDecode [] = +{ + { "**BAD_INSTRUCTION**", OPCODE_BAD , 0 }, + + { "SOURCE LINE NUMBER:", OPCODE_LINE_NUMBER, 2 }, + + /* Stack operations */ + + { "PUSH_INT_CONSTANT" , OPCODE_PUSH_INT_CONSTANT , sizeof(int) }, + { "PUSH_FLOAT_CONSTANT", OPCODE_PUSH_FLOAT_CONSTANT, sizeof(float) }, + { "PUSH_STRING_CONSTANT",OPCODE_PUSH_STRING_CONSTANT, 0 }, + { "PUSH_VARIABLE" , OPCODE_PUSH_VARIABLE , 1 }, + + { "STACK_DUPLICATE" , OPCODE_STACK_DUPLICATE , 0 }, + + { "POP_VARIABLE" , OPCODE_POP_VARIABLE , 0 }, + { "POP" , OPCODE_POP , 0 }, + + { "FETCH" , OPCODE_FETCH , 0 }, + { "INCREMENT_FETCH" , OPCODE_INCREMENT_FETCH , 0 }, + { "DECREMENT_FETCH" , OPCODE_DECREMENT_FETCH , 0 }, + { "INCREMENT_LVALUE" , OPCODE_INCREMENT_LVALUE , 0 }, + { "DECREMENT_LVALUE" , OPCODE_DECREMENT_LVALUE , 0 }, + + /* Variable creation */ + + { "SET_INT_ARRAY" , OPCODE_SET_INT_ARRAY , 1 }, + { "SET_FLOAT_ARRAY" , OPCODE_SET_FLOAT_ARRAY , 1 }, + { "SET_STRING_ARRAY" , OPCODE_SET_STRING_ARRAY , 1 }, + { "SET_INT_VARIABLE" , OPCODE_SET_INT_VARIABLE , 1 }, + { "SET_FLOAT_VARIABLE" , OPCODE_SET_FLOAT_VARIABLE , 1 }, + { "SET_STRING_VARIABLE", OPCODE_SET_STRING_VARIABLE, 1 }, + { "GET_PARAMETER" , OPCODE_GET_PARAMETER , 2 }, + + /* Flow Control */ + + { "CALLEXT", OPCODE_CALLEXT , 2 }, + { "CALL", OPCODE_CALL , 3 }, + { "RETURN", OPCODE_RETURN , 0 }, + { "PEEK_JUMP_FALSE", OPCODE_PEEK_JUMP_FALSE, 2 }, + { "PEEK_JUMP_TRUE", OPCODE_PEEK_JUMP_TRUE , 2 }, + { "JUMP_FALSE", OPCODE_JUMP_FALSE , 2 }, + { "JUMP_TRUE", OPCODE_JUMP_TRUE , 2 }, + { "JUMP", OPCODE_JUMP , 2 }, + { "PAUSE", OPCODE_PAUSE , 0 }, + { "HALT", OPCODE_HALT , 0 }, + + /* Math operators */ + + { "SUB", OPCODE_SUB , 0 }, + { "ADD", OPCODE_ADD , 0 }, + { "DIV", OPCODE_DIV , 0 }, + { "MULT", OPCODE_MULT , 0 }, + { "MOD", OPCODE_MOD , 0 }, + { "NEG", OPCODE_NEG , 0 }, + { "EXCHG", OPCODE_EXCHANGE , 0 }, + + /* Bitwise operators */ + + { "NOT", OPCODE_NOT , 0 }, + { "TWIDDLE", OPCODE_TWIDDLE , 0 }, + + { "OROR", OPCODE_OROR , 0 }, + { "ANDAND", OPCODE_ANDAND , 0 }, + + { "OR", OPCODE_OR , 0 }, + { "AND", OPCODE_AND , 0 }, + { "XOR", OPCODE_XOR , 0 }, + + { "SHIFT_LEFT", OPCODE_SHIFTLEFT , 0 }, + { "SHIFT_RIGHT", OPCODE_SHIFTRIGHT , 0 }, + + /* Boolean operators */ + + { "LESS", OPCODE_LESS , 0 }, + { "LESSEQUAL", OPCODE_LESSEQUAL , 0 }, + { "GREATER", OPCODE_GREATER , 0 }, + { "GREATEREQUAL", OPCODE_GREATEREQUAL, 0 }, + { "NOTEQUAL", OPCODE_NOTEQUAL , 0 }, + { "EQUAL", OPCODE_EQUAL , 0 }, + + { "POP_ADD_VARIABLE", OPCODE_POP_ADD_VARIABLE, 0 }, + { "POP_SUB_VARIABLE", OPCODE_POP_SUB_VARIABLE, 0 }, + { "POP_MUL_VARIABLE", OPCODE_POP_MUL_VARIABLE, 0 }, + { "POP_MOD_VARIABLE", OPCODE_POP_MOD_VARIABLE, 0 }, + { "POP_DIV_VARIABLE", OPCODE_POP_DIV_VARIABLE, 0 }, + { "POP_AND_VARIABLE", OPCODE_POP_AND_VARIABLE, 0 }, + { "POP_OR_VARIABLE" , OPCODE_POP_OR_VARIABLE , 0 }, + { "POP_XOR_VARIABLE", OPCODE_POP_XOR_VARIABLE, 0 }, + { "POP_SHL_VARIABLE", OPCODE_POP_SHL_VARIABLE, 0 }, + { "POP_SHR_VARIABLE", OPCODE_POP_SHR_VARIABLE, 0 }, + + { NULL, 0, 0 } +} ; + + +int pslCompiler::printOpcode ( FILE *fd, int addr ) const +{ + fprintf ( fd, "%3d: 0x%02x ", addr, (unsigned int)( code [ addr ] ) ) ; + + unsigned char op = code [ addr ] ; + + for ( int i = 0 ; opcodeDecode [ i ] . s != NULL ; i++ ) + if ( opcodeDecode [ i ] . opcode == op ) + { + for ( int j = 0 ; j < 4 ; j++ ) + if ( j < opcodeDecode [ i ] . numArgBytes ) + fprintf ( fd, "0x%02x ", code [ addr + j + 1 ] ) ; + else + fprintf ( fd, " " ) ; + + fprintf ( fd, " %s", opcodeDecode [ i ] . s ) ; + return 1 + opcodeDecode [ i ] . numArgBytes ; + } + + fprintf ( fd, "**** UNRECOGNISED OPCODE **** " ) ; + return 1 ; +} + + +void pslCompiler::dump () const +{ + int i ; + + printf ( "\n" ) ; + printf ( "Bytecode:\n" ) ; + + for ( i = 0 ; i < next_code ; ) + i += printInstruction ( stdout, i ) ; + + printf ( "\n" ) ; + printf ( "Global Variables:\n" ) ; + + for ( i = 0 ; i < MAX_SYMBOL ; i++ ) + if ( symtab [ i ] . symbol != NULL ) + { + printf ( "\t%5s => %4d", symtab[i].symbol, + symtab[i].address ) ; + + if ( i & 1 ) + printf ( "\n" ) ; + else + printf ( " " ) ; + } + + printf ( "\n" ) ; + printf ( "Functions:\n" ) ; + + for ( i = 0 ; i < MAX_SYMBOL ; i++ ) + if ( code_symtab [ i ] . symbol != NULL ) + { + printf ( "\t%5s => %4d", code_symtab[i].symbol, + code_symtab[i].address ) ; + + if ( i & 1 ) + printf ( "\n" ) ; + else + printf ( " " ) ; + } + + printf ( "\n" ) ; + + if ( num_errors > 0 ) + printf ( "PROGRAM FAILED TO COMPILE WITH %d WARNINGS AND %d ERRORS\n", + num_warnings, num_errors ) ; + else + if ( num_warnings > 0 ) + printf ( "PROGRAM COMPILED WITH %d WARNINGS\n", num_warnings ) ; + else + printf ( "PROGRAM COMPILED OK\n" ) ; + + printf ( "\n" ) ; +} + + +int pslCompiler::printInstruction ( FILE *fd, int addr ) const +{ + int skip = printOpcode ( fd, addr ) ; + + switch ( code [ addr ] ) + { + case OPCODE_LINE_NUMBER : + fprintf ( fd, "%d =====================", + code[addr+1] + ( code[addr+2] << 8 ) ) ; + break ; + + case OPCODE_SET_INT_ARRAY : + case OPCODE_SET_FLOAT_ARRAY : + case OPCODE_SET_STRING_ARRAY : + case OPCODE_SET_INT_VARIABLE : + case OPCODE_SET_FLOAT_VARIABLE : + case OPCODE_SET_STRING_VARIABLE : + case OPCODE_PUSH_VARIABLE : + fprintf ( fd, "\t\t[%d]", code [ addr+1 ] ) ; + break ; + + case OPCODE_GET_PARAMETER : + fprintf ( fd, "\t\t[%d],off=%d", code [ addr+1 ], code [ addr+2 ] ) ; + break ; + + case OPCODE_PUSH_INT_CONSTANT : + { + int f ; + memcpy ( &f, &code[addr+1], sizeof(int) ) ; + fprintf ( fd, "\t%d", f ) ; + } + break ; + + case OPCODE_PUSH_FLOAT_CONSTANT : + { + float f ; + memcpy ( &f, &code[addr+1], sizeof(float) ) ; + fprintf ( fd, "\t%f", f ) ; + } + break ; + + case OPCODE_PUSH_STRING_CONSTANT : + { + fprintf ( fd, "\t\"%s\"", &code[addr+1] ) ; + skip += strlen ( (char *) &code[addr+1] ) + 1 ; + } + break ; + + case OPCODE_CALLEXT : + fprintf ( fd, "\t\t\t%s,nargs=%d", + extensions[code[addr+1]].symbol, code[addr+2] ); + break ; + + case OPCODE_CALL : + fprintf ( fd, "\t\t\t%d,nargs=%d", code[addr+1] + ( code[addr+2] << 8 ), + code[addr+3] ) ; + break ; + + case OPCODE_PEEK_JUMP_FALSE : + case OPCODE_PEEK_JUMP_TRUE : + case OPCODE_JUMP_FALSE : + case OPCODE_JUMP_TRUE : + fprintf ( fd, "\t\t%d", code[addr+1] + ( code[addr+2] << 8 ) ) ; + break ; + + case OPCODE_JUMP : + fprintf ( fd, "\t\t\t%d", code[addr+1] + ( code[addr+2] << 8 ) ) ; + break ; + + default : break ; + } + + fprintf ( fd, "\n" ) ; + return skip ; +} + + diff --git a/src/psl/pslError.cxx b/src/psl/pslError.cxx new file mode 100644 index 0000000..2ef7f56 --- /dev/null +++ b/src/psl/pslError.cxx @@ -0,0 +1,126 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslError.cxx 1685 2002-09-15 14:32:53Z ude $ +*/ + + +#include "pslLocal.h" + +static char _pslErrorBuffer [ 1024 ] = { '\0' } ; + + +void (*_pslErrorCB) ( pslProgram *p, int severity, const char *progname, + int line_no , const char *message ) = NULL ; + + +void pslSetErrorCallback ( void (*CB) ( pslProgram *, int, const char *, + int, const char * ) ) +{ + _pslErrorCB = CB ; +} + + + +int pslCompiler::warning ( const char *fmt, ... ) +{ + va_list argp; + va_start ( argp, fmt ) ; + vsprintf ( _pslErrorBuffer, fmt, argp ) ; + va_end ( argp ) ; + + if ( _pslErrorCB != NULL ) + (*_pslErrorCB)( program, PSL_COMPILETIME_WARNING, + _pslGetFname(), _pslGetLineNo(), _pslErrorBuffer ) ; + else + fprintf ( stderr, "PSL: \"%s\" line %3d: WARNING - %s\n", + _pslGetFname(), _pslGetLineNo(), _pslErrorBuffer ) ; + + bumpWarnings () ; + return FALSE ; +} + + + +int pslCompiler::error ( const char *fmt, ... ) +{ + va_list argp; + va_start ( argp, fmt ) ; + vsprintf ( _pslErrorBuffer, fmt, argp ) ; + va_end ( argp ) ; + + if ( _pslErrorCB != NULL ) + (*_pslErrorCB)( program, PSL_COMPILETIME_ERROR, + _pslGetFname(), _pslGetLineNo(), _pslErrorBuffer ) ; + else + fprintf ( stderr, "PSL: \"%s\" line %3d: *ERROR* - %s\n", + _pslGetFname(), _pslGetLineNo(), _pslErrorBuffer ) ; + + bumpErrors () ; + return FALSE ; +} + + + +void pslContext::warning ( const char *fmt, ... ) +{ + va_list argp; + va_start ( argp, fmt ) ; + vsprintf ( _pslErrorBuffer, fmt, argp ) ; + va_end ( argp ) ; + + if ( _pslErrorCB != NULL ) + (*_pslErrorCB)( program, PSL_RUNTIME_WARNING, + getProgName(), getLineNo(), _pslErrorBuffer ) ; + else + if ( getLineNo () >= 0 ) + fprintf ( stderr, "PSL: \"%s\" Line %d: WARNING - %s\n", + getProgName(), getLineNo(), _pslErrorBuffer ) ; + else + fprintf ( stderr, "PSL: \"%s\": WARNING - %s\n", + getProgName(), _pslErrorBuffer ) ; + + bumpWarnings () ; +} + + + +void pslContext::error ( const char *fmt, ... ) +{ + va_list argp; + va_start ( argp, fmt ) ; + vsprintf ( _pslErrorBuffer, fmt, argp ) ; + va_end ( argp ) ; + + if ( _pslErrorCB != NULL ) + (*_pslErrorCB)( program, PSL_RUNTIME_ERROR, + getProgName(), getLineNo(), _pslErrorBuffer ) ; + else + if ( getLineNo () >= 0 ) + fprintf ( stderr, "PSL: \"%s\" Line %d: *ERROR* - %s\n", + getProgName(), getLineNo(), _pslErrorBuffer ) ; + else + fprintf ( stderr, "PSL: \"%s\": *ERROR* - %s\n", + getProgName(), _pslErrorBuffer ) ; + + bumpErrors () ; +} + + diff --git a/src/psl/pslExpression.cxx b/src/psl/pslExpression.cxx new file mode 100644 index 0000000..3fb7792 --- /dev/null +++ b/src/psl/pslExpression.cxx @@ -0,0 +1,519 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslExpression.cxx 1700 2002-09-22 18:25:16Z sjbaker $ +*/ + + +#include "pslLocal.h" + + + +int pslCompiler::genLValue () +{ + /* + Expect: + + variable ...or... + arrayname [ expression ] ...or... + structure . lvalue ...or... + structure -> lvalue + */ + + char c [ MAX_TOKEN ] ; + char n [ MAX_TOKEN ] ; + getToken ( c ) ; + getToken ( n ) ; + + int isArray = ( n[0] == '[' ) ; + int isStruct = ( strcmp ( n, "." ) == 0 || + strcmp ( n, "->" ) == 0 ) ; + + if ( ! isArray && ! isStruct ) + { + ungetToken ( n ) ; + genVariable ( c, FALSE ) ; + return TRUE ; + } + + if ( isStruct ) + return error ( "Structures are not supported yet." ) ; + + /* An array + index */ + + if ( ! genExpression () ) + return error ( "Missing expression for array index." ) ; + + getToken ( n ) ; + + if ( n[0] != ']' ) + return error ( "Missing ']' after array index." ) ; + + genVariable ( c, TRUE ) ; + return TRUE ; +} + + + + +int pslCompiler::genPrimitive () +{ + /* Expect: + + 'x' ...or... + "xxxxxx" ...or... + number ...or... + ( expression ) ...or... + + primitive ...or... + - primitive ...or... + ! primitive ...or... + ~ primitive ...or... + ++ lvalue ...or... + -- lvalue ...or... + function ( expression ) ...or... + lvalue ...or... + lvalue ++ ...or... + lvalue -- ...or... + lvalue = expression ...or... + lvalue += expression ...or... + lvalue -= expression + (etc) + */ + + char c [ MAX_TOKEN ] ; + getToken ( c ) ; + + /* Constant ? */ + + if ( c [ 0 ] == '\'' ) { genCharConstant ( c [ 1 ] ) ; return TRUE ; } + if ( c [ 0 ] == '"' ) { genStringConstant ( & c [ 1 ] ) ; return TRUE ; } + if ((c [ 0 ] == '.' && c [ 1 ] != '\0' ) || + isdigit ( c[0] ) ) { genConstant ( c ) ; return TRUE ; } + + + /* Bracketed expression */ + + if ( strcmp ( c, "(" ) == 0 ) + { + if ( ! genExpression () ) + { + ungetToken ( c ) ; + return error ( "Missing expression after '('" ) ; + } + + getToken ( c ) ; + + if ( strcmp ( c, ")" ) != 0 ) + { + ungetToken ( c ) ; + return error ( "Missing ')' (found '%s')", c ); + } + + return TRUE ; + } + + /* Various unary operators */ + + if ( strcmp ( c, "+" ) == 0 ) /* Skip over redundant unary '+' symbol */ + { + if ( genPrimitive () ) { /* Nothing */ return TRUE ; } + else { ungetToken ( c ) ; return FALSE ; } + } + + if ( strcmp ( c, "-" ) == 0 ) /* Unary '-' symbol */ + { + if ( genPrimitive () ) { genNegate () ; return TRUE ; } + else { ungetToken ( c ) ; return FALSE ; } + } + + if ( strcmp ( c, "!" ) == 0 ) /* Unary '!' symbol */ + { + if ( genPrimitive () ) { genNot () ; return TRUE ; } + else { ungetToken ( c ) ; return FALSE ; } + } + + if ( strcmp ( c, "~" ) == 0 ) /* Unary '~' symbol */ + { + if ( genPrimitive () ) { genTwiddle () ; return TRUE ; } + else { ungetToken ( c ) ; return FALSE ; } + } + + /* Pre-increment and pre-decrement */ + + if ( strcmp ( c, "++" ) == 0 ) + { + genLValue () ; + genIncrementLValue () ; + genFetch () ; + return TRUE ; + } + + if ( strcmp ( c, "--" ) == 0 ) + { + genLValue () ; + genDecrementLValue () ; + genFetch () ; + return TRUE ; + } + + /* Something illegal ?!? */ + + if ( ! isalpha ( c [ 0 ] ) && c [ 0 ] != '_' ) + { + ungetToken ( c ) ; + return FALSE ; + } + + /* + An LValue or a function call... + */ + + char n [ MAX_TOKEN ] ; + getToken ( n ) ; + + if ( n[0] == '(' ) + { + ungetToken ( n ) ; + genFunctionCall ( c ) ; + return TRUE ; + } + + /* + An LValue. + */ + + ungetToken ( n ) ; + ungetToken ( c ) ; + + if ( ! genLValue () ) + return error ( "Illegal expression." ) ; + + getToken ( n ) ; + + if ( strcmp ( n, "++" ) == 0 ) + { genIncrementFetch () ; return TRUE ; } + + if ( strcmp ( n, "--" ) == 0 ) + { genDecrementFetch () ; return TRUE ; } + + if ( strcmp ( n, "=" ) == 0 ) + { if ( genExpression () ) { genAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "+=" ) == 0 ) + { if ( genExpression () ) { genAddAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "-=" ) == 0 ) + { if ( genExpression () ) { genSubAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "*=" ) == 0 ) + { if ( genExpression () ) { genMulAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "%=" ) == 0 ) + { if ( genExpression () ) { genModAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "/=" ) == 0 ) + { if ( genExpression () ) { genDivAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "&=" ) == 0 ) + { if ( genExpression () ) { genAndAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "|=" ) == 0 ) + { if ( genExpression () ) { genOrAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "^=" ) == 0 ) + { if ( genExpression () ) { genXorAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, "<<=" ) == 0 ) + { if ( genExpression () ) { genSHLAssignment () ; return TRUE ; } + else return FALSE ; } + + if ( strcmp ( n, ">>=" ) == 0 ) + { if ( genExpression () ) { genSHRAssignment () ; return TRUE ; } + else return FALSE ; } + + ungetToken ( n ) ; + genFetch () ; + return TRUE ; +} + + + +int pslCompiler::genMultExpression () +{ + /* Expect: + + primitive ...or... + primitive * primitive + primitive / primitive + primitive % primitive + */ + + if ( ! genPrimitive () ) + return FALSE ; + + while ( TRUE ) + { + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "*" ) != 0 && + strcmp ( c, "/" ) != 0 && + strcmp ( c, "%" ) != 0 ) + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genPrimitive () ) + return FALSE ; + + if ( strcmp ( c, "*" ) == 0 ) + genMultiply () ; + else + if ( strcmp ( c, "/" ) == 0 ) + genDivide () ; + else + genModulo () ; + } +} + + + + +int pslCompiler::genAddExpression () +{ + /* Expect: + + multExp ...or... + multExp + multExp + multExp - multExp + */ + + if ( ! genMultExpression () ) + return FALSE ; + + while ( TRUE ) + { + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "+" ) != 0 && + strcmp ( c, "-" ) != 0 ) + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genMultExpression () ) + return FALSE ; + + if ( strcmp ( c, "+" ) == 0 ) + genAdd () ; + else + genSubtract () ; + } +} + + + + +int pslCompiler::genShiftExpression () +{ + /* Expect: + + addExp ...or... + addExp << addExp + addExp >> addExp + */ + + if ( ! genAddExpression () ) + return FALSE ; + + while ( TRUE ) + { + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "<<" ) != 0 && + strcmp ( c, ">>" ) != 0 ) + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genAddExpression () ) + return FALSE ; + + if ( strcmp ( c, "<<" ) == 0 ) + genShiftLeft () ; + else + genShiftRight () ; + } +} + + + +int pslCompiler::genBitwiseExpression () +{ + /* Expect: + + shiftExp ...or... + shiftExp & shiftExp + shiftExp | shiftExp + shiftExp ^ shiftExp + */ + + if ( ! genShiftExpression () ) + return FALSE ; + + while ( TRUE ) + { + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "|" ) != 0 && + strcmp ( c, "&" ) != 0 && + strcmp ( c, "^" ) != 0 ) + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genShiftExpression () ) + return FALSE ; + + if ( strcmp ( c, "|" ) == 0 ) + genOr () ; + else + if ( strcmp ( c, "&" ) == 0 ) + genAnd () ; + else + genXor () ; + } +} + + + + +int pslCompiler::genRelExpression () +{ + /* Expect: + + bitwiseExp ...or... + bitwiseExp < bitwiseExp + bitwiseExp > bitwiseExp + bitwiseExp == bitwiseExp + bitwiseExp <= bitwiseExp + bitwiseExp >= bitwiseExp + bitwiseExp != bitwiseExp + */ + + if ( ! genBitwiseExpression () ) + return FALSE ; + + while ( TRUE ) + { + char c [ MAX_TOKEN ] ; + + getToken ( c ) ; + + if ( strcmp ( c, "<" ) != 0 && + strcmp ( c, ">" ) != 0 && + strcmp ( c, "<=" ) != 0 && + strcmp ( c, ">=" ) != 0 && + strcmp ( c, "!=" ) != 0 && + strcmp ( c, "==" ) != 0 ) + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genBitwiseExpression () ) + return FALSE ; + + if ( strcmp ( c, "<" ) == 0 ) genLess () ; else + if ( strcmp ( c, ">" ) == 0 ) genGreater () ; else + if ( strcmp ( c, "<=" ) == 0 ) genLessEqual () ; else + if ( strcmp ( c, ">=" ) == 0 ) genGreaterEqual () ; else + if ( strcmp ( c, "!=" ) == 0 ) genNotEqual () ; else + if ( strcmp ( c, "==" ) == 0 ) genEqual () ; + } +} + + + +int pslCompiler::genBoolExpression () +{ + /* Expect: + + relExp ...or... + relExp && boolExp ...or... + relExp || boolExp + */ + + if ( ! genRelExpression () ) + return FALSE ; + + char c [ MAX_TOKEN ] ; + int shortcut ; + + getToken ( c ) ; + + if ( strcmp ( c, "&&" ) == 0 ) + shortcut = genPeekJumpIfFalse ( 0 ) ; + else + if ( strcmp ( c, "||" ) == 0 ) + shortcut = genPeekJumpIfTrue ( 0 ) ; + else + { + ungetToken ( c ) ; + return TRUE ; + } + + if ( ! genBoolExpression () ) + return error ( "Missing expression following '&&' or '||'" ) ; + + code [ shortcut ] = next_code & 0xFF ; + code [ shortcut+1 ] = ( next_code >> 8 ) & 0xFF ; + + return TRUE ; +} + + +int pslCompiler::genExpression () +{ + /* All expressions can be bool expressions */ + return genBoolExpression () ; +} + + diff --git a/src/psl/pslFileIO.cxx b/src/psl/pslFileIO.cxx new file mode 100644 index 0000000..fa833eb --- /dev/null +++ b/src/psl/pslFileIO.cxx @@ -0,0 +1,206 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslFileIO.cxx 1745 2003-01-06 05:10:14Z sjbaker $ +*/ + +#include "pslLocal.h" + +class pslFile ; + +static pslFile *defaultFileStack [ MAX_INCLUDE_DEPTH ] ; +static int fileStackPointer = 0 ; + +class pslFile +{ + FILE *fd ; + const char *memptr ; + char *fname ; + + int ungetStack [ MAX_UNGET_CHAR ] ; + int ungetSP ; + + int line_no ; + + void init () + { + ungetSP = 0 ; + line_no = 0 ; + + if ( fileStackPointer >= MAX_INCLUDE_DEPTH - 1 ) + fprintf ( stderr, "PSL: ERROR - Too many nested #includes" ) ; + else + defaultFileStack [ fileStackPointer++ ] = this ; + + /* + This is a kludge to get preprocessor directives to see + a '#' as the very first character in the file as if it came + immediately after a newline...also accounted for by line_no + starting at zero instead of 1. + */ + + unGetChar ( '\n' ) ; + line_no = 0 ; + } + +public: + + pslFile ( const char *_memptr, const char *_fname ) + { + fname = ulStrDup ( _fname ) ; + memptr = _memptr ; + fd = NULL ; + init () ; + } + + pslFile ( FILE *_fd, const char *_fname ) + { + fname = ulStrDup ( _fname ) ; + memptr = NULL ; + fd = _fd ; + init () ; + } + + ~pslFile () + { + if ( fileStackPointer <= 0 ) + fprintf ( stderr, "PSL: ERROR - #include stack underflow?!?" ) ; + else + { + if ( defaultFileStack [ --fileStackPointer ] != this ) + fprintf ( stderr, "PSL: ERROR - #include nesting problem?!?" ) ; + else + if ( fd != NULL ) + fclose ( fd ) ; + } + + delete [] fname ; + } + + void unGetChar ( int c ) + { + if ( ungetSP < MAX_UNGET_CHAR-1 ) + ungetStack [ ungetSP++ ] = c ; + else + fprintf ( stderr, "PSL: ERROR - Too many ungotten characters?!?" ) ; + + if ( c == '\n' ) line_no-- ; + } + + int getChar () + { + int c ; + + /* + Get a new character - either from the 'undo' stack or + from the file or from the memory pointer. + + Return -1 on EOF. + */ + + if ( ungetSP > 0 ) + c = ungetStack [ --ungetSP ] ; + else + if ( fd != NULL ) + c = getc ( fd ) ; + else + if ( memptr != NULL ) + { + c = (int)((unsigned char)(*(memptr++))) ; + + if ( c == '\0' ) + { + memptr = NULL ; + c = -1 ; + } + } + else + c = -1 ; + + if ( c == '\n' ) line_no++ ; + + return c ; + } + + int getLineNo () const { return line_no ; } + char *getFname () const { return fname ; } +} ; + + +char *_pslGetFname () + { return defaultFileStack [ fileStackPointer - 1 ] -> getFname () ; } +int _pslGetLineNo () + { return defaultFileStack [ fileStackPointer - 1 ] -> getLineNo () ; } + +void _pslPopDefaultFile () { delete defaultFileStack [ fileStackPointer - 1 ] ; } +void _pslPushDefaultFile ( FILE *fd, const char *fname ) { new pslFile ( fd, fname ) ; } +void _pslPushDefaultFile ( const char *memptr, const char *fname ) { new pslFile ( memptr, fname ) ; } + +void _pslPushDefaultFile ( const char *fname ) +{ + char filename [ 1024 ] ; + _pslMakeScriptPath ( filename, fname ) ; + + FILE *fd = fopen ( filename, "ra" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, + "PSL: ERROR - Can't open #include'ed file '%s'", filename ) ; + return ; + } + + _pslPushDefaultFile ( fd, fname ) ; +} + + + +int _pslGetChar () +{ + /* WARNING -- RECURSIVE -- WARNING -- RECURSIVE -- WARNING -- RECURSIVE */ + + int c = defaultFileStack [ fileStackPointer-1 ] -> getChar () ; + + /* + If we hit the end of the current file, then if this is an + included file, just pop the file stack and carry on reading + from the place we left off. Otherwise just return an EOF. + */ + + if ( c < 0 ) /* EOF */ + { + if ( fileStackPointer > 1 ) + { + _pslPopDefaultFile () ; + return _pslGetChar () ; + } + + return -1 ; + } + + return c ; +} + + +void _pslUnGetChar ( int c ) +{ + defaultFileStack [ fileStackPointer-1 ] -> unGetChar ( c ) ; +} + diff --git a/src/psl/pslFileIO.h b/src/psl/pslFileIO.h new file mode 100644 index 0000000..59e17da --- /dev/null +++ b/src/psl/pslFileIO.h @@ -0,0 +1,35 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslFileIO.h 1745 2003-01-06 05:10:14Z sjbaker $ +*/ + +char *_pslGetFname () ; +int _pslGetLineNo () ; + +void _pslPopDefaultFile () ; +void _pslPushDefaultFile ( FILE *fd, const char *fname ) ; +void _pslPushDefaultFile ( const char *memptr, const char *fname ) ; +void _pslPushDefaultFile ( const char *fname ) ; + +void _pslUnGetChar ( int c ) ; +int _pslGetChar () ; + + diff --git a/src/psl/pslLocal.h b/src/psl/pslLocal.h new file mode 100644 index 0000000..a4bfa57 --- /dev/null +++ b/src/psl/pslLocal.h @@ -0,0 +1,63 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslLocal.h 1707 2002-09-27 15:45:06Z sjbaker $ +*/ + + +#include +#include +#include +#include + +#include "psl.h" +#include "ul.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* Limits */ + +#define MAX_ARGS 64 +#define MAX_VARIABLE 256 +#define MAX_LABEL 256 +#define MAX_TOKEN 1024 +#define MAX_CODE 65536 +#define MAX_STACK 256 +#define MAX_NESTING 32 +#define MAX_UNGET 64 +#define MAX_UNGET_CHAR 1024 +#define MAX_SYMBOL (MAX_VARIABLE + MAX_LABEL) +#define MAX_INCLUDE_DEPTH 8 + +typedef unsigned short pslAddress ; + +extern int _pslInitialised ; + +#include "pslFileIO.h" +#include "pslOpcodes.h" +#include "pslSymbol.h" +#include "pslCompiler.h" +#include "pslContext.h" + +char* _pslMakeScriptPath ( char* path, const char* fname ) ; + diff --git a/src/psl/pslOpcodes.h b/src/psl/pslOpcodes.h new file mode 100644 index 0000000..8e22e75 --- /dev/null +++ b/src/psl/pslOpcodes.h @@ -0,0 +1,100 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslOpcodes.h 1700 2002-09-22 18:25:16Z sjbaker $ +*/ + + +/* Code Opcodes */ + +#define OPCODE_BAD 0x00 +#define OPCODE_PUSH_INT_CONSTANT 0x01 +#define OPCODE_PUSH_FLOAT_CONSTANT 0x02 +#define OPCODE_PUSH_STRING_CONSTANT 0x03 +#define OPCODE_CALL 0x04 +#define OPCODE_SUB 0x05 +#define OPCODE_ADD 0x06 +#define OPCODE_DIV 0x07 +#define OPCODE_MULT 0x08 +#define OPCODE_MOD 0x09 +#define OPCODE_NEG 0x0A +#define OPCODE_NOT 0x0B +#define OPCODE_TWIDDLE 0x0C +#define OPCODE_OROR 0x0D +#define OPCODE_ANDAND 0x0E +#define OPCODE_SHIFTLEFT 0x0F + +#define OPCODE_SHIFTRIGHT 0x10 +#define OPCODE_OR 0x11 +#define OPCODE_AND 0x12 +#define OPCODE_XOR 0x13 +#define OPCODE_LESS 0x14 +#define OPCODE_LESSEQUAL 0x15 +#define OPCODE_GREATER 0x16 +#define OPCODE_GREATEREQUAL 0x17 +#define OPCODE_NOTEQUAL 0x18 +#define OPCODE_EQUAL 0x19 +#define OPCODE_JUMP_FALSE 0x1A +#define OPCODE_JUMP_TRUE 0x1B +#define OPCODE_JUMP 0x1C +#define OPCODE_POP 0x1D +#define OPCODE_HALT 0x1E +#define OPCODE_CALLEXT 0x1F + +#define OPCODE_PAUSE 0x20 +#define OPCODE_RETURN 0x21 +#define OPCODE_PUSH_VARIABLE 0x22 +#define OPCODE_POP_VARIABLE 0x23 +#define OPCODE_SET_INT_VARIABLE 0x24 +#define OPCODE_SET_FLOAT_VARIABLE 0x25 +#define OPCODE_SET_STRING_VARIABLE 0x26 +#define OPCODE_STACK_DUPLICATE 0x27 +#define OPCODE_GET_PARAMETER 0x28 + +#define OPCODE_PEEK_JUMP_FALSE 0x2B +#define OPCODE_PEEK_JUMP_TRUE 0x2C +#define OPCODE_LINE_NUMBER 0x2D + + + +#define OPCODE_POP_ADD_VARIABLE 0x30 +#define OPCODE_POP_SUB_VARIABLE 0x31 +#define OPCODE_POP_MUL_VARIABLE 0x32 +#define OPCODE_POP_MOD_VARIABLE 0x33 +#define OPCODE_POP_DIV_VARIABLE 0x34 +#define OPCODE_POP_AND_VARIABLE 0x35 +#define OPCODE_POP_OR_VARIABLE 0x36 +#define OPCODE_POP_XOR_VARIABLE 0x37 +#define OPCODE_POP_SHL_VARIABLE 0x38 +#define OPCODE_POP_SHR_VARIABLE 0x39 +#define OPCODE_EXCHANGE 0x3A +#define OPCODE_SET_INT_ARRAY 0x3B +#define OPCODE_SET_FLOAT_ARRAY 0x3C +#define OPCODE_SET_STRING_ARRAY 0x3D + + + +#define OPCODE_FETCH 0x40 +#define OPCODE_INCREMENT_FETCH 0x41 +#define OPCODE_DECREMENT_FETCH 0x42 +#define OPCODE_INCREMENT_LVALUE 0x43 +#define OPCODE_DECREMENT_LVALUE 0x44 + + diff --git a/src/psl/pslProgram.cxx b/src/psl/pslProgram.cxx new file mode 100644 index 0000000..ca956cb --- /dev/null +++ b/src/psl/pslProgram.cxx @@ -0,0 +1,127 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslProgram.cxx 1941 2004-08-05 00:57:38Z puggles $ +*/ + + +#include "pslLocal.h" + +pslProgram::pslProgram ( const pslExtension *ext, const char *_prgnm ) +{ + if ( ! _pslInitialised ) + ulSetError ( UL_FATAL, + "PSL: You didn't call pslInit() before using PSL functions." ) ; + + code = new pslOpcode [ MAX_CODE ] ; + + extensions = ext ; + + progName = NULL ; + + if ( _prgnm == NULL ) _prgnm = "PSLprogram" ; + + setProgName ( _prgnm ) ; + + compiler = new pslCompiler ( this, code, ext, getProgName () ) ; + context = new pslContext ( this ) ; + + compiler-> init () ; + context -> reset () ; + + const char *force_trace_env = getenv ( "PSL_TRACE" ) ; + const char *force_stack_env = getenv ( "PSL_STACK" ) ; + + force_trace = ( force_trace_env != NULL && + ulStrEqual ( force_trace_env, "always" ) ) ; + force_stacktrace = ( force_stack_env != NULL && + ulStrEqual ( force_stack_env, "always" ) ) ; + + if ( force_trace ) + compiler -> generateLineNumbers () ; +} + + +pslProgram::pslProgram ( pslProgram *src, const char *_prgnm ) +{ + progName = NULL ; + + if ( _prgnm == NULL ) _prgnm = src -> getProgName () ; + + setProgName ( _prgnm ) ; + + /* This will fail if this pslProgram is ever deleted */ + /* We need ref-counting on code/compiler */ + + code = src -> getCode () ; + compiler = src -> getCompiler () ; + extensions = src -> getExtensions () ; + userData = src -> getUserData () ; + + context = new pslContext ( this ) ; + context -> reset () ; +} + + +pslProgram::~pslProgram () +{ + /* This will fail if this pslProgram was copy-constructed */ + /* We need ref-counting on code/compiler */ + /* DEBUG-ME! */ + delete [] progName ; + delete context ; + compiler -> ref(); +} + + +void pslProgram::dump () const { compiler-> dump () ; } +void pslProgram::reset () { context -> reset () ; } +pslResult pslProgram::step () { return force_trace ? + context -> trace () : + context -> step () ; } +pslResult pslProgram::trace () { return context -> trace () ; } + + + +int pslProgram::compile ( const char *memptr, const char *fname ) +{ + if ( strcmp ( getProgName(), "PSLprogram" ) == 0 ) + setProgName ( fname ) ; + + return compiler -> compile ( memptr, fname ) ; +} + + +int pslProgram::compile ( const char *fname ) +{ + if ( strcmp ( getProgName(), "PSLprogram" ) == 0 ) + setProgName ( fname ) ; + + return compiler -> compile ( fname ) ; +} + + +int pslProgram::compile ( FILE *fd ) +{ + return compiler -> compile ( fd ) ; +} + + + diff --git a/src/psl/pslSymbol.h b/src/psl/pslSymbol.h new file mode 100644 index 0000000..e5f1a33 --- /dev/null +++ b/src/psl/pslSymbol.h @@ -0,0 +1,49 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslSymbol.h 1612 2002-09-06 19:17:23Z ude $ +*/ + + +class pslSymbol +{ +public: + char *symbol ; + pslAddress address ; + int locality ; + + pslSymbol () + { + symbol = NULL ; + address = 0 ; + locality = 0 ; + } + + void set ( const char *s, pslAddress v, int loc ) + { + symbol = ulStrDup ( s ) ; + address = v ; + locality = loc ; + } + + ~pslSymbol () { delete [] symbol ; } +} ; + + diff --git a/src/psl/pslSymbols.cxx b/src/psl/pslSymbols.cxx new file mode 100644 index 0000000..653c3f6 --- /dev/null +++ b/src/psl/pslSymbols.cxx @@ -0,0 +1,168 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslSymbols.cxx 1626 2002-09-08 22:17:32Z sjbaker $ +*/ + + +#include "pslLocal.h" + + +pslAddress pslCompiler::setVarSymbol ( const char *s ) +{ + for ( int i = 0 ; i < next_var ; i++ ) + if ( strcmp ( s, symtab [ i ] . symbol ) == 0 && + symtab [ i ] . locality >= locality_sp ) + { + error ( "Multiple definition of '%s'.", s ) ; + return symtab [ i ] . address ; + } + + if ( next_var >= MAX_VARIABLE-1 ) + { + error ( "Too many variables." ) ; + next_var-- ; + } + + symtab [ next_var ] . set ( s, next_var, locality_sp ) ; + + return symtab [ next_var++ ] . address ; +} + + + +pslAddress pslCompiler::getVarSymbol ( const char *s ) +{ + /* Search Backwards so most local variable shows up first */ + + for ( int i = next_var-1 ; i >= 0 ; i-- ) + if ( strcmp ( s, symtab [ i ] . symbol ) == 0 ) + return symtab [ i ] . address ; + + error ( "Undefined symbol '%s'.", s ) ; + + return setVarSymbol ( s ) ; +} + + +int pslCompiler::getExtensionSymbol ( const char *s ) const +{ + for ( int i = 0 ; extensions [ i ] . symbol != NULL ; i++ ) + if ( strcmp ( s, extensions [ i ] . symbol ) == 0 ) + return i ; + + return -1 ; +} + + +void pslCompiler::addFwdRef ( const char *s, pslAddress where ) +{ + for ( int i = 0 ; i < next_fwdref ; i++ ) + { + if ( forward_ref [ i ] . symbol == NULL ) + { + forward_ref [ i ] . set ( s, where ) ; + return ; + } + } + + if ( next_fwdref >= MAX_SYMBOL ) + { + error ( "Too many unresolved forward references." ) ; + return ; + } + + forward_ref [ next_fwdref++ ] . set ( s, where ) ; +} + + + +void pslCompiler::fixup ( const char *s, pslAddress v ) +{ + for ( int i = 0 ; i < next_fwdref ; i++ ) + { + if ( forward_ref [ i ] . matches ( s ) ) + { + pslAddress p = forward_ref [ i ] . getWhere () ; + + code [ p ] = v & 0xFF ; + code [ p+1 ] = (v >> 8) & 0xFF ; + + delete [] forward_ref[i].symbol ; + forward_ref[i].symbol = NULL ; + } + } +} + + + +void pslCompiler::checkUnresolvedSymbols () +{ + for ( int i = 0 ; i < next_fwdref ; i++ ) + { + if ( forward_ref [ i ] . symbol != NULL ) + { + error ( "'%s' does not exist.", forward_ref [ i ] . symbol ) ; + + /* Prevent multiple errors for same symbol */ + + fixup ( forward_ref [ i ] . symbol, 0 ) ; + } + } +} + + + +pslAddress pslCompiler::getCodeSymbol ( const char *s, pslAddress where ) +{ + for ( int i = 0 ; i < next_code_symbol ; i++ ) + if ( strcmp ( s, code_symtab [ i ] . symbol ) == 0 ) + return code_symtab [ i ] . address ; + + /* Symbol is undefined - so make a forward reference to it */ + + addFwdRef ( s, where ) ; + + return 0 ; +} + + + +void pslCompiler::setCodeSymbol ( const char *s, pslAddress v ) +{ + for ( int i = 0 ; i < next_code_symbol ; i++ ) + if ( strcmp ( s, code_symtab [ i ] . symbol ) == 0 ) + { + error ( "Multiple definition of '%s'.", s ) ; + return ; + } + + if ( next_code_symbol >= MAX_VARIABLE-1 ) + { + error ( "Too many labels." ) ; + next_code_symbol-- ; + } + + code_symtab [ next_code_symbol++ ] . set ( s, v, locality_sp ) ; + + fixup ( s, v ) ; +} + + diff --git a/src/psl/pslToken.cxx b/src/psl/pslToken.cxx new file mode 100644 index 0000000..fb9de45 --- /dev/null +++ b/src/psl/pslToken.cxx @@ -0,0 +1,608 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pslToken.cxx 1686 2002-09-15 17:45:53Z sjbaker $ +*/ + + +#include "pslLocal.h" + +static char ungotten_token [ MAX_UNGET ][ MAX_TOKEN ] ; +static int unget_token_stack_depth = 0 ; + +void pslCompiler::skipToEOL () +{ + int c ; + + do + { + c = getChar () ; + } while ( c != '\n' && c != -1 ) ; +} + + + +int pslCompiler::getChar () +{ + int c ; + + /* Skip characters so long as the 'skipping' flag is set */ + + do + { + /* + If we got a newline then we have to test to see whether + a '#' preprocessor directive is on this line. + */ + + c = _pslGetChar () ; + + if ( c == '\n' ) + { + int d = _pslGetChar () ; + + if ( d == '#' ) + { + skipOverride = TRUE ; + c = doPreProcessorCommand () ; + skipOverride = FALSE ; + + if ( ! skipping () ) + return c ; + } + else + _pslUnGetChar ( d ) ; + } + } while ( skipping () && c != -1 ) ; + + /* + All done - return the character. + */ + + return c ; +} + + + +int pslCompiler::searchDefines ( const char *token ) const +{ + if ( ( token[0] < 'a' || token[0] > 'z' ) && + ( token[0] < 'A' || token[0] > 'Z' ) && + token[0] != '_' ) + return -1 ; + + for ( int i = 0 ; i < next_define ; i++ ) + if ( define_token [ i ] != NULL && + strcmp ( token, define_token [ i ] ) == 0 ) + return i ; + + return -1 ; +} + + +void pslCompiler::doUndefStatement () +{ + char token [ MAX_TOKEN ] ; + + /* + It's tempting to just getToken to get the undefined token + but that doesn't work because it get's define-processed. + */ + + getToken ( token, FALSE ) ; + + if ( ( token[0] < 'a' || token[0] > 'z' ) && + ( token[0] < 'A' || token[0] > 'Z' ) && + token[0] != '_' ) + { + error ( "#undef token is not a legal identifier" ) ; + skipToEOL () ; + return ; + } + + skipToEOL () ; + + int def = searchDefines ( token ) ; + + if ( def == -1 ) return ; /* Not an error to have undefined undef's */ + + if ( ! skipping () ) + { + delete define_token [ def ] ; + delete define_replacement [ def ] ; + define_token [ def ] = NULL ; + define_replacement [ def ] = NULL ; + } +} + + +void pslCompiler::doIfdefStatement () +{ + char token [ MAX_TOKEN ] ; + + getToken ( token, FALSE ) ; + + if ( next_skippingLevel >= 30 ) + { + error ( "Too many levels of #ifdef/#ifndef processing." ) ; + skipToEOL () ; + return ; + } + + if ( searchDefines ( token ) == -1 ) + skippingFlag |= ( 1 << next_skippingLevel++ ) ; + else + skippingFlag &= ~( 1 << next_skippingLevel++ ) ; +} + + +void pslCompiler::doIfndefStatement () +{ + char token [ MAX_TOKEN ] ; + + getToken ( token, FALSE ) ; + + if ( next_skippingLevel >= 30 ) + { + error ( "Too many levels of #ifdef/#ifndef processing." ) ; + skipToEOL () ; + return ; + } + + if ( searchDefines ( token ) == -1 ) + skippingFlag &= ~( 1 << next_skippingLevel++ ) ; + else + skippingFlag |= ( 1 << next_skippingLevel++ ) ; + + skipToEOL () ; +} + + +void pslCompiler::doElseStatement () +{ + if ( next_skippingLevel <= 1 ) + error ( "#else without prior matching #ifdef/#ifndef." ) ; + + skipToEOL () ; + skippingFlag ^= ( 1 << (next_skippingLevel-1) ) ; +} + + + +void pslCompiler::doEndifStatement () +{ + if ( next_skippingLevel <= 1 ) + error ( "#endif without prior matching #ifdef/#ifndef." ) ; + + skipToEOL () ; + skippingFlag &= ~( 1 << (--next_skippingLevel) ) ; +} + + +void pslCompiler::doDefineStatement () +{ + char token [ MAX_TOKEN ] ; + char subst [ 1024 ] ; + char *p ; + + getToken ( token, FALSE ) ; + + if ( ( token[0] < 'a' || token[0] > 'z' ) && + ( token[0] < 'A' || token[0] > 'Z' ) && + token[0] != '_' ) + { + error ( "#define token is not a legal identifier" ) ; + skipToEOL () ; + return ; + } + + int c ; + + p = subst ; + + do + { + c = getChar () ; + + *(p++) = c ; + } while ( c != -1 && c != '\n' ) ; + + *(p-1) = '\0' ; + + if ( ! skipping () ) + { + if ( searchDefines ( token ) != -1 ) + error ( "Attempt to re-#define %s", token ) ; + else + if ( next_define >= MAX_SYMBOL - 1 ) + error ( "Too many #define's\n" ) ; + else + { + define_token [ next_define ] = ulStrDup ( token ) ; + define_replacement [ next_define ] = ulStrDup ( subst ) ; + next_define++ ; + } + } +} + +void pslCompiler::doIncludeStatement () +{ + char token [ MAX_TOKEN ] ; + char *p ; + + getToken ( token ) ; + + if ( token[0] == '"' ) + p = token + 1 ; + else + if ( token[0] == '<' ) + { + p = token ; + + do + { + *p = getChar () ; + + } while ( *(p++) != '>' ) ; + + *(p-1) = '\0' ; + + p = token ; + } + else + { + error ( "Illegal character after '#include'" ) ; + skipToEOL () ; + return ; + } + + /* + Skip to the end of this line of text BEFORE we hand + control over to the next file. + */ + + skipToEOL () ; + + if ( ! skipping () ) + _pslPushDefaultFile ( p ) ; +} + + + +int pslCompiler::doPreProcessorCommand () +{ + char token [ MAX_TOKEN ] ; + + getToken ( token ) ; + + if ( strcmp ( token, "include" ) == 0 ) + { + doIncludeStatement () ; + return getChar () ; + } + + if ( strcmp ( token, "undef" ) == 0 ) + { + doUndefStatement () ; + return '\n' ; + } + + if ( strcmp ( token, "define" ) == 0 ) + { + doDefineStatement () ; + return '\n' ; + } + + if ( strcmp ( token, "ifndef" ) == 0 ) + { + doIfndefStatement () ; + return getChar () ; + } + + if ( strcmp ( token, "ifdef" ) == 0 ) + { + doIfdefStatement () ; + return getChar () ; + } + + if ( strcmp ( token, "endif" ) == 0 ) + { + doEndifStatement () ; + return getChar () ; + } + + if ( strcmp ( token, "else" ) == 0 ) + { + doElseStatement () ; + return getChar () ; + } + + error ( "Unrecognised preprocessor directive '%s'", token ) ; + + skipToEOL () ; + return '\n' ; +} + + +void pslCompiler::getToken ( char *res, int define_sub ) +{ + /* WARNING -- RECURSIVE -- WARNING -- RECURSIVE -- WARNING -- RECURSIVE */ + + if ( unget_token_stack_depth > 0 ) + { + strcpy ( res, ungotten_token [ --unget_token_stack_depth ] ) ; + return ; + } + + int c ; + + do + { + c = getChar () ; + + if ( c < 0 ) + { + res [ 0 ] = '\0' ; + return ; + } + + if ( c == '/' ) + { + int d = getChar () ; + + if ( d == '/' ) /* C++ style comment */ + { + do + { + d = getChar () ; + } while ( d != '\n' && d != -1 ) ; + + c = ' ' ; + } + else + if ( d == '*' ) /* C style comment */ + { + /* + YUK! This is *so* horrible to get right. + Just think about this case! ==> **/ + + do + { + /* Search for a star or an EOF */ + + do + { + d = getChar () ; + } while ( d != '*' && d != -1 ) ; + + c = getChar () ; + + /* If you get two stars in a row - unget the second one */ + + if ( c == '*' ) + _pslUnGetChar ( '*' ) ; + + } while ( c != '/' ) ; + + c = ' ' ; + } + else + _pslUnGetChar ( d ) ; + } + } while ( isspace ( c ) ) ; + + int tp = 0 ; + + if ( c == '"' ) + { + int isBkSlash = FALSE ; + + do + { + if ( c == '\\' ) + { + if ( isBkSlash ) + { + isBkSlash = FALSE ; + res [ tp++ ] = c ; + } + else + isBkSlash = TRUE ; + } + else + if ( isBkSlash ) + { + switch ( c ) + { + case '0' : res [ tp++ ] = '\0' ; break ; + case 'r' : res [ tp++ ] = '\r' ; break ; + case 't' : res [ tp++ ] = '\t' ; break ; + case 'n' : res [ tp++ ] = '\n' ; break ; + case 'f' : res [ tp++ ] = '\f' ; break ; + case 'b' : res [ tp++ ] = '\b' ; break ; + case 'a' : res [ tp++ ] = '\a' ; break ; + default : res [ tp++ ] = c ; break ; + } + + isBkSlash = FALSE ; + } + else + res [ tp++ ] = c ; + + c = getChar () ; + + if ( tp >= MAX_TOKEN - 1 ) + { + error ( "Input string is bigger than %d characters!", + MAX_TOKEN - 1 ) ; + tp-- ; + } + } while ( ( isBkSlash || c != '"' ) && c != -1 ) ; + + if ( c == -1 ) + error ( "Missing \\\" character" ) ; + + /* The trailing quotes character is not included into the string */ + res [ tp ] = '\0' ; + return ; + } + + if ( c == '\'' ) + { + res [ tp++ ] = '\'' ; + + c = getChar () ; + + if ( c == '\\' ) + { + c = getChar () ; + + switch ( c ) + { + case '0' : res [ tp++ ] = '\0' ; break ; + case 'r' : res [ tp++ ] = '\r' ; break ; + case 't' : res [ tp++ ] = '\t' ; break ; + case 'n' : res [ tp++ ] = '\n' ; break ; + case 'f' : res [ tp++ ] = '\f' ; break ; + case 'b' : res [ tp++ ] = '\b' ; break ; + case 'a' : res [ tp++ ] = '\a' ; break ; + default : res [ tp++ ] = c ; break ; + } + } + else + res [ tp++ ] = c ; + + c = getChar () ; + + if ( c != '\'' ) + error ( "Missing \\' character" ) ; + + /* The trailing quote character is not included into the string */ + res [ tp ] = '\0' ; + return ; + } + + if ( isalnum ( c ) || c == '.' || c == '_' ) /* variables and numbers */ + { + while ( isalnum ( c ) || c == '.' || c == '_' ) + { + res [ tp++ ] = c ; + c = getChar () ; + + if ( tp >= MAX_TOKEN - 1 ) + { + error ( "Input string is bigger than %d characters!", + MAX_TOKEN - 1 ) ; + tp-- ; + } + } + + _pslUnGetChar ( c ) ; + } + else + + /* Deal with: + <<, >>, <<=, >>=, + /=, +=, -=, *=, %=, &=, |=, + <=, >=, ==, !=, ++, -- + */ + + if ( c == '*' || c == '/' || c == '%' || + c == '<' || c == '>' || c == '=' || + c == '!' || c == '&' || c == '|' || + c == '+' || c == '-' ) + { + res [ tp++ ] = c ; + + int c2 = getChar () ; + + if ( c2 == '=' || ( c == '>' && c2 == '>' ) || + ( c == '<' && c2 == '<' ) || + ( c == '&' && c2 == '&' ) || + ( c == '|' && c2 == '|' ) || + ( c == '+' && c2 == '+' ) || + ( c == '-' && c2 == '-' ) ) + { + res [ tp++ ] = c2 ; + + if ( ( c == '<' && c2 == '<' ) || + ( c == '>' && c2 == '>' ) ) + { + int c3 = getChar () ; + + if ( c3 == '=' ) + res [ tp++ ] = c3 ; + else + _pslUnGetChar ( c3 ) ; + } + } + else + _pslUnGetChar ( c2 ) ; + } + else + { + res [ 0 ] = c ; + tp = 1 ; + } + + res [ tp ] = '\0' ; + + /* + Don't do define substituting if told not to + */ + + if ( ! define_sub ) + return ; + + /* Do #define expansion. */ + + int def = searchDefines ( res ) ; + + if ( def == -1 ) + return ; + + /* + If there is a replacement for this token, un-get it so it's the + next thing we'll read. + */ + + for ( int i = strlen ( define_replacement [ def ] ) - 1 ; i >= 0 ; i-- ) + _pslUnGetChar ( define_replacement [ def ][ i ] ) ; + + /* + Then have another try at reading the token. + */ + + getToken ( res ) ; +} + + +void pslCompiler::ungetToken ( const char *s ) +{ + if ( unget_token_stack_depth >= MAX_UNGET-1 ) + { + error ( "Too many ungetTokens! This must be an *UGLY* PSL program!" ) ; + exit ( -1 ) ; + } + + strcpy ( ungotten_token[unget_token_stack_depth++], s ) ; +} + + diff --git a/src/puAux/Makefile.am b/src/puAux/Makefile.am new file mode 100644 index 0000000..3fc6939 --- /dev/null +++ b/src/puAux/Makefile.am @@ -0,0 +1,32 @@ +if BUILD_PUAUX + +lib_LIBRARIES = libplibpuaux.a + +include_HEADERS = puAux.h puAuxLocal.h + +libplibpuaux_a_SOURCES = puAux.cxx \ + puAuxBiSlider.cxx \ + puAuxBiSliderWithEnds.cxx \ + puAuxComboBox.cxx \ + puAuxCompass.cxx \ + puAuxFileSelector.cxx \ + puAuxLargeInput.cxx \ + puAuxList.cxx \ + puAuxScrollBar.cxx \ + puAuxSelectBox.cxx \ + puAuxSliderWithInput.cxx \ + puAuxSpinBox.cxx \ + puAuxTriSlider.cxx \ + puAuxVerticalMenu.cxx \ + puAuxChooser.cxx + +INCLUDES = -I$(top_srcdir)/src/sg \ + -I$(top_srcdir)/src/pui \ + -I$(top_srcdir)/src/fnt \ + -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = puAux.dsp + + diff --git a/src/puAux/puAux.cxx b/src/puAux/puAux.cxx new file mode 100644 index 0000000..0d226cc --- /dev/null +++ b/src/puAux/puAux.cxx @@ -0,0 +1,31 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAux.cxx 1902 2004-03-21 19:03:23Z sjbaker $ +*/ + + +#include "puAuxLocal.h" + +void puaInit () +{ + /* Nothing to see here - move along. */ +} diff --git a/src/puAux/puAux.dsp b/src/puAux/puAux.dsp new file mode 100644 index 0000000..56a7ea4 --- /dev/null +++ b/src/puAux/puAux.dsp @@ -0,0 +1,160 @@ +# Microsoft Developer Studio Project File - Name="puAux" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=puAux - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "puAux.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "puAux.mak" CFG="puAux - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "puAux - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "puAux - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "puAux - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\fnt" /I "..\sg" /I "..\util" /I ".." /I "..\pui" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "PU_USE_NONE" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy puAux.h ..\..\puAux.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "puAux - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\fnt" /I "..\sg" /I "..\util" /I ".." /I "..\pui" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "PU_USE_NONE" /FR /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\puAux_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy puAux.h ..\..\puAux.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "puAux - Win32 Release" +# Name "puAux - Win32 Debug" +# Begin Source File + +SOURCE=.\puAux.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAux.h +# End Source File +# Begin Source File + +SOURCE=.\puAuxBiSlider.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxBiSliderWithEnds.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxComboBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxCompass.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxFileSelector.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxLargeInput.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxList.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxLocal.h +# End Source File +# Begin Source File + +SOURCE=.\puAuxScrollBar.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxSelectBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxSliderWithInput.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxSpinBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxTriSlider.cxx +# End Source File +# Begin Source File + +SOURCE=.\puAuxVerticalMenu.cxx +# End Source File +# End Target +# End Project diff --git a/src/puAux/puAux.h b/src/puAux/puAux.h new file mode 100644 index 0000000..b2c8898 --- /dev/null +++ b/src/puAux/puAux.h @@ -0,0 +1,944 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAux.h 2160 2010-02-27 03:48:23Z fayjf $ +*/ + +#ifndef _PUI_AUX_WIDGETS_H_ +#define _PUI_AUX_WIDGETS_H_ 1 + +#include "pu.h" + +// Widget Class Bit Patterns +// PUI classes take up 0x00000001 through 0x00020000 + +#define PUCLASS_FILESELECTOR 0x00040000 /* Because FilePicker is obsolete */ +#define PUCLASS_BISLIDER 0x00080000 +#define PUCLASS_TRISLIDER 0x00100000 +#define PUCLASS_VERTMENU 0x00200000 +#define PUCLASS_LARGEINPUT 0x00400000 +#define PUCLASS_COMBOBOX 0x00800000 +#define PUCLASS_SELECTBOX 0x01000000 +#define PUCLASS_SPINBOX 0x02000000 +#define PUCLASS_SCROLLBAR 0x04000000 +#define PUCLASS_BISLIDERWITHENDS 0x08000000 +#define PUCLASS_SLIDERWITHINPUT 0x10000000 +#define PUCLASS_COMPASS 0x20000000 +#define PUCLASS_CHOOSER 0x40000000 +#define PUCLASS_LIST 0x80000000 + + +// Widget Declarations +class puaSpinBox ; +class puaFileSelector ; +class puaBiSlider ; +class puaTriSlider ; +class puaVerticalMenu ; +class puaLargeInput ; +class puaComboBox ; +class puaSelectBox ; +class puaScrollBar ; +class puaBiSliderWithEnds ; +class puaCompass ; +class puaSliderWithInput ; +class puaChooser ; +class puaList ; + + +// A File selector widget +class puaFileSelector : public puDialogBox +{ + UL_TYPE_DATA + +protected: + char** files ; + char* dflag ; + int num_files ; + int arrow_count ; + + char *startDir ; + + void find_files ( void ) ; + static void handle_select ( puObject* ) ; + static void input_entered ( puObject* ) ; + + puFrame *frame ; + puListBox *list_box ; + puSlider *slider ; + puOneShot *cancel_button ; + puOneShot *ok_button ; + puInput *input ; + puArrowButton *up_arrow ; + puArrowButton *down_arrow ; + puArrowButton *fastup_arrow ; + puArrowButton *fastdown_arrow ; + + void puaFileSelectorInit ( int x, int y, int w, int h, + int arrows, const char *dir, const char *title ) ; + +public: + + puaFileSelector ( int x, int y, int w, int h, int arrows, + const char *dir, const char *title = "Pick a file" ) : + puDialogBox ( x, y ) + { + puaFileSelectorInit ( x, y, w, h, arrows, dir, title ) ; + } + + puaFileSelector ( int x, int y, int w, int h, + const char *dir, const char *title = "Pick a file" ) : + puDialogBox ( x, y ) + { + puaFileSelectorInit ( x, y, w, h, 1, dir, title ) ; + } + + puaFileSelector ( int x, int y, int arrows, + const char *dir, const char *title = "Pick a file" ) : + puDialogBox ( x, y ) + { + puaFileSelectorInit ( x, y, 220, 170, arrows, dir, title ) ; + } + + puaFileSelector ( int x, int y, + const char *dir, const char *title = "Pick a file" ) : + puDialogBox ( x, y ) + { + puaFileSelectorInit ( x, y, 220, 170, 1, dir, title ) ; + } + + ~puaFileSelector () ; + + /* Not for application use!! */ + puInput *__getInput ( void ) const { return input ; } + char *__getStartDir ( void ) const { return (char *) startDir ; } + + void setInitialValue ( const char *fname ) ; + void setSize ( int w, int h ) ; +} ; + + +// An input box that takes an arbitrary number of lines of input +class puaLargeInput : public puInputBase, public puGroup +{ + UL_TYPE_DATA + +protected: + int num_lines ; // Number of lines of text in the box + int lines_in_window ; // Number of lines showing in the window + int top_line_in_window ; // Number of the first line in the window + int max_width ; // Width of longest line of text in box, in pixels + int slider_width ; + int line_height ; // Text height + interline gap + int vgap ; // Gap on top and bottom + int hgap ; // Gap on the left and right side + int input_width ; // Width of display area (widget minus gaps & slider) + int input_height ; // Height of display area + + puFrame *frame ; + puFrame *plug ; // Little square in the bottom right corner + + puaScrollBar *bottom_slider ; // Horizontal slider at bottom of window + puaScrollBar *right_slider ; // Vertical slider at right of window + + int arrow_count ; // Number of up/down arrows above and below the right slider + + void normalizeCursors ( void ) ; + void removeSelectRegion ( void ) ; + void updateGeometry ( void ) ; + + char *wrapText ( int target_width = 0, int *numlines = 0, int *maxwidth = 0 ) ; + void getTextProperties(int *numlines, int *maxwidth) ; + char *getText () { return bottom_slider ? getStringValue () : getDisplayedText () ; } + +public: + puaLargeInput ( int x, int y, int w, int h, int arrows, int sl_width, int wrap_text = FALSE ) ; + ~puaLargeInput () + { + if ( puActiveWidget() == this ) + puDeactivateWidget () ; + } + + void setSize ( int w, int h ) ; + void setLegendFont ( puFont f ) { puGroup::setLegendFont ( f ) ; updateGeometry () ; } + + int getNumLines ( void ) const { return num_lines ; } + int getLinesInWindow ( void ) const { return lines_in_window ; } + void setTopLineInWindow ( int val ) { top_line_in_window = (val<0) ? 0 : ( (val>num_lines-2) ? num_lines-2 : val ) ; } + void setSliderPosition ( float fraction ) ; + + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) ; + void doHit ( int button, int updown, int x, int y ) ; + int checkKey ( int key, int updown ) ; + + void setSelectRegion ( int s, int e ) ; + void selectEntireLine ( void ) ; + + void invokeDownCallback ( void ) + { + rejectInput () ; + normalizeCursors () ; + if ( down_cb != NULL ) (*down_cb)(this) ; + } + + void setValue ( const char *s ) ; + void addNewLine ( const char *l ) ; + void addText ( const char *l ) ; + void appendText ( const char *l ) ; + void removeText ( int start, int end ) ; + char *getDisplayedText ( void ) + { + return ( displayed_text == NULL ? getStringValue () : displayed_text ) ; + } +} ; + + +// A box that contains a set of alternatives that drop down when the user clicks on the down-arrow +// Defined constants telling how the callback got triggered. This is needed for +// cases in which the user has deleted the entry in the input box and wants that +// entry deleted from the list of items. +#define PUACOMBOBOX_CALLBACK_NONE 0 +#define PUACOMBOBOX_CALLBACK_INPUT 1 +#define PUACOMBOBOX_CALLBACK_ARROW 2 +class puaComboBox : public puGroup +{ + UL_TYPE_DATA + +protected: + char ** list ; + int num_items ; + + int curr_item ; + + puInput *input ; + puArrowButton *arrow_btn ; + puPopupMenu *popup_menu ; + + int callback_source ; + + static void input_cb ( puObject *inp ) ; + static void input_active_cb ( puObject *inp ) ; + static void input_down_cb ( puObject *inp ) ; + static void handle_arrow ( puObject *arrow ) ; + static void handle_popup ( puObject *popupm ) ; + + void update_widgets ( void ) ; + void update_current_item ( void ) ; + void setCallbackSource ( int s ) { callback_source = s ; } + +public: + /* Not for application use ! */ + puPopupMenu * __getPopupMenu ( void ) const { return popup_menu ; } + + void newList ( char ** _list ) ; + int getNumItems ( void ) const { return num_items ; } + char *getNewEntry ( void ) const { return input->getStringValue () ; } + + int getCurrentItem ( void ) ; + void setCurrentItem ( int item ) + { + if ( ( item >= 0 ) && ( item < num_items ) ) + { + curr_item = item ; + update_widgets () ; + + callback_source = PUACOMBOBOX_CALLBACK_ARROW ; + invokeCallback () ; + } + } + + void setCurrentItem ( const char *item_ptr ) ; + + void setPosition ( int x, int y ) + { + puGroup::setPosition ( x, y ) ; + + /* Ensure that popup menu will show up at the right place */ + newList ( list ) ; + } + + void setSize ( int w, int h ) ; + + void setValue ( float f ) { puValue::setValue ( f ) ; input->setValue ( f ) ; } + void setValue ( int i ) { puValue::setValue ( i ) ; input->setValue ( i ) ; } + void setValue ( const char *s ) { puValue::setValue ( s ) ; input->setValue ( s ) ; } + void setValue ( puValue *pv ) { puValue::setValue ( pv ) ; input->setValue ( pv ) ; } + + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) ; + int checkKey ( int key, int updown ) ; + + int getCallbackSource ( void ) const { return callback_source ; } + + virtual void setColourScheme ( float r, float g, float b, float a = 1.0f ) ; + virtual void setColour ( int which, float r, float g, float b, float a = 1.0f ) ; + + puaComboBox ( int minx, int miny, int maxx, int maxy, + char **list, int editable = TRUE ) ; + + ~puaComboBox () + { + int i ; + for ( i = 0; i < num_items; i++ ) + delete [] list[i] ; + + delete [] list ; + } +} ; + + +// Like a menu bar, but the selections are one above the other +class puaVerticalMenu : public puGroup +{ + UL_TYPE_DATA + +protected: +public: + puaVerticalMenu ( int x = -1, int y = -1 ) : + + puGroup ( x < 0 ? puGetWindowWidth() - + ( puGetDefaultLegendFont().getStringWidth ( " " ) + + PUSTR_TGAP + PUSTR_BGAP ) : x, + + y < 0 ? puGetWindowHeight() - + ( puGetDefaultLegendFont().getStringHeight () + + PUSTR_TGAP + PUSTR_BGAP ) : y) + { + type |= PUCLASS_VERTMENU ; + floating = TRUE ; // DEPRECATED! -- we need to replace this code. + if ( y < 0 ) { setVStatus( TRUE ) ; } /* It is now supposed to stick to the top left - JCJ*/ + } + + void add_submenu ( const char *str, char *items[], puCallback _cb[], + void *_user_data[] = NULL ) ; + void close ( void ) ; +} ; + + +// Not sure what this one does ... +class puaSelectBox : public puGroup +{ + UL_TYPE_DATA + +protected: + char ** list ; + int num_items ; + + int curr_item ; + + puInput *input ; + puArrowButton *down_arrow ; + puArrowButton *up_arrow ; + + static void handle_arrow ( puObject *arrow ) ; + + void update_widgets ( void ) ; + +public: + void newList ( char ** _list ) ; + int getNumItems ( void ) const { return num_items ; } + + int getCurrentItem ( void ) const { return curr_item ; } + void setCurrentItem ( int item ) + { + if ( ( item >= 0 ) && ( item < num_items ) ) + { + curr_item = item ; + update_widgets () ; + + invokeCallback () ; + } + } + + void setSize ( int w, int h ) ; + void draw ( int dx, int dy ) ; + int checkKey ( int key, int updown ) ; + + virtual void setColourScheme ( float r, float g, float b, float a = 1.0f ) ; + virtual void setColour ( int which, float r, float g, float b, float a = 1.0f ) ; + + puaSelectBox ( int minx, int miny, int maxx, int maxy, + char **list ) ; +} ; + + +// A slider with up- and down-arrows on its ends +class puaScrollBar : public puSlider +{ + UL_TYPE_DATA + +protected: + int arrow_count ; + int active_arrow ; + float line_step_size ; + enum { NONE = 0, FASTDOWN = 1, DOWN = 2, UP = 4, FASTUP = 8 }; + +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) ; + puaScrollBar ( int minx, int miny, int sz, int arrows, int vertical = FALSE ) : + puSlider ( minx, miny, sz, vertical ) + { + type |= PUCLASS_SCROLLBAR ; + arrow_count = arrows ; + active_arrow = NONE ; + line_step_size = 0.0f ; + } + + /* Alternate constructor which lets you explicitly set width */ + + puaScrollBar ( int minx, int miny, int sz, int arrows, int vertical, int width ) : + puSlider ( minx, miny, sz, vertical, width ) + { + type |= PUCLASS_SCROLLBAR ; + arrow_count = arrows ; + active_arrow = NONE ; + line_step_size = 0.0f ; + } + + void setMaxValue ( float f ) + { + maximum_value = f ; + slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ; + puPostRefresh () ; + } + + + void setMinValue ( float i ) + { + minimum_value = i ; + slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ; + puPostRefresh () ; + } + + void setLineStepSize ( float s ) { line_step_size = s ; } + float getLineStepSize ( void ) const { return line_step_size ; } +} ; + + + +// A puSlider with two slide boxes instead of one +class puaBiSlider : public puSlider +{ + UL_TYPE_DATA + +protected: + float current_max ; + float current_min ; + + int active_button ; // Zero for none, one for min, two for max +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + puaBiSlider ( int minx, int miny, int sz, int vertical = FALSE ) : + puSlider ( minx, miny, sz, vertical ) + { + type |= PUCLASS_BISLIDER ; + setMaxValue ( 1.0f ) ; + setMinValue ( 0.0f ) ; + setStepSize ( 1.0f ) ; + current_max = 1.0f ; + current_min = 0.0f ; + active_button = 0 ; + } + + /* Alternate constructor which lets you explicitly set width */ + + puaBiSlider ( int minx, int miny, int sz, int vertical, int width ) : + puSlider ( minx, miny, sz, vertical, width ) + { + type |= PUCLASS_BISLIDER ; + setMaxValue ( 1.0f ) ; + setMinValue ( 0.0f ) ; + setStepSize ( 1.0f ) ; + current_max = 1.0f ; + current_min = 0.0f ; + active_button = 0 ; + } + + void setMaxValue ( float i ) + { + maximum_value = i ; + slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ; + puPostRefresh () ; + } + + + void setMinValue ( float i ) + { + minimum_value = i ; + slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ; + puPostRefresh () ; + } + + void setCurrentMax ( int i ) { current_max = (float) i ; puPostRefresh () ; } /* DEPRECATED */ + void setCurrentMax ( float f ) { current_max = f ; puPostRefresh () ; } + float getCurrentMax ( void ) const { return current_max ; } + + void setCurrentMin ( int i ) { current_min = (float) i ; puPostRefresh () ; } /* DEPRECATED */ + void setCurrentMin ( float f ) { current_min = f ; puPostRefresh () ; } + float getCurrentMin ( void ) const { return current_min ; } + + void setActiveButton ( int i ) { active_button = i ; } + int getActiveButton ( void ) const { return active_button ; } +} ; + + +// A puSlider with three slide boxes + +class puaTriSlider : public puaBiSlider +{ + UL_TYPE_DATA + +protected: + // "active_button" is now zero for none, one for min, two for middle, three for max + int freeze_ends ; // true to make end sliders unmovable +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + puaTriSlider ( int minx, int miny, int sz, int vertical = FALSE ) : + puaBiSlider ( minx, miny, sz, vertical ) + { + type |= PUCLASS_TRISLIDER ; + freeze_ends = TRUE ; + } + + /* Alternate constructor which lets you explicitly set width */ + + puaTriSlider ( int minx, int miny, int sz, int vertical, int width ) : + puaBiSlider ( minx, miny, sz, vertical, width ) + { + type |= PUCLASS_TRISLIDER ; + freeze_ends = TRUE ; + } + + int getFreezeEnds ( void ) const { return freeze_ends ; } + void setFreezeEnds ( int val ) { freeze_ends = val ; puPostRefresh () ; } +} ; + + +// A vertical puBiSlider with a puInput box above it showing the current maximum +// value and a puInput box below it showing the current minimum value + +class puaBiSliderWithEnds : public puGroup +{ + UL_TYPE_DATA + +protected: + puaBiSlider *slider ; + puInput *max_box ; + puInput *min_box ; + + static void handle_slider ( puObject *obj ) ; + static void handle_max ( puObject *obj ) ; + static void handle_min ( puObject *obj ) ; + static void input_down_callback ( puObject *obj ) ; + + void update_widgets ( void ) ; + +public: + // For internal use only: + void __setMax ( float f ) { max_box->setValue ( f ) ; } + void __setMin ( float f ) { min_box->setValue ( f ) ; } + + // For public use: + + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) + { + return puGroup::checkHit ( button, updown, x, y ) ; + } + + int checkKey ( int key, int updown ) ; + + puaBiSliderWithEnds ( int minx, int miny, int maxx, int maxy ) ; + + void setSize ( int w, int h ) ; + + void setMaxValue ( float f ) { slider->setMaxValue ( f ) ; } + float getMaxValue ( void ) const { return slider->getMaxValue () ; } + void setMinValue ( float f ) { slider->setMinValue ( f ) ; } + float getMinValue ( void ) const { return slider->getMinValue () ; } + + void setCurrentMax ( float f ) + { + slider->setCurrentMax ( f ) ; + max_box->setValue ( f ) ; + } + + float getCurrentMax ( void ) const { return slider->getCurrentMax () ; } + + void setCurrentMin ( float f ) + { + slider->setCurrentMin ( f ) ; + min_box->setValue ( f ) ; + } + + float getCurrentMin ( void ) const { return slider->getCurrentMin () ; } + + void setActiveButton ( int i ) { slider->setActiveButton ( i ) ; } + int getActiveButton ( void ) const { return slider->getActiveButton () ; } + + char *getValidData ( void ) const { return max_box->getValidData () ; } + void setValidData ( char *data ) { max_box->setValidData ( data ) ; min_box->setValidData ( data ) ; } + void addValidData ( char *data ) { max_box->addValidData ( data ) ; min_box->addValidData ( data ) ; } + + void setCBMode ( int m ) { slider->setCBMode ( m ) ; } + int getCBMode ( void ) const { return slider->getCBMode () ; } + + void setDelta ( float f ) { slider->setDelta ( f ) ; } + float getDelta ( void ) const { return slider->getDelta () ; } + + float getStepSize ( void ) const { return slider->getStepSize () ; } + void setStepSize ( float f ) { slider->setStepSize ( f ) ; } +} ; + + +// A vertical puSlider with a puInput box above or below it showing its value + +class puaSliderWithInput : public puGroup +{ + UL_TYPE_DATA + +protected: + puSlider *slider ; + puInput *input_box ; + int input_position ; + + static void handle_slider ( puObject *obj ) ; + static void handle_input ( puObject *obj ) ; + static void input_down_callback ( puObject *obj ) ; + + void update_widgets ( void ) ; + +public: + // For internal use only: + void __setInputBox ( float f ) { input_box->setValue ( f ) ; } + + // For public use: + + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) + { + return puGroup::checkHit ( button, updown, x, y ) ; + } + + int checkKey ( int key, int updown ) ; + + puaSliderWithInput ( int minx, int miny, int maxx, int maxy, int above = 0 ) ; + + void setSize ( int w, int h ) ; + + void setMaxValue ( float f ) { slider->setMaxValue ( f ) ; } + float getMaxValue ( void ) const { return slider->getMaxValue () ; } + void setMinValue ( float f ) { slider->setMinValue ( f ) ; } + float getMinValue ( void ) const { return slider->getMinValue () ; } + + void setValue ( int i ) { slider->setValue ( i ) ; input_box->setValue ( i ) ; } + void setValue ( float f ) { slider->setValue ( f ) ; input_box->setValue ( f ) ; } + virtual void setValue ( const char *s ) { slider->setValue ( s ) ; } + virtual void setValue ( bool b ) { slider->setValue ( b ) ; } + + int getIntegerValue ( void ) { return slider->getIntegerValue () ; } + float getFloatValue ( void ) { return slider->getFloatValue () ; } + char getCharValue ( void ) { return slider->getCharValue () ; } + char *getStringValue ( void ) { return slider->getStringValue () ; } + bool getBooleanValue ( void ) { return slider->getBooleanValue () ; } + + char *getValidData ( void ) const { return input_box->getValidData () ; } + void setValidData ( char *data ) { input_box->setValidData ( data ) ; } + void addValidData ( char *data ) { input_box->addValidData ( data ) ; } + + void setCBMode ( int m ) { slider->setCBMode ( m ) ; } + int getCBMode ( void ) const { return slider->getCBMode () ; } + + void setDelta ( float f ) { slider->setDelta ( f ) ; } + float getDelta ( void ) const { return slider->getDelta () ; } + + float getStepSize ( void ) const { return slider->getStepSize () ; } + void setStepSize ( float f ) { slider->setStepSize ( f ) ; } +} ; + +class puaSpinBox : public puRange, public puGroup +{ + UL_TYPE_DATA + +protected : + puInput *input_box ; + puArrowButton *up_arrow ; + puArrowButton *down_arrow ; + + int arrow_position ; + +public : + /* Whether the arrows are on the LEFT of the input box (0) or RIGHT (1 DEFAULT) */ + int getArrowPosition ( void ) const { return arrow_position ; } + /* Offered as a proportion of the input box height. Default = 0.5 */ + void setArrowHeight ( float height ) + { + puBox ibox = *(input_box->getABox()) ; + int size = int(height * ( ibox.max[1] - ibox.min[1] )) ; + up_arrow->setSize ( size, size ) ; + down_arrow->setSize ( size, size ) ; + int xpos = getArrowPosition () ? ibox.max[0] : ibox.min[0] - size ; + int ymid = ( ibox.max[1] + ibox.min[1] ) / 2 ; + if ( getArrowPosition () == 0 ) /* Arrows are on the left, adjust the x-position of the input box */ + { + ibox.min[0] -= xpos ; + input_box->setPosition ( ibox.min[0], ibox.min[1] ) ; + abox.min[0] += xpos ; + xpos = 0 ; + } + + if ( height > 0.5f ) /* Adjust the input box to be up from the bottom */ + { + input_box->setPosition ( ibox.min[0], ibox.min[1] + size - ymid ) ; + abox.min[1] += ymid - size ; + ymid = size ; + } + else /* Input box is at the bottom of the group area */ + { + input_box->setPosition ( ibox.min[0], 0 ) ; + abox.min[1] += ibox.min[1] ; + } + + up_arrow->setPosition ( xpos, ymid ) ; + down_arrow->setPosition ( xpos, ymid - size ) ; + recalc_bbox() ; + } + + float getArrowHeight ( void ) const + { + int awid, ahgt, iwid, ihgt ; + input_box->getSize ( &iwid, &ihgt ) ; + up_arrow->getSize ( &awid, &ahgt ) ; + return float(ahgt) / float(ihgt) ; + } + + puaSpinBox ( int minx, int miny, int maxx, int maxy, int arrow_pos = 1 ) ; + + void setValue ( float f ) { puValue::setValue ( f ) ; input_box->setValue ( f ) ; } + void setValue ( int i ) { puValue::setValue ( i ) ; input_box->setValue ( i ) ; } + void setValue ( const char *s ) { puValue::setValue ( s ) ; input_box->setValue ( s ) ; } + void setValue ( puValue *pv ) { puValue::setValue ( pv ) ; input_box->setValue ( pv ) ; } +} ; + + +/* + * Widget that looks like a 3-d coordinate system with quarter-circles in the coordinate planes. + * It is used in 3-d modeling to translate and rotate the scene. The coordinates rotate with + * the scene but do not translate. Defined values are: + * 0 - Nothing active + * 1 - Dot at origin active: reset (usually) + * 2 - X-axis dot active: rotate about y- and z-axes + * 3 - Y-axis dot active: rotate about z- and x-axes + * 4 - Z-axis dot active: rotate about x- and y-axes + * 5 - X-axis bar active: translate along x-axis + * 6 - Y-axis bar active: translate along y-axis + * 7 - Z-axis bar active: translate along z-axis + * 8 - XY-plane arc active: rotate about z-axis + * 9 - YZ-plane arc active: rotate about x-axis + * 10 - ZX-plane arc active: rotate about y-axis + * 11 - X-axis, Y-axis, and XY-arc active: translate in xy-plane + * 12 - Y-axis, Z-axis, and YZ-arc active: translate in yz-plane + * 13 - Z-axis, X-axis, and ZX-arc active: translate in zx-plane + */ +#define PUACOMPASS_INACTIVE 0 +#define PUACOMPASS_RESET 1 +#define PUACOMPASS_ROTATE_Y_Z 2 +#define PUACOMPASS_ROTATE_Z_X 3 +#define PUACOMPASS_ROTATE_X_Y 4 +#define PUACOMPASS_TRANSLATE_X 5 +#define PUACOMPASS_TRANSLATE_Y 6 +#define PUACOMPASS_TRANSLATE_Z 7 +#define PUACOMPASS_ROTATE_Z 8 +#define PUACOMPASS_ROTATE_X 9 +#define PUACOMPASS_ROTATE_Y 10 +#define PUACOMPASS_TRANSLATE_X_Y 11 +#define PUACOMPASS_TRANSLATE_Y_Z 12 +#define PUACOMPASS_TRANSLATE_Z_X 13 + +class puaCompass : public puObject +{ + UL_TYPE_DATA + +protected : + sgQuat rotation ; + sgVec3 translation ; + float point_size ; + int button_state ; + int trigger_button ; + float mouse_x, mouse_y, mouse_z ; + float translation_sensitivity ; + + float xint, yint, zint ; + float prev_angle ; + sgQuat prev_rotation ; + +public : + puaCompass ( int minx, int miny, int maxx, int maxy ) : puObject ( minx, miny, maxx, maxy ) + { + setValue ( PUACOMPASS_INACTIVE ) ; + +// sgSetQuat ( rotation, 1.0f, 0.0f, 0.0f, 0.0f ) ; +// sgSetQuat ( rotation, 0.707107f, 0.707107f, 0.0f, 0.0f ) ; +// sgSetQuat ( rotation, 0.5f, -0.5f, 0.5f, 0.5f ) ; +// sgSetQuat ( rotation, 0.866025f, -0.166667f, 0.166667f, 0.166667f ) ; + sgSetQuat ( rotation, 0.866025f, -0.408248f, 0.288675f, 0.0f ) ; + sgSetVec3 ( translation, 0.0f, 0.0f, 0.0f ) ; + + point_size = 10.0f ; + + button_state = PU_UP ; + trigger_button = active_mouse_button ; + mouse_x = mouse_y = mouse_z = 0.0f ; + translation_sensitivity = 1.0f ; + + xint = yint = zint = 0.0f ; + prev_angle = 0.0f ; + sgCopyQuat ( prev_rotation, rotation ) ; + } + + void draw ( int dx, int dy ) ; + void doHit ( int button, int updown, int x, int y ) ; + + // Accessors and mutators + void getRotation ( sgQuat q ) const { memcpy ( q, rotation, 4 * sizeof(sgFloat) ) ; } + void setRotation ( sgQuat q ) { memcpy ( rotation, q, 4 * sizeof(sgFloat) ) ; } + void setRotation ( sgFloat t, sgFloat x, sgFloat y, sgFloat z ) + { + sgFloat sinth = sgSin ( t / 2.0f ) ; + sgFloat norm = sgSqrt ( x * x + y * y + z * z ) ; + if ( norm == 0.0 ) norm = 1.0 ; + rotation[SG_W] = sgCos ( t / 2.0f ) ; + rotation[SG_X] = sinth * x / norm ; + rotation[SG_Y] = sinth * y / norm ; + rotation[SG_Z] = sinth * z / norm ; + } + + void getTranslation ( sgVec3 t ) const { memcpy ( t, translation, 3 * sizeof(sgFloat) ) ; } + void setTranslation ( sgVec3 t ) { memcpy ( translation, t, 3 * sizeof(sgFloat) ) ; } + void setTranslation ( sgFloat x, sgFloat y, sgFloat z ) { translation[SG_X] = x ; translation[SG_Y] = y ; translation[SG_Z] = z ; } + + float getPointSize () const { return point_size ; } + void setPointSize ( float p ) { point_size = p ; } + + int getTriggerButton () const { return trigger_button ; } + void setTriggerButton ( int b ) { trigger_button = b ; } + + float getTranslationSensitivity () const { return translation_sensitivity ; } + void setTranslationSensitivity ( float t ) { translation_sensitivity = t ; } +} ; + + +class puaChooser +{ + UL_TYPE_DATA + + puButton *chooser_button ; + puPopupMenu *popup_menu ; + + int x1, y1, x2, y2 ; + + static void static_popup_cb ( puObject * ) ; + static void static_menu_cb ( puObject * ) ; + +public: + + virtual ~puaChooser () + { + delete chooser_button ; + delete popup_menu ; + } + + puaChooser ( int _x1, int _y1, int _x2, int _y2, char *legend ) ; + + void add_item ( char *str, puCallback _cb, void *_user_data = NULL ) ; + void close () ; + + void popup_cb () ; + + void menuCleanup ( const char *s ) ; + + void hide () { chooser_button -> hide () ; popup_menu -> hide () ; } + void reveal () { chooser_button -> reveal () ; popup_menu -> hide () ; } + + static void menuCleanup ( puObject * ) ; +} ; + + +/** + * A scrolling list for PUI. + * + * Believe it or not, PUI does not have one of these. + * + * This widget consists of a puListBox, a slider and two + * arrow buttons. This makes the ListBox scrollable, + * very handy if the box is too small to show all items + * at once. + * + * (Original code taken from FlightGear 0.9.6 sources, + * modified by Jan Reucker) + */ +class puaList : public puGroup +{ + UL_TYPE_DATA + + char ** _contents; + puFrame * _frame; + puSlider * _slider; + puArrowButton * _up_arrow; + puArrowButton * _down_arrow; + int _style; + int _sw; // slider width + int _width, _height; + +protected: + virtual void init (int w, int h, short transparent); + puListBox * _list_box; + +public: + puaList (int x, int y, int w, int h, int sl_width = 20); + puaList (int x, int y, int w, int h, char ** contents, int sl_width = 20); + puaList (int x, int y, int w, int h, short transparent, int sl_width = 20); + puaList (int x, int y, int w, int h, short transparent, char ** contents, int sl_width = 20); + virtual ~puaList (); + + virtual void newList (char ** contents); + + virtual char * getStringValue (); + virtual int getIntegerValue (); + virtual void getValue (char **ps); + virtual void getValue (int *i); + + virtual void setColourScheme (float r, float g, float b, float a); + virtual void setColour (int which, float r, float g, float b, float a); + virtual void setSize (int w, int h); + + int checkHit ( int button, int updown, int x, int y ); + int getNumVisible ( void ) const { return _list_box->getNumVisible(); } + int getNumItems ( void ) const { return _list_box->getNumItems(); } + int getTopItem ( void ) const { return _list_box->getTopItem(); } + void setTopItem ( int item_index ); +}; + +#endif + diff --git a/src/puAux/puAuxBiSlider.cxx b/src/puAux/puAuxBiSlider.cxx new file mode 100644 index 0000000..13bd2fa --- /dev/null +++ b/src/puAux/puAuxBiSlider.cxx @@ -0,0 +1,170 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxBiSlider.cxx 2022 2005-05-06 18:39:18Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaBiSlider,puSlider) + + +void puaBiSlider::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + // Draw the slider box itself + + if ( ( style == PUSTYLE_BEVELLED ) || + ( style == PUSTYLE_SHADED ) ) + abox.draw ( dx, dy, -PUSTYLE_BOXED, colour, FALSE, 2 ) ; + else + abox.draw ( dx, dy, -style, colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + // Draw the current_max slider and label it + + float val ; + + if ( getMaxValue() > getMinValue() ) + val = (float)(getCurrentMax() - getMinValue()) / (float)(getMaxValue() - getMinValue()) ; + else + val = 1.0f ; + + char str_value[10] ; + sprintf (str_value, "%g", getCurrentMax() ) ; + + draw_slider_box ( dx, dy, abox, val, str_value ) ; + + // Draw the current_min slider and label it + + if ( getMaxValue() > getMinValue() ) + val = (float)(getCurrentMin() - getMinValue()) / (float)(getMaxValue() - getMinValue()) ; + else + val = 0.0f ; + + sprintf (str_value, "%g", getCurrentMin() ) ; + + draw_slider_box ( dx, dy, abox, val, str_value ) ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + + +void puaBiSlider::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( ( button == active_mouse_button ) && ( updown == PU_UP ) ) + { + setActiveButton ( 0 ) ; + puDeactivateWidget () ; + return ; + } + + if ( button == active_mouse_button ) + { + int sd = isVertical() ; + int sz = abox.max [sd] - abox.min [sd] ; + int coord = isVertical() ? y : x ; + + float next_value ; + + if ( sz == 0 ) + next_value = 0.5f ; + else + { + next_value = ( (float)coord - (float)abox.min[sd] - (float)sz * slider_fraction / 2.0f ) / + ( (float) sz * (1.0f - slider_fraction) ) ; + } + + next_value = (next_value < 0.0f) ? 0.0f : (next_value > 1.0) ? 1.0f : next_value ; + + float new_value = getMinValue() + next_value * ( getMaxValue() - getMinValue() ) ; + + if ( ( getActiveButton() == 0 ) || ( updown == PU_DOWN ) ) // No currently-active slider, set whichever is closest + { + if ( (new_value-getCurrentMin()) < (getCurrentMax()-new_value) ) // Closest to current_min + { + setCurrentMin ( checkStep(new_value) ) ; + setActiveButton ( 1 ) ; + } + else // Closest to current_max + { + setCurrentMax ( checkStep(new_value) ) ; + setActiveButton ( 2 ) ; + } + } + else if ( getActiveButton() == 1 ) // Currently moving current_min + { + setCurrentMin ( checkStep(new_value) ) ; + if ( getCurrentMax() < getCurrentMin() ) setCurrentMax ( getCurrentMin() ) ; + } + else if ( getActiveButton() == 2 ) // Currently moving current_max + { + setCurrentMax ( checkStep(new_value) ) ; + if ( getCurrentMax() < getCurrentMin() ) setCurrentMin ( getCurrentMax() ) ; + } + + + switch ( cb_mode ) + { + case PUSLIDER_CLICK : + if ( updown == active_mouse_edge ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_DELTA :/* Deprecated! */ + if ( fabs ( last_cb_value - next_value ) >= cb_delta ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_ALWAYS : + default : + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + break ; + } + } +} diff --git a/src/puAux/puAuxBiSliderWithEnds.cxx b/src/puAux/puAuxBiSliderWithEnds.cxx new file mode 100644 index 0000000..47df508 --- /dev/null +++ b/src/puAux/puAuxBiSliderWithEnds.cxx @@ -0,0 +1,99 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxBiSliderWithEnds.cxx 2076 2006-04-13 16:20:23Z bram $ +*/ + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaBiSliderWithEnds,puGroup) + +void puaBiSliderWithEnds::handle_slider ( puObject *obj ) +{ + puaBiSliderWithEnds *bisl = (puaBiSliderWithEnds *)obj->getUserData () ; + bisl->__setMax ( ((puaBiSlider *)obj)->getCurrentMax () ) ; + bisl->__setMin ( ((puaBiSlider *)obj)->getCurrentMin () ) ; + bisl->invokeCallback () ; +} + +void puaBiSliderWithEnds::handle_max ( puObject *obj ) +{ + puaBiSliderWithEnds *bisl = (puaBiSliderWithEnds *)obj->getUserData () ; + bisl->setCurrentMax ( obj->getFloatValue () ) ; + bisl->invokeCallback () ; +} + +void puaBiSliderWithEnds::handle_min ( puObject *obj ) +{ + puaBiSliderWithEnds *bisl = (puaBiSliderWithEnds *)obj->getUserData () ; + bisl->setCurrentMin ( obj->getFloatValue () ) ; + bisl->invokeCallback () ; +} + +puaBiSliderWithEnds::puaBiSliderWithEnds ( int minx, int miny, int maxx, int maxy ) : + puGroup ( minx, miny ) +{ + type |= PUCLASS_BISLIDERWITHENDS ; + slider = new puaBiSlider ( (maxx - minx)/2-10, 22, maxy-miny-44, TRUE ) ; + min_box = new puInput ( 0, 0, maxx-minx, 20 ) ; + max_box = new puInput ( 0, maxy-miny-20, maxx-minx, maxy-miny ) ; + min_box->setValue ( 0 ) ; + max_box->setValue ( 1 ) ; + slider->setUserData ( this ) ; + slider->setCallback ( handle_slider ) ; + max_box->setUserData ( this ) ; + max_box->setCallback ( handle_max ) ; + min_box->setUserData ( this ) ; + min_box->setCallback ( handle_min ) ; + close () ; +} + + +void puaBiSliderWithEnds::setSize ( int w, int h ) +{ + slider->setSize ( 20, h-44 ) ; + slider->setPosition ( w/2-10, 22 ) ; + + min_box->setSize ( w, 20 ) ; + + max_box->setSize ( w, 20 ) ; + max_box->setPosition ( 0, h-20 ) ; +} + + +void puaBiSliderWithEnds::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + draw_label ( dx, dy ) ; + + puGroup::draw ( dx, dy ) ; +} + + +int puaBiSliderWithEnds::checkKey ( int key, int updown ) +{ + if ( ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + return ( max_box->checkKey ( key, updown ) + min_box->checkKey ( key, updown ) ) ; +} + diff --git a/src/puAux/puAuxChooser.cxx b/src/puAux/puAuxChooser.cxx new file mode 100644 index 0000000..ac4c986 --- /dev/null +++ b/src/puAux/puAuxChooser.cxx @@ -0,0 +1,104 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxChooser.cxx 1942 2004-08-05 01:03:49Z puggles $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF(puaChooser) + + + +puaChooser::puaChooser ( int _x1, int _y1, int _x2, int _y2, char *legend ) +{ + x1 = _x1 ; x2 = _x2 ; y1 = _y1 ; y2 = _y2 ; + + chooser_button = new puButton ( x1, y1, x2, y2 ) ; + chooser_button->setValue ( FALSE ) ; + chooser_button->setLegend ( legend ) ; + chooser_button->setUserData ( this ) ; + chooser_button->setCallback ( static_popup_cb ) ; + + popup_menu = new puPopupMenu ( x1, y1 ) ; + popup_menu -> setUserData ( this ) ; + popup_menu -> setCallback ( static_menu_cb ) ; +} + + +void puaChooser::add_item ( char *str, puCallback _cb, void *_user_data ) +{ + popup_menu -> add_item ( str, _cb, _user_data ) ; +} + + +void puaChooser::close () +{ + popup_menu -> close () ; + // int x, y, w, h ; + // popup_menu -> getPosition ( &x, &y ) ; + // popup_menu -> getSize ( &w, &h ) ; + popup_menu -> setPosition ( x1, y2 ) ; +} + + +void puaChooser::popup_cb () +{ + chooser_button -> setValue ( TRUE ) ; + + if ( popup_menu -> isVisible () ) + popup_menu -> hide () ; + else + popup_menu -> reveal () ; +} + + +void puaChooser::menuCleanup ( const char *s ) +{ + popup_menu -> hide () ; + chooser_button -> setValue ( FALSE ) ; + + if ( s != NULL ) + chooser_button -> setLegend ( s ) ; +} + + +void puaChooser::menuCleanup ( puObject *ob ) +{ + puObject *p = ob -> getParent () ; + ((puaChooser*)( p -> getUserData () )) -> menuCleanup ( ob -> getLegend () ) ; +} + + +void puaChooser::static_popup_cb ( puObject *ob ) +{ + ((puaChooser*)( ob -> getUserData () )) -> popup_cb () ; +} + + + +void puaChooser::static_menu_cb ( puObject *ob ) +{ + ((puaChooser*)( ob -> getUserData () )) -> menuCleanup ( (const char *)NULL ) ; +} + + diff --git a/src/puAux/puAuxComboBox.cxx b/src/puAux/puAuxComboBox.cxx new file mode 100644 index 0000000..8b71ff0 --- /dev/null +++ b/src/puAux/puAuxComboBox.cxx @@ -0,0 +1,377 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxComboBox.cxx 2050 2005-11-10 20:07:08Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaComboBox,puGroup) + + +void puaComboBox::input_cb ( puObject *inp ) +{ + puaComboBox *cbox = (puaComboBox *) inp -> getUserData () ; + + if ( strcmp ( inp->getStringValue (), cbox->getStringValue () ) ) + { + cbox->update_current_item () ; + cbox->setCallbackSource ( PUACOMBOBOX_CALLBACK_INPUT ) ; + cbox -> invokeCallback () ; + } +} + +void puaComboBox::input_active_cb ( puObject *inp ) +{ + puaComboBox *cbox = (puaComboBox *) inp -> getUserData () ; + + cbox->update_current_item () ; + cbox->setCallbackSource ( PUACOMBOBOX_CALLBACK_INPUT ) ; + cbox -> invokeActiveCallback () ; +} + +void puaComboBox::input_down_cb ( puObject *inp ) +{ + puaComboBox *cbox = (puaComboBox *) inp -> getUserData () ; + + cbox->update_current_item () ; + cbox->setCallbackSource ( PUACOMBOBOX_CALLBACK_INPUT ) ; + cbox -> invokeCallback () ; +} + +void puaComboBox::handle_arrow ( puObject *arrow ) +{ + puaComboBox *cbox = (puaComboBox *) arrow -> getUserData () ; + + if ( ! cbox -> __getPopupMenu () -> isVisible () ) + cbox -> __getPopupMenu () -> reveal () ; + else + cbox -> __getPopupMenu () -> hide () ; + + /* arrow -> setStyle ( - arrow -> getStyle () ) ; */ +} + +void puaComboBox::handle_popup ( puObject *popupm ) +{ + puaComboBox *cbox = (puaComboBox *) popupm -> getUserData () ; + + cbox -> setCurrentItem ( popupm -> getLegend () ) ; +} + + +void puaComboBox::update_widgets ( void ) +{ + if ( curr_item >= 0 ) + /* There are some items */ + { + setValue ( list[curr_item] ) ; + + arrow_btn -> activate () ; + } + else + /* There aren't any items */ + { + setValue ( "" ) ; + + arrow_btn -> greyOut () ; + } + + input->setValue ( getStringValue () ) ; +} + + +void puaComboBox::update_current_item ( void ) +{ + if ( strcmp ( getStringValue (), input->getStringValue () ) ) + { + /* User has typed in an arbitrary string; see if it is in the list */ + int i ; + for ( i = 0 ; i < num_items ; i++ ) + { + if ( !strcmp ( list[i], input->getStringValue () ) ) + { + /* ... yes, it is ! */ + curr_item = i ; + break ; + } + } + + if ( ( i == num_items ) || ( num_items == 0 ) ) + { + /* Didn't find it in the list--or the list is empty */ + char **old_list = list ; + list = new char * [ num_items + 2 ] ; + for ( i = 0; i < num_items; i++ ) /* Copy over the old list */ + list[i] = old_list[i] ; + + delete old_list ; + + num_items++ ; + int len = strlen ( input->getStringValue () ) + 1 ; + curr_item = num_items - 1 ; + list[curr_item] = new char [ len ] ; + memcpy ( list[curr_item], input->getStringValue (), len ) ; + list[num_items] = NULL ; + newList ( list ) ; + update_widgets () ; + } + + setValue ( input->getStringValue () ) ; + } +} + + +void puaComboBox::newList ( char ** _list ) +{ + // Delete the existing list--if it has not been passed in as the argument + int i ; + if ( list && ( list != _list ) ) + { + for ( i = 0; i < num_items; i++ ) + delete list[i] ; + + delete list ; + list = NULL ; + } + + popup_menu -> empty () ; + + if ( _list == NULL ) + num_items = 0 ; + else + { + for ( num_items = 0 ; _list[num_items] != NULL ; num_items++ ) + /* Count number of items */ ; + + if ( num_items > 0 ) + { + if ( list != _list ) + { + list = new char * [ num_items + 1 ] ; /* '+ 1' to capture the trailing NULL */ + for ( i = 0; i < num_items; i++ ) + { + int len = strlen ( _list[i] ) + 1 ; /* "+ 1" to capture the \0 */ + list[i] = new char [ len ] ; + memcpy ( list[i], _list[i], len * sizeof(char) ) ; + } + + list[num_items] = NULL ; + } + + int dummy, h ; + int old_height = abox.max[1] - abox.min[1] ; + + puPushGroup ( popup_menu ) ; + + for ( i = (num_items - 1) ; i >= 0 ; i-- ) + { + puObject *menu_item = popup_menu -> add_item ( list[i], handle_popup ) ; + + menu_item -> setUserData ( this ) ; + menu_item -> getSize ( &dummy, &h ) ; + menu_item -> setSize ( abox.max[0] - abox.min[0], h ) ; + } + + popup_menu -> close() ; + + /* Position popup menu correctly */ + + popup_menu -> getSize ( &dummy, &h ) ; + + if ( (abox.min[1] - h) >= 0 ) + { + popup_menu -> setPosition ( 0, 0 - h ) ; + + arrow_btn -> setArrowType ( PUARROW_DOWN ) ; + } + else + { + popup_menu -> setPosition ( 0, old_height ) ; + + arrow_btn -> setArrowType ( PUARROW_UP ) ; + } + } + } + + if ( ( curr_item >= num_items ) || ( curr_item < 0 ) ) + { + curr_item = ( num_items > 0 ? 0 : - 1 ) ; + update_widgets () ; + } +} + +int puaComboBox::getCurrentItem ( void ) +{ + update_current_item () ; + + return curr_item ; +} + +void puaComboBox::setCurrentItem ( const char *item_ptr ) +{ + int i ; + + for ( i = 0 ; i < num_items ; i++ ) + { + if ( list[i] == item_ptr ) + { + setCurrentItem ( i ) ; + + break ; + } + } +} + + +void puaComboBox::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + draw_label ( dx, dy ) ; + + puGroup::draw ( dx, dy ) ; +} + +int puaComboBox::checkHit ( int button, int updown, int x, int y ) +{ + if ( input -> checkHit ( button, updown, x-abox.min[0], y-abox.min[1] ) ) + { + popup_menu -> hide () ; + puSetActiveWidget ( this, x, y ) ; + + return TRUE ; + } + else + return puGroup::checkHit ( button, updown, x, y ) ; +} + +int puaComboBox::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || ! input -> isAcceptingInput () || + ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + switch ( key ) + { + case PU_KEY_HOME : + setCurrentItem ( 0 ) ; + break ; + + case PU_KEY_END : + setCurrentItem ( num_items - 1 ) ; + break ; + + case PU_KEY_UP : + setCurrentItem ( getCurrentItem () + 1 ) ; + break ; + + case PU_KEY_DOWN : + setCurrentItem ( getCurrentItem () - 1 ) ; + break ; + + case PU_KEY_PAGE_UP : + case PU_KEY_PAGE_DOWN : + if ( ! popup_menu -> isVisible () ) + popup_menu -> reveal () ; + else + popup_menu -> hide () ; + + break ; + + default : + return input -> checkKey ( key, updown ) ; + break ; + } + + return TRUE ; +} + +void puaComboBox::setColourScheme ( float r, float g, float b, float a ) +{ + puObject::setColourScheme ( r, g, b, a ) ; + setChildColourScheme( PUCLASS_INPUT, + colour [ PUCOL_EDITFIELD ][0], + colour [ PUCOL_EDITFIELD ][1], + colour [ PUCOL_EDITFIELD ][2], + colour [ PUCOL_EDITFIELD ][3] ) ; +} + +void puaComboBox::setColour ( int which, float r, float g, float b, float a ) +{ + if (which == PUCOL_EDITFIELD) + input->setColourScheme ( r, g, b, a ) ; + else + puObject::setColour ( which, r, g, b, a ) ; +} + + +puaComboBox::puaComboBox ( int minx, int miny, int maxx, int maxy, + char **entries, int editable ) : + puGroup( minx, miny ) +{ + type |= PUCLASS_COMBOBOX ; + + char *stringval ; + int arrow_width = (int) ( (maxy-miny) / 1.5f ) ; + + input = new puInput ( 0, 0, maxx-minx - arrow_width, maxy-miny ) ; + input -> setUserData ( this ) ; + input -> setCallback ( input_cb ) ; + input -> setActiveCallback ( input_active_cb ) ; + input -> setDownCallback ( input_down_cb ) ; + + input -> setStyle ( PUSTYLE_SMALL_SHADED ) ; + + if ( ! editable ) + input -> disableInput () ; + + /* Share 'string' value with input box */ + input -> getValue ( &stringval ) ; +// setValuator ( stringval ) ; + + arrow_btn = new puArrowButton ( maxx-minx - arrow_width, 0, + maxx-minx, maxy-miny, + PUARROW_DOWN ) ; + arrow_btn -> setStyle ( PUSTYLE_SMALL_SHADED ) ; + arrow_btn -> setUserData ( this ) ; + arrow_btn -> setCallback ( handle_arrow ) ; + + popup_menu = new puPopupMenu ( 0, 0 ) ; + popup_menu -> close () ; + + close () ; + + list = NULL ; + curr_item = 0 ; + num_items = 0 ; + callback_source = PUACOMBOBOX_CALLBACK_NONE ; + newList ( entries ) ; + update_widgets () ; +} + +void puaComboBox::setSize ( int w, int h ) +{ + int arrow_width = (int) ( float(h) / 1.5f ) ; + input->setSize ( w - arrow_width, h ) ; + arrow_btn->setPosition ( w - arrow_width, 0 ) ; + arrow_btn->setSize ( arrow_width, h ) ; +} + diff --git a/src/puAux/puAuxCompass.cxx b/src/puAux/puAuxCompass.cxx new file mode 100644 index 0000000..85aa2f6 --- /dev/null +++ b/src/puAux/puAuxCompass.cxx @@ -0,0 +1,564 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxCompass.cxx 1942 2004-08-05 01:03:49Z puggles $ +*/ + + +#include "puAuxLocal.h" + +#ifdef UL_MAC_OSX +# include +#else +# include +#endif + +UL_RTTI_DEF1(puaCompass,puObject) + +#define SET_ARC_COLOUR1(value) \ + if ( getIntegerValue () == value ) \ + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; \ + else \ + glColor4fv ( colour [ PUCOL_FOREGROUND ] ) + +#define SET_ARC_COLOUR2(value1,value2) \ + if ( ( getIntegerValue () == value1 ) || ( getIntegerValue () == value2 ) ) \ + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; \ + else \ + glColor4fv ( colour [ PUCOL_FOREGROUND ] ) + +#define SET_ARC_COLOUR3(value1,value2,value3) \ + if ( ( getIntegerValue () == value1 ) || ( getIntegerValue () == value2 ) || ( getIntegerValue () == value3 ) ) \ + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; \ + else \ + glColor4fv ( colour [ PUCOL_FOREGROUND ] ) + +void puaCompass::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + // Draw the frame surrounding the compass + +// if ( ( style == PUSTYLE_BEVELLED ) || +// ( style == PUSTYLE_SHADED ) ) +// abox.draw ( dx, dy, -PUSTYLE_BOXED, colour, FALSE, 2 ) ; +// else +// abox.draw ( dx, dy, -style, colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + float xsize = float(abox.max[0] - abox.min[0]) ; + float ysize = float(abox.max[1] - abox.min[1]) ; + float xcenter = float(dx + abox.min[0]) + xsize / 2.0f ; + float ycenter = float(dy + abox.min[1]) + ysize / 2.0f ; + float size = ( ( xsize > ysize ) ? ysize : xsize ) / 2.5f ; + + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; + glTranslatef ( xcenter, ycenter, 0 ) ; + glScalef ( size, size, size ) ; + + /* Make the points circular instead of square */ + glEnable ( GL_POINT_SMOOTH ) ; + glPointSize ( point_size ) ; + + /* Ideally, we would be able to use a "glRotate" to rotate the compass. + * Unfortunately, PUI widgets are all 2-d and so our z-coordinate will cause + * the widget to be clipped. Our solution is to rotate all the compass + * components into the widget plane and just plot with a z-coordinate of zero. + */ + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + + sgVec3 pt ; + /* The points */ + glBegin ( GL_POINTS ) ; + SET_ARC_COLOUR1 ( PUACOMPASS_RESET ) ; + glVertex3f ( 0.0, 0.0, 0.0 ) ; /* the origin */ + SET_ARC_COLOUR1 ( PUACOMPASS_ROTATE_Y_Z ) ; + if ( getIntegerValue () != PUACOMPASS_ROTATE_Y_Z ) // Set x-point red for now since point size doesn't seem to work + glColor4f ( 1.0, 0.0, 0.0, 1.0 ) ; + sgSetVec3 ( pt, 1.0, 0.0, 0.0 ) ; /* x-axis point */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + SET_ARC_COLOUR1 ( PUACOMPASS_ROTATE_Z_X ) ; + if ( getIntegerValue () != PUACOMPASS_ROTATE_Z_X ) // Set y-point green for now since point size doesn't seem to work + glColor4f ( 0.0, 1.0, 0.0, 1.0 ) ; + sgSetVec3 ( pt, 0.0, 1.0, 0.0 ) ; /* y-axis point */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + SET_ARC_COLOUR1 ( PUACOMPASS_ROTATE_X_Y ) ; + if ( getIntegerValue () != PUACOMPASS_ROTATE_X_Y ) // Set z-point blue for now since point size doesn't seem to work + glColor4f ( 0.0, 0.0, 1.0, 1.0 ) ; + sgSetVec3 ( pt, 0.0, 0.0, 1.0 ) ; /* z-axis point */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + glEnd () ; + + glLineWidth ( 3 ) ; + + /* The axis lines */ + glBegin ( GL_LINES ) ; + SET_ARC_COLOUR3 ( PUACOMPASS_TRANSLATE_X, PUACOMPASS_TRANSLATE_X_Y, PUACOMPASS_TRANSLATE_Z_X ) ; + sgSetVec3 ( pt, 0.0, 0.0, 0.0 ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + sgSetVec3 ( pt, 1.0, 0.0, 0.0 ) ; /* x-axis */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + SET_ARC_COLOUR3 ( PUACOMPASS_TRANSLATE_Y, PUACOMPASS_TRANSLATE_X_Y, PUACOMPASS_TRANSLATE_Y_Z ) ; + sgSetVec3 ( pt, 0.0, 0.0, 0.0 ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + sgSetVec3 ( pt, 0.0, 1.0, 0.0 ) ; /* y-axis */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + SET_ARC_COLOUR3 ( PUACOMPASS_TRANSLATE_Z, PUACOMPASS_TRANSLATE_Y_Z, PUACOMPASS_TRANSLATE_Z_X ) ; + sgSetVec3 ( pt, 0.0, 0.0, 0.0 ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + sgSetVec3 ( pt, 0.0, 0.0, 1.0 ) ; /* z-axis */ + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + glEnd () ; + + /* The quarter-arcs */ + int num_points = 13 ; + float coords[13] = { .000000f, .130526f, .258819f, .382683f, .500000f, .608761f, + .707107f, .793353f, .866025f, .923879f, .965926f, .991445f, 1.0f } ; + int i ; + glBegin ( GL_LINE_STRIP ) ; + SET_ARC_COLOUR2 ( PUACOMPASS_ROTATE_Z, PUACOMPASS_TRANSLATE_X_Y ) ; + for ( i = 0; i < num_points; i++ ) + { + sgSetVec3 ( pt, coords[i], coords[num_points-1-i], 0.0 ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + } + glEnd () ; + + glBegin ( GL_LINE_STRIP ) ; + SET_ARC_COLOUR2 ( PUACOMPASS_ROTATE_Y, PUACOMPASS_TRANSLATE_Z_X ) ; + for ( i = 0; i < num_points; i++ ) + { + sgSetVec3 ( pt, coords[i], 0.0, coords[num_points-1-i] ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + } + glEnd () ; + + glBegin ( GL_LINE_STRIP ) ; + SET_ARC_COLOUR2 ( PUACOMPASS_ROTATE_X, PUACOMPASS_TRANSLATE_Y_Z ) ; + for ( i = 0; i < num_points; i++ ) + { + sgSetVec3 ( pt, 0.0, coords[i], coords[num_points-1-i] ) ; + sgRotateVecQuat ( pt, rotation ) ; + glVertex3f ( pt[SG_X], pt[SG_Y], 0.0f ) ; + } + glEnd () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + + +void puaCompass::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + float xsize = float(abox.max[0] - abox.min[0]) ; + float ysize = float(abox.max[1] - abox.min[1]) ; + float size = ( ( xsize > ysize ) ? ysize : xsize ) / 2.5f ; + + /* Set the "prev_rotation" quaternion. This will stop being updated after the downclick. + * We use it in the transformation so that the transformation "freezes" on the downclick. + * In the meantime, the actual rotation continues to change and updates the widget "look." + */ + if ( button_state == PU_UP ) + sgCopyQuat ( prev_rotation, rotation ) ; + + /* The mouse position forms a line in 3-space. The equation of this line in window + * coordinates is (xm,ym,0) + u k, where "xm" and "ym" are the mouse position (x and y), + * "u" is a parametric coordinate along the line, and "k" is the unit vector in the z- + * direction. We rotate these into the compass coordinate system to get the mouse click + * somewhere on a line R = R0 + u T, where R0 is (xm,ym,0) rotated and T is "k" rotated. + */ + sgLine3 mouse_click_line ; + mouse_click_line.direction_vector[0] = 2.0f * ( -prev_rotation[SG_W] * prev_rotation[SG_Y] + prev_rotation[SG_X] * prev_rotation[SG_Z] ) ; + mouse_click_line.direction_vector[1] = 2.0f * ( prev_rotation[SG_W] * prev_rotation[SG_X] + prev_rotation[SG_Y] * prev_rotation[SG_Z] ) ; + mouse_click_line.direction_vector[2] = prev_rotation[SG_W] * prev_rotation[SG_W] + prev_rotation[SG_Z] * prev_rotation[SG_Z] - prev_rotation[SG_X] * prev_rotation[SG_X] - prev_rotation[SG_Y] * prev_rotation[SG_Y] ; + + float xm = float(x - abox.min[0]) - xsize / 2.0f ; + float ym = float(y - abox.min[1]) - ysize / 2.0f ; + sgSetVec3 ( mouse_click_line.point_on_line, xm, ym, 0.0f ) ; + sgRotateCoordQuat ( mouse_click_line.point_on_line, prev_rotation ) ; + + /* If the button state is up (whether moving or down-clicking), find the + * nearest element in the compass (if any) to activate it. + */ + if ( button_state == PU_UP ) + { + /* Set the widget value to zero. Then perform thirteen tests, each one figuring out whether + * the mouse is over (or has clicked on) so that we can activate the appropriate part of + * the widget. + */ + setValue ( PUACOMPASS_INACTIVE ) ; + + sgVec3 point ; + /* Minimum distance (squared) from the points (1,0,0), (0,1,0), and (0,0,1) to the mouse click line */ + sgSetVec3 ( point, size, 0.0, 0.0 ) ; + float dmin2 = sgDistSquaredToLineVec3 ( mouse_click_line, point ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_ROTATE_Y_Z ) ; + + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( point, 0.0, size, 0.0 ) ; + dmin2 = sgDistSquaredToLineVec3 ( mouse_click_line, point ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_ROTATE_Z_X ) ; + } + + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( point, 0.0, 0.0, size ) ; + dmin2 = sgDistSquaredToLineVec3 ( mouse_click_line, point ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_ROTATE_X_Y ) ; + } + + /* Minimum distance (squared) from the origin */ + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( point, 0.0, 0.0, 0.0 ) ; + dmin2 = sgDistSquaredToLineVec3 ( mouse_click_line, point ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_RESET ) ; + } + + /* Minimum distance (squared) from the three axis segments */ + sgLineSegment3 axis ; + sgSetVec3 ( axis.a, 0.0, 0.0, 0.0 ) ; + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( axis.b, size, 0.0, 0.0 ) ; + dmin2 = sgDistSquaredToLineLineSegment ( axis, mouse_click_line ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_TRANSLATE_X ) ; + } + + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( axis.b, 0.0, size, 0.0 ) ; + dmin2 = sgDistSquaredToLineLineSegment ( axis, mouse_click_line ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_TRANSLATE_Y ) ; + } + + if ( getIntegerValue () == PUACOMPASS_INACTIVE ) + { + sgSetVec3 ( axis.b, 0.0, 0.0, size ) ; + dmin2 = sgDistSquaredToLineLineSegment ( axis, mouse_click_line ) ; + if ( dmin2 < point_size * point_size ) + setValue ( PUACOMPASS_TRANSLATE_Z ) ; + } + + /* Project the mouse click line on to each of the coordinate planes. If it intersects + * within the given radius (size +- point_size) of the origin, highlight the arc. + */ + sgFloat xydist2 = 0.0 ; + sgFloat xyplane_xint = 0.0, xyplane_yint = 0.0 ; + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[2] != 0.0 ) ) + { + xyplane_xint = mouse_click_line.point_on_line[0] - mouse_click_line.point_on_line[2] * mouse_click_line.direction_vector[0] / mouse_click_line.direction_vector[2] ; + xyplane_yint = mouse_click_line.point_on_line[1] - mouse_click_line.point_on_line[2] * mouse_click_line.direction_vector[1] / mouse_click_line.direction_vector[2] ; + xydist2 = xyplane_xint * xyplane_xint + xyplane_yint * xyplane_yint ; + if ( ( xyplane_xint > 0.0 ) && ( xyplane_yint > 0.0 ) ) + { + if ( fabs ( xydist2 - size * size ) < 2.0f * size * point_size ) + { + setValue ( PUACOMPASS_ROTATE_Z ) ; + xint = xyplane_xint ; + yint = xyplane_yint ; + } + } + } + + sgFloat yzdist2 = 0.0 ; + sgFloat yzplane_yint = 0.0, yzplane_zint = 0.0 ; + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[0] != 0.0 ) ) + { + yzplane_yint = mouse_click_line.point_on_line[1] - mouse_click_line.point_on_line[0] * mouse_click_line.direction_vector[1] / mouse_click_line.direction_vector[0] ; + yzplane_zint = mouse_click_line.point_on_line[2] - mouse_click_line.point_on_line[0] * mouse_click_line.direction_vector[2] / mouse_click_line.direction_vector[0] ; + yzdist2 = yzplane_yint * yzplane_yint + yzplane_zint * yzplane_zint ; + if ( ( yzplane_yint > 0.0 ) && ( yzplane_zint > 0.0 ) ) + { + if ( fabs ( yzdist2 - size * size ) < 2.0f * size * point_size ) + { + setValue ( PUACOMPASS_ROTATE_X ) ; + yint = yzplane_yint ; + zint = yzplane_zint ; + } + } + } + + sgFloat zxdist2 = 0.0 ; + sgFloat zxplane_zint = 0.0, zxplane_xint = 0.0 ; + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[1] != 0.0 ) ) + { + zxplane_zint = mouse_click_line.point_on_line[2] - mouse_click_line.point_on_line[1] * mouse_click_line.direction_vector[2] / mouse_click_line.direction_vector[1] ; + zxplane_xint = mouse_click_line.point_on_line[0] - mouse_click_line.point_on_line[1] * mouse_click_line.direction_vector[0] / mouse_click_line.direction_vector[1] ; + zxdist2 = zxplane_zint * zxplane_zint + zxplane_xint * zxplane_xint ; + if ( ( zxplane_zint > 0.0 ) && ( zxplane_xint > 0.0 ) ) + { + if ( fabs ( zxdist2 - size * size ) < 2.0f * size * point_size ) + { + setValue ( PUACOMPASS_ROTATE_Y ) ; + zint = zxplane_zint ; + xint = zxplane_xint ; + } + } + } + + /* If we missed the arc (meaning that the value is still INACTIVE), check whether we + * got inside the quadrants. + */ + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[2] != 0.0 ) ) + { + if ( ( xydist2 < size * size ) && ( xyplane_xint > 0.0 ) && ( xyplane_yint > 0.0 ) ) + { + setValue ( PUACOMPASS_TRANSLATE_X_Y ) ; + xint = xyplane_xint ; + yint = xyplane_yint ; + } + } + + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[0] != 0.0 ) ) + { + if ( ( yzdist2 < size * size ) && ( yzplane_yint > 0.0 ) && ( yzplane_zint > 0.0 ) ) + { + setValue ( PUACOMPASS_TRANSLATE_Y_Z ) ; + yint = yzplane_yint ; + zint = yzplane_zint ; + } + } + + if ( ( getIntegerValue () == PUACOMPASS_INACTIVE ) && ( mouse_click_line.direction_vector[1] != 0.0 ) ) + { + if ( ( zxdist2 < size * size ) && ( zxplane_zint > 0.0 ) && ( zxplane_xint > 0.0 ) ) + { + setValue ( PUACOMPASS_TRANSLATE_Z_X ) ; + zint = zxplane_zint ; + xint = zxplane_xint ; + } + } + } + + if ( button != getTriggerButton () ) + return ; + + if ( updown == PU_DOWN ) + { + if ( getIntegerValue () == PUACOMPASS_ROTATE_X ) + prev_angle = sgATan2 ( zint, yint ) ; + else if ( getIntegerValue () == PUACOMPASS_ROTATE_Y ) + prev_angle = sgATan2 ( xint, zint ) ; + else if ( getIntegerValue () == PUACOMPASS_ROTATE_Z ) + prev_angle = sgATan2 ( yint, xint ) ; + + mouse_x = xm ; + mouse_y = ym ; + button_state = PU_DOWN ; + + // If we are rotating about two axes, we need to mouse z-coordinate for future reference. + if ( ( getIntegerValue () == PUACOMPASS_ROTATE_Y_Z ) || + ( getIntegerValue () == PUACOMPASS_ROTATE_Z_X ) || + ( getIntegerValue () == PUACOMPASS_ROTATE_X_Y ) ) + { + sgVec3 temp ; + sgCopyVec3 ( temp, mouse_click_line.point_on_line ) ; + // Parametric coordinate of point on line closest to origin + float u_origin = -sgScalarProductVec3 ( temp, mouse_click_line.direction_vector ) ; + float u = 0.0 ; + + if ( getIntegerValue () == PUACOMPASS_ROTATE_Y_Z ) + { + temp[SG_X] -= size ; + u = -sgScalarProductVec3 ( temp, mouse_click_line.direction_vector ) ; + } + else if ( getIntegerValue () == PUACOMPASS_ROTATE_Z_X ) + { + temp[SG_Y] -= size ; + u = -sgScalarProductVec3 ( temp, mouse_click_line.direction_vector ) ; + } + else if ( getIntegerValue () == PUACOMPASS_ROTATE_X_Y ) + { + temp[SG_Z] -= size ; + u = -sgScalarProductVec3 ( temp, mouse_click_line.direction_vector ) ; + } + + mouse_z = size * size - xm * xm - ym * ym ; + if ( mouse_z < 0.0 ) mouse_z = 0.0 ; + mouse_z = sgSqrt ( mouse_z ) ; + if ( u < u_origin ) mouse_z = -mouse_z ; + } + } + else if ( updown == PU_UP ) + { + if ( getIntegerValue () == PUACOMPASS_RESET ) + { + sgSetQuat ( rotation, 1.0f, 0.0f, 0.0f, 0.0f ) ; + sgSetQuat ( prev_rotation, 1.0f, 0.0f, 0.0f, 0.0f ) ; + sgSetVec3 ( translation, 0.0f, 0.0f, 0.0f ) ; + setValue ( PUACOMPASS_INACTIVE ) ; + } + + button_state = PU_UP ; + } + else if ( updown == PU_DRAG ) + { + float zm = size * size - xm * xm - ym * ym ; + if ( zm < 0.0 ) zm = 0.0 ; + zm = ( mouse_z >= 0.0 ) ? sgSqrt ( zm ) : -sgSqrt ( zm ) ; + float new_size = sgSqrt ( xm * xm + ym * ym + zm * zm ) ; + float cosang = ( xm * mouse_x + ym * mouse_y + zm * mouse_z ) / ( size * new_size ) ; + cosang = sgSqrt ( ( 1.0f + cosang ) / 2.0f ) ; + + sgVec3 displacement ; + sgSetVec3 ( displacement, xm - mouse_x, ym - mouse_y, 0.0f ) ; + sgRotateVecQuat ( displacement, prev_rotation ) ; + + sgQuat rot ; + sgSetQuat ( rot, 2.0f, 2.0f, 2.0f, 2.0f ) ; /* Values will change if we have a rotation */ + + sgFloat angle = prev_angle ; + + switch ( getIntegerValue () ) + { + case PUACOMPASS_INACTIVE : + break ; + case PUACOMPASS_RESET : + break ; + case PUACOMPASS_ROTATE_Y_Z : +// rot[SG_X] = 0.0 ; +// rot[SG_Y] = -displacement[2] / size ; +// rot[SG_Z] = displacement[1] / size ; +// rot[SG_W] = sgSqrt ( 1.0f - rot[SG_Y] * rot[SG_Y] - rot[SG_Z] * rot[SG_Z] ) ; +// sgPostMultQuat ( rotation, rot ) ; +// break ; + case PUACOMPASS_ROTATE_Z_X : +// rot[SG_X] = displacement[2] / size ; +// rot[SG_Y] = 0.0 ; +// rot[SG_Z] = -displacement[0] / size ; +// rot[SG_W] = sgSqrt ( 1.0f - rot[SG_X] * rot[SG_X] - rot[SG_Z] * rot[SG_Z] ) ; +// sgPostMultQuat ( rotation, rot ) ; +// break ; + case PUACOMPASS_ROTATE_X_Y : +// rot[SG_X] = -displacement[1] / size ; +// rot[SG_Y] = displacement[0] / size ; +// rot[SG_Z] = 0.0 ; +// rot[SG_W] = sgSqrt ( 1.0f - rot[SG_X] * rot[SG_X] - rot[SG_Y] * rot[SG_Y] ) ; + rot[SG_W] = cosang ; + rot[SG_X] = ( mouse_y * zm - mouse_z * ym ) * 0.5f / ( size * new_size * cosang ) ; + rot[SG_Y] = ( mouse_z * xm - mouse_x * zm ) * 0.5f / ( size * new_size * cosang ) ; + rot[SG_Z] = ( mouse_x * ym - mouse_y * xm ) * 0.5f / ( size * new_size * cosang ) ; + sgRotateVecQuat ( rot, prev_rotation ) ; + printf ( "Rotation %f %f %f %f %f %f %f %f\n", prev_angle, xm, ym, zm, rot[SG_W], rot[SG_X], rot[SG_Y], rot[SG_Z] ) ; + sgPreMultQuat ( rotation, rot ) ; + break ; + case PUACOMPASS_TRANSLATE_X : + translation[0] += translation_sensitivity * displacement[0] ; + break ; + case PUACOMPASS_TRANSLATE_Y : + translation[1] += translation_sensitivity * displacement[1] ; + break ; + case PUACOMPASS_TRANSLATE_Z : + translation[2] += translation_sensitivity * displacement[2] ; + break ; + case PUACOMPASS_ROTATE_Z : + xint = mouse_click_line.point_on_line[0] - mouse_click_line.point_on_line[2] * mouse_click_line.direction_vector[0] / mouse_click_line.direction_vector[2] ; + yint = mouse_click_line.point_on_line[1] - mouse_click_line.point_on_line[2] * mouse_click_line.direction_vector[1] / mouse_click_line.direction_vector[2] ; + angle = sgATan2 ( yint, xint ) - prev_angle ; + rot[SG_X] = 0.0 ; + rot[SG_Y] = 0.0 ; + rot[SG_Z] = sgSin ( angle / 2.0f ) ; + rot[SG_W] = sgCos ( angle / 2.0f ) ; + sgPostMultQuat ( rotation, rot ) ; + prev_angle += angle ; + break ; + case PUACOMPASS_ROTATE_X : + yint = mouse_click_line.point_on_line[1] - mouse_click_line.point_on_line[0] * mouse_click_line.direction_vector[1] / mouse_click_line.direction_vector[0] ; + zint = mouse_click_line.point_on_line[2] - mouse_click_line.point_on_line[0] * mouse_click_line.direction_vector[2] / mouse_click_line.direction_vector[0] ; + angle = sgATan2 ( zint, yint ) - prev_angle ; + rot[SG_X] = sgSin ( angle / 2.0f ) ; + rot[SG_Y] = 0.0 ; + rot[SG_Z] = 0.0 ; + rot[SG_W] = sgCos ( angle / 2.0f ) ; + sgPostMultQuat ( rotation, rot ) ; + prev_angle += angle ; + break ; + case PUACOMPASS_ROTATE_Y : + zint = mouse_click_line.point_on_line[2] - mouse_click_line.point_on_line[1] * mouse_click_line.direction_vector[2] / mouse_click_line.direction_vector[1] ; + xint = mouse_click_line.point_on_line[0] - mouse_click_line.point_on_line[1] * mouse_click_line.direction_vector[0] / mouse_click_line.direction_vector[1] ; + angle = sgATan2 ( xint, zint ) - prev_angle ; + rot[SG_X] = 0.0 ; + rot[SG_Y] = sgSin ( angle / 2.0f ) ; + rot[SG_Z] = 0.0 ; + rot[SG_W] = sgCos ( angle / 2.0f ) ; + sgPostMultQuat ( rotation, rot ) ; + prev_angle += angle ; + break ; + case PUACOMPASS_TRANSLATE_X_Y : + translation[0] += translation_sensitivity * displacement[0] ; + translation[1] += translation_sensitivity * displacement[1] ; + break ; + case PUACOMPASS_TRANSLATE_Y_Z : + translation[1] += translation_sensitivity * displacement[1] ; + translation[2] += translation_sensitivity * displacement[2] ; + break ; + case PUACOMPASS_TRANSLATE_Z_X : + translation[2] += translation_sensitivity * displacement[2] ; + translation[0] += translation_sensitivity * displacement[0] ; + break ; + } + + mouse_x = xm * size / new_size ; + mouse_y = ym * size / new_size ; + mouse_z = zm * size / new_size ; +// if ( rot[0] < 1.5 ) +// { +// sgPostMultQuat ( rotation, rot ) ; +// prev_angle += angle ; +// printf ( "Rotation %f %f %f %f %f\n", prev_angle, rotation[SG_W], rotation[SG_X], rotation[SG_Y], rotation[SG_Z] ) ; +// } + } +} diff --git a/src/puAux/puAuxFileSelector.cxx b/src/puAux/puAuxFileSelector.cxx new file mode 100644 index 0000000..261222a --- /dev/null +++ b/src/puAux/puAuxFileSelector.cxx @@ -0,0 +1,692 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxFileSelector.cxx 1902 2004-03-21 19:03:23Z sjbaker $ +*/ + +/**** +* NAME +* puaFileSelector +* +* DESCRIPTION +* PUI dialog for selecting files +* +* AUTHOR +* Dave McClurg +* +* CREATION DATE +* August 2000 +* +* MODIFICATION HISTORY +* John Fay - many improvements +* Steve Baker - moved from FilePicker to FileSelector due to +* key API change. +****/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaFileSelector,puDialogBox) + + +#ifdef UL_MSVC +#define DOTDOTSLASH "..\\" +#define SLASH "\\" +#else +#define DOTDOTSLASH "../" +#define SLASH "/" +#endif +#define SLASH_LEN 1 + +static void puaFileSelectorHandleSlider ( puObject * slider ) +{ + float val ; + slider -> getValue ( &val ) ; + val = 1.0f - val ; + + puListBox* list_box = (puListBox*) slider -> getUserData () ; + int idx = int ( list_box -> getNumItems () * val ) ; + list_box -> setTopItem ( idx ) ; +} + +static void puaFileSelectorHandleArrow ( puObject *arrow ) +{ + puSlider *slider = (puSlider *) arrow->getUserData () ; + puListBox* list_box = (puListBox*) slider -> getUserData () ; + + int type = ((puArrowButton *)arrow)->getArrowType() ; + int inc = ( type == PUARROW_DOWN ) ? 1 : + ( type == PUARROW_UP ) ? -1 : + ( type == PUARROW_FASTDOWN ) ? 10 : + ( type == PUARROW_FASTUP ) ? -10 : 0 ; + + float val ; + slider -> getValue ( &val ) ; + val = 1.0f - val ; + int num_items = list_box->getNumItems () - 1 ; + if ( num_items > 0 ) + { + int idx = int ( num_items * val + 0.5f ) + inc ; + if ( idx > num_items ) idx = num_items ; + if ( idx < 0 ) idx = 0 ; + + slider -> setValue ( 1.0f - (float)idx / num_items ) ; + list_box -> setTopItem ( idx ) ; + } +} + + +static bool is_slash ( char ch ) +{ +#ifdef UL_WIN32 + /* win32 can use back or forward slash */ + return ch == '\\' || ch == '/' ; +#else + return ch == SLASH[0] ; +#endif +} + + +static void chop_file ( char *fname ) +{ + /* removes everything back to the last '/' */ + + for ( int i = strlen(fname)-1 ; ! is_slash ( fname[i] ) && i >= 0 ; i-- ) + fname[i] = '\0' ; +} + + +static void go_up_one_directory ( char *fname ) +{ + /* removes everything back to the last but one '/' */ + + chop_file ( fname ) ; + + if ( fname[0] == '\0' ) + { + /* Empty string! We must be at the root. */ + + strcpy ( fname, SLASH ) ; + return ; + } + + /* Otherwise, just delete the last element of the path. */ + + /* Remove the trailing slash - then remove the rest as + if it was a file name */ + + fname [ strlen(fname)-1 ] = '\0' ; + chop_file ( fname ) ; +} + + +void puaFileSelector::input_entered ( puObject* inp ) +{ + puaFileSelector* file_selector = (puaFileSelector*) (inp -> getUserData ()) ; + + /* + If the input selector is empty - do nothing + */ + + if ( inp -> getStringValue() [ 0 ] == '\0' ) + return ; + + if ( ulIsAbsolutePathName ( inp -> getStringValue () ) ) + file_selector -> setValue ( inp -> getStringValue () ) ; + else + { + char *s = new char [ strlen ( file_selector->__getStartDir() ) + + strlen ( SLASH ) + + strlen ( inp -> getStringValue () ) + + 1 ] ; + + ulMakePath ( s, file_selector -> __getStartDir(), + inp -> getStringValue () ) ; + file_selector -> setValue ( s ) ; + + delete [] s ; + } + + /* + If he typed a valid directory name - then + refresh the contents of the browser window + */ + + ulDir *d = ulOpenDir ( file_selector->getStringValue() ) ; + ulCloseDir ( d ) ; + + if ( d != NULL ) + file_selector -> find_files () ; +} + + +void puaFileSelector::handle_select ( puObject* l_box ) +{ + puaFileSelector* file_selector = (puaFileSelector*) l_box -> getUserData () ; + + int selected ; + l_box -> getValue ( &selected ) ; + + if ( selected >= 0 && selected < file_selector -> num_files ) + { + char *dst = file_selector -> getStringValue () ; + char *src = file_selector -> files [ selected ] ; + + chop_file ( dst ) ; + + if ( strcmp ( src, "[..]" ) == 0 ) + { + /* Do back up one level - so refresh. */ + + go_up_one_directory ( dst ) ; + file_selector -> find_files () ; + file_selector -> __getInput() -> setValue ( dst ) ; + return ; + } + + if ( file_selector -> dflag [ selected ] ) + { + /* If this is a directory - then descend into it and refresh */ + + src++ ; /* Remove leading '[' */ + + int dst_len = strlen ( dst ) ; + int src_len = strlen ( src ) ; + char *new_dst = new char [ dst_len + src_len + 1 ] ; + + memcpy ( new_dst, dst, dst_len ) ; + memcpy ( new_dst + dst_len, src, src_len + 1 ) ; /* Copy trailing '\0' */ + + /* Replace trailing ']' with slash */ + new_dst [ dst_len + src_len - 1 ] = SLASH[0] ; + + file_selector -> __getInput() -> setValue ( new_dst ) ; + file_selector -> setValue ( new_dst ) ; + + delete [] new_dst ; + file_selector -> find_files () ; + return ; + } + + /* If this is a regular file - then just append it to the string */ + + int dst_len = strlen ( dst ) ; + int src_len = strlen ( src ) ; + char *new_dst = new char [ dst_len + src_len + 1 ] ; + + memcpy ( new_dst, dst, dst_len ) ; + memcpy ( new_dst + dst_len, src, src_len + 1 ) ; /* Copy trailing '\0' */ + + file_selector -> __getInput() -> setValue ( new_dst ) ; + file_selector -> setValue ( new_dst ) ; + + delete [] new_dst ; + } + else + { + /* + The user clicked on blank screen - maybe we should + refresh just in case some other process created the + file. + */ + + file_selector -> find_files () ; + } +} + + +static void puaFileSelectorHandleCancel ( puObject* b ) +{ + puaFileSelector* file_selector = (puaFileSelector*) b -> getUserData () ; + file_selector -> setValue ( "" ) ; + file_selector -> invokeCallback () ; +} + +static void puaFileSelectorHandleOk ( puObject* b ) +{ + puaFileSelector* file_selector = (puaFileSelector*) b -> getUserData () ; + + file_selector -> invokeCallback () ; +} + +void puaFileSelector::setSize ( int w, int h ) +{ + // Resize the frame widget + frame->setSize ( w, h ) ; + + // Resize and position the slider + slider->setPosition ( w-30, 40+20*arrow_count ) ; + slider->setSize ( 20, h-60-40*arrow_count ) ; + + // Position the arrow buttons + if ( up_arrow ) + { + up_arrow->setPosition ( w-30, h-20-20*arrow_count ) ; + down_arrow->setPosition ( w-30, 20+20*arrow_count ) ; + } + + if ( fastup_arrow ) + { + fastup_arrow->setPosition ( w-30, h-40 ) ; + fastdown_arrow->setPosition ( w-30, 40 ) ; + } + + // Resize the list box + list_box->setSize ( w-40, h-80 ) ; + + // Resoze and position the buttons + cancel_button->setSize ( (w<170)?(w/2-15):70, 20 ) ; + ok_button->setSize ( (w<170)?(w/2-15):70, 20 ) ; + ok_button->setPosition ( (w<170)?(w/2+5):90, 10 ) ; +} + +puaFileSelector::~puaFileSelector () +{ + delete [] startDir ; + + if ( files != NULL ) + { + for ( int i=0; i 2 ) arrows = 2 ; + if ( arrows < 0 ) arrows = 0 ; + arrow_count = arrows ; + + frame = new puFrame ( 0, 0, w, h ); + + slider = new puSlider (w-30,40+20*arrows,h-60-40*arrows,TRUE,20); + slider->setValue(1.0f); + slider->setSliderFraction (0.2f) ; + slider->setCBMode( PUSLIDER_DELTA ); + + list_box = new puListBox ( 10, 60, w-40, h-20 ) ; + list_box -> setLabel ( title ); + list_box -> setLabelPlace ( PUPLACE_TOP_LEFT ) ; + list_box -> setStyle ( -PUSTYLE_SMALL_SHADED ) ; + list_box -> setUserData ( this ) ; + list_box -> setCallback ( handle_select ) ; + list_box -> setValue ( 0 ) ; + + find_files () ; + + handle_select ( list_box ) ; + + slider -> setUserData ( list_box ) ; + slider -> setCallback ( puaFileSelectorHandleSlider ) ; + + input = new puInput ( 10, 40, w-40, 60 ) ; + input -> setValue ( startDir ) ; + input -> setUserData ( this ) ; + input -> setCallback ( input_entered ) ; + input -> setDownCallback ( input_entered ) ; + + cancel_button = new puOneShot ( 10, 10, (w<170)?(w/2-5):80, 30 ) ; + cancel_button -> setLegend ( "Cancel" ) ; + cancel_button -> setUserData ( this ) ; + cancel_button -> setCallback ( puaFileSelectorHandleCancel ) ; + + ok_button = new puOneShot ( (w<170)?(w/2+5):90, 10, (w<170)?(w-10):160, 30 ) ; + ok_button -> setLegend ( "Ok" ) ; + ok_button -> setUserData ( this ) ; + ok_button -> setCallback ( puaFileSelectorHandleOk ) ; + + up_arrow = down_arrow = NULL ; + fastup_arrow = fastdown_arrow = NULL ; + + if ( arrows > 0 ) + { + down_arrow = new puArrowButton ( w-30, 20+20*arrows, w-10, 40+20*arrows, PUARROW_DOWN ) ; + down_arrow->setUserData ( slider ) ; + down_arrow->setCallback ( puaFileSelectorHandleArrow ) ; + + up_arrow = new puArrowButton ( w-30, h-20-20*arrows, w-10, h-20*arrows, PUARROW_UP ) ; + up_arrow->setUserData ( slider ) ; + up_arrow->setCallback ( puaFileSelectorHandleArrow ) ; + } + + if ( arrows == 2 ) + { + fastdown_arrow = new puArrowButton ( w-30, 40, w-10, 60, PUARROW_FASTDOWN ) ; + fastdown_arrow->setUserData ( slider ) ; + fastdown_arrow->setCallback ( puaFileSelectorHandleArrow ) ; + + fastup_arrow = new puArrowButton ( w-30, h-40, w-10, h-20, PUARROW_FASTUP ) ; + fastup_arrow->setUserData ( slider ) ; + fastup_arrow->setCallback ( puaFileSelectorHandleArrow ) ; + } + + close () ; + reveal () ; +} + +static int puaFileSelectorStringCompare ( const char *s1, const char *s2, + const char f1, const char f2 ) +{ + if ( f1 > f2 ) /* Directories before regular files. */ + return -1 ; + + if ( f1 < f2 ) + return 1 ; + + while ( 1 ) + { + char c1 = s1? (*s1++): 0 ; + char c2 = s2? (*s2++): 0 ; + + //end of string? + if ( !c1 || !c2 ) + { + if ( c1 ) + return 1 ; //s1 is longer + if ( c2 ) + return -1 ; //s1 is shorter + return 0 ; + } + + if ( c1 == c2 ) + continue ; + + /* + Windoze users are case-insensitive - so they presumably + want case ignored in the sorting. + + UNIX users, however, do things like calling their makefiles + 'Makefile' so as to force them to the start of the directory + listing. + */ + +#ifdef UL_MSVC + if ( c1 >= 'a' && c1 <= 'z' ) + c1 = c1 - ('a'-'A') ; + + if ( c2 >= 'a' && c2 <= 'z' ) + c2 = c2 - ('a'-'A') ; +#endif + + if ( c1 != c2 ) + { + if ( c1 < c2 ) + return -1 ; + return 1 ; + } + } + return 0 ; +} + + +void puaFileSelector::setInitialValue ( const char *s ) +{ + if ( ulIsAbsolutePathName ( s ) ) + { + input -> setValue ( s ) ; + input -> invokeCallback () ; + } + else + { + int str_val_len = strlen ( input->getStringValue () ) ; + int s_len = strlen ( s ) ; + char *s_new = new char [ str_val_len + s_len + 1 ] ; + + memcpy ( s_new, input->getStringValue (), str_val_len ) ; + memcpy ( s_new + str_val_len, s, s_len + 1 ) ; /* Plus one to get the final '\0' */ + + input->setValue ( s_new ) ; + delete [] s_new ; + input -> invokeCallback () ; + } +} + + +static void puaFileSelectorSort ( char** list, char *flags, int size ) +// +// comb sort - a modified bubble sort +// taken from BYTE, April 1991, ppg 315-320 +// +{ + int switches ; + int gap = size ; + + do + { + gap = ((gap * 197) >> 8); // gap /= 1.3; + + switch (gap) + { + case 0: // the smallest gap is 1 -- bubble sort + gap = 1; + break; + + case 9: // this is what makes this Combsort11 + case 10: + gap = 11; + break; + } + + switches = 0 ; // dirty pass flag + + int top = size - gap; + + for ( int i=0; i 0 ) + { + char* temp = list[i] ; + list[i] = list[j] ; + list[j] = temp ; + + char tmp2 = flags[i] ; + flags[i] = flags[j] ; + flags[j] = tmp2 ; + + ++switches; + } + } + } while ( switches || gap > 1 ) ; +} + + +void puaFileSelector::find_files ( void ) +{ + if ( files != NULL ) + { + for ( int i = 0 ; i < num_files ; i++ ) + delete [] files[i] ; + + delete [] files ; + delete [] dflag ; + } + + num_files = 0 ; + + int str_val_len = strlen ( getStringValue () ) ; + + if ( ! is_slash ( getStringValue () [ str_val_len - 1 ] ) ) + /* Someone forgot a trailing '/' */ + { + char *new_dir = new char [ str_val_len + SLASH_LEN + 1 ] ; + + memcpy ( new_dir, getStringValue (), str_val_len ) ; + memcpy ( new_dir + str_val_len, SLASH, SLASH_LEN + 1 ) ; /* Plus one to include the trailing '\0' */ + setValue ( new_dir ) ; + delete [] new_dir ; + } + + int ifile = 0 ; + + char *dir = getStringValue () ; + ulDir *dirp = ulOpenDir ( dir ) ; + ulDirEnt *dp ; + + if ( dirp == NULL ) + { + perror ("puaFileSelector") ; + ulSetError ( UL_WARNING, "PUI:puaFileSelector - can't open directory '%s'", dir ) ; + num_files = 0 ; + return ; + } + + /* Count the number of files - skipping "." */ + + while ( ( dp = ulReadDir(dirp) ) != NULL ) + if ( strcmp ( dp->d_name, "." ) != 0 ) + ifile++ ; + + ulCloseDir ( dirp ) ; + + num_files = ifile ; + if ( num_files > 5 ) + slider -> setDelta ( 0.5f / num_files ) ; + else + slider -> setDelta ( 0.1f ) ; + + if ( num_files == 0 ) + { + ulSetError ( UL_WARNING, + "PUI:puaFileSelector - no entries in directory '%s'?!", dir ) ; + return ; + } + + files = new char* [ num_files+1 ] ; + dflag = new char [ num_files+1 ] ; + + dirp = ulOpenDir ( dir ) ; + + if ( dirp == NULL ) + { + perror ("puaFileSelector") ; + ulSetError ( UL_WARNING, + "PUI:puaFileSelector - can't re-open directory '%s'", dir ) ; + num_files = 0 ; + return ; + } + + ifile = 0 ; + + while ( (dp = ulReadDir(dirp)) != NULL && ifile < num_files ) + { + /* Skip over the "." entry... */ + + if ( strcmp ( dp->d_name, "." ) != 0 ) + { + dflag[ ifile ] = dp->d_isdir ; + + int name_len = strlen ( dp->d_name ) ; + if ( dflag[ ifile ] ) + { + files[ ifile ] = new char[ name_len + 3 ] ; + + files [ ifile ] [ 0 ] = '[' ; + memcpy ( files [ ifile ] + 1, dp->d_name, name_len ) ; + strcpy ( files [ ifile ] + 1 + name_len, "]" ) ; + } + else + { + files[ ifile ] = new char[ name_len + 1 ] ; + memcpy ( files [ ifile ], dp->d_name, name_len + 1 ) ; /* Plus one to include the final '\0' */ + } + + ifile++ ; + } + } + + files [ ifile ] = NULL ; + + ulCloseDir ( dirp ) ; + + puaFileSelectorSort( files, dflag, num_files ) ; + + list_box -> newList ( files ) ; +} + diff --git a/src/puAux/puAuxLargeInput.cxx b/src/puAux/puAuxLargeInput.cxx new file mode 100644 index 0000000..6dceb29 --- /dev/null +++ b/src/puAux/puAuxLargeInput.cxx @@ -0,0 +1,1313 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxLargeInput.cxx 2161 2010-02-27 04:06:53Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF2(puaLargeInput,puInputBase,puGroup) + + +// Callbacks from the internal widgets + +static void puaLargeInputHandleRightSlider ( puObject *slider ) +{ + puaLargeInput* text = (puaLargeInput*) slider->getUserData () ; + text->setSliderPosition ( 1 - slider->getFloatValue () ) ; +} + +// Private function from the widget itself + +void puaLargeInput::normalizeCursors ( void ) +{ + puInputBase::normalizeCursors () ; + + // Set the top line in the window so that the last line is at the bottom of the window + if ( top_line_in_window > num_lines - lines_in_window + 2 ) + top_line_in_window = num_lines - lines_in_window + 2 ; + + if ( top_line_in_window < 0 ) top_line_in_window = 0 ; +} + +void puaLargeInput::removeSelectRegion ( void ) +{ + puInputBase::removeSelectRegion () ; + updateGeometry () ; +} + + +// Public functions from the widget itself + +puaLargeInput::puaLargeInput ( int x, int y, int w, int h, int arrows, int sl_width, int wrap_text ) : + puInputBase (), puGroup ( x, y ) +{ + setColour ( PUCOL_MISC, 0.1f, 0.1f, 1.0f ) ; // Colour of the 'I' bar cursor + + // Set the variables + + type |= PUCLASS_LARGEINPUT ; + num_lines = 1 ; + slider_width = sl_width ; + top_line_in_window = 0 ; + lines_in_window = 1 ; + max_width = 0 ; + widget = this ; + + // Set up the widgets + + frame = new puFrame ( 0, 0, w, h ) ; + + // filler for the little square in the lower right corner when both sliders are visible + plug = new puFrame ( 0, 0, slider_width, slider_width ) ; + plug->setStyle ( -PUSTYLE_SMALL_BEVELLED ) ; + plug->setBorderThickness ( 2 ) ; + + if (wrap_text ) + bottom_slider = NULL ; + else + { + bottom_slider = new puaScrollBar ( 0, 0, slider_width, (arrows / 10) & 3, FALSE, slider_width ) ; + bottom_slider->setValue ( 0.0f ) ; // All the way to the left + } + + right_slider = new puaScrollBar ( 0, 0, slider_width, (arrows % 10) & 3, TRUE, slider_width ) ; + right_slider->setValue ( 1.0f ) ; // All the way to the top + right_slider->setUserData ( this ) ; + right_slider->setCallback ( puaLargeInputHandleRightSlider ) ; + + setValue ( "" ) ; + setSize ( w, h ) ; + + close () ; + reveal () ; +} + +void puaLargeInput::setSize ( int w, int h ) +{ + puGroup::setSize ( w, h ) ; + if ( w < 2 * slider_width ) w = 2 * slider_width ; + if ( h < 2 * line_height ) h = 2 * line_height ; + frame->setSize ( w, h ) ; + abox.max[0] = abox.min[0] + w ; + abox.max[1] = abox.min[1] + h ; + updateGeometry () ; +} + +void puaLargeInput::setSliderPosition ( float fraction ) +{ + if ( num_lines > lines_in_window ) + top_line_in_window = int ( ( num_lines - lines_in_window ) * fraction + 0.5f ) ; + else + top_line_in_window = 0 ; + updateGeometry () ; +} + +void puaLargeInput::updateGeometry ( void ) +{ + enum { HORIZONTAL = 1, VERTICAL = 2, BOTH = 3 } ; + int w = abox.max[0] - abox.min[0] ; + int h = abox.max[1] - abox.min[1] ; + + int frame_width = getStyle() == PUSTYLE_BOXED ? getBorderThickness() : 0 ; + hgap = int(legendFont.getFloatStringWidth(" ")) + frame_width ; + vgap = int(legendFont.getStringHeight()) / 2 + frame_width ; + line_height = legendFont.getStringHeight() + legendFont.getStringDescender() + 1 ; + input_width = w - hgap - hgap - frame_width ; + input_height = h - vgap - vgap - frame_width ; + + // determine which sliders are necessary + int slider = 0 ; + if ( bottom_slider ) { // no line wrapping + if ( max_width > input_width ) { + slider |= HORIZONTAL ; + input_height -= slider_width ; + } + if ( num_lines > input_height / line_height ) { + slider |= VERTICAL ; + input_width -= slider_width ; + } + if ( max_width > input_width && !(slider & HORIZONTAL) ) { + slider |= HORIZONTAL ; + input_height -= slider_width ; + } + if ( num_lines > input_height / line_height && !(slider & VERTICAL) ) { + slider |= VERTICAL ; + input_height -= slider_width ; + } + + } else { // with line wrapping + delete [] displayed_text ; + displayed_text = wrapText ( input_width, &num_lines, &max_width ) ; + + if ( num_lines > input_height / line_height ) { + slider |= VERTICAL ; + input_width -= slider_width ; + + delete [] displayed_text ; + displayed_text = wrapText( input_width, &num_lines, &max_width ) ; + } + } + + // horizontal slider + if ( bottom_slider ) { + bottom_slider->setSize ( w - ( slider & VERTICAL ? slider_width : 0 ), slider_width ) ; + float page_step = 1.0f, line_step = 1.0f ; + if ( max_width > input_width ) { + page_step = float(input_width) / ( max_width - input_width ) ; + line_step = legendFont.getFloatStringWidth( "M" ) / ( max_width - input_width ) ; + bottom_slider->setSliderFraction ( float(input_width) / max_width ) ; + } else { + bottom_slider->setSliderFraction ( 1.0f ) ; + bottom_slider->setValue ( 0.0f ); + } + + bottom_slider->setPageStepSize ( page_step ) ; + bottom_slider->setLineStepSize ( line_step ) ; + + if ( slider & HORIZONTAL ) + bottom_slider->reveal () ; + else + bottom_slider->hide () ; + } + + // vertical slider + lines_in_window = input_height / line_height ; + + // if the slider was in bottom position, make sure the last line is shown after lines were added + if ( right_slider->getFloatValue() <= 0.0f && right_slider->getSliderFraction() < 1.0f ) + top_line_in_window = num_lines ; + + if ( top_line_in_window + lines_in_window > num_lines ) + top_line_in_window = num_lines - lines_in_window ; + + if ( top_line_in_window < 0 ) + top_line_in_window = 0 ; + + right_slider->setPosition ( w - slider_width, slider & HORIZONTAL ? slider_width : 0 ) ; + right_slider->setSize ( slider_width, h - ( slider & HORIZONTAL ? slider_width : 0 ) ) ; + right_slider->setSliderFraction ( float(lines_in_window) / num_lines ) ; + float page_step = 1.0f ; + if ( num_lines > lines_in_window ) + page_step = float(lines_in_window) / ( num_lines - lines_in_window ) ; + + right_slider->setPageStepSize ( page_step ) ; + right_slider->setLineStepSize ( page_step / lines_in_window ) ; + + float val = 0.0f ; + if ( num_lines > lines_in_window ) + val = 1.0f - float(top_line_in_window) / (num_lines - lines_in_window ) ; + right_slider->setValue (val) ; + + if ( slider & VERTICAL ) + right_slider->reveal () ; + else + right_slider->hide () ; + + // corner filler + plug->setPosition ( w - slider_width, 0 ) ; + if ( slider == BOTH ) + plug->reveal () ; + else + plug->hide () ; +} + +void puaLargeInput::setSelectRegion ( int s, int e ) +{ + select_start_position = s ; + select_end_position = e ; + char *lin_ptr = getText() ; + char *text_start = lin_ptr ; + + if ( num_lines > lines_in_window ) + { + int select_start_line = 0 ; + while ( lin_ptr && ( lin_ptr <= text_start + select_start_position ) ) // Count the lines + { + select_start_line++ ; + lin_ptr = strchr ( lin_ptr+1, '\n' ) ; + } + + int select_end_line = select_start_line ; + while ( lin_ptr && ( lin_ptr <= text_start + select_end_position ) ) // Count the lines + { + select_end_line++ ; + lin_ptr = strchr ( lin_ptr+1, '\n' ) ; + } + + if ( select_end_line > top_line_in_window + lines_in_window ) + top_line_in_window = select_end_line - lines_in_window - 1 ; + + if ( select_start_line < top_line_in_window ) + top_line_in_window = select_start_line - 1 ; + + if ( top_line_in_window < 0 ) top_line_in_window = 0 ; + + right_slider->setValue ( num_lines - lines_in_window - top_line_in_window ) ; + } +} + +void puaLargeInput::selectEntireLine ( void ) +{ + char *temp_text = getText() ; + + if ( select_start_position < 0 ) + select_start_position = 0 ; + + if ( ( *(temp_text + select_start_position + 1) != '\n' ) && + ( *(temp_text + select_start_position) != '\0' ) ) + select_start_position ++ ; + + while ( ( select_start_position > 0 ) && ( *(temp_text + select_start_position) != '\n' ) ) + select_start_position -- ; + + if ( select_start_position > 0 ) + select_start_position++ ; + + select_end_position = int ( strchr ( temp_text + select_end_position, '\n' ) + 1 - temp_text) ; + if ( select_end_position <= 1 ) select_end_position = strlen ( temp_text ) ; + //else select_end_position = int ( select_end_position - temp_text ) ;/** Needs real fixing **/ + + puPostRefresh () ; +} + +void puaLargeInput::addNewLine ( const char *l ) +{ + char *text = getStringValue () ; + if ( cursor_position > 0 ) // If not at start of line, go to start of next line + cursor_position = int (strchr ( text + cursor_position - 1, '\n' ) - text + 1) ; + + select_end_position = select_start_position = cursor_position ; + addText ( l ) ; +} + +void puaLargeInput::addText ( const char *l ) +{ + char *text = getStringValue () ; + + if ( l == NULL ) return ; + + int l_len = strlen ( l ) ; + int text_len = strlen ( text ) ; + int length = l_len + text_len /* Length of the final string */ + + select_start_position - select_end_position + 2 ; + if ( *(l+l_len-1) == '\n' ) length -- ; // Decrement "length" for each final + if ( text[select_end_position] == '\n' ) length -- ; // carriage return already there + + char *temp_text = new char [ length ] ; + + memcpy ( temp_text, text, select_start_position ) ; + memcpy ( temp_text + select_start_position, l, l_len + 1 ) ; /* Plus one to get the final '\0' */ + int temp_text_len = select_start_position + l_len ; + + if ( ( *(l+l_len-1) == '\n' ) && ( text[select_end_position] == '\n' ) ) + { + temp_text[temp_text_len-1] = '\0' ; /* Erase the duplicate carriage return */ + temp_text_len -- ; + } + else if ( ( *(l+l_len-1) != '\n' ) && ( text[select_end_position] != '\n' ) ) + { + strcpy ( temp_text + temp_text_len, "\n" ) ; /* Add a carriage return */ + temp_text_len ++ ; + } + + memcpy ( temp_text + temp_text_len, text + select_end_position, text_len - select_end_position + 1 ) ; + int temp_select_start = select_start_position ; + setValue ( temp_text ) ; + delete [] temp_text ; + setSelectRegion ( temp_select_start, + temp_select_start + strlen(l) ) ; + setCursor ( select_end_position ) ; +} + +void puaLargeInput::appendText ( const char *l ) +{ + if ( !l ) return ; + + int str_val_len = strlen ( getStringValue () ) ; + int l_len = strlen ( l ) ; + if ( str_val_len == 1 ) str_val_len = 0 ; /* Don't want null line at the beginning */ + int length = str_val_len + l_len + 2 ; + if ( *(l+l_len-1) == '\n' ) length -- ; /* Already have a trailing carriage return, decrement the length */ + + char *temp_text = new char [ length ] ; + int temp_text_len ; + + if ( str_val_len > 0 ) /* More than just the empty carriage return */ + { + memcpy ( temp_text, getStringValue (), str_val_len ) ; + temp_text_len = str_val_len ; + } + else + temp_text_len = 0 ; + + memcpy ( temp_text + temp_text_len, l, l_len + 1 ) ; /* Plus one to get the final '\0' */ + temp_text_len += l_len ; + if ( *(l+l_len-1) != '\n' ) + { + strcpy ( temp_text + temp_text_len, "\n" ) ; + temp_text_len ++ ; + } + + setValue ( temp_text ) ; + setSelectRegion ( str_val_len, temp_text_len ) ; + setCursor ( str_val_len ) ; + delete [] temp_text ; +} + +void puaLargeInput::removeText ( int start, int end ) +{ + int str_val_len = strlen ( getStringValue () ) ; + char *temp_text = new char [ str_val_len + start - end + 1 ] ; + memcpy ( temp_text, getStringValue (), start ) ; + memcpy ( temp_text + start, getStringValue ()+end, str_val_len - end + 1 ) ; /* Plus one to get the final '\0' */ + setValue ( temp_text ) ; + setCursor ( start ) ; + setSelectRegion ( start, start ) ; + delete [] temp_text ; +} + +void puaLargeInput::setValue ( const char *s ) +{ + if (!s || !*s) { + puValue::setValue ( "\n" ) ; + cursor_position = select_start_position = select_end_position = 0 ; + + } else { + int len = strlen(s); + if (s[len - 1] == '\n') { + puValue::setValue ( s ) ; + + } else { + char *buf = new char [ len + 2 ] ; + strcpy ( buf, s ) ; + strcat ( buf, "\n" ) ; + puValue::setValue ( buf ) ; + delete [] buf ; + } + } + + if ( bottom_slider ) + getTextProperties ( &num_lines, &max_width ) ; + + updateGeometry () ; + normalizeCursors () ; +} + + +void puaLargeInput::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + normalizeCursors () ; + + // 3D Input boxes look nicest if they are always in inverse style. + abox.draw ( dx, dy, ( (style==PUSTYLE_SMALL_BEVELLED || + style==PUSTYLE_SMALL_SHADED) ) ? -style : + (accepting ? -style : style ), colour, FALSE, border_thickness ) ; + plug->draw ( abox.min[0] + dx, abox.min[1] + dy ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + // Calculate window parameters: + + int xwidget = abox.min[0] + dx ; + int ywidget = abox.min[1] + dy ; + + int xx = hgap; + int yy = abox.max[1] - abox.min[1] - legendFont.getStringHeight () - vgap ; + + float bottom_value = bottom_slider ? bottom_slider->getFloatValue () : 0.0f ; + + int beg_pos // Position in window of start of line, in pixels + = int(( input_width - max_width ) * bottom_value ) ; +//// int end_pos // Position in window of end of line, in pixels +//// = beg_pos + max_width - 1 ; + if ( top_line_in_window < 0 ) top_line_in_window = 0 ; + int end_lin // Position on line count of bottom of window, in lines + = top_line_in_window + lines_in_window ; + + char *val = getText () ; + + // Highlight the select area + + if ( select_end_position > 0 && select_end_position != select_start_position ) + { + // First: find the positions on the window of the selection start and end + + char temp_char = val[ select_start_position ] ; + val [ select_start_position ] = '\0' ; + + xx = dx + abox.min[0] + hgap ; + yy = abox.max[1] - abox.min[1] - legendFont.getStringHeight () - vgap + + top_line_in_window * line_height ; // Offset y-coord for unprinted lines + + char *end_of_line = strchr ( val, '\n' ) ; + char *start_of_line = val ; + + // Step down the lines until you reach the line with the selection start + + int select_start_line = 0 ; + + while ( end_of_line ) + { + select_start_line++ ; + start_of_line = end_of_line + 1 ; + yy -= line_height ; + end_of_line = strchr ( start_of_line, '\n' ) ; + } + + int start_pos = int(legendFont.getFloatStringWidth ( start_of_line )) + xx + + beg_pos ; // Start of selection + + val [ select_start_position ] = temp_char ; + + // Now repeat the process for the end of the selection. + + temp_char = val[ select_end_position ] ; + val [ select_end_position ] = '\0' ; + + end_of_line = strchr ( start_of_line, '\n' ) ; + + // Step down the lines until you reach the line with the selection end + + int select_end_line = select_start_line ; + + while ( end_of_line ) + { + select_end_line++ ; + start_of_line = end_of_line + 1 ; + end_of_line = strchr ( start_of_line, '\n' ) ; + } + + int end_pos = int(legendFont.getFloatStringWidth ( start_of_line )) + xx + + beg_pos ; // End of selection + + val [ select_end_position ] = temp_char ; + + // Now draw the selection area. + + for ( int line_count = select_start_line ; ( ( line_count <= select_end_line ) && ( line_count < end_lin ) ) ; + line_count++ ) + { + if ( line_count >= top_line_in_window ) + { + int x_start, x_end ; + + if ( line_count == select_start_line ) + x_start = ( start_pos > xx ) ? start_pos : xx ; + else + x_start = xx ; + + x_start = ( x_start < abox.max[0] + dx ) ? x_start : abox.max[0] + dx ; + + if ( line_count == select_end_line ) + x_end = ( end_pos < abox.max[0] + dx ) ? end_pos : abox.max[0] + dx ; + else + x_end = abox.max[0] + dx ; + + x_end = ( x_end > xx ) ? x_end : xx ; + + int top = dy + abox.min[1] + yy + legendFont.getStringHeight () ; + int bot = dy + abox.min[1] + yy - legendFont.getStringDescender() ; + + glColor3f ( 1.0f, 1.0f, 0.7f ) ; + glRecti ( x_start, bot, x_end, top ) ; + } + + yy -= line_height ; + if ( line_count == end_lin ) break ; + } + } + + + // Draw the text + + { + // If greyed out then halve the opacity when drawing the text + + if ( active ) + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + else + glColor4f ( colour [ PUCOL_LEGEND ][0], + colour [ PUCOL_LEGEND ][1], + colour [ PUCOL_LEGEND ][2], + colour [ PUCOL_LEGEND ][3] / 2.0f ) ; // 50% more transparent + + char *val = getText () ; // Pointer to the actual text in the box + + if ( val ) + { + char *end_of_line = strchr (val, '\n') ; + int line_count = 0; + + xx = hgap; + yy = abox.max[1] - abox.min[1] - legendFont.getStringHeight () - vgap + 1 ; + + while ( end_of_line ) // While there is a carriage return in the string + { + if ( line_count < top_line_in_window ) + { // Before the start of the window + val = end_of_line + 1 ; + end_of_line = strchr (val, '\n') ; // Just go to the next line + } + else if ( line_count < end_lin ) // Within the window, draw it + { + char temp_char = *end_of_line ; // Temporary holder for last char on line + + *end_of_line = '\0' ; // Make end-of-line be an end-of-string + + int start_pos = beg_pos ; + int end_pos // Position in window of end of line, in pixels + = start_pos + int(legendFont.getFloatStringWidth ( val )) ; + + if ( end_pos > start_pos ) // If we actually have text in the line + { + char * lastonleft = val ; + char * firstonright = end_of_line ; + int leftpos = start_pos ; + int rightpos = end_pos ; + while ( lastonleft < firstonright - 1 ) + { + int chpos = -leftpos * ( firstonright - lastonleft ) / ( rightpos - leftpos ) + 1; + if ( chpos >= firstonright - lastonleft ) chpos = firstonright - lastonleft - 1 ; + char placeholder = *(lastonleft + chpos) ; + *(lastonleft + chpos) = '\0' ; + int strwidth = legendFont.getStringWidth ( val ) ; + *(lastonleft + chpos) = placeholder ; + if ( strwidth + start_pos < 0 ) /* Still to the left of the window */ + { + lastonleft = lastonleft + chpos ; + placeholder = *lastonleft ; + *lastonleft = '\0' ; + leftpos = start_pos + legendFont.getStringWidth ( val ) ; + *lastonleft = placeholder ; + } + else + { + firstonright = lastonleft + chpos ; + placeholder = *firstonright ; + *firstonright = '\0' ; + rightpos = start_pos + legendFont.getStringWidth ( val ) ; + *firstonright = placeholder ; + } + } + if ( leftpos >= 0 ) + { + val = lastonleft ; + start_pos = leftpos ; + } + else + { + val = firstonright ; + start_pos = rightpos ; + } + } + + while ( end_pos > input_width ) // Step up the line until it is in the window + { + *end_of_line = temp_char ; + end_of_line-- ; + temp_char = *end_of_line ; + *end_of_line = '\0' ; + end_pos = start_pos + legendFont.getStringWidth ( val ) ; + } + + if ( val < end_of_line ) // If any text shows in the window, + legendFont.drawString ( val, // draw it. + dx + abox.min[0] + xx + start_pos, + dy + abox.min[1] + yy ) ; + + *end_of_line = temp_char ; // Restore the end-of-line character + + if ( temp_char != '\n' ) // If we had to step up from the end of + end_of_line = strchr (val, '\n') ; // the line, go back to the actual end + + yy -= line_height ; + val = end_of_line + 1 ; + end_of_line = strchr (val, '\n') ; // On to the next line + } + else if ( line_count >= end_lin ) // Have gone beyond window, end process + end_of_line = NULL ; + + line_count++ ; + + } // while ( end_of_line ) + } // if ( val ) + } + + if ( accepting ) + { + char *val = getText () ; // Pointer to the actual text in the box + + // Draw the 'I' bar cursor. + + if ( val && ( cursor_position >= 0 ) ) + { + char temp_char = val[ cursor_position ] ; + val [ cursor_position ] = '\0' ; + + xx = hgap ; + yy = abox.max[1] - abox.min[1] - legendFont.getStringHeight () - vgap + + top_line_in_window * line_height ; // Offset y-coord for unprinted lines + + char *end_of_line = strchr ( val, '\n' ) ; + char *start_of_line = val ; + + // Step down the lines until you reach the line with the cursor + + int line_count = 1 ; + + while ( end_of_line ) + { + line_count++ ; + start_of_line = end_of_line + 1 ; + yy -= line_height ; + end_of_line = strchr ( start_of_line, '\n' ) ; + } + + if ( ( line_count > top_line_in_window ) && ( line_count <= end_lin ) ) + { + int begpos // Position in window of start of line, in pixels + = int( ( input_width - max_width ) * bottom_value ) ; + int cpos = int( legendFont.getFloatStringWidth ( start_of_line ) + xx + + abox.min[0] + begpos ) ; + int top = int( abox.min[1] + yy + legendFont.getStringHeight () ) ; + int bot = int( abox.min[1] + yy - legendFont.getStringDescender () ) ; + if ( ( cpos > abox.min[0] ) && ( cpos < abox.max[0] ) ) + { + glColor4fv ( colour [ PUCOL_MISC ] ) ; + glBegin ( GL_LINES ) ; + glVertex2i ( dx + cpos , dy + bot ) ; + glVertex2i ( dx + cpos , dy + top ) ; + glVertex2i ( dx + cpos - 1, dy + bot ) ; + glVertex2i ( dx + cpos - 1, dy + top ) ; + glVertex2i ( dx + cpos - 4, dy + bot ) ; + glVertex2i ( dx + cpos + 3, dy + bot ) ; + glVertex2i ( dx + cpos - 4, dy + top ) ; + glVertex2i ( dx + cpos + 3, dy + top ) ; + glEnd () ; + } + } + + val[ cursor_position ] = temp_char ; + } + } + + // Draw the other widgets in the large input box + + if ( bottom_slider ) bottom_slider->draw ( xwidget, ywidget ) ; + right_slider->draw ( xwidget, ywidget ) ; + } + + draw_label ( dx, dy ) ; +} + + +int puaLargeInput::checkHit ( int button, int updown, int x, int y ) +{ + if ( ( button == PU_SCROLL_UP_BUTTON || button == PU_SCROLL_DOWN_BUTTON ) && isHit( x, y ) ) + { + int sx, sy; + right_slider->getPosition ( &sx, &sy ); + return right_slider->checkHit ( button, updown, ++sx, ++sy ) ; + } + + int xwidget = x - abox.min[0] ; + int ywidget = y - abox.min[1] ; + + if ( bottom_slider ) + { + if ( bottom_slider->checkHit ( button, updown, xwidget, ywidget ) ) return TRUE ; + } + + if ( right_slider->checkHit ( button, updown, xwidget, ywidget ) ) return TRUE ; + + // If the user has clicked within the bottom slider or to its right, don't activate. + + if ( y < slider_width ) return FALSE ; + + if ( puObject::checkHit ( button, updown, x, y ) ) + return TRUE ; + + return FALSE ; +} + + +void puaLargeInput::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + // Active widget exists and is not this one; call its down callback if it exists + + puActiveWidget()->invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ) ; + + if ( button == active_mouse_button ) + { + // Most GUI's activate a button on button-UP not button-DOWN. + + // Text and window parameters: + + float bottom_value = bottom_slider ? bottom_slider->getFloatValue () : 0.0f ; + + int beg_pos // Position in window of start of line, in pixels + = int( ( input_width - max_width ) * bottom_value ) ; +// int end_pos // Position in window of end of line, in pixels +// = (int)( beg_pos + max_width - 1 ) ; + if ( top_line_in_window < 0 ) top_line_in_window = 0 ; +// int end_lin // Position on line count of bottom of window, in lines +// = top_line_in_window + lines_in_window - 1 ; + +// int xx = legendFont.getStringWidth ( " " ) ; + int yy = int( abox.max[1] - legendFont.getStringHeight () - vgap + + top_line_in_window * line_height ) ; // Offset y-coord for unprinted lines + + // Get the line number and position on the line of the mouse + char *tmpval = ulStrDup ( getText() ) ; + + int i = strlen ( tmpval ) ; + + char *end_of_line = strchr ( tmpval, '\n' ) ; + char *start_of_line = tmpval ; + + // Step down the lines until the y-coordinate is less than the mouse + + int line_count = 0 ; + + while ( ( yy > y ) && end_of_line ) + { + line_count++ ; + start_of_line = end_of_line + 1 ; + yy -= line_height ; + end_of_line = strchr ( start_of_line, '\n' ) ; + } + + if ( end_of_line ) + { + *end_of_line = '\0' ; + + i = strlen ( tmpval ) ; + + int length, prev_length = 0 ; + while ( x <= (length = legendFont.getStringWidth ( start_of_line ) + + abox.min[0] + beg_pos) && + i > 0 ) + { + prev_length = length ; + tmpval[--i] = '\0' ; + } + + if ( ( x - length ) < ( prev_length - x ) ) + i-- ; // Mouse is closer to previous character than next character + } + + // Now process the mouse click itself. + + if ( updown == active_mouse_edge || active_mouse_edge == PU_UP_AND_DOWN ) + { + lowlight () ; + + accepting = TRUE ; + cursor_position = i ; + normalizeCursors () ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + else if ( updown == PU_DOWN ) + { + // We get here if the active edge is not down but the mouse button has + // been pressed. Start a selection. + + select_start_position = i ; + select_end_position = i ; + } + else if ( updown == PU_DRAG ) + { + // Drag -- extend the selection. + + if ( (select_end_position - i) > (i - select_start_position) ) + select_start_position = i ; // Cursor closer to start than to end + else + select_end_position = i ; // Cursor closer to end than to start + + if (select_start_position > select_end_position) + { + i = select_end_position ; + select_end_position = select_start_position ; + select_start_position = i ; + } + } + else + highlight () ; + + delete [] tmpval ; + } + else + lowlight () ; +} + +int puaLargeInput::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || !isAcceptingInput () || !isActive () || + !isVisible () || ( window != puGetWindow () ) ) + return FALSE ; + + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + // Active widget exists and is not this one; call its down callback if it exists + + puActiveWidget()->invokeDownCallback () ; + puDeactivateWidget () ; + } + + normalizeCursors () ; + + //char *old_text = getStringValue () ; + char *old_text = getText () ; + int lines_in_window = getLinesInWindow () ; /* Added lines_in_window and num_lines to allow for "END" */ + int num_lines = getNumLines () ; /* and PGUP/DOWN to work properly - JCJ 20 Jun 2002 */ + int line_width = 0 ; /* Width of current line (for bottomslider) */ + int line_width_to_cursor = 0 ; /* Width of current line up to the cursor position */ + int prev_line_width = 0 ; /* Width of previous line (for left mouse arrow) */ + int i = 1; /* Happy useful variable */ + int line_counter = 0 ; /* Happy useful variable #2 */ + int tmp_cursor_position = cursor_position ; /* Temporary cursor position for counting without moving the cursor */ + int bottom_line_in_window = 0; /* # of bottom line in window */ + int current_line_in_window = 0; /* Current line # in window */ + char *line_end = 0 ; + char* p = new char[ strlen(old_text)+1 ] ; + float bottom_value = bottom_slider ? bottom_slider->getFloatValue () : 0.0f ; /* Value of the bottom slider */ + if ( old_text[1] != '\0' ) /* Ensure that we don't delete something that doesn't exist! - JCJ 22 July 2002 */ + { + /*Count how many characters from the beginning of the line the cursor is*/ + while ( ( old_text [ cursor_position - i ] != '\n' ) && + ( i < cursor_position ) ) + i++ ; /* Step back to the beginning of the line */ + if ( i < cursor_position ) i-- ; + + /*Find the length of the current line*/ + while ( ( old_text [ tmp_cursor_position ] != '\n' ) && + ( tmp_cursor_position > 0) ) + tmp_cursor_position-- ; + + strcpy ( p, ( old_text + tmp_cursor_position + 1 ) ) ; + if ( bottom_slider ) + { + line_end = strchr ( p, '\n' ) ; + if ( line_end ) // Found an end-of-line + { + *line_end = '\0' ; // Temporary break in line + line_width = int(legendFont.getFloatStringWidth ( p )) ; + *line_end = '\n' ; // Reset the carriage return + } + /*Now delete all characters in the string beyond i, and re-find its width*/ + p [i+1] = '\0' ; + line_width_to_cursor = int(legendFont.getFloatStringWidth ( p )) ; + } + + /* Now find the length of the previous line */ + tmp_cursor_position-- ; + while ( ( old_text [ tmp_cursor_position ] != '\n' ) && + ( tmp_cursor_position > 0) ) + tmp_cursor_position-- ; + + strcpy ( p, ( old_text + tmp_cursor_position + 1 ) ) ; + if ( bottom_slider ) + { + line_end = strchr ( p, '\n' ) ; + if ( line_end ) /*Actually, we KNOW there's a line after this one, but hey, let's be safe, eh? - JCJ 21 Jun 2002 */ + { + *line_end = '\0' ; // Temporary break in line + prev_line_width = legendFont.getStringWidth ( p ) ; + *line_end = '\n' ; // Reset the carriage return + } + } + + delete [] p ; + p = NULL ; + } + + bool done = true ; + + switch ( key ) + { + case PU_KEY_PAGE_UP : + while ( old_text [ cursor_position ] != '\0' ) /* Move the cursor to the top of the data */ + cursor_position-- ; + select_start_position = select_end_position = cursor_position ; + setTopLineInWindow ( top_line_in_window - lines_in_window + 2 ) ; + right_slider->setValue (1.0f - float(top_line_in_window) / num_lines ) ; + break; + case PU_KEY_PAGE_DOWN : + while ( old_text [ cursor_position ] != '\0' ) /* Move the cursor to the end of the data */ + cursor_position++ ; + select_start_position = select_end_position = cursor_position ; + setTopLineInWindow ( top_line_in_window + lines_in_window - 2 ) ; /* Plus two for consistency - JCJ 20 Jun 2002 */ + right_slider->setValue (1.0f - float(top_line_in_window) / num_lines ) ; + break; + case PU_KEY_INSERT : return FALSE ; + + case PU_KEY_UP : + case PU_KEY_DOWN : + /* Determine the current line, the line at the top of the window, and the line at the bottom.*/ + bottom_line_in_window = top_line_in_window + getLinesInWindow() ; + while ( line_counter < cursor_position) { + if ( old_text [ line_counter ] == '\n' ) current_line_in_window++ ; + line_counter++ ; + } + if ( key == PU_KEY_UP ) + { + // Step backwards to the beginning of the previous line + cursor_position -= (i + 2) ; + while ( ( old_text [ cursor_position ] != '\n' ) && + ( cursor_position > 0 ) ) + cursor_position-- ; + if ( cursor_position > 0 ) cursor_position++ ; + + // Step down the line "i" spaces or to the end of the line + while ( ( old_text [ cursor_position ] != '\n' ) && + ( i > 0 ) ) + { + cursor_position++ ; + i-- ; + } + if (current_line_in_window <= top_line_in_window) { + setTopLineInWindow ( top_line_in_window - 1 ); /* Go up - JCJ 21 Jun 2002 */ + right_slider->setValue (1.0f - float(top_line_in_window) / num_lines ) ; + } + } + else // Down-arrow key + { + // Skip to beginning of next line + while ( old_text [ cursor_position ] != '\n' && old_text [ cursor_position ] != '\0' ) + cursor_position++ ; + cursor_position++ ; + + // Step down the line "i" spaces or to the end of the line + // or to the end of the text + + while ( ( old_text [ cursor_position ] != '\n' ) && + ( cursor_position < (int)strlen ( old_text ) ) && + ( i > 0 ) ) + { + cursor_position++ ; + i-- ; + } + if ((current_line_in_window+1) >= bottom_line_in_window) { + setTopLineInWindow ( top_line_in_window + 1 ); /* Go down - JCJ 21 Jun 2002 */ + right_slider->setValue (1.0f - float(top_line_in_window) / num_lines ) ; + } + } + + select_start_position = select_end_position = cursor_position ; + break ; + + case 0x1B : // ESC + case '\t' : // TAB -- End of input + rejectInput () ; + normalizeCursors () ; + invokeCallback () ; + puDeactivateWidget () ; + break ; + + case PU_KEY_HOME : + while ( ( old_text [ cursor_position-1 ] != '\n' ) && + ( cursor_position > 0 ) ) /* Move the cursor to the start of the line, but minus one so */ + cursor_position-- ; /* it does not find a \n immediately if located at the end. */ + select_start_position = select_end_position = cursor_position ; /* Also corrects overshoots. - JCJ 20 Jun 2002 */ + if ( bottom_slider ) bottom_slider->setValue ( 0.0f ) ; + break ; + case PU_KEY_END : + if (bottom_slider) + { + bottom_slider->setValue ( ( (line_width - input_width)<0 ) ? 0.0f : float(line_width+10) / max_width); + } + while ( old_text [ cursor_position ] != '\n' ) /* Move the cursor to the end of the line - JCJ 20 Jun 2002 */ + cursor_position++ ; + select_start_position = select_end_position = cursor_position ; + break ; + + case PU_KEY_LEFT : + case PU_KEY_RIGHT : + if ( key == PU_KEY_LEFT ) + { + /* This stops it crashing out sometimes when wrappable text is used. + * Since there is no bottom scrollbar when wrapped text is turned on, the bottom + * slider code could make the application die when the user pressed the left + * cursor key before they began the first line. + * Fix by Simon */ + cursor_position-- ; /* Left key pressed */ + if ( bottom_slider ) + { + if (old_text [ cursor_position ] == '\n') + bottom_slider->setValue( ( (prev_line_width - input_width)<0 ) ? 0.0f : float(prev_line_width+10) /max_width); + /* If the cursor is going off the left edge of the box, scroll left. */ + else if ((bottom_value*max_width) > line_width_to_cursor+5) { + bottom_slider->setValue( ((bottom_value*max_width)-(input_width/2)-5)<0 ? 0.0f : + ((bottom_value*max_width)-(input_width/2)-5)/max_width ) ; + } + } + } else { + cursor_position++ ; /* Right key pressed */ + if ( bottom_slider ) { + if (old_text [ cursor_position-1 ] == '\n') + bottom_slider->setValue(0.0f) ; + else if ((bottom_value*max_width)+(input_width) < line_width_to_cursor+5) { + bottom_slider->setValue( ((bottom_value*max_width)+(input_width/2)+5)/max_width ) ; + } + } + } + select_start_position = select_end_position = cursor_position ; + break ; + + default : + done = false ; + break ; + } + + if ( ! done && ! input_disabled ) + { + char *p = NULL ; + int temp_cursor = cursor_position ; + + switch ( key ) + { + case '\b' : // Backspace + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + else if ( cursor_position > 0 ) + { + p = new char [ strlen(old_text) ] ; + memcpy ( p, old_text, cursor_position ) ; + --cursor_position ; + strcpy ( p + cursor_position, old_text + cursor_position + 1 ) ; + setValue ( p ) ; + setCursor ( temp_cursor - 1 ) ; + delete [] p ; + } + + break ; + + case 0x7F : // DEL + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + else if (cursor_position != (int)strlen ( old_text ) ) + { + p = new char [ strlen(old_text) ] ; + memcpy ( p, old_text, cursor_position ) ; + strcpy ( p + cursor_position, old_text + cursor_position + 1 ) ; + setValue ( p ) ; + setCursor ( temp_cursor ) ; + delete [] p ; + } + + break ; + + case 0x15 /* ^U */ : getStringValue () [ 0 ] = '\0' ; break ; + case 0x03 /* ^C */ : + case 0x18 /* ^X */ : /* Cut or copy selected text */ + if ( select_start_position != select_end_position ) + { + extern void puSetPasteBuffer ( const char *ch ) ; + p = getStringValue () ; + char ch = p[select_end_position] ; + p[select_end_position] = '\0' ; + puSetPasteBuffer ( p + select_start_position ) ; + p[select_end_position] = ch ; + + if ( key == 0x18 ) /* Cut, remove text from string */ + removeSelectRegion () ; + } + + break ; + + case 0x16 /* ^V */ : /* Paste buffer into text */ + { + extern char *puGetPasteBuffer () ; + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + + if ( puGetPasteBuffer () ) // Make sure something has been cut previously! + { + int str_val_len = strlen ( getStringValue () ) ; + int paste_len = strlen ( puGetPasteBuffer () ) ; + p = new char [ str_val_len + paste_len + 1 ] ; + memcpy ( p, getStringValue (), cursor_position ) ; + memcpy ( p + cursor_position, puGetPasteBuffer (), paste_len ) ; + memcpy ( p + cursor_position + paste_len, getStringValue() + cursor_position, str_val_len - cursor_position + 1 ) ; + temp_cursor += paste_len ; + setValue ( p ) ; + setCursor ( temp_cursor ) ; + delete [] p ; + } + } + + break ; + + default: + if ( ( key < ' ' || key > 127 ) && ( key != '\n' ) + && ( key != '\r' ) ) return FALSE ; + + if ( key == '\r' ) key = '\n' ; + + if ( valid_data ) + { + if ( !strchr ( valid_data, key ) ) return TRUE ; + } + + if ( select_start_position != select_end_position ) // remove selected text + { + temp_cursor -= ( select_end_position - select_start_position ) ; + removeSelectRegion () ; + } + + p = new char [ strlen(old_text) + 2 ] ; + + memcpy ( p, old_text, cursor_position ) ; + + p [ cursor_position ] = key ; + + strcpy ( p + cursor_position + 1, ( old_text + cursor_position ) ) ; + bottom_line_in_window = top_line_in_window + getLinesInWindow() ; + + /* If running off the screen, scroll right. - JCJ 28 Jun 2002 */ + if ((bottom_value*max_width)+(input_width) < line_width_to_cursor+5) { + bottom_slider->setValue( ((bottom_value*max_width)+(input_width/2)+5)/max_width ) ; + } + + if (key == '\n') { + /* If pressing enter, figure out which line this is. */ + while ( line_counter < cursor_position) { + if ( old_text [ line_counter ] == '\n' ) current_line_in_window++ ; + line_counter++ ; + } + /* If hitting enter at the bottom of the screen, scroll down. - JCJ 28 Jun 2002 */ + if ( (current_line_in_window+1) >= bottom_line_in_window ) { + setTopLineInWindow ( top_line_in_window + 1 ); + right_slider->setValue (1.0f - float(top_line_in_window) / num_lines ) ; + } + } + + setValue ( p ) ; + setCursor ( temp_cursor + 1 ) ; + delete [] p ; + + break ; + } + } + + normalizeCursors () ; + return TRUE ; +} + +void puaLargeInput::getTextProperties(int *numlines, int *maxwidth) +{ + int num = 0, max = 0 ; + char *buf = ulStrDup ( getStringValue () ), *s = buf ; + while ( *s ) { + char *cr = strchr ( s, '\n' ) ; + if ( cr ) + *cr = '\0' ; + int w = legendFont.getStringWidth (s) ; + if ( w > max ) + max = w ; + num++ ; + if ( cr ) + s = ++cr ; + } + *numlines = num ; + *maxwidth = max ; + delete [] buf ; +} + +char *puaLargeInput::wrapText ( int target_width, int *numlines, int *maxwidth ) +{ + char *buf = ulStrDup ( getStringValue () ) ; + int numl = 0 ; + int maxw = 0 ; + + char *displayed_text_wp = buf, + *space_ptr, + *old_space_ptr ; + + /* Somewhat inspired by tuxracer */ + while ( *displayed_text_wp != '\0' ) + { + old_space_ptr = NULL ; + space_ptr = strchr ( displayed_text_wp, ' ' ) ; + + while (1) + { + if ( space_ptr != NULL ) + *space_ptr = '\0' ; + + int w = legendFont.getStringWidth ( displayed_text_wp ) ; + if ( w > target_width ) + break ; + + if ( w > maxw ) maxw = w ; + old_space_ptr = space_ptr ; + + if ( space_ptr == NULL ) + /* Entire string fits in widget */ + break ; + + // Check for carriage return in the original string + for (char *s = 0; (s = strchr ( displayed_text_wp, '\n' )); displayed_text_wp = s + 1) + numl++; + + *space_ptr = ' ' ; + + space_ptr = strchr ( space_ptr+1, ' ' ) ; + } + + numl++ ; + if ( old_space_ptr == NULL ) + /* Either string is too wide for area, or the entire remaining portion + of string fits in area (space_ptr == NULL). */ + { + displayed_text_wp += strlen (displayed_text_wp) ; + + if ( space_ptr != NULL ) + /* Advance past the NULL since there's more string left */ + displayed_text_wp += 1 ; + } + else + { + if ( space_ptr != NULL ) + *space_ptr = ' ' ; + *old_space_ptr = '\n' ; + + displayed_text_wp = old_space_ptr + 1 ; + } + } + if (numlines) *numlines = numl ; + if (maxwidth) *maxwidth = maxw ; + return buf ; +} + + diff --git a/src/puAux/puAuxList.cxx b/src/puAux/puAuxList.cxx new file mode 100644 index 0000000..2e3d570 --- /dev/null +++ b/src/puAux/puAuxList.cxx @@ -0,0 +1,317 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxList.cxx 2161 2010-02-27 04:06:53Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaList,puGroup) +// puAuxList.cxx - implementation of a scrolling list box. + +#include "puAux.h" + + +/** + * Static function: handle slider movements. + */ +static void +handle_slider (puObject * slider) +{ + puListBox * list_box = (puListBox *)slider->getUserData(); + + int total = list_box->getNumItems(); + int visible = list_box->getNumVisible(); + // negative numbers are OK -- setTopItem() clamps anyway + int index = int((total - visible) * (1.0 - slider->getFloatValue())); + list_box->setTopItem(index); +} + + +/** + * Static function: handle list entry selection. + */ +static void +handle_list_entry (puObject * listbox) +{ + puaList * box = (puaList *)listbox->getUserData(); + box->invokeCallback(); +} + + +/** + * Static function: handle arrow clicks. + */ +static void +handle_arrow (puObject * arrow) +{ + puSlider * slider = (puSlider *)arrow->getUserData(); + puListBox * list_box = (puListBox *)slider->getUserData(); + puaList * list = (puaList *)list_box->getUserData(); + + int step; + switch (((puArrowButton *)arrow)->getArrowType()) { + case PUARROW_DOWN: + step = 1; + break; + case PUARROW_UP: + step = -1; + break; + default: + step = 0; + break; + } + + int index = list->getTopItem(); + list->setTopItem(index + step); + slider->setValue(1.0f - float(list->getTopItem()) / (list->getNumItems() - list->getNumVisible())); +} + +/* Create an empty list box. */ +puaList::puaList (int x, int y, int w, int h, int sl_width) : + puGroup(x, y), + _sw(sl_width) +{ + type |= PUCLASS_LIST; + init(w, h, 1); +} + +/* Create a filled list box. */ +puaList::puaList (int x, int y, int w, int h, char ** contents, int sl_width) : + puGroup(x, y), + _sw(sl_width) +{ + type |= PUCLASS_LIST; + init(w, h, 1); + newList(contents); +} + +/* Create an empty list box. */ +puaList::puaList (int x, int y, int w, int h, short transparent, int sl_width) : + puGroup(x, y), + _sw(sl_width) +{ + type |= PUCLASS_LIST; + init(w, h, transparent); +} + +/* Create a filled list box. */ +puaList::puaList (int x, int y, int w, int h, short transparent, + char ** contents, int sl_width) : + puGroup(x, y), + _sw(sl_width) +{ + type |= PUCLASS_LIST; + init(w, h, transparent); + newList(contents); +} + +puaList::~puaList () +{ +} + +void +puaList::newList (char ** contents) +{ + _list_box->newList(contents); + _contents = contents; + + // new size calculation to consider slider visibility + setSize(_width, _height); +} + +void +puaList::setTopItem (int item_index) +{ + _list_box->setTopItem(item_index); + item_index = _list_box->getTopItem(); + // read clamped value back in, and only set slider if it doesn't match the new + // index to avoid jumps + int slider_index = int((1.0f - _slider->getFloatValue()) * (getNumItems() - getNumVisible())); + if (slider_index != item_index) + _slider->setValue(1.0f - float(getTopItem()) / (getNumItems() - getNumVisible())); +} + +/* + * Return a pointer to a character string with the + * contents of the selected entry. + */ +char * +puaList::getStringValue () +{ + int currElem = _list_box->getIntegerValue(); + if (currElem < 0) + { + return NULL; + } + else + { + return _contents[currElem]; + } +} + +/* + * Set a pointer to a character string with the + * contents of the selected entry + */ +void +puaList::getValue(char **ps) +{ + int currElem = _list_box->getIntegerValue(); + if (currElem < 0) + { + *ps = NULL; + } + else + { + *ps = _contents[currElem]; + } +} + + +/* + * Set the integer pointed to by "i" to + * the index of the currently selected entry. + */ +void +puaList::getValue(int *i) +{ + *i = _list_box->getIntegerValue(); +} + +/* Return the index of the currently selected entry. */ +int +puaList::getIntegerValue() +{ + return _list_box->getIntegerValue(); +} + +void +puaList::setColourScheme (float r, float g, float b, float a) +{ + puObject::setColourScheme(r, g, b, a); + _list_box->setColourScheme(r, g, b, a); +} + +void +puaList::setColour (int which, float r, float g, float b, float a) +{ + puObject::setColour(which, r, g, b, a); + _list_box->setColour(which, r, g, b, a); +} + +void +puaList::setSize (int w, int h) +{ + _width = w; + _height = h; + puObject::setSize(w, h); + if (_frame) + _frame->setSize(w, h); + + int total = getNumItems(); + int visible = getNumVisible(); + + if (total > visible) + { + if (!_slider->isVisible()) + { + _slider->setValue(1.0f); + _slider->reveal(); + _up_arrow->reveal(); + _down_arrow->reveal(); + } + _list_box->setSize(w-_sw, h); + + _slider->setPosition(w-_sw, _sw); + _slider->setSize(_sw, h-2*_sw); + _slider->setSliderFraction(float(visible) / total); + _slider->setPageStepSize(float(visible) / (total - visible)); + + _down_arrow->setPosition(w-_sw, 0); + _up_arrow->setPosition(w-_sw, h-_sw); + + } + else + { + if (_slider->isVisible()) + { + _slider->hide(); + _up_arrow->hide(); + _down_arrow->hide(); + } + _list_box->setSize(w, h); + } +} + +int +puaList::checkHit (int button, int updown, int x, int y) +{ + if (updown == PU_UP && isHit(x, y)) + { + if (button == PU_SCROLL_UP_BUTTON) + { + _up_arrow->invokeCallback(); + return TRUE; + } + if (button == PU_SCROLL_DOWN_BUTTON) + { + _down_arrow->invokeCallback(); + return TRUE; + } + } + return puGroup::checkHit(button, updown, x, y); +} + +void +puaList::init (int w, int h, short transparent) +{ + if ( transparent ) + _frame = NULL ; + else + _frame = new puFrame(0, 0, w, h); + + _list_box = new puListBox(0, 0, w-_sw, h); + _list_box->setStyle(-PUSTYLE_SMALL_SHADED); + _list_box->setUserData(this); + _list_box->setCallback(handle_list_entry); + _list_box->setValue(0); + + _slider = new puSlider(w-_sw, _sw, h-2*_sw, true, _sw); + _slider->setValue(1.0f); + _slider->setUserData(_list_box); + _slider->setCallback(handle_slider); + _slider->setCBMode(PUSLIDER_ALWAYS); + + _down_arrow = new puArrowButton(w-_sw, 0, w, _sw, PUARROW_DOWN) ; + _down_arrow->setUserData(_slider); + _down_arrow->setCallback(handle_arrow); + + _up_arrow = new puArrowButton(w-_sw, h-_sw, w, h, PUARROW_UP); + _up_arrow->setUserData(_slider); + _up_arrow->setCallback(handle_arrow); + + setSize(w, h); + close(); +} + +// end of puAuxList.cxx diff --git a/src/puAux/puAuxLocal.h b/src/puAux/puAuxLocal.h new file mode 100644 index 0000000..3d9526f --- /dev/null +++ b/src/puAux/puAuxLocal.h @@ -0,0 +1,4 @@ + +#define PU_USE_NONE 1 +#include "puAux.h" + diff --git a/src/puAux/puAuxScrollBar.cxx b/src/puAux/puAuxScrollBar.cxx new file mode 100644 index 0000000..309e9e9 --- /dev/null +++ b/src/puAux/puAuxScrollBar.cxx @@ -0,0 +1,278 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxScrollBar.cxx 2161 2010-02-27 04:06:53Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaScrollBar,puSlider) + + +void puaScrollBar::draw ( int dx, int dy ) +{ + extern void puDrawArrowButtonTriangle ( int pos_x, int pos_y, int size_x, int size_y, + puColour colour, int arrow_type, int active ) ; + + if ( !visible || ( window != puGetWindow () ) ) return ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + puBox box = abox ; + + int width = isVertical () ? abox.max[0] - abox.min[0] : abox.max[1] - abox.min[1] ; + + /* Draw the arrow buttons */ + + if ( arrow_count == 2 ) /* Double-arrow buttons */ + { + if ( isVertical () ) + { + box.min[1] = abox.max[1] - width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_FASTUP, active_arrow & FASTUP ) ; + box.min[1] = abox.min[1] ; + box.max[1] = box.min[1] + width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_FASTDOWN, active_arrow & FASTDOWN ) ; + } + else + { + box.min[0] = abox.max[0] - width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_FASTRIGHT, active_arrow & FASTUP ) ; + box.min[0] = abox.min[0] ; + box.max[0] = box.min[0] + width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_FASTLEFT, active_arrow & FASTDOWN ) ; + } + } + + if ( arrow_count > 0 ) /* Single-arrow buttons */ + { + if ( isVertical () ) + { + box.min[1] = abox.max[1] - arrow_count * width ; + box.max[1] = box.min[1] + width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_UP, active_arrow & UP ) ; + box.max[1] = abox.min[1] + arrow_count * width ; + box.min[1] = box.max[1] - width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_DOWN, active_arrow & DOWN ) ; + } + else + { + box.min[0] = abox.max[0] - arrow_count * width ; + box.max[0] = box.min[0] + width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_RIGHT, active_arrow & UP ) ; + box.max[0] = abox.min[0] + arrow_count * width ; + box.min[0] = box.max[0] - width ; + box.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + puDrawArrowButtonTriangle ( dx+box.min[0]+width/2, dy+box.min[1]+width/2, width, width, colour [ PUCOL_MISC ], + PUARROW_LEFT, active_arrow & DOWN ) ; + } + } + + /* Draw the surrounding box */ + + box.min[0] = abox.min[0] + ( isVertical () ? 0 : arrow_count * width ) ; + box.max[0] = abox.max[0] - ( isVertical () ? 0 : arrow_count * width ) ; + box.min[1] = abox.min[1] + ( isVertical () ? arrow_count * width : 0 ) ; + box.max[1] = abox.max[1] - ( isVertical () ? arrow_count * width : 0 ) ; + + if ( ( style == PUSTYLE_BEVELLED ) || + ( style == PUSTYLE_SHADED ) ) + box.draw ( dx, dy, -PUSTYLE_BOXED, colour, FALSE, 2 ) ; + else + box.draw ( dx, dy, -style, colour, FALSE, border_thickness ) ; + + /* Draw the slider box */ + + float val = getFloatValue () ; + + draw_slider_box ( dx, dy, box, ( val - getMinValue () ) / ( getMaxValue () - getMinValue () ) ) ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + +int puaScrollBar::checkHit ( int button, int updown, int x, int y ) +{ + if ( updown == PU_UP && ( button == PU_SCROLL_UP_BUTTON || button == PU_SCROLL_DOWN_BUTTON ) && isHit ( x, y ) ) + { + float last_value = clamp ( getFloatValue () ), next_value = last_value ; + float range = maximum_value - minimum_value ; + float line_step = line_step_size ; + if ( line_step <= 0.0f ) line_step = range / 10.0f ; + if ( button == PU_SCROLL_UP_BUTTON ) + next_value += line_step; + else + next_value -= line_step; + setValue ( checkStep ( clamp ( next_value ) ) ) ; + if ( next_value != last_value ) invokeCallback () ; + return TRUE; + } + return puSlider::checkHit ( button, updown, x, y ) ; +} + +void puaScrollBar::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( button != active_mouse_button ) + return; + + if ( updown == PU_UP ) + { + active_arrow = NONE ; + puDeactivateWidget () ; + return ; + } + + int sd = isVertical() ; + int coord = sd ? y : x ; + float range = maximum_value - minimum_value ; + float last_value = clamp ( getFloatValue () ), next_value = last_value ; + float norm_value = ( last_value - minimum_value ) / range ; + int width = abox.max[!sd] - abox.min[!sd] ; + int box_len = abox.max [sd] - abox.min [sd] - arrow_count * width * 2 ; + + if ( updown == PU_DOWN ) { + int lower = abox.min [sd] + int(float(box_len) * (1.0 - slider_fraction) * norm_value) + + arrow_count * width ; + int upper = lower + int(float(box_len) * slider_fraction) ; // upper/lower slider margin in pixels + + float line_step = line_step_size ; + float page_step = page_step_size ; + if ( line_step <= 0.0f ) line_step = range / 10.0f ; + if ( page_step <= 0.0f ) page_step = range ; + + start_offset = -1 ; + if ( arrow_count && coord < abox.min[sd] + width ) { // lowest button + if (arrow_count == 2) { + next_value -= page_step ; + active_arrow |= FASTDOWN ; + } else { + next_value -= line_step ; + active_arrow |= DOWN ; + } + + } else if ( arrow_count == 2 && coord < abox.min[sd] + 2 * width ) { // 2nd low button + next_value -= line_step ; + active_arrow |= DOWN ; + + } else if ( arrow_count && coord > abox.max[sd] - width ) { // highest button + if (arrow_count == 2) { + next_value += page_step ; + active_arrow |= FASTUP ; + } else { + next_value += line_step ; + active_arrow |= UP ; + } + + } else if ( arrow_count == 2 && coord > abox.max[sd] - 2 * width ) { // 2nd high button + next_value += line_step ; + active_arrow |= UP ; + + } else if ( page_step_size <= 0.0f ) { // old slider behavior (jumping) + start_offset = int(float(box_len) * slider_fraction * 0.5f) + 2 * arrow_count * width ; + updown = PU_DRAG ; + + } else if (coord < lower) { // lower background + next_value -= page_step ; + + } else if (coord > upper) { // upper background + next_value += page_step ; + + } else { // slider handle (new behavior) + start_offset = coord + arrow_count * width - abox.min [sd] + - int(float(box_len) * (1.0f - slider_fraction) * norm_value) ; + puSetActiveWidget ( this, x, y ) ; + return; + } + } + + if ( updown == PU_DRAG && start_offset >= 0 ) { + if ( box_len == 0 ) { + norm_value = 0.5f ; + } else if ( slider_fraction >= 1.0f ) { + norm_value = 0.0f ; + } else { + norm_value = ( coord + arrow_count * width - abox.min[sd] - start_offset ) + / ( box_len * (1.0f - slider_fraction) ) ; + } + next_value = norm_value * range + minimum_value ; + } + + setValue ( checkStep ( clamp ( next_value ) ) ) ; + + if ( next_value == last_value ) return ; + + switch ( cb_mode ) + { + case PUSLIDER_CLICK : + if ( updown == active_mouse_edge ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_DELTA : /* Deprecated! */ + if ( fabs ( last_cb_value - next_value ) >= cb_delta ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_ALWAYS : + default : + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + break ; + } +} + + diff --git a/src/puAux/puAuxSelectBox.cxx b/src/puAux/puAuxSelectBox.cxx new file mode 100644 index 0000000..b1ac285 --- /dev/null +++ b/src/puAux/puAuxSelectBox.cxx @@ -0,0 +1,206 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxSelectBox.cxx 2084 2006-05-02 16:28:07Z bram $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaSelectBox,puGroup) + + +void puaSelectBox::handle_arrow ( puObject *arrow ) +{ + puaSelectBox *selbox = (puaSelectBox *) arrow -> getUserData () ; + int arrow_type = ((puArrowButton*) arrow) -> getArrowType () ; + + if ( arrow_type == PUARROW_DOWN ) + selbox -> setCurrentItem ( selbox -> getCurrentItem () - 1 ) ; + else if ( arrow_type == PUARROW_UP ) + selbox -> setCurrentItem ( selbox -> getCurrentItem () + 1 ) ; +} + + +void puaSelectBox::update_widgets ( void ) +{ + if ( curr_item >= 0 ) + /* There are some items */ + { + setValue ( list[curr_item] ) ; + input->setValue( list[curr_item] ); + + if ( curr_item == 0 ) + /* Current item is first item, deactivate 'down' button */ + down_arrow -> greyOut () ; + else + down_arrow -> activate () ; + + if ( curr_item == (num_items - 1) ) + /* Current item is last item, deactivate 'up' button */ + up_arrow -> greyOut () ; + else + up_arrow -> activate () ; + } + else + /* There aren't any items */ + { + setValue ( "" ) ; + + down_arrow -> greyOut () ; + up_arrow -> greyOut () ; + } +} + + +void puaSelectBox::newList ( char ** _list ) +{ + list = _list ; + + if ( list == NULL ) + num_items = 0 ; + else + { + for ( num_items = 0 ; list[num_items] != NULL ; num_items++ ) + /* Count number of items */ ; + } + + curr_item = ( num_items > 0 ? 0 : - 1 ) ; + update_widgets () ; +} + + +void puaSelectBox::setSize ( int w, int h ) +{ + int arrow_size = h / 2 ; + + /* Resize the puInput widget */ + + input -> setSize ( w - arrow_size, h ) ; + + /* Resize and reposition the arrow buttons */ + + down_arrow -> setPosition ( w - arrow_size, 0 ) ; + down_arrow -> setSize ( arrow_size, arrow_size ) ; + + up_arrow -> setPosition ( w - arrow_size, arrow_size ) ; + up_arrow -> setSize ( arrow_size, arrow_size ) ; +} + +void puaSelectBox::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + draw_label ( dx, dy ) ; + + puGroup::draw ( dx, dy ) ; +} + +int puaSelectBox::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || ! input -> isAcceptingInput () || + ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + switch ( key ) + { + case PU_KEY_HOME : + setCurrentItem ( 0 ) ; + break ; + + case PU_KEY_END : + setCurrentItem ( num_items - 1 ) ; + break ; + + case PU_KEY_UP : + case PU_KEY_PAGE_UP : + setCurrentItem ( curr_item + 1 ) ; + break ; + + case PU_KEY_DOWN : + case PU_KEY_PAGE_DOWN : + setCurrentItem ( curr_item - 1 ) ; + break ; + + default : + /* We might decide to make the input box editable on request + in future - and it does not hurt ... */ + return input -> checkKey ( key, updown ) ; + break ; + } + + return TRUE ; +} + +void puaSelectBox::setColourScheme ( float r, float g, float b, float a ) +{ + puObject::setColourScheme ( r, g, b, a ) ; + setChildColourScheme( PUCLASS_INPUT, + colour [ PUCOL_EDITFIELD ][0], + colour [ PUCOL_EDITFIELD ][1], + colour [ PUCOL_EDITFIELD ][2], + colour [ PUCOL_EDITFIELD ][3] ) ; +} + +void puaSelectBox::setColour ( int which, float r, float g, float b, float a ) +{ + if (which == PUCOL_EDITFIELD) + input->setColourScheme ( r, g, b, a ) ; + else + puObject::setColour ( which, r, g, b, a ) ; +} + + +puaSelectBox::puaSelectBox ( int minx, int miny, int maxx, int maxy, + char **entries ) : + puGroup( minx, miny ) +{ + type |= PUCLASS_SELECTBOX ; + + char *stringval ; + int arrow_size = (maxy - miny) / 2 ; + + input = new puInput ( 0, 0, maxx-minx - arrow_size, maxy-miny ) ; + input -> setStyle ( PUSTYLE_SMALL_SHADED ) ; + input -> disableInput () ; + + /* Share 'string' value with input box */ + input -> getValue ( &stringval ) ; + setValuator ( stringval ) ; + + down_arrow = new puArrowButton ( maxx-minx - arrow_size, 0, + maxx-minx, arrow_size, + PUARROW_DOWN ) ; + down_arrow -> setStyle ( PUSTYLE_SMALL_SHADED ) ; + down_arrow -> setUserData ( this ) ; + down_arrow -> setCallback ( handle_arrow ) ; + + up_arrow = new puArrowButton ( maxx-minx - arrow_size, arrow_size, + maxx-minx, maxy-miny, + PUARROW_UP ) ; + up_arrow -> setStyle ( PUSTYLE_SMALL_SHADED ) ; + up_arrow -> setUserData ( this ) ; + up_arrow -> setCallback ( handle_arrow ) ; + + newList ( entries ) ; + + close () ; +} + diff --git a/src/puAux/puAuxSliderWithInput.cxx b/src/puAux/puAuxSliderWithInput.cxx new file mode 100644 index 0000000..713109f --- /dev/null +++ b/src/puAux/puAuxSliderWithInput.cxx @@ -0,0 +1,89 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxSliderWithInput.cxx 1902 2004-03-21 19:03:23Z sjbaker $ +*/ + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaSliderWithInput,puGroup) + +void puaSliderWithInput::handle_slider ( puObject *obj ) +{ + puaSliderWithInput *sl = (puaSliderWithInput *)obj->getUserData () ; + sl->setValue ( obj->getFloatValue () ) ; + sl->__setInputBox ( obj->getFloatValue () ) ; + + sl->invokeCallback () ; +} + +void puaSliderWithInput::handle_input ( puObject *obj ) +{ + puaSliderWithInput *sl = (puaSliderWithInput *)obj->getUserData () ; + sl->setValue ( obj->getFloatValue () ) ; + sl->invokeCallback () ; +} + +puaSliderWithInput::puaSliderWithInput ( int minx, int miny, int maxx, int maxy, int above ) : + puGroup ( minx, miny ) +{ + type |= PUCLASS_SLIDERWITHINPUT ; + slider = new puSlider ( (maxx - minx)/2-10, above ? 0 : 20, maxy-miny-20, TRUE ) ; + input_box = new puInput ( 0, above ? maxy - miny - 20 : 0, maxx-minx, above ? maxy - miny : 20 ) ; + input_box->setValue ( 0 ) ; + slider->setUserData ( this ) ; + slider->setCallback ( handle_slider ) ; + input_box->setUserData ( this ) ; + input_box->setCallback ( handle_input ) ; + close () ; + + input_position = above ; +} + + +void puaSliderWithInput::setSize ( int w, int h ) +{ + slider->setSize ( 20, h-40 ) ; + slider->setPosition ( w/2-10, input_position ? 0 : 20 ) ; + + input_box->setSize ( w, 20 ) ; + input_box->setPosition ( 0, input_position ? h-20 : 0 ) ; +} + + +void puaSliderWithInput::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + draw_label ( dx, dy ) ; + + puGroup::draw ( dx, dy ) ; +} + + +int puaSliderWithInput::checkKey ( int key, int updown ) +{ + if ( ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + return ( input_box->checkKey ( key, updown ) ) ; +} + diff --git a/src/puAux/puAuxSpinBox.cxx b/src/puAux/puAuxSpinBox.cxx new file mode 100644 index 0000000..8bfacbf --- /dev/null +++ b/src/puAux/puAuxSpinBox.cxx @@ -0,0 +1,97 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxSpinBox.cxx 1902 2004-03-21 19:03:23Z sjbaker $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF2(puaSpinBox,puRange,puGroup) + + +puaSpinBox::puaSpinBox ( int minx, int miny, int maxx, int maxy, int arrow_pos ) : + puRange ( 1.0f, 10.0f, 1.0f ), puGroup ( minx, miny ) +{ + extern void puaSpinBox_handle_input ( puObject* ob ) ; + extern void puaSpinBox_handle_input_active ( puObject* ob ) ; + extern void puaSpinBox_handle_arrow ( puObject* ob ) ; + type |= PUCLASS_SPINBOX ; + arrow_position = arrow_pos ; + int arrow_size = ( maxy - miny ) / 2 ; + if ( arrow_pos == 1 ) + input_box = new puInput ( 0, 0, maxx - minx - arrow_size, maxy - miny ) ; + else + input_box = new puInput ( arrow_size, 0, maxx - minx, maxy - miny ) ; + + input_box->setCallback ( puaSpinBox_handle_input ) ; + input_box->setActiveCallback ( puaSpinBox_handle_input_active ) ; + input_box->setDownCallback ( puaSpinBox_handle_input ) ; + input_box->setUserData ( this ) ; + + up_arrow = new puArrowButton ( arrow_position?maxx-minx-arrow_size:0, arrow_size, + arrow_position?maxx-minx:arrow_size, maxy-miny, PUARROW_UP ) ; + up_arrow->setCallback ( puaSpinBox_handle_arrow ) ; + up_arrow->setUserData ( this ) ; + + down_arrow = new puArrowButton ( arrow_position?maxx-minx-arrow_size:0, 0, + arrow_position?maxx-minx:arrow_size, arrow_size, PUARROW_DOWN ) ; + down_arrow->setCallback ( puaSpinBox_handle_arrow ) ; + down_arrow->setUserData ( this ) ; + + close () ; +} + +void puaSpinBox_handle_arrow ( puObject *ob ) +{ + puaSpinBox *master = (puaSpinBox *)(ob->getUserData ()) ; + float val = master->getFloatValue () ; + if ( ((puArrowButton *)ob)->getArrowType () == PUARROW_UP ) + val += master->getStepSize () ; + else + val -= master->getStepSize () ; + + if ( val > master->getMaxValue () ) val = master->getMaxValue () ; + if ( val < master->getMinValue () ) val = master->getMinValue () ; + master->setValue ( val ) ; + master->invokeCallback () ; +} + +void puaSpinBox_handle_input ( puObject *ob ) +{ + puaSpinBox *master = (puaSpinBox *)(ob->getUserData ()) ; + float val = ob->getFloatValue () ; + if ( val > master->getMaxValue () ) val = master->getMaxValue () ; + if ( val < master->getMinValue () ) val = master->getMinValue () ; + master->setValue ( val ) ; + master->invokeCallback () ; +} + +void puaSpinBox_handle_input_active ( puObject *ob ) +{ + puaSpinBox *master = (puaSpinBox *)(ob->getUserData ()) ; + float val = ob->getFloatValue () ; + if ( val > master->getMaxValue () ) val = master->getMaxValue () ; + if ( val < master->getMinValue () ) val = master->getMinValue () ; + master->setValue ( val ) ; + master->invokeActiveCallback () ; +} + diff --git a/src/puAux/puAuxTriSlider.cxx b/src/puAux/puAuxTriSlider.cxx new file mode 100644 index 0000000..51539a6 --- /dev/null +++ b/src/puAux/puAuxTriSlider.cxx @@ -0,0 +1,211 @@ +/* + PUI Auxiliary Widget Library + Derived from PLIB, the Portable Game Library by Steve Baker. + Copyright (C) 1998,2002,2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxTriSlider.cxx 2022 2005-05-06 18:39:18Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaTriSlider,puaBiSlider) + + +void puaTriSlider::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + // Draw the slider box itself + + if ( ( style == PUSTYLE_BEVELLED ) || + ( style == PUSTYLE_SHADED ) ) + abox.draw ( dx, dy, -PUSTYLE_BOXED, colour, FALSE, 2 ) ; + else + abox.draw ( dx, dy, -style, colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + // Draw the central slider and label it + + float val; + if ( getMaxValue() > getMinValue() ) + val = (float)(getFloatValue () - getMinValue()) / (float)(getMaxValue() - getMinValue()) ; + else + val = 0.5f ; + + char str_value[10] ; + sprintf (str_value, "%g", getFloatValue () ) ; + + draw_slider_box ( dx, dy, abox, val, str_value ) ; + + if ( val < 0.0f ) val = 0.0f ; + if ( val > 1.0f ) val = 1.0f ; + + // Draw the current_max slider and label it + + if ( getMaxValue() > getMinValue() ) + val = (float)(getCurrentMax() - getMinValue()) / (float)(getMaxValue() - getMinValue()) ; + else + val = 1.0f ; + + sprintf (str_value, "%g", getCurrentMax() ) ; + + draw_slider_box ( dx, dy, abox, val, str_value ) ; + + // Draw the current_min slider and label it + + if ( getMaxValue() > getMinValue() ) + val = (float)(getCurrentMin() - getMinValue()) / (float)(getMaxValue() - getMinValue()) ; + else + val = 0.0f ; + + sprintf (str_value, "%g", getCurrentMin() ) ; + + draw_slider_box ( dx, dy, abox, val, str_value ) ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + + +void puaTriSlider::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + { + puMoveToLast ( this ); + /* Bug Fixed: If you moved a slider while dragging the mouse (click and drag), then move */ + /* the cursor outside of the widget and released the mouse button, the active_button would */ + /* be locked to the last slider you moved, and unable to release. - JCJ 11 June 2002 */ + setActiveButton ( 0 ) ; + } + + if ( ( button == active_mouse_button ) && ( updown == PU_UP ) ) + { + setActiveButton ( 0 ) ; + puDeactivateWidget () ; + return ; + } + + if ( button == active_mouse_button ) + { + int sd = isVertical() ; + int sz = abox.max [sd] - abox.min [sd] ; + int coord = isVertical() ? y : x ; + + float next_value ; + + if ( sz == 0 ) + next_value = 0.5f ; + else + { + next_value = ( (float)coord - (float)abox.min[sd] - (float)sz * slider_fraction / 2.0f ) / + ( (float) sz * (1.0f - slider_fraction) ) ; + } + + next_value = (next_value < 0.0f) ? 0.0f : (next_value > 1.0) ? 1.0f : next_value ; + + float new_value = getMinValue() + next_value * ( getMaxValue() - getMinValue() ) ; + + if ( getFreezeEnds() ) // Cannot move end sliders, must move middle one + { + setActiveButton ( 2 ) ; + setValue ( checkStep(new_value) ) ; /* Ensure that the middle slider can't move beyond the barriers - JCJ 10 Jun 2002 */ + if ( new_value < getCurrentMin() ) setValue ( getCurrentMin() ) ; + if ( new_value > getCurrentMax() ) setValue ( getCurrentMax() ) ; + } + else + { + if ( getActiveButton() == 0 ) // No currently-active slider, set whichever is closest + { + if ( (new_value-getCurrentMin()) < (getFloatValue()-new_value) ) // Closest to current_min + { + setCurrentMin ( checkStep(new_value) ) ; + setActiveButton ( 1 ) ; + } + else if ( (new_value-getFloatValue()) > (getCurrentMax()-new_value) ) // Closest to current_max + { + setCurrentMax ( checkStep(new_value) ) ; + setActiveButton ( 3 ) ; + } + else // closest to the center slider -- ties go to the center slider + { + setValue ( checkStep(new_value) ) ; + setActiveButton ( 2 ) ; + } + } + else if ( getActiveButton() == 1 ) // Currently moving current_min + { + setCurrentMin ( checkStep(new_value) ) ; + if ( getFloatValue() < getCurrentMin() ) setValue ( getCurrentMin() ) ; + if ( getCurrentMax() < getCurrentMin() ) setCurrentMax ( getCurrentMin() ) ; + } + else if ( getActiveButton() == 2 ) // Currently moving central value + { + setValue ( checkStep(new_value) ) ; + if ( getCurrentMin() > new_value ) setCurrentMin ( new_value ) ; + if ( getCurrentMax() < new_value ) setCurrentMax ( new_value ) ; + } + else if ( getActiveButton() == 3 ) // Currently moving current_max + { + setCurrentMax ( checkStep(new_value) ) ; + if ( getFloatValue() > getCurrentMax() ) setValue ( getCurrentMax() ) ; + if ( getCurrentMax() < getCurrentMin() ) setCurrentMin ( getCurrentMax() ) ; + } + } + + switch ( cb_mode ) + { + case PUSLIDER_CLICK : + if ( updown == active_mouse_edge ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_DELTA :/* Deprecated! */ + if ( fabs ( last_cb_value - next_value ) >= cb_delta ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_ALWAYS : + default : + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + break ; + } + } +} diff --git a/src/puAux/puAuxVerticalMenu.cxx b/src/puAux/puAuxVerticalMenu.cxx new file mode 100644 index 0000000..e18dda5 --- /dev/null +++ b/src/puAux/puAuxVerticalMenu.cxx @@ -0,0 +1,174 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puAuxVerticalMenu.cxx 2022 2005-05-06 18:39:18Z fayjf $ +*/ + + +#include "puAuxLocal.h" + +UL_RTTI_DEF1(puaVerticalMenu,puGroup) + + +extern void puMenuBar_drop_down_the_menu ( puObject *b ); + +/* If there is only one entry in a vmenu, just act as if clicking the menu clicks the oneshot underneath it. */ +void puaVerticalMenu::add_submenu ( const char *str, char *items[], + puCallback _cb[], void *_user_data[] ) +{ + int w, h ; + getSize ( &w, &h ) ; + + if (items[1] != NULL) + { + + // More than one entry in the submenu, set the callback to drop down the menu + // and make the mouse activation edge up or down. Create the popup menu + // and load the items into it. + + puOneShot *b = new puOneShot ( w+10, 0, str ) ; + b -> setStyle ( PUSTYLE_SPECIAL_UNDERLINED ) ; + b -> setColourScheme ( colour[PUCOL_FOREGROUND][0], + colour[PUCOL_FOREGROUND][1], + colour[PUCOL_FOREGROUND][2], + colour[PUCOL_FOREGROUND][3] ) ; + + b -> setCallback ( puMenuBar_drop_down_the_menu ) ; + b -> setActiveDirn ( PU_UP_AND_DOWN ) ; + + puPopupMenu *p = new puPopupMenu ( w+10, 0 ) ; + p->setActiveButton ( active_mouse_button ) ; + + b -> setUserData ( p ) ; + + if ( _user_data != NULL ) + for ( int i = 0 ; items[i] != NULL ; i++ ) + p -> add_item ( items[i], _cb[i], _user_data[i] ) ; + else + for ( int i = 0 ; items[i] != NULL ; i++ ) + p -> add_item ( items[i], _cb[i] ) ; + + p->close () ; + } + else + { + // Only one entry in the submenu, set the callback to the entry's callback + // and make the mouse activation edge up only. Set the user data to null. + + puOneShot *b = new puOneShot ( w+10, 0, str ) ; + b -> setStyle ( PUSTYLE_SPECIAL_UNDERLINED ) ; + b -> setColourScheme ( colour[PUCOL_FOREGROUND][0], + colour[PUCOL_FOREGROUND][1], + colour[PUCOL_FOREGROUND][2], + colour[PUCOL_FOREGROUND][3] ) ; + + b -> setCallback ( _cb[0] ); + b -> setActiveDirn ( PU_UP ) ; + + b -> setUserData ( NULL ) ; + } + + recalc_bbox () ; +} + +void puaVerticalMenu::close (void) +{ + puGroup::close () ; + + if ( dlist == NULL ) + return ; + + int height = 0 ; + int fullheight; + puObject *ob ; + + // Use alternate objects - which gets the puOneShot/puPopupMenu pairs + // If the "puOneShot" has a user data of null, it is a single entry and its + // puPopup item is missing. + + for ( ob = dlist ; ob != NULL ; ob = ob -> getNextObject() ) + { + int w, h ; + + // Reposition the button so it looks nice + + ob -> getSize ( &w, &h ) ; + ob -> setPosition ( 0, height ) ; + + if (ob -> getUserData () ) + { + + ob = ob -> getNextObject() ; + + // Reposition the submenu so it sits under the button + + int w2, h2 ; + ob -> getSize ( &w2, &h2 ) ; + ob -> setPosition ( 0, height-h2 ) ; + } + + // Next please! + fullheight = height; + height -= h ; + } + + // Now step through the list again, adding the final value of "height" to + // all the vertical positions so that the (0,0) point will be the lower left- + // hand corner. + + for ( ob = dlist ; ob != NULL ; ob = ob -> getNextObject() ) + { + int x, y ; + + // Reposition the button so it looks nice + + ob -> getPosition ( &x, &y ) ; + ob -> setPosition ( x, y-fullheight ) ; + + if (ob -> getUserData () ) + { + + ob = ob -> getNextObject() ; + + // Reposition the submenu so it sits under the button + + ob -> getPosition ( &x, &y ) ; + ob -> setPosition ( x, y-fullheight ) ; + } + } + + recalc_bbox () ; + + // If in its own window (its window number is not the same as its parent's + // window number), then resize the window + + if (window != parent -> getWindow () ) + { + int temp_window = puGetWindow () ; + puSetWindow ( window ) ; + puSetWindowSize ( bbox.max[0] - bbox.min[0], bbox.max[1] - bbox.min[1] ) ; + setPosition ( 0, 0 ) ; + + puSetWindow ( temp_window ) ; + } + +} + + diff --git a/src/pui/Makefile.am b/src/pui/Makefile.am new file mode 100644 index 0000000..d695d81 --- /dev/null +++ b/src/pui/Makefile.am @@ -0,0 +1,23 @@ +if BUILD_PUI + +lib_LIBRARIES = libplibpu.a + +include_HEADERS = pu.h puGLUT.h puFLTK.h puSDL.h puNative.h puPW.h + +libplibpu_a_SOURCES = \ + pu.cxx puBox.cxx puButton.cxx puButtonBox.cxx \ + puArrowButton.cxx puDialogBox.cxx puFrame.cxx puGroup.cxx \ + puInput.cxx puInterface.cxx puLocal.h puMenuBar.cxx \ + puObject.cxx puOneShot.cxx puPopup.cxx puPopupMenu.cxx \ + puListBox.cxx puSlider.cxx puText.cxx \ + puValue.cxx puFont.cxx \ + puDial.cxx \ + puRange.cxx \ + puInputBase.cxx + +AM_CPPFLAGS = -I$(top_srcdir)/src/sg -I$(top_srcdir)/src/fnt +AM_CPPFLAGS += -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = pui.dsp diff --git a/src/pui/pu.cxx b/src/pui/pu.cxx new file mode 100644 index 0000000..2e3728f --- /dev/null +++ b/src/pui/pu.cxx @@ -0,0 +1,482 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pu.cxx 2030 2005-05-23 21:24:26Z fayjf $ +*/ + +#include "puLocal.h" + +#if defined(UL_AGL) +# include +#elif defined(UL_CGL) +# include +#elif defined(UL_GLX) +# include +#endif + + +int puRefresh = TRUE ; + + +static puGetWindowCallback cbGetWindow; +static puSetWindowCallback cbSetWindow; +static puGetWindowSizeCallback cbGetWindowSize; +static puSetWindowSizeCallback cbSetWindowSize; + + +void puSetWindowFuncs ( puGetWindowCallback getWindow, + puSetWindowCallback setWindow, + puGetWindowSizeCallback getWindowSize, + puSetWindowSizeCallback setWindowSize ) +{ + cbGetWindow = getWindow; + cbSetWindow = setWindow; + cbGetWindowSize = getWindowSize; + cbSetWindowSize = setWindowSize; +} + + +int puGetWindow () +{ + if ( !cbGetWindow ) + ulSetError ( UL_FATAL, "puGetWindow: Callbacks not initialized." ) ; + return ( *cbGetWindow ) () ; +} + +void puSetWindow ( int window ) +{ + if ( !cbSetWindow ) + ulSetError ( UL_WARNING, "puSetWindow: Cannot set window (null callback)." ) ; + else + ( *cbSetWindow ) ( window ) ; +} + +void puGetWindowSize ( int *width, int *height ) +{ + if ( !cbGetWindowSize ) + ulSetError ( UL_FATAL, "puGetWindowSize: Callbacks not initialized." ) ; + ( *cbGetWindowSize ) ( width, height ) ; +} + +void puSetWindowSize ( int width, int height ) +{ + if ( !cbSetWindowSize ) + ulSetError ( UL_WARNING, "puSetWindowSize: Cannot change size (null callback)." ) ; + else + ( *cbSetWindowSize ) ( width, height ) ; +} + +int puGetWindowWidth () +{ + int w, h ; + puGetWindowSize ( &w, &h ) ; + return w ; +} + +int puGetWindowHeight () +{ + int w, h ; + puGetWindowSize ( &w, &h ) ; + return h ; +} + + + +static bool glIsValidContext ( void ) +{ +#if defined(UL_WGL) + return ( wglGetCurrentContext () != NULL ) ; +#elif defined(UL_AGL) + return ( aglGetCurrentContext () != NULL ) ; +#elif defined(UL_CGL) + return ( CGLGetCurrentContext () != NULL ) ; +#elif defined(UL_GLX) + return ( glXGetCurrentContext () != NULL ) ; +#else + return true ; +#endif +} + + +static int _puCursor_enable = FALSE ; +static int _puCursor_x = 0 ; +static int _puCursor_y = 0 ; +static float _puCursor_bgcolour [4] = { 1.0f, 1.0f, 1.0f, 1.0f } ; +static float _puCursor_fgcolour [4] = { 0.0f, 0.0f, 0.0f, 1.0f } ; + +void puHideCursor ( void ) { _puCursor_enable = FALSE ; } +void puShowCursor ( void ) { _puCursor_enable = TRUE ; } +int puCursorIsHidden ( void ) { return ! _puCursor_enable ; } + +void puCursor ( int x, int y ) +{ + _puCursor_x = x ; + _puCursor_y = y ; +} + + +static void puDrawCursor ( int x, int y ) +{ + glColor4fv ( _puCursor_bgcolour ) ; + + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( x, y ) ; + glVertex2i ( x + 13, y - 4 ) ; + glVertex2i ( x + 4, y - 13 ) ; + + glVertex2i ( x + 8, y - 3 ) ; + glVertex2i ( x + 17, y - 12 ) ; + glVertex2i ( x + 12, y - 17 ) ; + + glVertex2i ( x + 12, y - 17 ) ; + glVertex2i ( x + 3, y - 8 ) ; + glVertex2i ( x + 8, y - 3 ) ; + glEnd () ; + + glColor4fv ( _puCursor_fgcolour ) ; + + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( x+1, y-1 ) ; + glVertex2i ( x + 11, y - 4 ) ; + glVertex2i ( x + 4, y - 11 ) ; + + glVertex2i ( x + 8, y - 5 ) ; + glVertex2i ( x + 15, y - 12 ) ; + glVertex2i ( x + 12, y - 15 ) ; + + glVertex2i ( x + 12, y - 15 ) ; + glVertex2i ( x + 5, y - 8 ) ; + glVertex2i ( x + 8, y - 5 ) ; + glEnd () ; +} + + +// Pointer to linked list of objects to delete +// as a result of keyboarding or mouse clicking + +static puObject *objects_to_delete = NULL; + + +void puDeleteObject ( puObject *ob ) +{ + if ( ob == NULL ) + return ; + + puGroup *parent = ob->getParent () ; + + /* Add object to linked list to be deleted */ + if ( objects_to_delete == NULL ) + objects_to_delete = ob ; + else + { + /* Ensure that objects are deleted in the order of puDeleteObject calls */ + + puObject *last ; + + for ( last = objects_to_delete ; + last -> getNextObject() != NULL ; + last = last -> getNextObject() ) + /* Find last object. */ ; + + last -> setNextObject ( ob ) ; + } + + /* Remove from parent interface */ + + if ( parent != ob && parent != NULL ) + parent -> remove ( ob ) ; /* Sets object's next and previous pointers to null as well */ +} + + +void puCleanUpJunk ( void ) +{ + puObject * local_objects_to_delete = objects_to_delete ; + objects_to_delete = NULL ; + /* Step through the linked list of objects to delete, removing them. */ + while ( local_objects_to_delete != NULL ) + { + puObject *next_ob = local_objects_to_delete -> getNextObject() ; + delete local_objects_to_delete ; + local_objects_to_delete = next_ob ; + + /* If we've reached the end of the list, start over (in case we've deleted a group and + * it has put new widgets on the delete list) + */ + if ( local_objects_to_delete == NULL ) + { + local_objects_to_delete = objects_to_delete ; + objects_to_delete = NULL ; + } + } +} + + +static puObject *active_widget ; /* Widget which is currently receiving user input */ +static char *input_paste_buffer ; /* Cut/Copy/Paste buffer for input widgets */ + +static int firsttime = TRUE ; + +void puRealInit ( void ) +{ + if ( firsttime ) + { + if ( ! glIsValidContext () ) + { + ulSetError ( UL_FATAL, + "puInit called without a valid OpenGL context."); + } + + new puInterface ( 0, 0 ) ; + + active_widget = NULL ; + input_paste_buffer = NULL ; + + firsttime = FALSE ; + } +} + +void puExit ( void ) +{ + if ( firsttime ) + ulSetError ( UL_FATAL, "puExit called without a previous call to puInit." ) ; + + delete puGetBaseLiveInterface () ; + firsttime = TRUE ; +} + +static void puSetOpenGLState ( void ) +{ + int w, h ; + + puGetWindowSize ( &w, &h ) ; + + glPushAttrib ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; + + glDisable ( GL_LIGHTING ) ; + glDisable ( GL_FOG ) ; + glDisable ( GL_TEXTURE_2D ) ; + glDisable ( GL_DEPTH_TEST ) ; + glDisable ( GL_CULL_FACE ) ; + + glViewport ( 0, 0, w, h ) ; + + glMatrixMode ( GL_PROJECTION ) ; + glPushMatrix () ; + glLoadIdentity () ; + glOrtho ( 0, w, 0, h, -1, 1 ) ; + glMatrixMode ( GL_MODELVIEW ) ; + glPushMatrix () ; + glLoadIdentity () ; +} + +static void puRestoreOpenGLState ( void ) +{ + glMatrixMode ( GL_PROJECTION ) ; + glPopMatrix () ; + glMatrixMode ( GL_MODELVIEW ) ; + glPopMatrix () ; + glPopAttrib () ; +} + + +void puDisplay ( void ) +{ + puCleanUpJunk () ; + + puSetOpenGLState () ; + puGetUltimateLiveInterface () -> draw ( 0, 0 ) ; + + int h = puGetWindowHeight () ; + + if ( _puCursor_enable ) + puDrawCursor ( _puCursor_x, + h - _puCursor_y ) ; + + puRestoreOpenGLState () ; + + puRefresh = FALSE ; +} + + +void puDisplay ( int window_number ) /* Deprecated */ +{ + if ( window_number == puGetWindow () ) + puDisplay () ; +} + + +int puKeyboard ( int key, int updown, int, int ) +{ + return puKeyboard ( key, updown ) ; +} + + +int puKeyboard ( int key, int updown ) +{ + int return_value = puGetBaseLiveInterface () -> checkKey ( key, updown ) ; + + puCleanUpJunk () ; + + return return_value ; +} + + +static int last_buttons = 0 ; +static int pu_mouse_x = 0 ; +static int pu_mouse_y = 0 ; +static int pu_mouse_offset_x = 0 ; +static int pu_mouse_offset_y = 0 ; + +int puGetPressedButton () +{ + return last_buttons ; +} + +int puMouse ( int button, int updown, int x, int y ) +{ + puCursor ( x, y ) ; + + int h = puGetWindowHeight () ; + + if ( updown == PU_DOWN ) + last_buttons |= ( 1 << button ) ; + else + last_buttons &= ~( 1 << button ) ; + + pu_mouse_x = x ; + pu_mouse_y = h - y ; + + puObject *active = puActiveWidget () ; + + if ( ( last_buttons != 0 ) && ( active != NULL ) ) + { + int x_offset, y_offset ; + active -> getAbsolutePosition ( &x_offset, &y_offset ) ; + + x_offset -= active -> getABox () -> min [0] ; + y_offset -= active -> getABox () -> min [1] ; + + if ( ( ! active -> isHit ( pu_mouse_x - x_offset, pu_mouse_y - y_offset ) ) && + ( active -> getWhenToDeactivate () == PUDEACTIVATE_ON_MOUSE_CLICK ) ) + { + active -> invokeDownCallback () ; + puDeactivateWidget () ; + } + } + + int return_value = puGetBaseLiveInterface () -> checkHit ( button, + updown, pu_mouse_x, pu_mouse_y ) ; + + puCleanUpJunk () ; + + return return_value ; +} + + +int puMouse ( int x, int y ) +{ + puCursor ( x, y ) ; + + // Pick buttons in order of descending priority: Left, Right, Middle + int button = + (last_buttons & (1<checkHit(button, PU_DRAG, pu_mouse_x - pu_mouse_offset_x, + pu_mouse_y - pu_mouse_offset_y) ) + return TRUE ; + else + return FALSE ; + } + + int return_value = puGetBaseLiveInterface () -> checkHit ( button, + PU_DRAG, pu_mouse_x, pu_mouse_y ) ; + + puCleanUpJunk () ; + + return return_value ; +} + +void puMoveToLast (puObject *ob) +{ + puGroup *parent = ob -> getParent () ; + + /* If no parent interface, return. */ + + if ( ! parent ) return; + + /* Remove "ob" from present place in the "dlist" list */ + + parent -> remove (ob) ; + + /* Place at the end of the list */ + + parent -> add (ob) ; + + /* + Now repeat the process for the parent interface so that the interface will + be drawn last of all interfaces. + */ + + puMoveToLast ( parent ); +} + +void puDeactivateWidget ( void ) { active_widget = NULL ; } +void puSetActiveWidget ( puObject *w, int x, int y ) +{ + active_widget = w ; + pu_mouse_offset_x = pu_mouse_x - x ; + pu_mouse_offset_y = pu_mouse_y - y ; +} + +puObject *puActiveWidget ( void ) { return active_widget ; } + +void puSetPasteBuffer ( const char *ch ) +{ + delete [] input_paste_buffer ; + input_paste_buffer = ulStrDup ( ch ) ; +} + +char *puGetPasteBuffer ( void ) { return input_paste_buffer ; } + +int puNeedRefresh ( void ) { return puRefresh ; } +void puPostRefresh ( void ) { puRefresh = TRUE ; } + + +void puSetResizeMode ( int mode ) +{ + static int last = 0; + if ( last == 0 && mode != 0 ) + ulSetError ( UL_WARNING, "puSetResizeMode is deprecated!" ) ; + last = mode ; +} diff --git a/src/pui/pu.h b/src/pui/pu.h new file mode 100644 index 0000000..ba81a9e --- /dev/null +++ b/src/pui/pu.h @@ -0,0 +1,1615 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pu.h 2160 2010-02-27 03:48:23Z fayjf $ +*/ + +#ifndef _PU_H_ +#define _PU_H_ 1 + +#include +#include "fnt.h" +#include "ulRTTI.h" + +/* + Configuration +*/ + +#define PU_NOBUTTON -1 +#define PU_LEFT_BUTTON 0 +#define PU_MIDDLE_BUTTON 1 +#define PU_RIGHT_BUTTON 2 +#define PU_SCROLL_UP_BUTTON 3 +#define PU_SCROLL_DOWN_BUTTON 4 +#define PU_DOWN 0 +#define PU_UP 1 + +class puFont +{ +protected: + fntFont * fnt_font_handle ; + float pointsize ; + float slant ; + +public: + + puFont () ; + + puFont ( fntFont *tfh, float ps = 13, float sl = 0 ) + { + initialize ( tfh, ps, sl ) ; + } + + void initialize ( fntFont *tfh, float ps, float sl = 0 ) + { + fnt_font_handle = tfh ; + pointsize = ps ; + slant = sl ; + } + + float getPointSize ( ) const { return pointsize; } + + int getStringDescender ( void ) const ; + int getStringHeight ( const char *str ) const ; + int getStringHeight ( void ) const { return getStringHeight ( "" ) ; } + + float getFloatStringWidth ( const char *str ) const ; + int getStringWidth ( const char *str ) const /* Deprecated ? */ + { + return (int) getFloatStringWidth ( str ) ; + } + + void drawString ( const char *str, int x, int y ) ; +} ; + + +extern puFont PUFONT_8_BY_13 ; +extern puFont PUFONT_9_BY_15 ; +extern puFont PUFONT_TIMES_ROMAN_10 ; +extern puFont PUFONT_TIMES_ROMAN_24 ; +extern puFont PUFONT_HELVETICA_10 ; +extern puFont PUFONT_HELVETICA_12 ; +extern puFont PUFONT_HELVETICA_18 ; + +#define PU_UP_AND_DOWN 254 +#define PU_DRAG 255 +#define PU_CONTINUAL PU_DRAG + +/* + WARNING: These have to be the same as PW_KEY_whatever and also + the same as (GLUT_KEY_whatever+256) +*/ + +#define PU_KEY_GLUT_SPECIAL_OFFSET 256 + +#define PU_KEY_F1 (1 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F2 (2 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F3 (3 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F4 (4 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F5 (5 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F6 (6 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F7 (7 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F8 (8 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F9 (9 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F10 (10 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F11 (11 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_F12 (12 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_LEFT (100 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_UP (101 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_RIGHT (102 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_DOWN (103 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_PAGE_UP (104 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_PAGE_DOWN (105 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_HOME (106 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_END (107 + PU_KEY_GLUT_SPECIAL_OFFSET) +#define PU_KEY_INSERT (108 + PU_KEY_GLUT_SPECIAL_OFFSET) + +#define PUARROW_UP 0 +#define PUARROW_DOWN 1 +#define PUARROW_FASTUP 2 +#define PUARROW_FASTDOWN 3 +#define PUARROW_LEFT 4 +#define PUARROW_RIGHT 5 +#define PUARROW_FASTLEFT 6 +#define PUARROW_FASTRIGHT 7 + +#define PUBUTTON_NORMAL 0 +#define PUBUTTON_RADIO 1 +#define PUBUTTON_CIRCLE 2 +#define PUBUTTON_VCHECK 3 /* v-shaped checkmark */ +#define PUBUTTON_XCHECK 4 /* X checkmark */ + +/* Rational Definitions of PUI Legend and Label Places */ +#define PUPLACE_TOP_LEFT 0 +#define PUPLACE_TOP_CENTERED 1 +#define PUPLACE_TOP_RIGHT 2 + +#define PUPLACE_CENTERED_LEFT 3 +#define PUPLACE_CENTERED_RIGHT 4 + +#define PUPLACE_BOTTOM_LEFT 5 +#define PUPLACE_BOTTOM_CENTERED 6 +#define PUPLACE_BOTTOM_RIGHT 7 + +/* Additional definitions for PUI Legend places */ +#define PUPLACE_CENTERED_CENTERED 8 + +/* Additional definitions for PUI Label places */ +#define PUPLACE_ABOVE_LEFT 9 +#define PUPLACE_ABOVE_RIGHT 10 + +#define PUPLACE_BELOW_LEFT 11 +#define PUPLACE_BELOW_RIGHT 12 + +#define PUPLACE_UPPER_LEFT 13 +#define PUPLACE_UPPER_RIGHT 14 + +#define PUPLACE_LOWER_LEFT 15 +#define PUPLACE_LOWER_RIGHT 16 + +/* Default places */ +#define PUPLACE_LABEL_DEFAULT PUPLACE_LOWER_RIGHT +#define PUPLACE_LEGEND_DEFAULT PUPLACE_CENTERED_CENTERED + +/* Keep these for backwards compatibility but deprecate them */ +#define PUPLACE_ABOVE PUPLACE_TOP_LEFT +#define PUPLACE_BELOW PUPLACE_BOTTOM_LEFT +#define PUPLACE_LEFT PUPLACE_LOWER_LEFT +#define PUPLACE_RIGHT PUPLACE_LOWER_RIGHT +#define PUPLACE_CENTERED PUPLACE_CENTERED_CENTERED +#define PUPLACE_TOP_CENTER PUPLACE_TOP_CENTERED +#define PUPLACE_BOTTOM_CENTER PUPLACE_BOTTOM_CENTERED +#define PUPLACE_LEFT_CENTER PUPLACE_CENTERED_LEFT +#define PUPLACE_RIGHT_CENTER PUPLACE_CENTERED_RIGHT + +#define PUPLACE_DEFAULT PUPLACE_LABEL_DEFAULT + +#define PUCOL_FOREGROUND 0 +#define PUCOL_BACKGROUND 1 +#define PUCOL_HIGHLIGHT 2 +#define PUCOL_LABEL 3 +#define PUCOL_LEGEND 4 +#define PUCOL_MISC 5 +#define PUCOL_EDITFIELD 6 +#define PUCOL_MAX 7 + +#define PUSLIDER_CLICK 0 +#define PUSLIDER_ALWAYS 1 +#define PUSLIDER_DELTA 2 + +/* These styles may be negated to get 'highlighted' graphics */ + +#define PUSTYLE_DEFAULT PUSTYLE_SHADED +#define PUSTYLE_NONE 0 +#define PUSTYLE_PLAIN 1 +#define PUSTYLE_BEVELLED 2 +#define PUSTYLE_BOXED 3 +#define PUSTYLE_DROPSHADOW 4 +#define PUSTYLE_SPECIAL_UNDERLINED 5 +#define PUSTYLE_SMALL_BEVELLED 6 +#define PUSTYLE_RADIO 7 /* deprecated ! */ +#define PUSTYLE_SHADED 8 +#define PUSTYLE_SMALL_SHADED 9 +#define PUSTYLE_MAX 10 + +/* These are the gaps that we try to leave around text objects */ + +#define PUSTR_TGAP 5 +#define PUSTR_BGAP 5 +#define PUSTR_LGAP 5 +#define PUSTR_RGAP 5 + +#define PU_RADIO_BUTTON_SIZE 16 + +/* When to deactivate a widget and call its down callback */ +#define PUDEACTIVATE_ON_MOUSE_CLICK 0 +#define PUDEACTIVATE_ON_NEXT_WIDGET_ACTIVATION 1 + +extern int puRefresh ; /* Should not be used directly by applications any + longer. Instead, use puPostRefresh () and + puNeedRefresh (). */ + +#define PUCLASS_VALUE 0x00000001 +#define PUCLASS_OBJECT 0x00000002 +#define PUCLASS_GROUP 0x00000004 +#define PUCLASS_INTERFACE 0x00000008 +#define PUCLASS_FRAME 0x00000010 +#define PUCLASS_TEXT 0x00000020 +#define PUCLASS_BUTTON 0x00000040 +#define PUCLASS_ONESHOT 0x00000080 +#define PUCLASS_POPUP 0x00000100 +#define PUCLASS_POPUPMENU 0x00000200 +#define PUCLASS_MENUBAR 0x00000400 +#define PUCLASS_INPUT 0x00000800 +#define PUCLASS_BUTTONBOX 0x00001000 +#define PUCLASS_SLIDER 0x00002000 +#define PUCLASS_DIALOGBOX 0x00004000 +#define PUCLASS_ARROW 0x00008000 +#define PUCLASS_LISTBOX 0x00010000 +#define PUCLASS_DIAL 0x00020000 + +class puValue ; +class puObject ; +class puGroup ; +class puInterface ; +class puButtonBox ; +class puFrame ; +class puText ; +class puButton ; +class puOneShot ; +class puPopup ; +class puPopupMenu ; +class puMenuBar ; +class puInput ; +class puSlider ; +class puListBox ; +class puArrowButton ; +class puDial ; + +// Global function to move active object to the end of the "dlist" +// so it is displayed in front of everything else + +void puMoveToLast ( puObject *ob ) ; + +typedef float puColour [ 4 ] ; /* RGBA */ +typedef puColour puColor ; + +struct puBox +{ + int min [ 2 ] ; + int max [ 2 ] ; + + void draw ( int dx, int dy, int style, puColour colour[], int am_default, int border ) ; + void extend ( puBox *bx ) ; + + void empty ( void ) { min[0]=min[1]=1000000 ; max[0]=max[1]=-1000000 ; } + int isEmpty ( void ) const { return min[0]>max[0] || min[1]>max[1] ; } +} ; + +#define PUSTRING_MAX 256 + +/* With many memory managers, allocating powers of two is more efficient */ +#define PUSTRING_INITIAL 64 + + +inline void puSetColour ( puColour dst, const puColour src ) +{ + dst[0] = src[0] ; dst[1] = src[1] ; dst[2] = src[2] ; dst[3] = src[3] ; +} +inline void puSetColor ( puColour dst, const puColour src ) +{ + dst[0] = src[0] ; dst[1] = src[1] ; dst[2] = src[2] ; dst[3] = src[3] ; +} + +inline void puSetColour ( puColour c, float r, float g, float b, float a = 1.0f ) +{ + c [ 0 ] = r ; c [ 1 ] = g ; c [ 2 ] = b ; c [ 3 ] = a ; +} +inline void puSetColor ( puColour c, float r, float g, float b, float a = 1.0f ) +{ + c [ 0 ] = r ; c [ 1 ] = g ; c [ 2 ] = b ; c [ 3 ] = a ; +} + + +// puInit () -- is a macro, see below +void puRealInit ( void ) ; +void puExit ( void ) ; +void puDisplay ( void ) ; +void puDisplay ( int window_number ) ; /* Deprecated */ +int puMouse ( int button, int updown, int x, int y ) ; +int puMouse ( int x, int y ) ; +int puKeyboard ( int key, int updown, int x, int y ) ; /* For PW */ +int puKeyboard ( int key, int updown ) ; +void puHideCursor ( void ) ; +void puShowCursor ( void ) ; +int puCursorIsHidden ( void ) ; +void puDeleteObject ( puObject *ob ) ; + +int puNeedRefresh ( void ) ; +void puPostRefresh ( void ) ; + +int puGetWindow ( void ) ; +void puGetWindowSize ( int *width, int *height ) ; +int puGetWindowWidth ( void ) ; +int puGetWindowHeight ( void ) ; + +void puSetWindow ( int w ) ; // don't use it! +void puSetWindowSize ( int width, int height ) ; // don't use it! + +void puSetResizeMode ( int mode ) ; // DEPRECATED + + +// Active widget functions + +void puDeactivateWidget ( void ) ; +void puSetActiveWidget ( puObject *w, int x, int y ) ; +puObject *puActiveWidget ( void ) ; + +// Return the currently active mouse button +extern int puGetPressedButton () ; + + + +class puValue +{ + UL_TYPE_DATA + +protected: + int type ; + + int integer ; + float floater ; + char *string ; + bool boolean ; + + int *res_integer ; + float *res_floater ; + char *res_string ; + bool *res_bool ; + + int string_size ; + int res_string_sz ; + + int convert ; + + void re_eval ( void ) ; + void update_res ( void ) const { } /* Obsolete ! */ ; + + void copy_stringval ( const char *str ) ; + + int * getIntegerp ( void ) { return res_integer != NULL ? res_integer : &integer ; } + float * getFloaterp ( void ) { return res_floater != NULL ? res_floater : &floater ; } + char * getStringp ( void ) { return res_string != NULL ? res_string : string ; } + bool * getBooleanp ( void ) { return res_bool != NULL ? res_bool : &boolean ; } + + void enableConversion ( void ) { convert = TRUE ; } + void disableConversion ( void ) { convert = FALSE ; } + int conversionEnabled ( void ) const { return convert ; } + +public: + puValue () + { + convert = TRUE ; + + string_size = PUSTRING_INITIAL ; + string = new char [ string_size ] ; + + type = PUCLASS_VALUE ; + res_integer = NULL ; + res_floater = NULL ; + res_string = NULL ; + res_bool = NULL ; + clrValue () ; + } + + virtual ~puValue () { delete [] string ; } + + int getType ( void ) const { return type ; } + const char *getTypeString ( void ) const ; + void clrValue ( void ) { setValue ( "" ) ; } + + virtual void setValue ( puValue *pv ) + { + *getIntegerp () = pv -> getIntegerValue () ; + *getFloaterp () = pv -> getFloatValue () ; + copy_stringval ( pv -> getStringValue () ) ; + *getBooleanp () = pv -> getBooleanValue () ; + puPostRefresh () ; + } + + void setValuator ( int *i ) + { + res_integer = i ; + + if ( convert == TRUE ) + { + res_floater = NULL ; res_string = NULL ; res_bool = NULL ; + re_eval () ; + } + } + + void setValuator ( float *f ) + { + res_floater = f ; + + if ( convert == TRUE ) + { + res_integer = NULL ; res_string = NULL ; res_bool = NULL ; + re_eval () ; + } + } + + void setValuator ( char *s, int size ) + { + res_string = s ; + res_string_sz = size ; + + if ( convert == TRUE ) + { + res_integer = NULL ; res_floater = NULL ; res_bool = NULL ; + re_eval () ; + } + } + + void setValuator ( bool *b ) + { + res_bool = b ; + + if ( convert == TRUE ) + { + res_integer = NULL ; res_floater = NULL ; res_string = NULL ; + re_eval () ; + } + } + + /* Obsolete ! */ + void setValuator ( char *s ) { setValuator ( s, PUSTRING_MAX ) ; } + + virtual void setValue ( int i ) + { + *getIntegerp () = i ; + + if ( convert == TRUE ) + { + *getFloaterp () = (float) i ; sprintf ( getStringp (), "%d", i ) ; *getBooleanp () = ( i != 0 ) ; + } + + puPostRefresh () ; + } + + virtual void setValue ( float f ) + { + *getFloaterp () = f ; + + if ( convert == TRUE ) + { + *getIntegerp () = (int) f ; sprintf ( getStringp (), "%g", f ) ; *getBooleanp () = ( f != 0.0 ) ; + } + + puPostRefresh () ; + } + + virtual void setValue ( const char *s ) ; + + virtual void setValue ( bool b ) + { + *getBooleanp () = b ; + + if ( convert == TRUE ) + { + *getIntegerp () = b ? 1 : 0 ; *getFloaterp () = b ? 1.0f : 0.0f ; sprintf ( getStringp (), "%s", b ? "1" : "0" ) ; + } + + puPostRefresh () ; + } + + void getValue ( int *i ) { re_eval () ; *i = *getIntegerp () ; } + void getValue ( float *f ) { re_eval () ; *f = *getFloaterp () ; } + void getValue ( char **s ) { re_eval () ; *s = getStringp () ; } + void getValue ( char *s, int size ) + { + re_eval () ; + + /* Work around ANSI strncpy's null-fill behaviour */ + + s[0] = '\0' ; + strncat ( s, getStringp (), size-1 ) ; + } + + void getValue ( char *s ) { getValue ( s, PUSTRING_MAX ) ; } /* Obsolete ! */ + void getValue ( bool *b ) { re_eval () ; *b = *getBooleanp () ; } + + int getValue ( void ) { return getIntegerValue () ; } /* Obsolete ! */ + + virtual int getIntegerValue ( void ) { re_eval () ; return *getIntegerp () ; } + virtual float getFloatValue ( void ) { re_eval () ; return *getFloaterp () ; } + virtual char getCharValue ( void ) { re_eval () ; return getStringp ()[0]; } + virtual char *getStringValue ( void ) { re_eval () ; return getStringp () ; } + virtual bool getBooleanValue ( void ) { re_eval () ; return *getBooleanp () ; } + + /* RTTI */ + ulRTTItypeid getTypeInfo ( void ) const { return RTTI_vinfo () ; } +} ; + +typedef void (*puCallback)(class puObject *) ; +typedef void (*puRenderCallback)(class puObject *, int dx, int dy, void *) ; + +void puSetDefaultStyle ( int style ) ; +int puGetDefaultStyle ( void ) ; +void puSetDefaultBorderThickness ( int t ) ; +int puGetDefaultBorderThickness ( void ) ; +void puSetDefaultFonts ( puFont legendFont, puFont labelFont ) ; +void puGetDefaultFonts ( puFont *legendFont, puFont *labelFont ) ; + +puFont puGetDefaultLabelFont ( void ) ; +puFont puGetDefaultLegendFont ( void ) ; + +void puSetDefaultColourScheme ( float r, float g, float b, float a = 1.0f ) ; +inline void puSetDefaultColorScheme ( float r, float g, float b, float a = 1.0f ) +{ + puSetDefaultColourScheme ( r, g, b, a ) ; +} + +void puGetDefaultColourScheme ( float *r, float *g, float *b, float *a = NULL ); +inline void puGetDefaultColorScheme ( float *r, float *g, float *b, float *a = NULL ) +{ + puGetDefaultColourScheme ( r, g, b, a ) ; +} + +class puObject : public puValue +{ + UL_TYPE_DATA + +protected: + puValue default_value ; + + puBox bbox ; /* Bounding box of entire Object */ + puBox abox ; /* Active (clickable) area */ + puColour colour [ PUCOL_MAX ] ; + puGroup *parent ; + + int active_mouse_edge ; /* is it PU_UP or PU_DOWN (or both) that activates this? */ + int active_mouse_button ; /* which mouse button or buttons activate this */ + int style ; + int visible ; + int active ; + int highlighted ; + int am_default ; + int window ; /* Which window does the object appear in? */ + int v_status ; /* 1 if the Object should lock in the top left corner, 0 if not */ + + const char *label ; puFont labelFont ; int labelPlace ; + const char *legend ; puFont legendFont ; int legendPlace ; + + void *user_data ; + puCallback cb ; + puCallback active_cb ; + puCallback down_cb ; + puRenderCallback r_cb ; + void *render_data ; + int border_thickness ; + + short when_to_deactivate ; /* On next mouseclick or on next widget activation */ + + virtual void draw_legend ( int dx, int dy ) ; + virtual void draw_label ( int dx, int dy ) ; + +public: + virtual int isHit ( int x, int y ) const { return isVisible() && isActive() && + x > abox.min[0] && + x < abox.max[0] && + y > abox.min[1] && + y < abox.max[1] && + window == puGetWindow () ; } + + virtual void doHit ( int button, int updown, int x, int y ) ; + + puObject ( int minx, int miny, int maxx, int maxy ) ; + ~puObject () ; + + puObject *next ; /* Should not be used directly by applications any longer. */ + puObject *prev ; /* Instead, use the setNextObject and setPrevObject + methods. */ + + puBox *getBBox ( void ) const { return (puBox *) &bbox ; } + puBox *getABox ( void ) const { return (puBox *) &abox ; } + + void getAbsolutePosition ( int *x, int *y ) const ; + + virtual void setPosition ( int x, int y ) + { + if ( abox.isEmpty() ) + { + abox.max[0] = abox.min[0] = x ; + abox.max[1] = abox.min[1] = y ; + } + else + { + abox.max[0] += x - abox.min[0] ; + abox.max[1] += y - abox.min[1] ; + abox.min[0] = x ; + abox.min[1] = y ; + } + recalc_bbox() ; puPostRefresh () ; + } + + virtual void setSize ( int w, int h ) + { + abox.max[0] = abox.min[0] + w ; + abox.max[1] = abox.min[1] + h ; + recalc_bbox() ; puPostRefresh () ; + } + + void getPosition ( int *x, int *y ) const + { + if ( abox.isEmpty () ) + { + if ( x ) *x = 0 ; + if ( y ) *y = 0 ; + } + else + { + if ( x ) *x = abox.min[0] ; + if ( y ) *y = abox.min[1] ; + } + } + + void getSize ( int *w, int *h ) const + { + if ( abox.isEmpty () ) + { + if ( w ) *w = 0 ; + if ( h ) *h = 0 ; + } + else + { + if ( w ) *w = abox.max[0] - abox.min[0] ; + if ( h ) *h = abox.max[1] - abox.min[1] ; + } + } + + virtual void recalc_bbox ( void ) ; + virtual int checkHit ( int button, int updown, int x, int y ) ; + virtual int checkKey ( int key , int updown ) ; + virtual void draw ( int dx, int dy ) = 0 ; + + puGroup *getParent ( void ) const { return parent ; } + void setParent ( puGroup* p ) { parent = p ; } + + void setNextObject ( puObject *obj ) { next = obj ; } + puObject *getNextObject ( void ) const { return next ; } + void setPrevObject ( puObject *obj ) { prev = obj ; } + puObject *getPrevObject ( void ) const { return prev ; } + + void setCallback ( puCallback c ) { cb = c ; } + puCallback getCallback ( void ) const { return cb ; } + void invokeCallback ( void ) { if ( cb != NULL ) (*cb)(this) ; } + + void setActiveCallback ( puCallback c ) { active_cb = c ; } + puCallback getActiveCallback ( void ) const { return active_cb ; } + void invokeActiveCallback ( void ) { if ( active_cb != NULL ) (*active_cb)(this) ; } + + void setDownCallback ( puCallback c ) { down_cb = c ; } + puCallback getDownCallback ( void ) const { return down_cb ; } + virtual void invokeDownCallback ( void ) { if ( down_cb != NULL ) (*down_cb)(this) ; } + + void setRenderCallback ( puRenderCallback c, void *d = NULL ) { r_cb = c ; render_data = d ; } + puRenderCallback getRenderCallback ( void ) const { return r_cb ; } + void *getRenderCallbackData ( void ) const { return render_data ; } + void invokeRenderCallback ( int dx, int dy ) { if ( r_cb != NULL ) (*r_cb)(this, dx, dy, render_data) ; } + + void setBorderThickness ( int t ) { border_thickness = t ; puPostRefresh () ; } + int getBorderThickness ( void ) const { return border_thickness ; } + + void makeReturnDefault ( int def ) { am_default = def ; puPostRefresh () ; } + int isReturnDefault ( void ) const { return am_default ; } + + int getWindow ( void ) const { return window ; } + void setWindow ( int w ) { window = w ; puPostRefresh () ; } + + void setActiveDirn ( int e ) { active_mouse_edge = e ; } + int getActiveDirn ( void ) const { return active_mouse_edge ; } + + void setActiveButton ( int b ) { active_mouse_button = b ; } + int getActiveButton ( void ) const { return active_mouse_button ; } + + void setWhenToDeactivate ( short d ) { when_to_deactivate = d ; } + short getWhenToDeactivate ( void ) const { return when_to_deactivate ; } + + void setLegend ( const char *l ) { legend = l ; recalc_bbox() ; puPostRefresh () ; } + const char *getLegend ( void ) const { return legend ; } + + void setLegendFont ( puFont f ) { legendFont = f ; recalc_bbox() ; puPostRefresh () ; } + puFont getLegendFont ( void ) const { return legendFont ; } + + void setLegendPlace ( int lp ) { legendPlace = lp ; recalc_bbox() ; puPostRefresh () ; } + int getLegendPlace ( void ) const { return legendPlace ; } + + void setLabel ( const char *l ) { label = l ; recalc_bbox() ; puPostRefresh () ; } + const char *getLabel ( void ) const { return label ; } + + void setLabelFont ( puFont f ) { labelFont = f ; recalc_bbox() ; puPostRefresh () ; } + puFont getLabelFont ( void ) const { return labelFont ; } + + void setLabelPlace ( int lp ) { labelPlace = lp ; recalc_bbox() ; puPostRefresh () ; } + int getLabelPlace ( void ) const { return labelPlace ; } + + void activate ( void ) { if ( ! active ) { active = TRUE ; puPostRefresh () ; } } + void greyOut ( void ) { if ( active ) { active = FALSE ; puPostRefresh () ; } } + int isActive ( void ) const { return active ; } + + void highlight ( void ) { if ( ! highlighted ) { highlighted = TRUE ; puPostRefresh () ; } } + void lowlight ( void ) { if ( highlighted ) { highlighted = FALSE ; puPostRefresh () ; } } + int isHighlighted( void ) const { return highlighted ; } + + void reveal ( void ) { if ( ! visible ) { visible = TRUE ; puPostRefresh () ; } } + void hide ( void ) { if ( visible ) { visible = FALSE ; puPostRefresh () ; } } + int isVisible ( void ) const { return visible ; } + + void setStyle ( int which ) + { + style = which ; + + switch ( abs(style) ) + { + case PUSTYLE_SPECIAL_UNDERLINED : + border_thickness = 1 ; + break ; + + case PUSTYLE_SMALL_BEVELLED : + case PUSTYLE_SMALL_SHADED : + case PUSTYLE_BOXED : + border_thickness = 2 ; + break ; + + case PUSTYLE_BEVELLED : + case PUSTYLE_SHADED : + case PUSTYLE_DROPSHADOW : + border_thickness = 5 ; + break ; + } + + recalc_bbox () ; + puPostRefresh () ; + } + + int getStyle ( void ) const { return style ; } + + virtual void setColourScheme ( float r, float g, float b, float a = 1.0f ) ; + void setColorScheme ( float r, float g, float b, float a = 1.0f ) + { + setColourScheme ( r, g, b, a ) ; + } + + virtual void setColour ( int which, float r, float g, float b, float a = 1.0f ) + { + puSetColour ( colour [ which ], r, g, b, a ) ; + puPostRefresh () ; + } + void setColor ( int which, float r, float g, float b, float a = 1.0f ) + { + setColour ( which, r, g, b, a ) ; + } + + void getColour ( int which, float *r, float *g, float *b, float *a = NULL ) const + { + if ( r ) *r = colour[which][0] ; + if ( g ) *g = colour[which][1] ; + if ( b ) *b = colour[which][2] ; + if ( a ) *a = colour[which][3] ; + } + void getColor ( int which, float *r, float *g, float *b, float *a = NULL ) const + { + getColour ( which, r, g, b, a ); + } + + void setUserData ( void *data ) { user_data = data ; } + void *getUserData ( void ) const { return user_data ; } + + void defaultValue ( void ) { setValue ( & default_value ) ; } + + void setDefaultValue ( int i ) { default_value.setValue ( i ) ; } + void setDefaultValue ( float f ) { default_value.setValue ( f ) ; } + void setDefaultValue ( const char *s ) { default_value.setValue ( s ) ; } + + void getDefaultValue ( int *i ) { default_value.getValue ( i ) ; } + void getDefaultValue ( float *f ) { default_value.getValue ( f ) ; } + void getDefaultValue ( char **s ) { default_value.getValue ( s ) ; } + void getDefaultValue ( char *s ) { default_value.getValue ( s ) ; } + + int getDefaultValue ( void ) { return default_value.getValue () ; } /* Obsolete ! */ + + int getDefaultIntegerValue ( void ) { return default_value.getIntegerValue () ; } + float getDefaultFloatValue ( void ) { return default_value.getFloatValue () ; } + char *getDefaultStringValue ( void ) { return default_value.getStringValue () ; } + + int getVStatus ( void ) const { return v_status ; } /* JCJ 6 Jun 2002 */ + void setVStatus ( int vstat ) { v_status = vstat ; } +} ; + +/* + The 'live' interface stack is used for clicking and rendering. +*/ + +void puPushLiveInterface ( puInterface *in ) ; +void puPopLiveInterface ( puInterface *in = 0 ) ; +int puNoLiveInterface ( void ) ; +puInterface *puGetBaseLiveInterface ( void ) ; +puInterface *puGetUltimateLiveInterface ( void ) ; + +/* + The regular group stack is used for adding widgets +*/ + +void puPushGroup ( puGroup *in ) ; +void puPopGroup ( void ) ; +int puNoGroup ( void ) ; +puGroup *puGetCurrGroup ( void ) ; + +class puGroup : public puObject +{ + UL_TYPE_DATA + +protected: + int num_children ; + puObject *dlist ; + + int mouse_x ; // Coordinates of mouse when right button pressed for + int mouse_y ; // drag and drop + + int mouse_active; // Flag telling whether interface is presently being dragged + + void doHit ( int button, int updown, int x, int y ) ; + + int floating; // DEPRECATED! -- Flag telling whether the interface floats in the window or stays put + +public: + + puGroup ( int x, int y ) : puObject ( x, y, x, y ) + { + type |= PUCLASS_GROUP ; + dlist = NULL ; + num_children = 0 ; + mouse_x = 0 ; + mouse_y = 0 ; + mouse_active = FALSE ; + floating = FALSE ; // DEPRECATED! + puPushGroup ( this ) ; + } + + ~puGroup () ; + + void recalc_bbox ( void ) ; + virtual void add ( puObject *new_object ) ; + virtual void remove ( puObject *old_object ) ; + virtual void empty ( void ) ; + + void draw ( int dx, int dy ) ; + int checkHit ( int button, int updown, int x, int y ) ; + int checkKey ( int key , int updown ) ; + + puObject *getFirstChild ( void ) const { return dlist ; } + puObject *getLastChild ( void ) const + { + puObject *bo = dlist ; + + if ( bo != NULL ) + { + while ( bo -> getNextObject() != NULL ) + bo = bo -> getNextObject() ; + } + + return bo ; + } + int getNumChildren ( void ) const { return num_children ; } + + virtual void close ( void ) + { + if ( puGetCurrGroup () != this ) + ulSetError ( UL_WARNING, "PUI: puGroup::close() is mismatched!" ) ; + else + puPopGroup () ; + } + + void setFloating ( int value ) { floating = value ; } // DEPRECATED! + int getFloating ( void ) const { return floating ; } // DEPRECATED! + + void setChildStyle ( int childs, int which, int recursive = FALSE ) ; + void setChildBorderThickness ( int childs, int t, int recursive = FALSE ) ; + + void setChildColour ( int childs, int which, + float r, float g, float b, float a = 1.0f, + int recursive = FALSE ) ; + void setChildColor ( int childs, int which, + float r, float g, float b, float a = 1.0f, + int recursive = FALSE ) + { + setChildColour ( childs, which, r, g, b, a, recursive ) ; + } + + void setChildColourScheme ( int childs, + float r, float g, float b, float a = 1.0f, + int recursive = FALSE ) ; + void setChildColorScheme ( int childs, + float r, float g, float b, float a = 1.0f, + int recursive = FALSE ) + { + setChildColourScheme ( childs, r, g, b, a, recursive ) ; + } + + void setChildLegendFont ( int childs, puFont f, int recursive = FALSE ) ; + void setChildLabelFont ( int childs, puFont f, int recursive = FALSE ) ; +} ; + + +class puInterface : public puGroup +{ + UL_TYPE_DATA + +public: + + puInterface ( int x, int y ) : puGroup ( x, y ) + { + type |= PUCLASS_INTERFACE ; + puPushLiveInterface ( this ) ; + } + + ~puInterface () ; +} ; + + +class puFrame : public puObject +{ + UL_TYPE_DATA + +public: + void draw ( int dx, int dy ) ; + puFrame ( int minx, int miny, int maxx, int maxy ) : + puObject ( minx, miny, maxx, maxy ) + { + type |= PUCLASS_FRAME ; + } + + void doHit ( int button, int updown, int x, int y ) + { + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + // Active widget exists and is not this one; call its down callback if it exists + + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( isHit ( x, y ) && ( updown != PU_DRAG ) ) + puMoveToLast ( this -> parent ); + } +} ; + + + +class puText : public puObject +{ + UL_TYPE_DATA + +public: + virtual int isHit ( int /* x */, int /* y */ ) const { return FALSE ; } + void draw ( int dx, int dy ) ; + puText ( int x, int y ) : puObject ( x, y, x, y ) + { + type |= PUCLASS_TEXT ; + } +} ; + + +class puButton : public puObject +{ + UL_TYPE_DATA + +protected: + int button_type ; + +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + + int getButtonType ( void ) const { return button_type ; } + void setButtonType ( int btype ) { button_type = btype ; puPostRefresh () ; } + + puButton ( int minx, int miny, const char *l ) : + puObject ( minx, miny, + minx + puGetDefaultLegendFont().getStringWidth ( l ) + PUSTR_LGAP + PUSTR_RGAP, + miny + puGetDefaultLegendFont().getStringHeight ( l ) + puGetDefaultLegendFont().getStringDescender () + PUSTR_TGAP + PUSTR_BGAP ) + { + type |= PUCLASS_BUTTON ; + button_type = PUBUTTON_NORMAL ; + setLegend ( l ) ; + } + + puButton ( int minx, int miny, int maxx, int maxy, int btype = PUBUTTON_NORMAL ) : + puObject ( minx, miny, maxx, maxy ) + { + type |= PUCLASS_BUTTON ; + button_type = btype ; + } +} ; + + +class puOneShot : public puButton +{ + UL_TYPE_DATA + +protected: +public: + void doHit ( int button, int updown, int x, int y ) ; + + puOneShot ( int minx, int miny, const char *l ) : puButton ( minx, miny, l ) + { + type |= PUCLASS_ONESHOT ; + } + + puOneShot ( int minx, int miny, int maxx, int maxy ) : + puButton ( minx, miny, maxx, maxy ) + { + type |= PUCLASS_ONESHOT ; + } +} ; + + + +class puArrowButton : public puOneShot +{ + UL_TYPE_DATA + +protected: + int arrow_type ; + +public: + void draw ( int dx, int dy ) ; + + int getArrowType ( void ) const { return arrow_type ; } + void setArrowType ( int i ) { arrow_type = i ; puPostRefresh () ; } + + puArrowButton ( int minx, int miny, int maxx, int maxy, int ptype ) : + puOneShot ( minx, miny, maxx, maxy ) + { + type |= PUCLASS_ARROW ; + arrow_type = ptype ; + } +} ; + +class puRange +{ + UL_TYPE_DATA + +protected: + float minimum_value ; + float maximum_value ; + float step_size ; + float last_cb_value ; + float cb_delta ; + int cb_mode ; + + void puRange_init ( float minval, float maxval, float step ) + { + if ( maxval == minval ) + { + maxval = 1.0f ; + minval = 0.0f ; + } + + minimum_value = minval ; + maximum_value = maxval ; + step_size = step ; + last_cb_value = -1.0f ; + cb_delta = 0.1f ; + cb_mode = PUSLIDER_ALWAYS ; + } + + float clamp ( float f ) + { + if ( f < minimum_value ) return minimum_value ; + else if ( f > maximum_value ) return maximum_value ; + return f ; + } + +public: + puRange () + { + puRange_init ( 0.0f, 1.0f, 0.0f ) ; + } + + puRange ( float minval, float maxval, float step = 0.0f ) + { + puRange_init ( minval, maxval, step ) ; + } + + virtual ~puRange() + { + } + + float getMaxValue ( void ) const { return maximum_value ; } + virtual void setMaxValue ( float f ) { maximum_value = f ; } + + float getMinValue ( void ) const { return minimum_value ; } + virtual void setMinValue ( float f ) { minimum_value = f ; } + + float getStepSize ( void ) const { return step_size ; } + void setStepSize ( float f ) { step_size = f ; } + + float checkStep ( float val_to_check ) const + { + float step = val_to_check ; + + if ( getStepSize () > 0.0f ) + { + step = val_to_check - (float) fmod ( val_to_check, getStepSize () ) ; + + if ( ( val_to_check - step ) > ( step + getStepSize() - val_to_check ) ) + step += getStepSize () ; + } + + return step ; + } + + void setCBMode ( int m ) { cb_mode = m ; } + int getCBMode ( void ) const { return cb_mode ; } + + void setDelta ( float f ) { cb_delta = (f<=0.0f) ? 0.1f : (f>=1.0f) ? 0.9f : f ; } + float getDelta ( void ) const { return cb_delta ; } +} ; + +class puSlider : public puRange, public puObject +{ + UL_TYPE_DATA + +protected: + int vert ; + int start_offset ; + float slider_fraction ; + float page_step_size ; + void draw_slider_box ( int dx, int dy, const puBox &box, float val, const char *box_label = NULL ) ; + + void puSlider_init ( int vertical ) + { + type |= PUCLASS_SLIDER ; + vert = vertical ; + slider_fraction = 0.1f ; + page_step_size = 0.0f ; + } + +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + puSlider ( int minx, int miny, int sz, int vertical = FALSE ) : + puRange (), + puObject ( minx, miny, vertical ? + ( minx + puGetDefaultLegendFont().getStringWidth ( "W" ) + + PUSTR_LGAP + PUSTR_RGAP ) : + ( minx + sz ), + vertical ? + ( miny + sz ) : + ( miny + puGetDefaultLegendFont().getStringHeight () + + puGetDefaultLegendFont().getStringDescender () + + PUSTR_TGAP + PUSTR_BGAP ) + ) + { + puSlider_init ( vertical ) ; + } + + /* Blake Friesen - alternate constructor which lets you explicitly set width */ + puSlider ( int minx, int miny, int sz, int vertical, int width ) : + puRange (), + puObject ( minx, miny, vertical ? + ( minx + width ) : + ( minx + sz ), + vertical ? + ( miny + sz ) : + ( miny + width ) + ) + { + puSlider_init ( vertical ) ; + } + + int isVertical ( void ) const { return vert ; } + + void setSliderFraction ( float f ) ; + float getSliderFraction ( void ) const { return slider_fraction ; } + void setPageStepSize ( float s ) { page_step_size = s ; } + float getPageStepSize ( void ) const { return page_step_size ; } +} ; + + +class puListBox : public puButton +{ + UL_TYPE_DATA + +protected: + char ** list ; + int num ; + int top ; + +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + puListBox ( int minx, int miny, int maxx, int maxy, char** list = NULL ) ; + + void newList ( char ** _list ) ; + int getNumItems ( void ) const { return num ; } + int getNumVisible ( void ) const + { + int ysize = abox.max[1] - abox.min[1] + 1 ; + int yinc = legendFont.getStringHeight () + PUSTR_BGAP ; + return (ysize - PUSTR_BGAP) / yinc ; + } + + int getTopItem ( void ) const { return top ; } + void setTopItem ( int item_index ) ; +} ; + + +class puDial : public puRange, public puObject +{ + UL_TYPE_DATA + +protected: + int wrap ; // Flag telling whether you can wrap around the bottom of the dial +public: + void doHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; + + puDial ( int minx, int miny, int sz ) : + puRange (), puObject ( minx, miny, minx+sz, miny+sz ) + { + type |= PUCLASS_DIAL ; + setValue ( 0.0f ) ; + wrap = TRUE ; + } + + puDial ( int minx, int miny, int sz, float minval, float maxval, float step = 0.0f ) : + puRange ( minval, maxval, step ), + puObject ( minx, miny, minx+sz, miny+sz ) + { + type |= PUCLASS_DIAL ; + setValue ( 0.0f ) ; + wrap = TRUE ; + } + + void setWrap ( int in ) { wrap = in ; } + int getWrap ( void ) const { return wrap ; } +} ; + + + +class puPopup : public puInterface +{ + UL_TYPE_DATA + +protected: +public: + puPopup ( int x, int y ) : puInterface ( x, y ) + { + type |= PUCLASS_POPUP ; + hide () ; + } +} ; + + +class puPopupMenu : public puPopup +{ + UL_TYPE_DATA + +protected: +public: + puPopupMenu ( int x, int y ) : puPopup ( x, y ) + { + type |= PUCLASS_POPUPMENU ; + } + + puObject *add_item ( const char *str, puCallback _cb, + void *_user_data = NULL ) ; + int checkHit ( int button, int updown, int x, int y ) ; + int checkKey ( int key , int updown ) ; + void close ( void ) ; +} ; + + +class puMenuBar : public puInterface +{ + UL_TYPE_DATA + +protected: + int bar_height ; + +public: + puMenuBar ( int h = -1 ) : puInterface ( 0, 0 ) + { + type |= PUCLASS_MENUBAR ; + + bar_height = h ; + } + + void add_submenu ( const char *str, char *items[], puCallback _cb[], + void *_user_data[] = NULL ) ; + void close ( void ) ; +} ; + + +class puInputBase +{ + UL_TYPE_DATA + +protected: + int accepting ; + int cursor_position ; + int select_start_position ; + int select_end_position ; + char *valid_data ; + int input_disabled ; + + char *displayed_text ; // Pointer to text as it is displayed in the box (chopped or word-wrapped) + + puObject *widget ; /* Pointer to associated input box widget */ + + virtual void normalizeCursors ( void ) ; + virtual void removeSelectRegion ( void ) ; + +public: + int isAcceptingInput ( void ) const { return accepting ; } + void rejectInput ( void ) { accepting = FALSE ; puPostRefresh () ; } + + void acceptInput ( void ) + { + accepting = TRUE ; + cursor_position = strlen ( widget -> getStringValue () ) ; + select_start_position = select_end_position = -1 ; + puPostRefresh () ; + } + + int getCursor ( void ) const { return cursor_position ; } + void setCursor ( int c ) { cursor_position = c ; puPostRefresh () ; } + + virtual void setSelectRegion ( int s, int e ) + { + select_start_position = s ; + select_end_position = e ; + puPostRefresh () ; + } + + void getSelectRegion ( int *s, int *e ) const + { + if ( s ) *s = select_start_position ; + if ( e ) *e = select_end_position ; + } + + char *getValidData ( void ) const { return valid_data ; } + void setValidData ( const char *data ) + { + delete [] valid_data ; + valid_data = ( data != NULL ) ? ulStrDup ( data ) : NULL ; + } + + void addValidData ( const char *data ) ; + + int isValidCharacter ( char c ) const + { + if ( valid_data != NULL ) + return ( strchr ( valid_data, c ) != NULL ) ? 1 : 0 ; + else + return 1 ; + } + + void enableInput ( void ) { input_disabled = FALSE ; } + void disableInput ( void ) { input_disabled = TRUE ; } + int inputDisabled ( void ) const { return input_disabled ; } + + puInputBase ( void ) + { + accepting = FALSE ; + cursor_position = 0 ; + select_start_position = -1 ; + select_end_position = -1 ; + valid_data = NULL; + displayed_text = NULL ; + + widget = (puObject *)NULL ; + + input_disabled = FALSE ; + } + + virtual ~puInputBase () { delete [] valid_data ; delete [] displayed_text ; } +} ; + + +class puInput : public puInputBase, public puObject +{ + UL_TYPE_DATA + + int display_starting_point ; + + char *getDisplayedText ( void ) + { + return ( displayed_text == NULL ? getStringValue () : displayed_text ) ; + } + +public: + void draw ( int dx, int dy ) ; + void doHit ( int button, int updown, int x, int y ) ; + int checkKey ( int key, int updown ) ; + + void invokeDownCallback ( void ) + { + rejectInput () ; + normalizeCursors () ; + if ( down_cb != NULL ) (*down_cb)(this) ; + } + + puInput ( int minx, int miny, int maxx, int maxy ) : + puInputBase (), puObject ( minx, miny, maxx, maxy ) + { + type |= PUCLASS_INPUT ; + + display_starting_point = 0 ; + + setColourScheme ( colour [ PUCOL_EDITFIELD ][0], + colour [ PUCOL_EDITFIELD ][1], + colour [ PUCOL_EDITFIELD ][2], + colour [ PUCOL_EDITFIELD ][3] ) ; + setColour ( PUCOL_MISC, 0.1f, 0.1f, 1.0f ) ; /* Colour of 'I' bar cursor */ + + widget = this ; + } + + virtual void setValue ( puValue *pv ) + { + puValue::setValue ( pv ) ; + delete [] displayed_text ; displayed_text = NULL ; + } + + virtual void setValue ( int i ) + { + puValue::setValue ( i ) ; + delete [] displayed_text ; displayed_text = NULL ; + } + + virtual void setValue ( float f ) + { + puValue::setValue ( f ) ; + delete [] displayed_text ; displayed_text = NULL ; + } + + virtual void setValue ( const char *s ) + { + puValue::setValue ( s ) ; + delete [] displayed_text ; displayed_text = NULL ; + } + + virtual void setValue ( bool b ) + { + puValue::setValue ( b ) ; + delete [] displayed_text ; displayed_text = NULL ; + } +} ; + + +class puButtonBox : public puObject +{ + UL_TYPE_DATA + +protected: + int one_only ; + int num_kids ; + char **button_labels ; + +public: + + puButtonBox ( int minx, int miny, int maxx, int maxy, + char **labels, int one_button ) ; + + int isOneButton ( void ) const { return one_only ; } + + void newList ( char ** _list ) ; + int getNumItems ( void ) const { return num_kids ; } + + int checkKey ( int key , int updown ) ; + int checkHit ( int button, int updown, int x, int y ) ; + void draw ( int dx, int dy ) ; +} ; + + +class puDialogBox : public puPopup +{ + UL_TYPE_DATA + +protected: +public: + + puDialogBox ( int x, int y ) : puPopup ( x, y ) + { + type |= PUCLASS_DIALOGBOX ; + } +} ; + + + +/* + * Window System Integration + * ------------------------- + * + * PUI has direct support for GLUT, PW, FLTK, SDL and GTK. All code is provided + * inline, making PUI itself independent. There are several ways to choose + * implementation, for instance: + * + * #include + * #include + * + * or simply: + * + * #include + * + * Note that both cases may fail if pu.h has been included earlier. + * A safer option is therefore to compile with -DPU_USE_FLTK. + * + * The default system is GLUT for backwards compability. + * + * To use PUI with an unsupported system, define PU_USE_NONE and + * provide your own callbacks (if needed, the defaults will work + * in many cases). + * + * When compiling the PUI library itself, PU_USE_NONE must be defined. + * + */ + +typedef int (*puGetWindowCallback) ( ) ; +typedef void (*puSetWindowCallback) ( int window ) ; +typedef void (*puGetWindowSizeCallback) ( int *width, int *height ) ; +typedef void (*puSetWindowSizeCallback) ( int width, int height ) ; + +void puSetWindowFuncs ( puGetWindowCallback, + puSetWindowCallback, + puGetWindowSizeCallback, + puSetWindowSizeCallback ) ; + + +// Choose implementation + +#if !defined(PU_USE_GLUT) && \ + !defined(PU_USE_PW) && \ + !defined(PU_USE_FLTK) && \ + !defined(PU_USE_SDL) && \ + !defined(PU_USE_NATIVE) && \ + !defined(PU_USE_NONE) + +// Nothing selected. Try to figure out which one to use. +#if defined(PW_IS_PRESENT) +# define PU_USE_PW +#elif defined(FL_MAJOR_VERSION) +# define PU_USE_FLTK +#elif defined(SDL_MAJOR_VERSION) +# define PU_USE_SDL +#else +# define PU_USE_GLUT +#endif + +#endif + + +// Roll out the code and define puInit + +#if defined(PU_USE_GLUT) +# include "puGLUT.h" +# define puInit puInitGLUT +#elif defined(PU_USE_PW) +# include "puPW.h" +# define puInit puInitPW +#elif defined(PU_USE_FLTK) +# include "puFLTK.h" +# define puInit puInitFLTK +#elif defined(PU_USE_SDL) +# include "puSDL.h" +# define puInit puInitSDL +#elif defined(PU_USE_NATIVE) +# include "puNative.h" +# define puInit puInitNative +#else +# define puInit +#endif + + +#endif diff --git a/src/pui/puArrowButton.cxx b/src/pui/puArrowButton.cxx new file mode 100644 index 0000000..0b77352 --- /dev/null +++ b/src/pui/puArrowButton.cxx @@ -0,0 +1,170 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puArrowButton.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puArrowButton,puButton) + + +void puDrawArrowButtonTriangle ( int pos_x, int pos_y, int size_x, int size_y, + puColour colour, int arrow_type, int active ) +{ + /* + If greyed out then halve the opacity when drawing + the widget + */ + + if ( active ) + glColor4fv ( colour ) ; + else + glColor4f ( colour [0], + colour [1], + colour [2], + colour [3] / 2.0f ) ; // 50% more transparent + + switch ( arrow_type ) + { + case PUARROW_UP : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x , pos_y + size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y - size_y/4 ) ; + glEnd () ; + break; + + case PUARROW_DOWN : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y + size_y/4 ) ; + glVertex2i ( pos_x , pos_y - size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y + size_y/4 ) ; + glEnd () ; + break; + + case PUARROW_FASTUP : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x , pos_y ) ; + glVertex2i ( pos_x + size_x/4, pos_y - size_y/4 ) ; + + glVertex2i ( pos_x - size_x/4, pos_y ) ; + glVertex2i ( pos_x , pos_y + size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y ) ; + glEnd () ; + break; + + case PUARROW_FASTDOWN : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y + size_y/4 ) ; + glVertex2i ( pos_x , pos_y ) ; + glVertex2i ( pos_x + size_x/4, pos_y + size_y/4 ) ; + + glVertex2i ( pos_x - size_x/4, pos_y ) ; + glVertex2i ( pos_x , pos_y - size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y ) ; + glEnd () ; + break; + + case PUARROW_LEFT : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x + size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x - size_x/4, pos_y ) ; + glVertex2i ( pos_x + size_x/4, pos_y + size_y/4 ) ; + glEnd () ; + break; + + case PUARROW_RIGHT : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y ) ; + glVertex2i ( pos_x - size_x/4, pos_y + size_y/4 ) ; + glEnd () ; + break; + + case PUARROW_FASTLEFT : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x + size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x , pos_y ) ; + glVertex2i ( pos_x + size_x/4, pos_y + size_y/4 ) ; + + glVertex2i ( pos_x , pos_y - size_y/4 ) ; + glVertex2i ( pos_x - size_x/4, pos_y ) ; + glVertex2i ( pos_x , pos_y + size_y/4 ) ; + glEnd () ; + break; + + case PUARROW_FASTRIGHT : + glBegin ( GL_TRIANGLES ) ; + glVertex2i ( pos_x - size_x/4, pos_y - size_y/4 ) ; + glVertex2i ( pos_x , pos_y ) ; + glVertex2i ( pos_x - size_x/4, pos_y + size_y/4 ) ; + + glVertex2i ( pos_x , pos_y - size_y/4 ) ; + glVertex2i ( pos_x + size_x/4, pos_y ) ; + glVertex2i ( pos_x , pos_y + size_y/4 ) ; + glEnd () ; + break; + + default : + ulSetError ( UL_WARNING, "PUI: Unrecognised 'arrow_type' %d", arrow_type ) ; + break; + } +} + +void puArrowButton::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + /* + If arrow button is pushed or highlighted - + use inverse style for button itself + */ + + if ( getIntegerValue() ^ highlighted ) + { + if ( parent && ( ( parent->getType() & PUCLASS_POPUPMENU ) || + ( parent->getType() & PUCLASS_MENUBAR ) ) ) + abox.draw ( dx, dy, PUSTYLE_SMALL_SHADED, colour, isReturnDefault(), 2 ) ; + else + abox.draw ( dx, dy, -style, colour, isReturnDefault(), border_thickness ) ; + } + else + abox.draw ( dx, dy, style, colour, isReturnDefault(), border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + int size_x = abox.max[0] - abox.min[0] ; + int size_y = abox.max[1] - abox.min[1] ; + + int pos_x = dx + ( abox.max[0] + abox.min[0] ) / 2 ; + int pos_y = dy + ( abox.max[1] + abox.min[1] ) / 2 ; + + puDrawArrowButtonTriangle ( pos_x, pos_y, size_x, size_y, colour [ PUCOL_MISC ], arrow_type, active ) ; + } + + draw_label ( dx, dy ) ; +} + + diff --git a/src/pui/puBox.cxx b/src/pui/puBox.cxx new file mode 100644 index 0000000..1cd98ea --- /dev/null +++ b/src/pui/puBox.cxx @@ -0,0 +1,266 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puBox.cxx 1943 2004-08-05 01:05:48Z puggles $ +*/ + + +#include "puLocal.h" + +#define PU_DFLT_OFFSET 5 + +void puBox::extend ( puBox *bx ) +{ + if ( bx -> isEmpty () ) return ; + + if ( min[0]>bx->min[0] ) min[0] = bx->min[0] ; + if ( min[1]>bx->min[1] ) min[1] = bx->min[1] ; + if ( max[0]max[0] ) max[0] = bx->max[0] ; + if ( max[1]max[1] ) max[1] = bx->max[1] ; +} + + + +void puBox::draw ( int dx, int dy, int style, puColour colour[], int am_default, int border ) +{ + int mid = PUCOL_FOREGROUND ; + int hi = PUCOL_HIGHLIGHT ; + int lo = PUCOL_BACKGROUND ; + + /* Colour assignments */ + + switch ( style ) + { + case PUSTYLE_NONE : + return ; + + case PUSTYLE_PLAIN : + case PUSTYLE_DROPSHADOW : + mid = PUCOL_FOREGROUND ; + lo = PUCOL_BACKGROUND ; + break ; + + case PUSTYLE_SMALL_SHADED : + case PUSTYLE_SHADED : + case PUSTYLE_SMALL_BEVELLED : + case PUSTYLE_BEVELLED : + case PUSTYLE_BOXED : + case PUSTYLE_SPECIAL_UNDERLINED : + mid = PUCOL_FOREGROUND ; + hi = PUCOL_HIGHLIGHT ; + lo = PUCOL_BACKGROUND ; + break ; + + case PUSTYLE_RADIO : + hi = PUCOL_HIGHLIGHT ; + lo = PUCOL_LEGEND ; + break ; + + case -PUSTYLE_RADIO : + hi = PUCOL_HIGHLIGHT ; + lo = PUCOL_BACKGROUND ; + break ; + + case -PUSTYLE_PLAIN : + case -PUSTYLE_DROPSHADOW : + mid = PUCOL_HIGHLIGHT ; + lo = PUCOL_BACKGROUND ; + break ; + + case -PUSTYLE_SMALL_BEVELLED : + case -PUSTYLE_BEVELLED : + case -PUSTYLE_SMALL_SHADED : + case -PUSTYLE_SHADED : + case -PUSTYLE_BOXED : + case -PUSTYLE_SPECIAL_UNDERLINED : + mid = PUCOL_FOREGROUND ; + hi = PUCOL_BACKGROUND ; + lo = PUCOL_HIGHLIGHT ; + break ; + + default : + ulSetError ( UL_WARNING, "PUI: Unrecognised 'style' %d", style ) ; + return ; + } + + switch ( abs(style) ) + { + case PUSTYLE_PLAIN : + glColor4fv ( colour [ mid ] ) ; + glRecti ( dx + min[0], dy + min[1], + dx + max[0], dy + max[1] ) ; + break ; + + case PUSTYLE_SMALL_BEVELLED : + case PUSTYLE_SMALL_SHADED : + case PUSTYLE_BEVELLED : + case PUSTYLE_SHADED : + glColor4fv ( colour [ hi ] ) ; + glBegin ( GL_QUAD_STRIP ) ; + glVertex2i ( dx + min[0] + border, dy + min[1] + border ) ; + glVertex2i ( dx + min[0], dy + min[1] ) ; + glVertex2i ( dx + min[0] + border, dy + max[1] - border ) ; + glVertex2i ( dx + min[0], dy + max[1] ) ; + glVertex2i ( dx + max[0] - border, dy + max[1] - border ) ; + glVertex2i ( dx + max[0], dy + max[1] ) ; + glEnd () ; + glColor4fv ( colour [ lo ] ) ; + glBegin ( GL_QUAD_STRIP ) ; + glVertex2i ( dx + min[0], dy + min[1] ) ; + glVertex2i ( dx + min[0] + border, dy + min[1] + border ) ; + glVertex2i ( dx + max[0], dy + min[1] ) ; + glVertex2i ( dx + max[0] - border, dy + min[1] + border ) ; + glVertex2i ( dx + max[0], dy + max[1] ) ; + glVertex2i ( dx + max[0] - border, dy + max[1] - border ) ; + glEnd () ; + + if ( ( abs(style) == PUSTYLE_SMALL_BEVELLED ) || + ( abs(style) == PUSTYLE_BEVELLED ) ) + { + glColor4fv ( colour [ mid ] ) ; + glRecti ( dx + min[0] + border, dy + min[1] + border, + dx + max[0] - border, dy + max[1] - border ) ; + } + else + { + glShadeModel ( GL_SMOOTH ) ; + glBegin ( GL_POLYGON ) ; + glColor4fv ( colour [ mid ] ) ; + glVertex2i ( dx + min[0] + border, dy + min[1] + border ) ; + if ( ( style == PUSTYLE_SMALL_SHADED ) || + ( style == PUSTYLE_SHADED ) ) + glColor4f ( colour [mid][0] + (colour[lo][0] - colour[mid][0])/2.0f, + colour [mid][1] + (colour[lo][1] - colour[mid][1])/2.0f, + colour [mid][2] + (colour[lo][2] - colour[mid][2])/2.0f, + colour [lo][3] ) ; + else + glColor4f ( colour [mid][0] + (colour[hi][0] - colour[mid][0])/2.0f, + colour [mid][1] + (colour[hi][1] - colour[mid][1])/2.0f, + colour [mid][2] + (colour[hi][2] - colour[mid][2])/2.0f, + colour [hi][3] ) ; + glVertex2i ( dx + min[0] + border, dy + max[1] - border ) ; + glColor4fv ( colour [ mid ] ) ; + glVertex2i ( dx + max[0] - border, dy + max[1] - border ) ; + if ( ( style == -PUSTYLE_SMALL_SHADED ) || + ( style == -PUSTYLE_SHADED ) ) + glColor4f ( colour [mid][0] + (colour[lo][0] - colour[mid][0])/2.0f, + colour [mid][1] + (colour[lo][1] - colour[mid][1])/2.0f, + colour [mid][2] + (colour[lo][2] - colour[mid][2])/2.0f, + colour [lo][3] ) ; + else + glColor4f ( colour [mid][0] + (colour[hi][0] - colour[mid][0])/2.0f, + colour [mid][1] + (colour[hi][1] - colour[mid][1])/2.0f, + colour [mid][2] + (colour[hi][2] - colour[mid][2])/2.0f, + colour [hi][3] ) ; + glVertex2i ( dx + max[0] - border, dy + min[1] + border ) ; + glEnd () ; + glShadeModel (GL_FLAT) ; + + if ( ( style == -PUSTYLE_SMALL_SHADED ) || + ( style == -PUSTYLE_SHADED ) ) + { + glColor4fv ( colour [ lo ] ) ; + glBegin ( GL_QUAD_STRIP ) ; + glVertex2i ( dx + min[0] + border , dy + min[1] + border ) ; + glVertex2i ( dx + min[0] + border/2 , dy + min[1] + border/2 ) ; + glVertex2i ( dx + min[0] + border , dy + max[1] - border ) ; + glVertex2i ( dx + min[0] + border/2 , dy + max[1] - border/2 ) ; + glVertex2i ( dx + max[0] - border , dy + max[1] - border ) ; + glVertex2i ( dx + max[0] - border/2 , dy + max[1] - border/2 ) ; + glEnd () ; + glColor4fv ( colour [ hi ] ) ; + glBegin ( GL_QUAD_STRIP ) ; + glVertex2i ( dx + min[0] + border/2 , dy + min[1] + border/2 ) ; + glVertex2i ( dx + min[0] + border , dy + min[1] + border ) ; + glVertex2i ( dx + max[0] - border/2 , dy + min[1] + border/2 ) ; + glVertex2i ( dx + max[0] - border , dy + min[1] + border ) ; + glVertex2i ( dx + max[0] - border/2 , dy + max[1] - border/2 ) ; + glVertex2i ( dx + max[0] - border , dy + max[1] - border ) ; + glEnd () ; + } + } + break ; + + case PUSTYLE_BOXED : + glColor4fv ( colour [ hi ] ) ; + glRecti ( dx + min[0], dy + min[1], + dx + max[0], dy + max[1] ) ; + glColor4fv ( colour [ mid ] ) ; + glRecti ( dx + min[0]+border, dy + min[1]+border, + dx + max[0]-border, dy + max[1]-border ) ; + break ; + + case PUSTYLE_RADIO : + glColor4fv ( colour [ lo ] ) ; + glBegin ( GL_LINE_LOOP ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE/2, dy + min[1] ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE , dy + min[1] + PU_RADIO_BUTTON_SIZE/2 ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE/2, dy + min[1] + PU_RADIO_BUTTON_SIZE ) ; + glVertex2i ( dx + min[0] , dy + min[1] + PU_RADIO_BUTTON_SIZE/2 ) ; + glEnd () ; + + if ( style < 0 ) + { + glColor4fv ( colour [ hi ] ) ; + glBegin ( GL_QUADS ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE/2, dy + min[1] + 2 ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE-2, dy + min[1] + PU_RADIO_BUTTON_SIZE/2 ) ; + glVertex2i ( dx + min[0] + PU_RADIO_BUTTON_SIZE/2, dy + min[1] + PU_RADIO_BUTTON_SIZE-2 ) ; + glVertex2i ( dx + min[0] + 2 , dy + min[1] + PU_RADIO_BUTTON_SIZE/2 ) ; + glEnd () ; + } + break ; + + case PUSTYLE_SPECIAL_UNDERLINED : + glColor4fv ( colour [ hi ] ) ; + glRecti ( dx + min[0], dy + min[1], + dx + max[0], dy + min[1]+border ) ; + glColor4fv ( colour [ mid ] ) ; + glRecti ( dx + min[0], dy + min[1]+border, + dx + max[0], dy + max[1] ) ; + break ; + + case PUSTYLE_DROPSHADOW : + glColor4fv ( colour [ lo ] ) ; + glRecti ( dx + min[0] + border, dy + min[1] - border, + dx + max[0] + border, dy + max[1] - border ) ; + glColor4fv ( colour [ mid ] ) ; + glRecti ( dx + min[0], dy + min[1], + dx + max[0], dy + max[1] ) ; + break ; + } + + if ( am_default ) + { + glColor4fv ( colour [ PUCOL_BACKGROUND ] ) ; + glLineStipple ( 1, 0xF0F0 ) ; + glEnable ( GL_LINE_STIPPLE ) ; + glBegin ( GL_LINE_LOOP ) ; + glVertex2f ( dx + min[0] + PU_DFLT_OFFSET + 0.5f, dy + min[1] + PU_DFLT_OFFSET + 0.5f ) ; + glVertex2f ( dx + min[0] + PU_DFLT_OFFSET + 0.5f, dy + max[1] - PU_DFLT_OFFSET - 0.5f ) ; + glVertex2f ( dx + max[0] - PU_DFLT_OFFSET - 0.5f, dy + max[1] - PU_DFLT_OFFSET - 0.5f ) ; + glVertex2f ( dx + max[0] - PU_DFLT_OFFSET - 0.5f, dy + min[1] + PU_DFLT_OFFSET + 0.5f ) ; + glEnd () ; + glDisable ( GL_LINE_STIPPLE ) ; + } +} + + + diff --git a/src/pui/puButton.cxx b/src/pui/puButton.cxx new file mode 100644 index 0000000..518dce0 --- /dev/null +++ b/src/pui/puButton.cxx @@ -0,0 +1,236 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puButton.cxx 2064 2006-01-05 21:32:59Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puButton,puObject) + + +void puButton::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + if ( button_type == PUBUTTON_NORMAL ) + { + /* If button is pushed or highlighted - use inverse style */ + + if ( getIntegerValue() ^ highlighted ) + { + if ( parent && ( ( parent->getType() & PUCLASS_POPUPMENU ) || + ( parent->getType() & PUCLASS_MENUBAR ) ) ) + abox.draw ( dx, dy, active ? PUSTYLE_SMALL_SHADED : PUSTYLE_PLAIN, colour, + isReturnDefault(), active ? 2 : 0 ) ; + else + abox.draw ( dx, dy, -style, colour, isReturnDefault(), border_thickness ) ; + } + else + abox.draw ( dx, dy, style, colour, isReturnDefault(), border_thickness ) ; + } + else if ( ( button_type == PUBUTTON_VCHECK ) || + ( button_type == PUBUTTON_XCHECK ) ) + abox.draw ( dx, dy, PUSTYLE_BOXED, colour, isReturnDefault(), 1 ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + switch ( button_type ) + { + case PUBUTTON_NORMAL : + draw_legend ( dx, dy ) ; + break ; + + case PUBUTTON_RADIO : + { + int btn_width = abox.max[0] - abox.min[0], + btn_height = abox.max[1] - abox.min[1] ; + + glColor4fv ( ( getIntegerValue () ^ highlighted ) ? + colour [ PUCOL_BACKGROUND ] : colour [ PUCOL_LEGEND ] ) ; + + glBegin ( GL_LINE_LOOP ) ; + glVertex2i ( dx+abox.min[0] + btn_width/2, dy+abox.min[1] ) ; + glVertex2i ( dx+abox.min[0] + btn_width, dy+abox.min[1] + btn_height/2 ) ; + glVertex2i ( dx+abox.min[0] + btn_width/2, dy+abox.min[1] + btn_height ) ; + glVertex2i ( dx+abox.min[0], dy+abox.min[1] + btn_height/2 ) ; + glEnd () ; + + if ( getIntegerValue () ^ highlighted ) + { + /* If greyed out then halve the opacity when drawing the widget */ + + if ( active ) + glColor4fv ( colour [ PUCOL_HIGHLIGHT ] ) ; + else + glColor4f ( colour [ PUCOL_HIGHLIGHT ][0], + colour [ PUCOL_HIGHLIGHT ][1], + colour [ PUCOL_HIGHLIGHT ][2], + colour [ PUCOL_HIGHLIGHT ][3] / 2.0f ) ; /* 50% more transparent */ + + glBegin ( GL_QUADS ) ; + glVertex2i ( dx+abox.min[0] + btn_width/2, + dy+abox.min[1] + 2 ) ; + glVertex2i ( dx+abox.min[0] + btn_width - 2, + dy+abox.min[1] + btn_height/2 ) ; + glVertex2i ( dx+abox.min[0] + btn_width/2, + dy+abox.min[1] + btn_height - 2 ) ; + glVertex2i ( dx+abox.min[0] + 2, + dy+abox.min[1] + btn_height/2 ) ; + glEnd () ; + } + + break ; + } + + case PUBUTTON_CIRCLE : + { + float rad = ( (abox.max[0]-abox.min[0]) < (abox.max[1]-abox.min[1]) ) ? + (abox.max[0]-abox.min[0])/2.0f : (abox.max[1]-abox.min[1])/2.0f ; + float dtheta = 2.0f / rad ; + float theta ; + + /* Draw the outer circle */ + + glColor4fv ( colour [ PUCOL_FOREGROUND ] ) ; + + glBegin ( GL_POLYGON ) ; + for ( theta = -SG_PI ; theta <= SG_PI ; theta += dtheta ) + glVertex2f ( dx + abox.min[0] + rad + (rad * float(cos ( theta ))), + dy + abox.min[1] + rad + (rad * float(sin ( theta ))) ) ; + glEnd () ; + + if ( getIntegerValue () ^ highlighted ) + /* If clicked, draw the inner circle with half the radius */ + { + rad /= 2 ; + dtheta = 2.0f / rad ; + + /* If greyed out then halve the opacity when drawing the widget */ + + if ( active ) + glColor4fv ( colour [ PUCOL_MISC ] ) ; + else + glColor4f ( colour [ PUCOL_MISC ][0], + colour [ PUCOL_MISC ][1], + colour [ PUCOL_MISC ][2], + colour [ PUCOL_MISC ][3] / 2.0f ) ; /* 50% more transparent */ + + glBegin ( GL_POLYGON ) ; + for ( theta = -SG_PI ; theta <= SG_PI ; theta += dtheta ) + glVertex2f ( dx + abox.min[0] + rad*2 + (rad * float(cos ( theta ))), + dy + abox.min[1] + rad*2 + (rad * float(sin ( theta ))) ) ; + glEnd () ; + } + + break ; + } + + case PUBUTTON_VCHECK : + case PUBUTTON_XCHECK : + if ( getIntegerValue () ^ highlighted ) + { + /* If greyed out then halve the opacity when drawing the widget */ + + if ( active ) + glColor4fv ( colour [ PUCOL_MISC ] ) ; + else + glColor4f ( colour [ PUCOL_MISC ][0], + colour [ PUCOL_MISC ][1], + colour [ PUCOL_MISC ][2], + colour [ PUCOL_MISC ][3] / 2.0f ) ; /* 50% more transparent */ + + glPushAttrib ( GL_LINE_BIT ) ; + + glLineWidth ( 2.0f ) ; + + if ( button_type == PUBUTTON_VCHECK ) + { + glBegin ( GL_LINE_STRIP ) ; + glVertex2i ( dx + abox.min[0], + dy + abox.min[1] + (abox.max[1] - abox.min[1])/2 ) ; + glVertex2i ( dx + abox.min[0] + (abox.max[0] - abox.min[0])/3, + dy + abox.min[1] ) ; + glVertex2i ( dx + abox.max[0], + dy + abox.max[1] ) ; + glEnd () ; + } + else if ( button_type == PUBUTTON_XCHECK ) + { + glBegin ( GL_LINES ) ; + glVertex2i ( dx + abox.min[0] + 1, dy + abox.min[1] + 1 ) ; + glVertex2i ( dx + abox.max[0] - 1, dy + abox.max[1] - 1 ) ; + glVertex2i ( dx + abox.max[0] - 1, dy + abox.min[1] + 1 ) ; + glVertex2i ( dx + abox.min[0] + 1, dy + abox.max[1] - 1 ) ; + glEnd () ; + } + + glPopAttrib () ; + } + + break ; + + default : + ulSetError ( UL_WARNING, "PUI: Unrecognised 'button_type' %d", button_type ) ; + break; + } + } + + draw_label ( dx, dy ) ; +} + + +void puButton::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( button == active_mouse_button ) + { + if ( ( updown == active_mouse_edge ) || ( active_mouse_edge == PU_UP_AND_DOWN ) ) + { + lowlight () ; + setValue ( (int) ! getIntegerValue () ) ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + else + { + /* If the mouse is over the widget, highlight it; otherwise lowlight it */ + if ( ( abox.min[0] <= x ) && ( abox.max[0] >= x ) && ( abox.min[1] <= y ) && ( abox.max[1] >= y ) ) + highlight () ; + else + lowlight () ; + } + } + else + lowlight () ; +} + + diff --git a/src/pui/puButtonBox.cxx b/src/pui/puButtonBox.cxx new file mode 100644 index 0000000..df33399 --- /dev/null +++ b/src/pui/puButtonBox.cxx @@ -0,0 +1,162 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puButtonBox.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puButtonBox,puObject) + + +puButtonBox::puButtonBox ( int minx, int miny, int maxx, int maxy, + char **labels, int one_button ) : + puObject ( minx, miny, maxx, maxy ) +{ + type |= PUCLASS_BUTTONBOX ; + one_only = one_button ; + + button_labels = labels ; + + newList ( labels ) ; +} + + +void puButtonBox::newList ( char ** _list ) +{ + button_labels = _list ; + + if ( button_labels == NULL ) + num_kids = 0 ; + else + for ( num_kids = 0 ; button_labels [ num_kids ] != NULL ; num_kids++ ) + /* Count number of items */ ; + + puPostRefresh() ; +} + + +int puButtonBox::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || + ! isReturnDefault() || + ( key != '\r' && key != '\n' ) || ( window != puGetWindow () ) ) + return FALSE ; + + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + int v = getIntegerValue () ; + + if ( ! one_only ) + v = ~v ; + else + if ( ++v > num_kids ) + v = 0 ; + + setValue ( v ) ; +// puSetActiveWidget ( this, x, y ) ; + invokeCallback() ; + return TRUE ; +} + + +int puButtonBox::checkHit ( int /*button*/, int updown, int x, int y ) +{ + if ( ! isHit ( x, y ) || + ( updown != active_mouse_edge && + active_mouse_edge != PU_UP_AND_DOWN ) ) + return FALSE ; + + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + int i = num_kids - 1 - (( y - abox.min[1] - PUSTR_BGAP ) * num_kids ) / + ( abox.max[1] - abox.min[1] - PUSTR_BGAP - PUSTR_TGAP ) ; + + if ( i < 0 ) i = 0 ; + if ( i >= num_kids ) i = num_kids - 1 ; + + if ( one_only ) + setValue ( i ) ; + else + setValue ( getIntegerValue () ^ ( 1 << i ) ) ; + + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + return TRUE ; +} + + +void puButtonBox::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + abox.draw ( dx, dy, style, colour, isReturnDefault(), border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + for ( int i = 0 ; i < num_kids ; i++ ) + { + puBox tbox ; + + tbox.min [ 0 ] = abox.min [ 0 ] + PUSTR_LGAP + PUSTR_LGAP ; + tbox.min [ 1 ] = abox.min [ 1 ] + ((abox.max[1]-abox.min[1]-PUSTR_TGAP-PUSTR_BGAP)/num_kids) * (num_kids-1-i) ; + tbox.max [ 0 ] = tbox.min [ 0 ] ; + tbox.max [ 1 ] = tbox.min [ 1 ] ; + + if (( one_only && i == getIntegerValue() ) || + ( !one_only && ((1< 1.0f ) val = 1.0f ; + + val = ( 2.0f * val - 1.0f ) * SG_PI ; + + glBegin ( GL_LINES ) ; + + glVertex2f ( (float)x_cen, (float)y_cen ) ; + glVertex2f ( (float)x_cen + rad * (float)sin ( (double)val ), (float)y_cen + rad * (float)cos ( (double)val ) ) ; + + glEnd () ; + + glPopAttrib () ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + + +void puDial::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( ( button == active_mouse_button ) && ( updown == PU_UP ) ) + { + puDeactivateWidget () ; + return ; + } + + if ( button == active_mouse_button ) + { + int x_cen = ( abox.max [0] + abox.min [0] ) / 2 ; + int y_cen = ( abox.max [1] + abox.min [1] ) / 2 ; + float angle = (float)atan2 ( (double)(x-x_cen), (double)(y-y_cen) ) / SG_PI ; + + // Move to within the (0,1) interval + + if ( angle < -1.0f ) + angle += 2.0f ; + else if (angle > 1.0f ) + angle -= 2.0f ; + + angle = ( angle + 1.0f ) / 2.0f ; + + // Check for hitting the limits (user has dragged the mouse around the bottom + // of the widget) + if ( !wrap ) + { + if ( ( angle > 0.75f ) && ( getFloatValue () < 0.25f ) ) + angle = 0.0f ; + else if ( ( angle < 0.25f ) && ( getFloatValue () > 0.75f ) ) + angle = 1.0f ; + } + + angle = angle * (maximum_value - minimum_value) + minimum_value ; + setValue( checkStep(angle) ) ; + + switch ( cb_mode ) + { + case PUSLIDER_CLICK : + if ( updown == active_mouse_edge ) + { + last_cb_value = angle ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_DELTA :/* Deprecated! */ + if ( fabs ( last_cb_value - angle ) >= cb_delta ) + { + last_cb_value = angle ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_ALWAYS : + default : + last_cb_value = angle ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + break ; + } + } +} + + diff --git a/src/pui/puDialogBox.cxx b/src/pui/puDialogBox.cxx new file mode 100644 index 0000000..a8b3cbf --- /dev/null +++ b/src/pui/puDialogBox.cxx @@ -0,0 +1,28 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puDialogBox.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puDialogBox,puPopup) + diff --git a/src/pui/puFLTK.h b/src/pui/puFLTK.h new file mode 100644 index 0000000..e684864 --- /dev/null +++ b/src/pui/puFLTK.h @@ -0,0 +1,68 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puFLTK.h 1857 2004-02-16 13:49:03Z stromberg $ +*/ + +#ifndef _PU_FLTK_H_ +#define _PU_FLTK_H_ + +#ifndef PU_USE_FLTK +# define PU_USE_FLTK +#endif + +#include "pu.h" +#include + + +inline int puGetWindowFLTK () +{ + return (int) Fl_Window::current () ; +} + +inline void puSetWindowFLTK ( int window ) +{ + ((Fl_Gl_Window *) window) -> make_current () ; +} + +inline void puGetWindowSizeFLTK ( int *width, int *height ) +{ + Fl_Window * window = Fl_Window::current () ; + *width = window->w() ; + *height = window->h() ; +} + +inline void puSetWindowSizeFLTK ( int width, int height ) +{ + Fl_Window * window = Fl_Window::current () ; + window -> resize ( window->x(), window->y(), width, height ) ; +} + +inline void puInitFLTK () +{ + puSetWindowFuncs ( puGetWindowFLTK, + puSetWindowFLTK, + puGetWindowSizeFLTK, + puSetWindowSizeFLTK ) ; + puRealInit () ; +} + + +#endif diff --git a/src/pui/puFont.cxx b/src/pui/puFont.cxx new file mode 100644 index 0000000..f10799c --- /dev/null +++ b/src/pui/puFont.cxx @@ -0,0 +1,135 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puFont.cxx 1943 2004-08-05 01:05:48Z puggles $ +*/ + + +#include "puLocal.h" + + +puFont PUFONT_8_BY_13 ( fntGetBitmapFont( FNT_BITMAP_8_BY_13 ) ) ; +puFont PUFONT_9_BY_15 ( fntGetBitmapFont( FNT_BITMAP_9_BY_15 ) ) ; +puFont PUFONT_TIMES_ROMAN_10 ( fntGetBitmapFont( FNT_BITMAP_TIMES_ROMAN_10 ) ) ; +puFont PUFONT_TIMES_ROMAN_24 ( fntGetBitmapFont( FNT_BITMAP_TIMES_ROMAN_24 ) ) ; +puFont PUFONT_HELVETICA_10 ( fntGetBitmapFont( FNT_BITMAP_HELVETICA_10 ) ) ; +puFont PUFONT_HELVETICA_12 ( fntGetBitmapFont( FNT_BITMAP_HELVETICA_12 ) ) ; +puFont PUFONT_HELVETICA_18 ( fntGetBitmapFont( FNT_BITMAP_HELVETICA_18 ) ) ; + + +puFont::puFont () +{ + fnt_font_handle = fntGetBitmapFont( FNT_BITMAP_8_BY_13 ); + pointsize = 13 ; + slant = 0 ; +} + + +float puFont::getFloatStringWidth ( const char *str ) const +{ + if ( str == NULL ) + return 0.0f ; + + if ( fnt_font_handle != NULL ) + { + float r, l ; + fnt_font_handle -> getBBox ( str, pointsize, slant, &l, &r, NULL, NULL ) ; + return ( r - l ) ; + } + + return 0.0f ; +} + + +static int count_lines ( const char *str ) +{ + int k = 0 ; + + if ( str != NULL ) + { + for ( k = 1 ; *str != '\0' ; str++ ) + if ( *str == '\n' ) + k++ ; + } + + return k ; +} + + +int puFont::getStringHeight ( const char *s ) const +{ + if (!s || !fnt_font_handle) return 0 ; + + // The height of a single line is the ascender plus descender, not + // including whitespace above and below. Each additional line + // adds one point size, of course. Remember to round up to avoid + // overlapping artifacts. The bbox string is just a sample of + // "likely to be tall" glyphs. + float ascend; + fnt_font_handle -> getBBox ( "$lfKL", pointsize, slant, NULL, NULL, NULL, &ascend ) ; + float height = ascend + getStringDescender() ; + int lines = count_lines ( s ) ; + if ( lines > 1 ) + height += ( lines - 1 ) * pointsize ; + + return int ( height + 0.999 ) ; + // return int ( pointsize * count_lines ( s ) + 0.999 ) - getStringDescender(); +} + + +int puFont::getStringDescender ( void ) const +{ + if ( fnt_font_handle != NULL ) + { + float b ; + fnt_font_handle -> getBBox ( "y", pointsize, slant, NULL, NULL, &b, NULL ) ; + return (int) -b ; + } + + return 0 ; +} + + +void puFont::drawString ( const char *str, int x, int y ) +{ + if ( str == NULL ) + return ; + + if ( fnt_font_handle != NULL ) + { + sgVec3 curpos ; + sgSetVec3 ( curpos, (float)x, (float)y, 0.0f ) ; + + glPushAttrib( GL_COLOR_BUFFER_BIT ); // NHV + glEnable ( GL_ALPHA_TEST ) ; + glEnable ( GL_BLEND ) ; + glAlphaFunc ( GL_GREATER, 0.1f ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + fnt_font_handle -> begin () ; + fnt_font_handle -> puts ( curpos, pointsize, slant, str ) ; + fnt_font_handle -> end () ; + + glPopAttrib () ; + glDisable ( GL_TEXTURE_2D ) ; + return ; + } +} + diff --git a/src/pui/puFrame.cxx b/src/pui/puFrame.cxx new file mode 100644 index 0000000..8e17019 --- /dev/null +++ b/src/pui/puFrame.cxx @@ -0,0 +1,44 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puFrame.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puFrame,puObject) + + +void puFrame::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + abox.draw ( dx, dy, style, colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + draw_legend ( dx, dy ) ; + + draw_label ( dx, dy ) ; +} + + diff --git a/src/pui/puGLUT.h b/src/pui/puGLUT.h new file mode 100644 index 0000000..bd564a0 --- /dev/null +++ b/src/pui/puGLUT.h @@ -0,0 +1,75 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puGLUT.h 2122 2007-09-15 12:34:36Z fayjf $ +*/ + +#ifndef _PU_GLUT_H_ +#define _PU_GLUT_H_ + +#ifndef PU_USE_GLUT +# define PU_USE_GLUT +#endif + +#include "pu.h" + +#ifdef UL_MAC_OSX +# include +#else +# ifdef FREEGLUT_IS_PRESENT /* for FreeGLUT like PLIB 1.6.1*/ +# include +# else +# include +# endif +#endif + + +inline int puGetWindowGLUT() +{ + return glutGetWindow () ; +} + +inline void puSetWindowGLUT ( int window ) +{ + glutSetWindow ( window ) ; +} + +inline void puGetWindowSizeGLUT ( int *width, int *height ) +{ + *width = glutGet ( (GLenum) GLUT_WINDOW_WIDTH ) ; + *height = glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ; +} + +inline void puSetWindowSizeGLUT ( int width, int height ) +{ + glutReshapeWindow ( width, height ) ; +} + +inline void puInitGLUT () +{ + puSetWindowFuncs ( puGetWindowGLUT, + puSetWindowGLUT, + puGetWindowSizeGLUT, + puSetWindowSizeGLUT ) ; + puRealInit () ; +} + + +#endif diff --git a/src/pui/puGroup.cxx b/src/pui/puGroup.cxx new file mode 100644 index 0000000..e5d35b0 --- /dev/null +++ b/src/pui/puGroup.cxx @@ -0,0 +1,476 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puGroup.cxx 2076 2006-04-13 16:20:23Z bram $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puGroup,puObject) + + +#define PUSTACK_MAX 100 + + /* + This is a stack of groups that are currently being filled with other widgets. + A group is pushed onto the stack when it is created and it is popped off the + stack when its "close" function is called. + */ + +static int currGroup = -1 ; +static puGroup *groupStack [ PUSTACK_MAX ] ; + +void puPushGroup ( puGroup *in ) +{ + if ( currGroup < PUSTACK_MAX ) + groupStack [ ++currGroup ] = in ; + else + ulSetError ( UL_WARNING, "PUI: Too many puGroups open at once!" ) ; +} + + +void puPopGroup ( void ) +{ + if ( currGroup > 0 ) + --currGroup ; + else + ulSetError ( UL_WARNING, "PUI: puGroup stack is empty!" ) ; +} + +int puNoGroup ( void ) +{ + return currGroup < 0 ; +} + + +puGroup *puGetCurrGroup ( void ) +{ + if ( currGroup < 0 ) + { + ulSetError ( UL_WARNING, "PUI: No Group!" ) ; + return NULL ; + } + + return groupStack [ currGroup ] ; +} + +void puGroup::remove ( puObject *obj ) +{ + if ( obj -> getParent () != this ) + return ; + + /* Are we the first object in the list */ + + if ( obj -> getPrevObject() == NULL ) + dlist = obj -> getNextObject() ; + else + obj -> getPrevObject() -> setNextObject( obj -> getNextObject() ) ; + + /* Are we the last object in the list */ + + if ( obj -> getNextObject() != NULL ) + obj -> getNextObject() -> setPrevObject( obj -> getPrevObject() ) ; + + obj -> setNextObject ( NULL ) ; + obj -> setPrevObject ( NULL ) ; + obj -> setParent ( NULL ) ; + + num_children-- ; + recalc_bbox () ; +} + +void puGroup::empty ( void ) +{ + puObject *obj = getLastChild () ; + while ( obj != NULL ) + { + if ( obj->getType () & PUCLASS_GROUP ) + { + puGroup *group = (puGroup *)obj ; + group->empty () ; + } + + puObject *temp = obj->getPrevObject () ; + delete obj ; + obj = temp ; + } + + dlist = NULL ; +} + +void puGroup::add ( puObject *new_obj ) +{ + if ( new_obj -> getParent () != NULL ) + new_obj -> getParent () -> remove ( new_obj ) ; + + new_obj -> setParent ( this ) ; + + if ( dlist == NULL ) + { + dlist = new_obj ; + new_obj -> setNextObject ( NULL ) ; + new_obj -> setPrevObject ( NULL ) ; + } + else + { + puObject *last ; + + for ( last = dlist ; + last -> getNextObject() != NULL ; + last = last -> getNextObject() ) + /* Search for end of list. */ ; + + last -> setNextObject ( new_obj ) ; + new_obj -> setPrevObject ( last ) ; + new_obj -> setNextObject ( NULL ) ; + } + + num_children++ ; + recalc_bbox () ; +} + +int puGroup::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || dlist == NULL || ! isVisible () || ! isActive () ) + return FALSE ; + + puObject *bo ; + + /* + We have to walk the list backwards to ensure that + the click order is the same as the DRAW order. + */ + + bo = getLastChild () ; + + for ( ; bo != NULL ; bo = bo -> getPrevObject() ) + if ( bo -> checkKey ( key, updown ) ) + return TRUE ; + + return FALSE ; +} + +int puGroup::checkHit ( int button, int updown, int x, int y ) +{ + if ( dlist == NULL || ! isVisible () || ! isActive () ) + return FALSE ; + + /* + This might be a bit redundant - but it's too hard to keep + track of changing abox sizes when daughter objects are + changing sizes. + */ + + recalc_bbox () ; + + puObject *bo ; + + x -= abox.min[0] ; + y -= abox.min[1] ; + + /* + We have to walk the list backwards to ensure that + the click order is the same as the DRAW order. + */ + + if ( !mouse_active ) + { + bo = getLastChild () ; + + for ( ; bo != NULL ; bo = bo -> getPrevObject() ) + { + /* If this is a menu bar and the mouse is over the button, highlight the button, if it's in the right window - JCJ 6 Jun 2002 */ + if ( ( getType () & PUCLASS_MENUBAR ) && ( bo->getType () & PUCLASS_ONESHOT ) && ( window == puGetWindow () ) ) + /* + Changing this statement to something like this: + if ( bo->getType () & PUCLASS_ONESHOT ) + Breaks a lot of stuff... like pressing buttons actually working in filepicker. Need + to just define everything that can be pressed passively above, and things should + be fröhlich. + */ + { + puBox *box = bo->getABox () ; + if ( ( x >= box->min[0] ) && ( x <= box->max[0] ) && + ( y >= box->min[1] ) && ( y <= box->max[1] ) ) + bo->highlight () ; + else + bo->lowlight () ; + } + + if ( bo -> checkHit ( button, updown, x, y ) ) + return TRUE ; + } + } + + /* + If right mouse button is pressed, save mouse coordinates for + dragging and dropping. Do this only if the "floating" flag is set. + */ + + if ( mouse_active || ( isHit ( x+abox.min[0], y+abox.min[1]) && + floating && ( button == PU_RIGHT_BUTTON ) ) ) // DEPRECATED! -- we need to replace this code. + { + puMoveToLast ( this ); + + /* + Return (x, y) to coordinates of parent interface to avoid "jumping" of + present interface as mouse drags + */ + + x += abox.min[0] ; + y += abox.min[1] ; + + if ( updown == PU_DOWN ) + { + mouse_x = x; /* Save mouse coordinates for dragging */ + mouse_y = y; + + mouse_active = TRUE ; + + return TRUE ; + } + else if ( updown == PU_DRAG ) + { + int curr_x, curr_y; + + getPosition ( &curr_x, &curr_y ); + setPosition ( curr_x+x-mouse_x, curr_y+y-mouse_y ); /* Move to new position */ + + mouse_x = x; /* Save new coordinates */ + mouse_y = y; + + return TRUE ; + } + else if ( updown == PU_UP ) + { + mouse_active = FALSE ; + + return TRUE ; + } + } + + return FALSE ; +} + + +void puGroup::draw ( int dx, int dy ) +{ + if ( ! isVisible () ) + return ; + + for ( puObject *bo = dlist ; bo != NULL ; bo = bo -> getNextObject() ) + { + /* June 16th, 98, Shammi : + * The next if statement checks if the object is + * a menu bar and makes sure it is repositioned + * correctly. + */ + + int x, y ; + int xdraw = dx + abox.min[0] ; + int ydraw = dy + abox.min[1] ; + bo -> getPosition (&x, &y) ; + + /* If the object is a menubar or a vertmenu and supposed to be locked to the top, */ + /* then move it there. - JCJ 6 June 2002 */ + if ( ( bo -> getType () & PUCLASS_MENUBAR ) && + ( bo -> getWindow () == puGetWindow () ) ) + { + int obWidth, obHeight ; + bo -> getSize ( &obWidth, &obHeight ) ; + bo -> setPosition ( 0, puGetWindowHeight() - obHeight ) ; + } + else + { + if ( ( y < 0 ) && ( bo->getType () & PUCLASS_POPUPMENU ) ) + { + /* IF the object's bottom left corner lies outside the window, THEN */ + /* move the object to the top left - JCJ and Fay 5 Jun 2002 */ + int absx, absy ; + bo -> getAbsolutePosition (&absx, &absy) ; + if ( absy < 0 ) + ydraw -= absy ; + } + } + + bo -> draw ( xdraw, ydraw ) ; + } + + if ( getWindow () == puGetWindow () ) draw_label ( dx, dy ) ; +} + + +void puGroup::recalc_bbox ( void ) +{ + puBox contents ; + contents . empty () ; + + for ( puObject *bo = dlist ; + bo != NULL ; + bo = bo -> getNextObject() ) + contents . extend ( bo -> getBBox() ) ; + + if ( contents . isEmpty () ) + { + abox . max[0] = abox . min[0] ; + abox . max[1] = abox . min[1] ; + } + else + { + abox . max[0] = abox . min[0] + contents . max[0] ; + abox . max[1] = abox . min[1] + contents . max[1] ; + } + + puObject::recalc_bbox () ; +} + + +void puGroup::doHit ( int, int, int, int ) +{ +} + + +puGroup::~puGroup () +{ + void puCleanUpJunk ( void ) ; + + puObject *bo = getLastChild () ; + + while ( bo != NULL ) + { + dlist = bo ; + bo = bo -> getPrevObject() ; + puDeleteObject ( dlist ) ; + } + + // Since this is an object destructor, it should be okay to delete the child objects as well. + puCleanUpJunk () ; +} + + +void puGroup::setChildStyle ( int childs, int which, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildStyle ( childs, style, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setStyle ( style ) ; + } + } +} + +void puGroup::setChildBorderThickness ( int childs, int t, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildBorderThickness ( childs, t, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setBorderThickness ( t ) ; + } + } +} + +void puGroup::setChildColour ( int childs, int which, float r, float g, float b, float a, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildColour ( childs, which, r, g, b, a, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setColour ( which, r, g, b, a ) ; + } + } +} + +void puGroup::setChildColourScheme ( int childs, float r, float g, float b, float a, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildColourScheme ( childs, r, g, b, a, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setColourScheme ( r, g, b, a ) ; + } + } +} + +void puGroup::setChildLegendFont ( int childs, puFont f, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildLegendFont ( childs, f, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setLegendFont ( f ) ; + } + } +} + +void puGroup::setChildLabelFont ( int childs, puFont f, int recursive ) +{ + puObject *curr_obj ; + + for ( curr_obj = getFirstChild () ; + curr_obj != NULL ; + curr_obj = curr_obj->getNextObject () ) + { + if ( ( recursive == TRUE ) && ( curr_obj->getType () & PUCLASS_GROUP ) ) + ((puGroup*)curr_obj)->setChildLabelFont ( childs, f, TRUE ) ; + else + { + if ( curr_obj->getType () & childs ) + curr_obj->setLabelFont ( f ) ; + } + } +} + diff --git a/src/pui/puInput.cxx b/src/pui/puInput.cxx new file mode 100644 index 0000000..b80fb2a --- /dev/null +++ b/src/pui/puInput.cxx @@ -0,0 +1,423 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puInput.cxx 2154 2009-02-09 14:55:47Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF2(puInput,puInputBase,puObject) + + +static char *chop_to_width ( puFont fnt, const char *s, int width, int cursor_position, int *display_starting_point ) +{ + int new_len = strlen ( s ) ; + char *res = new char [ new_len + 1 ] ; + int n = 0 ; + int w ; + + if ( new_len == 0 ) + { + *res = '\0' ; + if ( display_starting_point != NULL ) *display_starting_point = 0 ; + } + else + { + do + { + memcpy ( res, s + n, new_len + 1 ) ; + n++ ; + new_len -- ; + w = fnt.getStringWidth ( res ) + 2 * PUSTR_RGAP + PUSTR_LGAP ; + } while ( ( w >= width ) && ( n < cursor_position - 1 ) ) ; + + if ( display_starting_point != NULL ) *display_starting_point = n - 1 ; + + n = 0 ; + + while ( w >= width ) + { + res[new_len] = '\0' ; + n++ ; + new_len -- ; + w = fnt.getStringWidth ( res ) + 2 * PUSTR_RGAP + PUSTR_LGAP ; + } + } + + return res ; +} + +void puInput::draw ( int dx, int dy ) +{ + normalizeCursors () ; + + if ( !visible || ( window != puGetWindow () ) ) return ; + + /* 3D Input boxes look nicest if they are always in inverse style. */ + + abox.draw ( dx, dy, ( (style==PUSTYLE_SMALL_BEVELLED || + style==PUSTYLE_SMALL_SHADED) ) ? -style : + (accepting ? -style : style ), colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + int xx = legendFont.getStringWidth ( " " ) ; + int yy = ( ( abox.max[1] - abox.min[1] - legendFont.getStringHeight () ) / 2 + + legendFont.getStringDescender () ) ; + + if ( !displayed_text ) + displayed_text = chop_to_width ( legendFont, getStringValue(), abox.max[0]-abox.min[0], getCursor (), &display_starting_point ) ; + + if ( accepting ) + { + /* Highlight the select area */ + + if ( select_end_position > 0 && + select_end_position != select_start_position ) + { + int sep = select_end_position - display_starting_point ; + int ssp = select_start_position - display_starting_point ; + + if ( sep < 0 ) sep = 0 ; + if ( ssp < 0 ) ssp = 0 ; + + if ( ssp < sep ) + { + char save_char = displayed_text [ sep ] ; + displayed_text [ sep ] = '\0' ; + int cpos2 = legendFont.getStringWidth ( displayed_text ) + + xx + dx + abox.min[0] ; + displayed_text [ sep ] = save_char ; + save_char = displayed_text [ ssp ] ; + displayed_text [ ssp ] = '\0' ; + int cpos1 = legendFont.getStringWidth ( displayed_text ) + + xx + dx + abox.min[0] ; + displayed_text [ ssp ] = save_char ; + + glColor3f ( 1.0f, 1.0f, 0.7f ) ; + glRecti ( cpos1, dy + abox.min[1] + 2 , + cpos2, dy + abox.max[1] - 2 ) ; + } + } + } + + /* Draw the text */ + + /* If greyed out then halve the opacity when drawing the text */ + + if ( active ) + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + else + glColor4f ( colour [ PUCOL_LEGEND ][0], + colour [ PUCOL_LEGEND ][1], + colour [ PUCOL_LEGEND ][2], + colour [ PUCOL_LEGEND ][3] / 2.0f ) ; /* 50% more transp */ + + legendFont.drawString ( displayed_text, + dx + abox.min[0] + xx, + dy + abox.min[1] + yy ) ; + + + if ( accepting ) + { + /* Draw the 'I' bar cursor. */ + + if ( cursor_position - display_starting_point >= 0 ) + { + char save_char = displayed_text [ cursor_position-display_starting_point ] ; + displayed_text [ cursor_position-display_starting_point ] = '\0' ; + + float cpos = 0.5f + legendFont.getStringWidth ( displayed_text ) + xx + dx + abox.min[0] ; + displayed_text [ cursor_position-display_starting_point ] = save_char ; + + float top = 0.5f + dy + abox.min[1] + yy + legendFont.getPointSize () ; + float bot = 0.5f + dy + abox.min[1] + yy - legendFont.getStringDescender() ; + + glColor4fv ( colour [ PUCOL_MISC ] ) ; + glBegin ( GL_LINES ) ; + glVertex2f ( cpos , bot ) ; + glVertex2f ( cpos , top ) ; + glVertex2f ( cpos - 1, bot ) ; + glVertex2f ( cpos - 1, top ) ; + glVertex2f ( cpos - 3, bot ) ; + glVertex2f ( cpos + 3, bot ) ; + glVertex2f ( cpos - 3, top ) ; + glVertex2f ( cpos + 3, top ) ; + glEnd () ; + } + } + } + + draw_label ( dx, dy ) ; +} + + +void puInput::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + /* Active widget exists and is not this one; call its down callback if it exists */ + + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( button == active_mouse_button ) + { + /* Most GUI's activate a button on button-UP not button-DOWN. */ + + /* Find the position of the mouse on the line of text */ + + if ( !displayed_text ) + displayed_text = chop_to_width ( legendFont, getStringValue(), + abox.max[0]-abox.min[0], getCursor (), &display_starting_point ) ; + int i = strlen ( displayed_text ) ; + + int length, prev_length ; + length = legendFont.getStringWidth ( displayed_text ) + abox.min[0] ; + prev_length = length ; + + while ( ( x <= prev_length ) && ( i > 0 ) ) + { + prev_length = length ; + displayed_text[--i] = '\0' ; + length = legendFont.getStringWidth ( displayed_text ) + abox.min[0] ; + } + + if ( ( x - length ) > ( prev_length - x ) ) + i++ ; /* Mouse is closer to next character than previous character */ + + i += display_starting_point ; + + /* Process the mouse click. */ + + if ( updown == active_mouse_edge || active_mouse_edge == PU_UP_AND_DOWN ) + { + lowlight () ; + + accepting = TRUE ; + cursor_position = i ; + normalizeCursors () ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + else if ( updown == PU_DOWN ) + { + /* + * We get here if the active edge is not down but the mouse button has + * been pressed. Start a selection if this isn't the initial activation + * of the widget. + */ + + if ( this == puActiveWidget() ) + { + select_start_position = i ; + select_end_position = i ; + } + } + else if ( updown == PU_DRAG ) + { + + if ( (select_end_position - i) > (i - select_start_position) ) + select_start_position = i ; /* Cursor closer to start than to end */ + else + select_end_position = i ; /* Cursor closer to end than to start */ + + if (select_start_position > select_end_position) + { + i = select_end_position ; + select_end_position = select_start_position ; + select_start_position = i ; + } + } + else + highlight () ; + + /* Required because we have pretty much destroyed "displayed_text" */ + delete [] displayed_text ; + displayed_text = chop_to_width ( legendFont, getStringValue(), + abox.max[0]-abox.min[0], getCursor (), &display_starting_point ) ; + } + else + lowlight () ; +} + + +int puInput::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP || ! isAcceptingInput() || ! isActive () || ! isVisible () || ( window != puGetWindow () ) ) + return FALSE ; + + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + /* Active widget exists and is not this one; call its down callback if it exists */ + + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + normalizeCursors () ; + + char *p = NULL ; + + int char_used = TRUE ; + + switch ( key ) + { + case PU_KEY_PAGE_UP : + case PU_KEY_PAGE_DOWN : + case PU_KEY_INSERT : return FALSE ; + + case PU_KEY_UP : + case PU_KEY_DOWN : + case 0x1B /* ESC */ : + case '\t' : + case '\r' : + case '\n' : /* Carriage return/Line Feed/TAB -- End of input */ + rejectInput () ; + normalizeCursors () ; + invokeCallback () ; + puDeactivateWidget () ; + break ; + + case PU_KEY_HOME : cursor_position = 0 ; break ; + case PU_KEY_END : + cursor_position = strlen ( getStringValue () ) ; + break ; + case PU_KEY_LEFT : cursor_position-- ; break ; + case PU_KEY_RIGHT : cursor_position++ ; break ; + default : char_used = FALSE ; + } + + if ( ! input_disabled && ! char_used ) + { + switch ( key ) + { + case '\b' : /* Backspace */ + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + else if ( cursor_position > 0 ) + { + p = new char [ strlen ( getStringValue () ) ] ; + strncpy ( p, getStringValue (), cursor_position - 1 ) ; + strcpy ( p + cursor_position - 1, + getStringValue () + cursor_position ) ; + cursor_position-- ; + } + + break ; + + case 0x7F : /* DEL */ + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + else if ( cursor_position != (int)strlen ( getStringValue() ) ) + { + p = new char [ strlen ( getStringValue () ) ] ; + strncpy ( p, getStringValue (), cursor_position ) ; + strcpy ( p + cursor_position, + getStringValue () + cursor_position + 1 ) ; + } + + break ; + + case 0x15 /* ^U */ : setValue ( "\0" ) ; break ; + case 0x03 /* ^C */ : + case 0x18 /* ^X */ : /* Cut or copy selected text */ + if ( select_start_position != select_end_position ) + { + p = getStringValue () ; + char ch = p[select_end_position] ; + p[select_end_position] = '\0' ; + puSetPasteBuffer ( p + select_start_position ) ; + p[select_end_position] = ch ; + + if ( key == 0x18 ) /* Cut, remove text from string */ + removeSelectRegion () ; + + /* + So we don't set the widget to its value at the end of the + function + */ + + p = NULL ; + } + + break ; + + case 0x16 /* ^V */ : /* Paste buffer into text */ + { + if ( ! puGetPasteBuffer () ) + break ; + + if ( select_start_position != select_end_position ) + removeSelectRegion () ; + + int str_val_len = strlen ( getStringValue () ) ; + int paste_len = strlen ( puGetPasteBuffer () ) ; + p = new char [ str_val_len + paste_len + 1 ] ; + memcpy ( p, getStringValue (), cursor_position ) ; + memcpy ( p + cursor_position, puGetPasteBuffer (), paste_len ) ; + memcpy ( p + cursor_position + paste_len, getStringValue () + cursor_position, str_val_len - cursor_position + 1 ) ; + cursor_position += paste_len ; + } + + break ; + + default: + if ( key < ' ' || key > 127 ) return FALSE ; + if ( valid_data ) + { + if ( !strchr ( valid_data, key ) ) return TRUE ; + } + + if ( select_start_position != select_end_position ) // remove selected text + removeSelectRegion () ; + + p = new char [ strlen ( getStringValue () ) + 2 ] ; + strncpy ( p, getStringValue (), cursor_position ) ; + p[cursor_position] = key ; + strcpy ( p + cursor_position + 1, + getStringValue () + cursor_position ) ; + + cursor_position++ ; + break ; + } + + if ( p != NULL ) + setValue ( p ) ; /* Set the widget value to the new string */ + + delete [] p ; + } + + /* Since the keystroke has changed the text value, we need to change the displayed text as well. */ + delete [] displayed_text ; + displayed_text = NULL ; + + normalizeCursors () ; + return TRUE ; +} + + diff --git a/src/pui/puInputBase.cxx b/src/pui/puInputBase.cxx new file mode 100644 index 0000000..732d00d --- /dev/null +++ b/src/pui/puInputBase.cxx @@ -0,0 +1,88 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puInputBase.cxx 1709 2002-09-27 23:57:33Z ude $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF(puInputBase) + +void puInputBase::normalizeCursors ( void ) +{ + int sl = strlen ( widget -> getStringValue () ) ; + + /* Clamp the positions to the limits of the text. */ + + if ( cursor_position < 0 ) cursor_position = 0 ; + if ( select_start_position < 0 ) select_start_position = 0 ; + if ( select_end_position < 0 ) select_end_position = 0 ; + if ( cursor_position > sl ) cursor_position = sl ; + if ( select_start_position > sl ) select_start_position = sl ; + if ( select_end_position > sl ) select_end_position = sl ; + + /* Swap the ends of the select window if they get crossed over */ + + if ( select_end_position < select_start_position ) + { + int tmp = select_end_position ; + select_end_position = select_start_position ; + select_start_position = tmp ; + } +} + +void puInputBase::removeSelectRegion ( void ) +{ + char *text = widget -> getStringValue () ; + int text_len = strlen ( text ) ; + char *p = new char [ text_len + 1 - + ( select_end_position - select_start_position ) ] ; + + memcpy ( p, text, select_start_position ) ; + memcpy ( p + select_start_position, + text + select_end_position, + text_len - select_end_position + 1 ) ; + + widget -> setValue ( p ) ; + delete [] p ; + + cursor_position = select_end_position = select_start_position ; +} + + +void puInputBase::addValidData ( const char *data ) +{ + int valid_len = valid_data != NULL ? strlen ( valid_data ) : 0 ; + int data_len = data != NULL ? strlen ( data ) : 0 ; + int new_data_len = valid_len + data_len ; + + char *new_data = new char [ new_data_len + 1 ] ; + + if ( valid_len != 0 ) + memcpy ( new_data, valid_data, valid_len ) ; + if ( data_len != 0 ) + memcpy ( new_data + valid_len, data, data_len ) ; + + new_data [ new_data_len ] = '\0' ; + delete [] valid_data ; + valid_data = new_data ; +} + diff --git a/src/pui/puInterface.cxx b/src/pui/puInterface.cxx new file mode 100644 index 0000000..a666939 --- /dev/null +++ b/src/pui/puInterface.cxx @@ -0,0 +1,127 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puInterface.cxx 1834 2003-12-16 23:16:04Z puggles $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puInterface,puGroup) + + +#define PUSTACK_MAX 100 + +static int currLiveInterface = -1 ; +static puInterface *liveInterfaceStack [ PUSTACK_MAX ] ; + +void puPushLiveInterface ( puInterface *in ) +{ + if ( currLiveInterface < PUSTACK_MAX - 1 ) + liveInterfaceStack [ ++currLiveInterface ] = in ; + else + ulSetError ( UL_WARNING, "PUI: Too many live puInterfaces open at once!\n" ) ; +} + +void puPopLiveInterface ( puInterface *in ) +{ + if ( currLiveInterface < 0 ) + { + ulSetError ( UL_WARNING, "PUI: Live puInterface stack is empty!\n" ) ; + return; + } + + if ( in == NULL ) + --currLiveInterface ; + else + { + for ( int i = currLiveInterface ; i >= 0 ; i-- ) + { + if ( in == liveInterfaceStack [ i ] ) + { + /* Handle interfaces that are buried in the stack */ + while ( i < currLiveInterface ) + { + liveInterfaceStack [ i ] = liveInterfaceStack [ i+1 ] ; + i++ ; + } + + --currLiveInterface ; + + break ; + } + } + } +} + +int puNoLiveInterface ( void ) +{ + return currLiveInterface < 0 ; +} + +puInterface *puGetUltimateLiveInterface ( void ) +{ + if ( currLiveInterface < 0 ) + ulSetError ( UL_FATAL, "PUI: No Live Interface! Forgot to call puInit ?\n" ) ; + + return liveInterfaceStack [ 0 ] ; +} + + +puInterface *puGetBaseLiveInterface ( void ) +{ + if ( currLiveInterface < 0 ) + ulSetError ( UL_FATAL, "PUI: No Live Interface! Forgot to call puInit ?\n" ) ; + + /* + Work down the interface stack until you + either get to the bottom or find a block + in the form of a puDialogBox. + */ + + for ( int i = currLiveInterface ; i > 0 ; i-- ) + if ( liveInterfaceStack [ i ] -> getType () & PUCLASS_DIALOGBOX ) + return liveInterfaceStack [ i ] ; + + return liveInterfaceStack [ 0 ] ; +} + + +puInterface::~puInterface () +{ + void puCleanUpJunk ( void ) ; + + puObject *bo = getLastChild () ; + + while ( bo != NULL ) + { + dlist = bo ; + bo = bo -> getPrevObject() ; + puDeleteObject ( dlist ) ; + } + + // Since this is an object destructor, it should be okay to delete the child objects as well. + puCleanUpJunk () ; + + dlist = NULL ; + + puPopLiveInterface ( this ) ; +} + diff --git a/src/pui/puListBox.cxx b/src/pui/puListBox.cxx new file mode 100644 index 0000000..60abbca --- /dev/null +++ b/src/pui/puListBox.cxx @@ -0,0 +1,201 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puListBox.cxx 2088 2006-10-16 08:11:16Z bram $ +*/ + +/**** +* NAME +* puListBox +* +* DESCRIPTION +* list of strings +* +* AUTHOR +* Dave McClurg +* +* CREATION DATE +* August 2000 +* +* MODIFICATION HISTORY +* John Fay - many improvements +****/ + +#include "puLocal.h" + +UL_RTTI_DEF1(puListBox,puButton) + + +puListBox::puListBox ( int minx, int miny, int maxx, int maxy, char** _list ) : + puButton ( minx, miny, maxx, maxy ) +{ + type |= PUCLASS_LISTBOX ; + + newList ( _list ) ; +} + + +void puListBox::newList ( char ** _list ) +{ + list = _list ; + + if ( list == NULL ) + num = 0 ; + else + for ( num = 0 ; list [ num ] != NULL ; num++ ) + /* Count number of items */ ; + + top = 0 ; + + /* Set index of selected item */ + setValue ( -1 ) ; + + puPostRefresh () ; +} + + +void puListBox::setTopItem( int item_index ) +{ + top = item_index ; + int visible = getNumVisible(); + if ( top < 0 || num <= visible ) + top = 0 ; + else if ( num > 0 && top > num-visible ) + top = num-visible; + + puPostRefresh () ; +} + + +void puListBox::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) || list == NULL ) return ; + + abox.draw ( dx, dy, style, colour, isReturnDefault(), border_thickness ) ; + + /* If greyed out then halve the opacity when drawing the text */ + + if ( active ) + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + else + glColor4f ( colour [ PUCOL_LEGEND ][0], + colour [ PUCOL_LEGEND ][1], + colour [ PUCOL_LEGEND ][2], + colour [ PUCOL_LEGEND ][3] / 2.0f ) ; /* 50% more transparent */ + + int xsize = abox.max[0] - abox.min[0] + 1 ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + int yinc = legendFont.getStringHeight () + PUSTR_BGAP ; + int num_vis = getNumVisible () ; + + int selected ; + getValue ( &selected ) ; + + for ( int i = top ; i < num && i < top + num_vis ; i++ ) + { + if ( i == selected ) + glColor4f ( (colour [ PUCOL_BACKGROUND ][0] + colour [ PUCOL_LABEL ][0]) / 2.0f, + (colour [ PUCOL_BACKGROUND ][1] + colour [ PUCOL_LABEL ][1]) / 2.0f, + (colour [ PUCOL_BACKGROUND ][2] + colour [ PUCOL_LABEL ][2]) / 2.0f, + (colour [ PUCOL_BACKGROUND ][3] + colour [ PUCOL_LABEL ][3]) / 2.0f ); + else + glColor4f ( colour [ PUCOL_LABEL ][0], + colour [ PUCOL_LABEL ][1], + colour [ PUCOL_LABEL ][2], + colour [ PUCOL_LABEL ][3] ) ; + + int x = PUSTR_LGAP ; + int y = yinc * ((i-top)+1) ; + + int xx = dx + abox.min[0] + x ; + int yy = dy + abox.max[1] - y ; + + int width ; + char str [ PUSTRING_MAX ] ; + strcpy ( str, list [ i ] ) ; + + /* + Does the string fit into the box? + + If not, chop it down one character at a time until + it does fit. + */ + + while ( 1 ) + { + width = legendFont.getStringWidth ( (char *)str ) + PUSTR_LGAP ; + + if ( width < xsize ) + break ; + + /* + Nibble off one character and try again + (Do that sneakily by replacing the last 4 characters with 3 dots) + */ + + strcpy ( & str [ strlen(str) - 4 ], "..." ) ; + } + + legendFont.drawString ( (char*)str, xx, yy ) ; + } + } + + draw_label ( dx, dy ) ; +} + + +void puListBox::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( button == active_mouse_button ) + { + if ( ( updown == active_mouse_edge ) || ( active_mouse_edge == PU_UP_AND_DOWN ) ) + { + lowlight () ; + + int yinc = legendFont.getStringHeight () + PUSTR_BGAP ; + int idx = top + ( abox.max[1] - PUSTR_BGAP - y ) / yinc; + if ( idx < 0 ) + idx = 0; + else if ( idx >= num ) + idx = num-1; + + setValue ( idx ) ; + + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + else + highlight () ; + } + else + lowlight () ; +} + + diff --git a/src/pui/puLocal.h b/src/pui/puLocal.h new file mode 100644 index 0000000..9ffdfcb --- /dev/null +++ b/src/pui/puLocal.h @@ -0,0 +1,31 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puLocal.h 1902 2004-03-21 19:03:23Z sjbaker $ +*/ + + +#define PU_USE_NONE 1 +#include "pu.h" + + +void puSetPasteBuffer ( const char *ch ) ; +char *puGetPasteBuffer () ; + diff --git a/src/pui/puMenuBar.cxx b/src/pui/puMenuBar.cxx new file mode 100644 index 0000000..8aefd54 --- /dev/null +++ b/src/pui/puMenuBar.cxx @@ -0,0 +1,179 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puMenuBar.cxx 2021 2005-05-06 18:31:27Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puMenuBar,puInterface) + + +void puMenuBar_drop_down_the_menu ( puObject *b ) +{ + // Inputs: b = pointer to the button on the menu which has been pushed + // p = pointer to the popup menu which is b's submenu + + puPopupMenu *p = (puPopupMenu *) b -> getUserData () ; + + if ( b -> getIntegerValue () ) + { +/* + SJBL Test hack. +*/ + puDeactivateWidget () ; + + p->reveal () ; // Reveal the submenu + + // If the parent floats in its own window, and the submenu drops off the window, + // expand the window to fit. + +#if 1 + puGroup *parent = p -> getParent () ; + + if ( ( parent != parent -> getParent () ) && parent -> getFloating () ) // DEPRECATED! -- we need to replace this code. + { + int temp_window = puGetWindow () ; + puSetWindow ( parent -> getWindow () ) ; + + puBox *par_box = parent -> getBBox () ; + puBox *cur_box = p -> getBBox () ; + int x_min = (cur_box->min[0] < 0) ? par_box->min[0] + cur_box->min[0] : par_box->min[0] ; + int x_max = (par_box->max[0] > par_box->min[0] + cur_box->max[0]) ? + par_box->max[0] : par_box->min[0] + cur_box->max[0] ; + int y_min = (cur_box->min[1] < 0) ? par_box->min[1] + cur_box->min[1] : par_box->min[1] ; + int y_max = (par_box->max[1] > par_box->min[1] + cur_box->max[1]) ? + par_box->max[1] : par_box->min[1] + cur_box->max[1] ; + int x_siz, y_siz; + puGetWindowSize ( &x_siz, &y_siz ) ; + if ( x_siz < (x_max - x_min) ) x_siz = x_max - x_min ; // Adjust the present size + if ( y_siz < (y_max - y_min) ) y_siz = y_max - y_min ; + puSetWindowSize ( x_siz, y_siz ) ; + + x_min = par_box->min[0] - x_min ; + y_min = y_siz - ( par_box->max[1] - par_box->min[1] ) ; + + /* If the parent window is SUPPOSED to be stuck at the top of the screen, move it. + - JCJ 6 June 2002 */ + if (parent -> getVStatus () == 1) + parent -> setPosition ( x_min, y_min ) ; + + puSetWindow ( temp_window ) ; + } +#endif + } + else + p->hide () ; + + for ( puObject *child = b -> getParent () -> getFirstChild () ; + child != NULL ; child = child -> getNextObject() ) + { + if (( child -> getType() & PUCLASS_BUTTON ) != 0 && child != b ) child -> clrValue () ; + if (( child -> getType() & PUCLASS_POPUPMENU ) != 0 && child != p ) child -> hide () ; + } + + // Move the popup menu to the last item in the "dlist" so it is drawn last + // (in front of everything else). + + puMoveToLast ( p ); +} + +void puMenuBar::add_submenu ( const char *str, char *items[], puCallback _cb[], + void *_user_data[] ) +{ + int w, h ; + getSize ( &w, &h ) ; + + puOneShot *b ; + + if ( bar_height > 0 ) + { + b = new puOneShot ( w+10, + 0, + w+10 + PUSTR_LGAP + puGetDefaultLegendFont().getStringWidth ( str ) + PUSTR_RGAP, + bar_height ) ; + b -> setLegend ( str ) ; + } + else + b = new puOneShot ( w+10, 0, str ) ; + + b -> setStyle ( PUSTYLE_SPECIAL_UNDERLINED ) ; + b -> setColourScheme ( colour[PUCOL_FOREGROUND][0], + colour[PUCOL_FOREGROUND][1], + colour[PUCOL_FOREGROUND][2], + colour[PUCOL_FOREGROUND][3] ) ; + b -> setCallback ( puMenuBar_drop_down_the_menu ) ; + b -> setActiveDirn ( PU_UP_AND_DOWN ) ; + + puPopupMenu *p = new puPopupMenu ( w+10, 0 ) ; + p->setActiveButton ( active_mouse_button ) ; + + b -> setUserData ( p ) ; + + if ( _user_data != NULL ) + for ( int i = 0 ; items[i] != NULL ; i++ ) + p -> add_item ( items[i], _cb[i], _user_data[i] ) ; + else + for ( int i = 0 ; items[i] != NULL ; i++ ) + p -> add_item ( items[i], _cb[i] ) ; + + p->close () ; + recalc_bbox () ; +} + +void puMenuBar::close (void) +{ + puInterface::close () ; + + if ( dlist == NULL ) + return ; + + int width = 0 ; + puObject *ob ; + + /* + Use alternate objects - which gets the puOneShot/puPopupMenu pairs + */ + + for ( ob = dlist ; ob != NULL ; ob = ob -> getNextObject() ) + { + int w, h ; + + /* Reposition the button so it looks nice */ + + ob -> getSize ( &w, &h ) ; + ob -> setPosition ( width, 0 ) ; + ob = ob -> getNextObject() ; + + /* Reposition the submenu so it sits under the button */ + + int w2, h2 ; + ob -> getSize ( &w2, &h2 ) ; + ob -> setPosition ( width, -h2 ) ; + + /* Next please! */ + width += w ; + } + + recalc_bbox () ; +} + + diff --git a/src/pui/puNative.h b/src/pui/puNative.h new file mode 100644 index 0000000..0abbb4f --- /dev/null +++ b/src/pui/puNative.h @@ -0,0 +1,101 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puNative.h 1857 2004-02-16 13:49:03Z stromberg $ +*/ + +#ifndef _PU_NATIVE_H_ +#define _PU_NATIVE_H_ + +#ifndef PU_USE_NATIVE +# define PU_USE_NATIVE +#endif + +#include "pu.h" + +#if defined(UL_GLX) +# include +#elif defined(UL_WGL) +// nothing +#elif defined(UL_AGL) +# include +#elif defined(UL_CGL) +# include +#endif + + +inline int puGetWindowNative () +{ +#if defined(UL_GLX) + return (int) glXGetCurrentDrawable () ; +#elif defined(UL_WGL) + return (int) wglGetCurrentDC () ; +#elif defined(UL_AGL) + return (int) aglGetCurrentDrawable () ; +#elif defined(UL_CGL) + return (int) CGLGetCurrentContext () ; +#else + return 0 ; +#endif +} + + +inline void puGetWindowSizeNative ( int *width, int *height ) +{ +#if defined(UL_GLX) + + Window root ; + int x, y ; + unsigned int w, h, b, d ; + + XGetGeometry ( glXGetCurrentDisplay (), + glXGetCurrentDrawable (), + &root, &x, &y, &w, &h, &b, &d ) ; + *width = w ; + *height = h ; + +#elif defined(UL_WGL) + + RECT r ; + GetClientRect( WindowFromDC( wglGetCurrentDC() ), &r ); + *width = r.right ; + *height = r.bottom ; + +#else // Help! Need implementations for more systems. + + GLint vp[4] ; + glGetIntegerv ( GL_VIEWPORT, vp ) ; + *width = vp[0] + vp[2] ; + *height = vp[1] + vp[3] ; + // Note: puSetOpenGLState calls glViewport(0, 0, w, h). + +#endif +} + + +inline void puInitNative () +{ + puSetWindowFuncs ( puGetWindowNative, NULL, + puGetWindowSizeNative, NULL ) ; + puRealInit () ; +} + + +#endif diff --git a/src/pui/puObject.cxx b/src/pui/puObject.cxx new file mode 100644 index 0000000..060888a --- /dev/null +++ b/src/pui/puObject.cxx @@ -0,0 +1,520 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puObject.cxx 2051 2005-11-10 20:21:22Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puObject,puValue) + + +static inline float clamp01 ( float x ) +{ + return (x >= 1.0f) ? 1.0f : x ; +} + +static puColour _puDefaultColourTable[] = +{ + { 0.5f, 0.5f, 0.5f, 1.0f }, /* PUCOL_FOREGROUND */ + { 0.3f, 0.3f, 0.3f, 1.0f }, /* PUCOL_BACKGROUND */ + { 0.7f, 0.7f, 0.7f, 1.0f }, /* PUCOL_HIGHLIGHT */ + { 0.0f, 0.0f, 0.0f, 1.0f }, /* PUCOL_LABEL */ + { 1.0f, 1.0f, 1.0f, 1.0f }, /* PUCOL_LEGEND */ + { 0.0f, 0.0f, 0.0f, 1.0f }, /* PUCOL_MISC */ + { 0.8f, 0.7f, 0.7f, 1.0f } /* PUCOL_EDITFIELD */ +} ; + + +static void load_colour_scheme ( float col[][4], float r, float g, + float b, float a ) +{ + puSetColour ( col [ PUCOL_FOREGROUND ], r, g, b, a ) ; + puSetColour ( col [ PUCOL_BACKGROUND ], r/2.0f, g/2.0f, b/2.0f, a ) ; + puSetColour ( col [ PUCOL_HIGHLIGHT ], clamp01(r*1.3f), clamp01(g*1.3f), + clamp01(b*1.3f), a ) ; + + if ( 4.0f * g + 3.0f * r + b > 4.0f ) + { + puSetColour ( col [ PUCOL_LEGEND ], 0.0f, 0.0f, 0.0f, a ) ; + puSetColour ( col [ PUCOL_MISC ], 0.0f, 0.0f, 0.0f, a ) ; + } + else + { + puSetColour ( col [ PUCOL_LEGEND ], 1.0f, 1.0f, 1.0f, a ) ; + puSetColour ( col [ PUCOL_MISC ], 1.0f, 1.0f, 1.0f, a ) ; + } +} + + +static int defaultStyle = PUSTYLE_DEFAULT ; +static int defaultBorderThickness = 5 ; +static puFont defaultLegendFont ; +static puFont defaultLabelFont ; +static float defaultColourScheme [ 4 ] ; + +void puSetDefaultStyle ( int style ) +{ + defaultStyle = style ; + + switch ( abs(style) ) + { + case PUSTYLE_SPECIAL_UNDERLINED : + defaultBorderThickness = 1 ; + break ; + + case PUSTYLE_SMALL_BEVELLED : + case PUSTYLE_SMALL_SHADED : + case PUSTYLE_BOXED : + defaultBorderThickness = 2 ; + break ; + + case PUSTYLE_BEVELLED : + case PUSTYLE_SHADED : + case PUSTYLE_DROPSHADOW : + defaultBorderThickness = 5 ; + break ; + } +} +int puGetDefaultStyle ( void ) { return defaultStyle ; } + +void puSetDefaultBorderThickness ( int t ) { defaultBorderThickness = t ; } +int puGetDefaultBorderThickness ( void ) { return defaultBorderThickness ; } + +void puSetDefaultFonts ( puFont legendFont, puFont labelFont ) +{ + defaultLegendFont = legendFont ; + defaultLabelFont = labelFont ; +} + +puFont puGetDefaultLabelFont ( void ) { return defaultLabelFont ; } +puFont puGetDefaultLegendFont ( void ) { return defaultLegendFont ; } + +void puGetDefaultFonts ( puFont *legendFont, puFont *labelFont ) +{ + if ( legendFont ) *legendFont = defaultLegendFont ; + if ( labelFont ) *labelFont = defaultLabelFont ; +} + +void puSetDefaultColourScheme ( float r, float g, float b, float a ) +{ + defaultColourScheme[0] = r ; + defaultColourScheme[1] = g ; + defaultColourScheme[2] = b ; + defaultColourScheme[3] = a ; + load_colour_scheme ( _puDefaultColourTable, r, g, b, a ) ; +} + +void puGetDefaultColourScheme ( float *r, float *g, float *b, float *a ) +{ + if ( r ) *r = defaultColourScheme[0] ; + if ( g ) *g = defaultColourScheme[1] ; + if ( b ) *b = defaultColourScheme[2] ; + if ( a ) *a = defaultColourScheme[3] ; +} + + + +void puObject::setColourScheme ( float r, float g, float b, float a ) +{ + load_colour_scheme ( colour, r, g, b, a ) ; + puPostRefresh () ; +} + +puObject::puObject ( int minx, int miny, int maxx, int maxy ) : puValue () +{ + type |= PUCLASS_OBJECT ; + + bbox.min[0] = abox.min[0] = minx ; + bbox.min[1] = abox.min[1] = miny ; + bbox.max[0] = abox.max[0] = maxx ; + bbox.max[1] = abox.max[1] = maxy ; + + active_mouse_edge = PU_UP ; + active_mouse_button = PU_LEFT_BUTTON ; + style = defaultStyle ; + visible = active = TRUE ; + highlighted = FALSE ; + am_default = FALSE ; + window = puGetWindow () ; + v_status = 0 ; + + cb = NULL ; + active_cb = NULL ; + down_cb = NULL ; + r_cb = NULL ; + border_thickness = defaultBorderThickness ; + render_data = NULL ; + user_data = NULL ; + next = prev = NULL ; + label = NULL ; + labelPlace = PUPLACE_LABEL_DEFAULT ; + labelFont = defaultLabelFont ; + legend = NULL ; + legendFont = defaultLegendFont ; + legendPlace = PUPLACE_LEGEND_DEFAULT ; + + when_to_deactivate = PUDEACTIVATE_ON_MOUSE_CLICK ; + + for ( int i = 0 ; i < PUCOL_MAX ; i++ ) + puSetColour ( colour[i], _puDefaultColourTable[i] ) ; + + parent = NULL ; + + if ( ! puNoGroup() ) + puGetCurrGroup() -> add ( this ) ; + + puPostRefresh () ; +} + + +puObject::~puObject () +{ + if ( parent != this && parent != NULL ) + parent -> remove ( this ) ; + + if ( this == puActiveWidget () ) + puDeactivateWidget () ; + + puPostRefresh () ; +} + +void puObject::recalc_bbox ( void ) +{ + bbox = abox ; + + if ( label != NULL ) + { + switch ( labelPlace ) // Extend the bounding box left and right + { + case PUPLACE_ABOVE_LEFT : + case PUPLACE_UPPER_LEFT : + case PUPLACE_CENTERED_LEFT : + case PUPLACE_LOWER_LEFT : + case PUPLACE_BELOW_LEFT : + bbox.min[0] -= labelFont.getStringWidth ( label ) + PUSTR_LGAP ; + break ; + + case PUPLACE_ABOVE_RIGHT : + case PUPLACE_UPPER_RIGHT : + case PUPLACE_CENTERED_RIGHT : + case PUPLACE_LOWER_RIGHT : + case PUPLACE_BELOW_RIGHT : + bbox.max[0] += labelFont.getStringWidth ( label ) + PUSTR_RGAP ; + break ; + } + + switch ( labelPlace ) // Extend the bounding box up and down + { + case PUPLACE_ABOVE_LEFT : + case PUPLACE_TOP_LEFT : + case PUPLACE_TOP_CENTERED : + case PUPLACE_TOP_RIGHT : + case PUPLACE_ABOVE_RIGHT : + bbox.max[1] += labelFont.getStringHeight ( label ) + labelFont.getStringDescender () + + PUSTR_TGAP ; + break ; + + case PUPLACE_BELOW_LEFT : + case PUPLACE_BOTTOM_LEFT : + case PUPLACE_BOTTOM_CENTERED : + case PUPLACE_BOTTOM_RIGHT : + case PUPLACE_BELOW_RIGHT : + bbox.min[1] -= labelFont.getStringHeight ( label ) + labelFont.getStringDescender () + + PUSTR_BGAP ; + break ; + } + } + + if ( parent != NULL ) + parent -> recalc_bbox () ; +} + +void puObject::getAbsolutePosition ( int *x, int *y ) const +{ + puGroup *par ; + *x = abox.min[0] ; + *y = abox.min[1] ; + + for ( par = getParent () ; par != NULL ; par = par -> getParent () ) + { + int x_offset, y_offset ; + par -> getPosition ( &x_offset, &y_offset ) ; + + *x += x_offset ; + *y += y_offset ; + } +} + +void puObject::draw_legend ( int dx, int dy ) +{ + if ( legend == NULL ) + return ; + + int xx, yy ; + + int lgap = PUSTR_LGAP ; + int rgap = PUSTR_RGAP ; + int tgap = PUSTR_TGAP ; + int bgap = PUSTR_BGAP ; + + if ( ( abs(style) != PUSTYLE_NONE ) && + ( abs(style) != PUSTYLE_PLAIN ) && + ( abs(style) != PUSTYLE_DROPSHADOW ) ) + { + if ( abs(style) != PUSTYLE_SPECIAL_UNDERLINED ) + { + lgap += getBorderThickness () ; + rgap += getBorderThickness () ; + tgap += getBorderThickness () ; + } + bgap += getBorderThickness () ; + } + + /* If greyed out then halve the opacity when drawing the legend */ + + if ( active ) + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + else + glColor4f ( colour [ PUCOL_LEGEND ][0], + colour [ PUCOL_LEGEND ][1], + colour [ PUCOL_LEGEND ][2], + colour [ PUCOL_LEGEND ][3] / 2.0f ) ; /* 50% more transparent */ + + switch ( getLegendPlace() ) + { + case PUPLACE_TOP_LEFT : + case PUPLACE_CENTERED_LEFT : + case PUPLACE_BOTTOM_LEFT : + case PUPLACE_LOWER_LEFT : + xx = lgap ; + break ; + + case PUPLACE_TOP_CENTERED : + case PUPLACE_CENTERED_CENTERED : + case PUPLACE_BOTTOM_CENTERED : + xx = ( abox.max[0] - abox.min[0] - legendFont.getStringWidth (legend) ) / 2 ; + break ; + + case PUPLACE_TOP_RIGHT : + case PUPLACE_CENTERED_RIGHT : + case PUPLACE_BOTTOM_RIGHT : + case PUPLACE_LOWER_RIGHT : + xx = abox.max[0] - abox.min[0] - legendFont.getStringWidth ( legend ) - rgap ; + break ; + + default : + ulSetError ( UL_WARNING, "PUI: Unrecognised LEGEND place %d", getLegendPlace() ) ; + return ; + } + + switch ( getLegendPlace() ) + { + case PUPLACE_TOP_LEFT : + case PUPLACE_TOP_CENTERED : + case PUPLACE_TOP_RIGHT : + yy = abox.max[1] - abox.min[1] - legendFont.getStringHeight ( legend ) - + legendFont.getStringDescender () - tgap ; + break ; + + case PUPLACE_CENTERED_LEFT : + case PUPLACE_CENTERED_CENTERED : + case PUPLACE_CENTERED_RIGHT : + case PUPLACE_LOWER_LEFT : /* Backwards compatibility to PUPLACE_LEFT */ + case PUPLACE_LOWER_RIGHT : /* Backwards compatibility to PUPLACE_RIGHT */ + default : + yy = ( abox.max[1] - abox.min[1] + - legendFont.getStringHeight ( legend ) ) / 2 + + legendFont.getStringDescender () ; + break ; + + case PUPLACE_BOTTOM_LEFT : + case PUPLACE_BOTTOM_CENTERED : + case PUPLACE_BOTTOM_RIGHT : + yy = bgap + legendFont.getStringDescender () ; + break ; + } + + legendFont.drawString ( legend, dx + abox.min[0] + xx, + dy + abox.min[1] + yy ) ; +} + +void puObject::draw_label ( int dx, int dy ) +{ + if ( label == NULL ) + return ; + + int xx, yy ; + + /* If greyed out then halve the opacity when drawing the label */ + + if ( active ) + glColor4fv ( colour [ PUCOL_LABEL ] ) ; + else + glColor4f ( colour [ PUCOL_LABEL ][0], + colour [ PUCOL_LABEL ][1], + colour [ PUCOL_LABEL ][2], + colour [ PUCOL_LABEL ][3] / 2.0f ) ; /* 50% more transparent */ + + switch ( getLabelPlace() ) + { + case PUPLACE_ABOVE_LEFT : + case PUPLACE_UPPER_LEFT : + case PUPLACE_CENTERED_LEFT : + case PUPLACE_LOWER_LEFT : + case PUPLACE_BELOW_LEFT : + xx = 0 ; + break ; + + case PUPLACE_TOP_LEFT : + case PUPLACE_BOTTOM_LEFT : + xx = abox.min[0] - bbox.min[0] + PUSTR_LGAP ; + break ; + + case PUPLACE_TOP_CENTERED : + case PUPLACE_BOTTOM_CENTERED : + xx = ( bbox.max[0] - bbox.min[0] - labelFont.getStringWidth ( label ) ) / 2 ; + break ; + + case PUPLACE_TOP_RIGHT : + case PUPLACE_BOTTOM_RIGHT : + xx = abox.max[0] - bbox.min[0] - labelFont.getStringWidth ( label ) - PUSTR_RGAP ; + break ; + + case PUPLACE_ABOVE_RIGHT : + case PUPLACE_UPPER_RIGHT : + case PUPLACE_CENTERED_RIGHT : + case PUPLACE_LOWER_RIGHT : + case PUPLACE_BELOW_RIGHT : + xx = bbox.max[0] - bbox.min[0] - labelFont.getStringWidth ( label ) ; + break ; + + default : + ulSetError ( UL_WARNING, "PUI: Unrecognised LABEL place %d", getLegendPlace() ) ; + return ; + } + + switch ( getLabelPlace() ) + { + case PUPLACE_ABOVE_LEFT : + case PUPLACE_TOP_LEFT : + case PUPLACE_TOP_CENTERED : + case PUPLACE_TOP_RIGHT : + case PUPLACE_ABOVE_RIGHT : + yy = bbox.max[1] - bbox.min[1] - labelFont.getStringHeight () - + labelFont.getStringDescender () ; + break ; + + case PUPLACE_UPPER_LEFT : + case PUPLACE_UPPER_RIGHT : + yy = abox.max[1] - bbox.min[1] - labelFont.getStringHeight ( label ) - PUSTR_TGAP ; + break ; + + case PUPLACE_CENTERED_LEFT : + case PUPLACE_CENTERED_RIGHT : + default : + yy = ( bbox.max[1] - bbox.min[1] - + labelFont.getStringHeight ( label ) ) / 2 + + labelFont.getStringDescender () ; + break ; + + case PUPLACE_LOWER_LEFT : + case PUPLACE_LOWER_RIGHT : + yy = abox.min[1] - bbox.min[1] + labelFont.getStringDescender () + PUSTR_BGAP ; + break ; + + case PUPLACE_BELOW_LEFT : + case PUPLACE_BOTTOM_LEFT : + case PUPLACE_BOTTOM_CENTERED : + case PUPLACE_BOTTOM_RIGHT : + case PUPLACE_BELOW_RIGHT : + yy = labelFont.getStringDescender () ; + break ; + } + + labelFont.drawString ( label, dx + bbox.min[0] + xx, + dy + bbox.min[1] + yy ) ; +} + + +int puObject::checkKey ( int key, int updown ) +{ + if ( updown == PU_UP ) + return FALSE ; + + if ( isReturnDefault() && ( key == '\r' || key == '\n' ) && ( window == puGetWindow () ) ) + { + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + checkHit ( active_mouse_button, PU_DOWN, (abox.min[0]+abox.max[0])/2, + (abox.min[1]+abox.max[1])/2 ) ; + checkHit ( active_mouse_button, PU_UP , (abox.min[0]+abox.max[0])/2, + (abox.min[1]+abox.max[1])/2 ) ; + return TRUE ; + } + + return FALSE ; +} + + +void puObject::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( button == active_mouse_button ) + { + if ( ( updown == active_mouse_edge ) || ( active_mouse_edge == PU_UP_AND_DOWN ) ) + { + lowlight () ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + else + highlight () ; + } + else + lowlight () ; +} + +int puObject::checkHit ( int button, int updown, int x, int y ) +{ + if ( ( puGetPressedButton () || ( updown == PU_UP ) ) && isHit( x, y ) || + puGetPressedButton () && ( isHit( x, y ) || ( puActiveWidget () == this ) ) ) + { + doHit ( button, updown, x, y ) ; + return TRUE ; + } + + return FALSE ; +} + + diff --git a/src/pui/puOneShot.cxx b/src/pui/puOneShot.cxx new file mode 100644 index 0000000..16b9625 --- /dev/null +++ b/src/pui/puOneShot.cxx @@ -0,0 +1,35 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puOneShot.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puOneShot,puButton) + + +void puOneShot::doHit ( int button, int updown, int x, int y ) +{ + puButton::doHit ( button, updown, x, y ) ; + setValue ( 0 ) ; +} + diff --git a/src/pui/puPW.h b/src/pui/puPW.h new file mode 100644 index 0000000..3b030ef --- /dev/null +++ b/src/pui/puPW.h @@ -0,0 +1,66 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puPW.h 1863 2004-02-17 01:43:21Z sjbaker $ +*/ + +#ifndef _PU_PW_H_ +#define _PU_PW_H_ + +#include "pu.h" + +#define PUPW_WINDOW_MAGIC 0x3DEB4938 /* Random! */ + +inline int puGetWindowPW() +{ + return PUPW_WINDOW_MAGIC ; +} + +inline void puSetWindowPW ( int window ) +{ + // Not possible because PW is a single-window library. + // But we can at least check that the handle matches. + + assert ( window == PUPW_WINDOW_MAGIC ) ; +} + +inline void puGetWindowSizePW ( int *width, int *height ) +{ + pwGetSize ( width, height ) ; +} + +inline void puSetWindowSizePW ( int width, int height ) +{ + pwSetSize ( width, height ) ; +} + +inline void puInitPW () +{ + puSetWindowFuncs ( puGetWindowPW, + puSetWindowPW, + puGetWindowSizePW, + puSetWindowSizePW ) ; + puRealInit () ; +} + + +#endif + diff --git a/src/pui/puPopup.cxx b/src/pui/puPopup.cxx new file mode 100644 index 0000000..7fde9d2 --- /dev/null +++ b/src/pui/puPopup.cxx @@ -0,0 +1,28 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puPopup.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puPopup,puInterface) + diff --git a/src/pui/puPopupMenu.cxx b/src/pui/puPopupMenu.cxx new file mode 100644 index 0000000..e5a49aa --- /dev/null +++ b/src/pui/puPopupMenu.cxx @@ -0,0 +1,245 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puPopupMenu.cxx 2021 2005-05-06 18:31:27Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puPopupMenu,puPopup) + + +puObject *puPopupMenu::add_item ( const char *str, puCallback _cb, + void *_user_data ) +{ + int w, h ; + getSize ( &w, &h ) ; + puOneShot *b = new puOneShot ( 0, h, str ) ; + b->setStyle ( PUSTYLE_PLAIN ) ; + b->setColourScheme ( colour[PUCOL_FOREGROUND][0], + colour[PUCOL_FOREGROUND][1], + colour[PUCOL_FOREGROUND][2], + colour[PUCOL_FOREGROUND][3] ) ; + b->setCallback ( _cb ) ; + b->setLegendPlace ( PUPLACE_LOWER_LEFT ) ; + b->setUserData ( _user_data ) ; + b->setActiveButton ( active_mouse_button ) ; + b->setWindow ( getWindow () ) ; + recalc_bbox () ; + return b ; +} + +void puPopupMenu::close ( void ) +{ + puPopup::close () ; + + int widest = 0 ; + puObject *ob = dlist ; + + /* + * June 17th, 1998, Shammi + * There seems to be some mismatch with the + * #define pumenusize and the actual size + * There seems to be some overlap resulting + * in more than one option being highlighted. + * By setting the size to the actual values, + * the overlap area seems to be less now. + */ + + int w, h ; + + for ( ob = dlist ; ob != NULL ; ob = ob -> getNextObject() ) + { + ob -> getSize ( &w, &h ) ; + + if ( w > widest ) widest = w ; + } + + for ( ob = dlist ; ob != NULL ; ob = ob -> getNextObject() ) + { + ob -> getSize ( &w, &h ) ; + ob -> setSize ( widest, h ) ; + } + + recalc_bbox () ; +} + + +int puPopupMenu::checkKey ( int key, int updown ) +{ + if ( dlist == NULL || ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown == PU_DOWN ) + { + hide () ; + + /* Turn everything off ready for next time. */ + + for ( puObject *bo = dlist ; bo != NULL ; bo = bo -> getNextObject() ) + bo -> clrValue () ; + } + + puObject *bo ; + + /* + We have to walk the list backwards to ensure that + the click order is the same as the DRAW order. + */ + + for ( bo = dlist ; + bo -> getNextObject() != NULL ; + bo = bo -> getNextObject() ) + /* Find the last object in our list. */ ; + + for ( ; bo != NULL ; bo = bo -> getPrevObject() ) + if ( bo -> checkKey ( key, updown ) ) + return TRUE ; + + return FALSE ; +} + + +int puPopupMenu::checkHit ( int button, int updown, int x, int y ) +{ + if ( dlist == NULL || ! isVisible () || ! isActive () || ( window != puGetWindow () ) ) + return FALSE ; + + /* If an item is going to fall out the bottom of the window and puGroup will */ + /* move it upward (around line 303 of puGroup.cxx), then make sure checkhit */ + /* realizes the thing has moved -- JCJ and Fay 5 June 2002 */ + + int absx, absy ; + getAbsolutePosition (&absx, &absy) ; + if ( absy < 0 ) + y += absy ; + + /* Must test 'isHit' before making the menu invisible! */ + + /* + * June 17th, 1998, Shammi : + * There seemed to be a miscalculation with the menus initially + * Therefore I moved the recalculation stuff before the clearing. + */ + + /* + This might be a bit redundant - but it's too hard to keep + track of changing abox sizes when daughter objects are + changing sizes. + */ + + int hit = isHit ( x, y ) ; + + /* If the mouse is clicked on another widget, hide the window - JCJ 18 Jun 2002 */ + if ( updown != PU_DRAG && !hit ) + hide () ; + + recalc_bbox(); + x -= abox.min[0] ; + y -= abox.min[1] ; + + /* + * June 17th, 1998, Shammi : + * Also clear the menu when the dragging the mouse and not hit. + */ + + if ( updown == active_mouse_edge || active_mouse_edge == PU_UP_AND_DOWN || + ( updown == PU_DRAG && !hit ) ) + { + + /* June 17th, 1998, Shammi : + * Do not hide the menu if mouse is dragged out + */ + + if ( updown != PU_DRAG ) + hide () ; + + /* Turn everything off ready for next time. */ + + /* June 17th, 1998, Shammi: + * Make sure we check for a hit, if the mouse is moved + * out of the menu. + */ + + for ( puObject *bo = dlist ; bo != NULL ; bo = bo -> getNextObject() ) + { + if ( ! hit ) + bo -> checkHit ( button, updown, x , y ) ; + + bo -> clrValue () ; + } + } + + if ( ! hit ) + return FALSE ; + + puObject *bo ; + int objecthit = FALSE ; + /* + We have to walk the list backwards to ensure that + the click order is the same as the DRAW order. + */ + + /* June 17th, 1998, Shammi : + * If the mouse is dragged and the menuItem is not hit, + * clear it + */ + + for ( bo = dlist ; + bo -> getNextObject() != NULL ; + bo = bo -> getNextObject() ) + if ( updown == PU_DRAG && ! bo -> checkHit ( button, updown, x, y ) ) + bo -> clrValue () ; + + /* Find the last object in our list. */ ; + + /* Following code permits highlighting and lowlighting */ + /* of oneshots underneath a PopupMenu. */ + /* - JCJ and Fay 31 May 2002 */ + + for ( ; bo != NULL ; bo = bo -> getPrevObject() ) + { + puBox *box = bo->getABox () ; + if ( ( x > box->min[0] ) && ( x < box->max[0] ) && + ( y > box->min[1] ) && ( y < box->max[1] ) ) + bo->highlight () ; + else + bo->lowlight () ; + /* Since actually RETURNing here would break the for loop early and kill the */ + /* correct lowlighting, just set a var to check later. - JCJ 3 Jun 2002 */ + if ( bo -> checkHit ( button, updown, x, y ) ) + objecthit = TRUE ; + } + + /* If the object was hit in the above for loop, then it.. was hit.*/ + if (objecthit) + return TRUE ; + + return FALSE ; +} + + diff --git a/src/pui/puRange.cxx b/src/pui/puRange.cxx new file mode 100644 index 0000000..3e107c0 --- /dev/null +++ b/src/pui/puRange.cxx @@ -0,0 +1,27 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF(puRange) + diff --git a/src/pui/puSDL.h b/src/pui/puSDL.h new file mode 100644 index 0000000..c9c48a8 --- /dev/null +++ b/src/pui/puSDL.h @@ -0,0 +1,55 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puSDL.h 2130 2007-11-18 21:46:37Z fayjf $ +*/ + +#ifndef _PU_SDL_H_ +#define _PU_SDL_H_ + +#ifndef PU_USE_SDL +# define PU_USE_SDL +#endif + +#include "pu.h" +#include "SDL.h" + + +inline int puGetWindowSDL () +{ + return 0; +} + +inline void puGetWindowSizeSDL ( int *width, int *height ) +{ + SDL_Surface *display = SDL_GetVideoSurface () ; + *width = display->w ; + *height = display->h ; +} + +inline void puInitSDL () +{ + puSetWindowFuncs ( puGetWindowSDL, NULL, + puGetWindowSizeSDL, NULL ) ; + puRealInit () ; +} + + +#endif diff --git a/src/pui/puSlider.cxx b/src/pui/puSlider.cxx new file mode 100644 index 0000000..0ffaa18 --- /dev/null +++ b/src/pui/puSlider.cxx @@ -0,0 +1,210 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puSlider.cxx 2148 2009-01-29 17:11:24Z fayjf $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF2(puSlider,puRange,puObject) + + +void puSlider::draw_slider_box ( int dx, int dy, const puBox &box, float val, const char *box_label ) +{ + int sd, od ; + if ( isVertical() ) { sd = 1 ; od = 0 ; } else { sd = 0 ; od = 1 ; } + + int sz = box.max [sd] - box.min [sd] ; // Size of slider box, in pixels + + if ( val < 0.0f ) val = 0.0f ; + if ( val > 1.0f ) val = 1.0f ; + + val *= (float) sz * (1.0f - slider_fraction) ; + + puBox bx ; + + bx.min [ sd ] = box.min [ sd ] + (int) val ; + bx.max [ sd ] = (int) ( (float) bx.min [ sd ] + (float) sz * slider_fraction ) ; + bx.min [ od ] = box.min [ od ] + 2 ; + bx.max [ od ] = box.max [ od ] - 2 ; + + bx.draw ( dx, dy, PUSTYLE_SMALL_SHADED, colour, FALSE, 2 ) ; + + if ( box_label ) + { + int xx ; + int yy ; + if ( isVertical () ) // Vertical slider, text goes to the right of it + { + xx = bx.max[0] + PUSTR_LGAP ; + yy = ( bx.max[1] + bx.min[1] - legendFont.getStringHeight ( box_label ) ) / 2 ; + } + else // Horizontal slider, text goes above it + { + xx = ( bx.max[0] + bx.min[0] - legendFont.getStringWidth ( box_label ) ) / 2 ; + yy = bx.max[1] + PUSTR_BGAP ; + } + + /* If greyed out then halve the opacity when drawing the label */ + + if ( active ) + glColor4fv ( colour [ PUCOL_LABEL ] ) ; + else + glColor4f ( colour [ PUCOL_LABEL ][0], + colour [ PUCOL_LABEL ][1], + colour [ PUCOL_LABEL ][2], + colour [ PUCOL_LABEL ][3] / 2.0f ) ; /* 50% more transparent */ + + legendFont.drawString ( box_label, dx + xx, dy + yy ) ; + } +} + +void puSlider::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + if ( ( style == PUSTYLE_BEVELLED ) || + ( style == PUSTYLE_SHADED ) ) + abox.draw ( dx, dy, -PUSTYLE_BOXED, colour, FALSE, 2 ) ; + else + abox.draw ( dx, dy, -style, colour, FALSE, border_thickness ) ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + else + { + float val = getFloatValue () ; + + draw_slider_box ( dx, dy, abox, ( val - minimum_value ) / ( maximum_value - minimum_value ) ) ; + + draw_legend ( dx, dy ) ; + } + + draw_label ( dx, dy ) ; +} + + +void puSlider::doHit ( int button, int updown, int x, int y ) +{ + if ( puActiveWidget() && ( this != puActiveWidget() ) ) + { + puActiveWidget() -> invokeDownCallback () ; + puDeactivateWidget () ; + } + + if ( updown != PU_DRAG ) + puMoveToLast ( this ); + + if ( button != active_mouse_button ) + return ; + + if ( updown == PU_UP ) + { + puDeactivateWidget () ; + return ; + } + + int sd = isVertical() ; + int coord = sd ? y : x ; + float range = maximum_value - minimum_value ; + float last_value = clamp ( getFloatValue () ); + float next_value = last_value ; + float norm_value = ( next_value - minimum_value ) / range ; + int box_len = abox.max [sd] - abox.min [sd] ; + + if ( updown == PU_DOWN ) { + int lower = abox.min [sd] + int(box_len * (1.0 - slider_fraction) * norm_value) ; + int upper = lower + int(box_len * slider_fraction) ; // upper/lower slider margin in pixels + float page_step = getPageStepSize () ; + + start_offset = -1 ; + if ( page_step_size <= 0.0f ) { // old slider behavior + start_offset = int(box_len * slider_fraction * 0.5f) ; + updown = PU_DRAG ; + + } else if ( coord < lower ) { + next_value -= page_step ; + + } else if ( coord > upper ) { + next_value += page_step ; + + } else { // new slider behavior + start_offset = coord - abox.min [sd] - int(box_len * (1.0 - slider_fraction) * norm_value) ; + puSetActiveWidget ( this, x, y ) ; + return; + } + } + + if ( updown == PU_DRAG && start_offset >= 0 ) { + if ( box_len == 0 ) { + norm_value = 0.5f ; + } else if ( slider_fraction >= 1.0f ) { + norm_value = 0.0f ; + } else { + norm_value = ( coord - abox.min[sd] - start_offset ) / ( box_len * (1.0f - slider_fraction) ) ; + } + next_value = norm_value * range + minimum_value ; + } + + setValue ( checkStep ( clamp ( next_value ) ) ) ; + + if ( next_value == last_value ) return ; + + switch ( cb_mode ) + { + case PUSLIDER_CLICK : + if ( updown == active_mouse_edge ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_DELTA : /* Deprecated! */ + if ( fabs ( last_cb_value - next_value ) >= cb_delta ) + { + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + } + break ; + + case PUSLIDER_ALWAYS : + default : + last_cb_value = next_value ; + puSetActiveWidget ( this, x, y ) ; + invokeCallback () ; + break ; + } +} + + +void puSlider::setSliderFraction ( float f ) +{ + int i = isVertical() ? 1 : 0 ; + int sz = abox.max [i] - abox.min [i] ; // Size of slider box, in pixels + float minf = 8.0f / sz ; // fraction that makes a 8px handle + + slider_fraction = (f1.0f) ? 1.0f : f ; + puPostRefresh () ; +} + diff --git a/src/pui/puText.cxx b/src/pui/puText.cxx new file mode 100644 index 0000000..217a44f --- /dev/null +++ b/src/pui/puText.cxx @@ -0,0 +1,39 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puText.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF1(puText,puObject) + + +void puText::draw ( int dx, int dy ) +{ + if ( !visible || ( window != puGetWindow () ) ) return ; + + if ( r_cb ) + r_cb ( this, dx, dy, render_data ) ; + + draw_label ( dx, dy ) ; +} + diff --git a/src/pui/puValue.cxx b/src/pui/puValue.cxx new file mode 100644 index 0000000..093008e --- /dev/null +++ b/src/pui/puValue.cxx @@ -0,0 +1,206 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: puValue.cxx 2076 2006-04-13 16:20:23Z bram $ +*/ + + +#include "puLocal.h" + +UL_RTTI_DEF(puValue) + + +static inline void skip_whitespace ( const char **str ) +{ + while ( isspace ( **str ) != 0 ) + (*str)++ ; +} + +static int strtoint ( const char *str ) +{ + skip_whitespace ( &str ) ; + + if ( *str == '\0') + return 0 ; + + int result ; + + if ( *str == '-' ) + { + result = -1 ; + skip_whitespace ( &(++str) ) ; + } + else + { + result = 1 ; + + if ( *str == '+' ) + skip_whitespace ( &(++str) ) ; + } + + if ( ulStrNEqual ( str, "0x", 2 ) == TRUE ) + result *= (int) strtol ( str + 2, NULL, 16 ) ; /* try hexadecimal */ + else if ( ulStrNEqual ( str, "0o", 2 ) == TRUE ) + result *= (int) strtol ( str + 2, NULL, 8 ) ; /* try octal */ + else if ( ulStrNEqual ( str, "0b", 2 ) == TRUE ) + result *= (int) strtol ( str + 2, NULL, 2 ) ; /* try binary */ + else + result *= (int) strtol ( str, NULL, 10 ) ; /* try decimal */ + + return result ; +} + + +void puValue::re_eval ( void ) +{ + if ( convert == FALSE ) + return ; + + if ( res_integer != NULL ) + { + floater = (float) *res_integer ; + + /* + Needed for puInput / puLargeInput: + Do not modify the string value unless necessary + */ + if ( *res_integer != strtoint ( string ) ) + sprintf ( string, "%d", *res_integer ) ; + + boolean = ( *res_integer != 0 ) ; + + puPostRefresh () ; + } + else if ( res_floater != NULL ) + { + integer = (int) *res_floater ; + + /* + Needed for puInput / puLargeInput: + Do not modify the string value unless necessary + */ + if ( *res_floater != strtod ( string, NULL ) ) + sprintf ( string, "%g", *res_floater ) ; + + boolean = ( *res_floater != 0.0f ) ; + + puPostRefresh () ; + } + else if ( res_string != NULL ) + { + integer = strtoint ( res_string ) ; + floater = (float) strtod ( res_string, NULL ) ; + boolean = ( strcmp ( res_string, "0" ) != 0 ) ; + puPostRefresh () ; + } + else if ( res_bool != NULL ) + { + integer = *res_bool ? 1 : 0 ; + floater = *res_bool ? 1.0f : 0.0f ; + + /* + Needed for puInput / puLargeInput: + Do not modify the string value unless necessary + */ + if ( *res_bool != ( strcmp ( string, "0" ) != 0 ) ) + sprintf ( string, "%d", *res_bool ? 1 : 0 ) ; + + puPostRefresh () ; + } +} + +void puValue::copy_stringval ( const char *str ) +{ + if ( res_string != NULL ) + { + /* Work around ANSI strncpy's null-fill behaviour */ + + res_string[0] = '\0' ; + strncat ( res_string, str, res_string_sz-1 ) ; + } + else + { + int str_len = strlen ( str ) ; + int new_size = string_size ; + + while ( new_size < str_len + 1 ) + /* While our array is too small, double it. */ + new_size += new_size ; + + while ( ( new_size > 4 * str_len + 1 ) && ( new_size > PUSTRING_INITIAL ) ) + /* While our array is too big, halve it */ + new_size /= 2 ; + + if ( new_size != string_size ) + /* If the array size has changed, allocate a new array */ + { + delete [] string ; + string = new char [ new_size ] ; + string_size = new_size ; + } + + memcpy ( string, str, str_len + 1 ) ; + } +} + +void puValue::setValue ( const char *s ) +{ + if ( s == NULL ) + s = "" ; + + copy_stringval ( s ) ; + + if ( convert == TRUE ) + { + *getIntegerp () = strtoint ( s ) ; + *getFloaterp () = (float) strtod ( s, NULL ) ; + *getBooleanp () = ( strcmp ( s, "0" ) != 0 ) ; + } + + puPostRefresh () ; +} + + +const char *puValue::getTypeString ( void ) const +{ + int i = getType () ; + + if ( i & PUCLASS_DIAL ) return "puDial" ; + if ( i & PUCLASS_LISTBOX ) return "puListBox" ; + if ( i & PUCLASS_ARROW ) return "puArrowButton" ; + if ( i & PUCLASS_DIALOGBOX ) return "puDialogBox" ; + if ( i & PUCLASS_SLIDER ) return "puSlider" ; + if ( i & PUCLASS_BUTTONBOX ) return "puButtonBox" ; + if ( i & PUCLASS_INPUT ) return "puInput" ; + if ( i & PUCLASS_MENUBAR ) return "puMenuBar" ; + if ( i & PUCLASS_POPUPMENU ) return "puPopupMenu" ; + if ( i & PUCLASS_POPUP ) return "puPopup" ; + if ( i & PUCLASS_ONESHOT ) return "puOneShot" ; + if ( i & PUCLASS_BUTTON ) return "puButton" ; + if ( i & PUCLASS_TEXT ) return "puText" ; + if ( i & PUCLASS_FRAME ) return "puFrame" ; + if ( i & PUCLASS_INTERFACE ) return "puInterface" ; + if ( i & PUCLASS_GROUP ) return "puGroup" ; + if ( i & PUCLASS_OBJECT ) return "puObject" ; + if ( i & PUCLASS_VALUE ) return "puValue" ; + + return "Unknown Object type." ; +} + diff --git a/src/pui/pui.dsp b/src/pui/pui.dsp new file mode 100644 index 0000000..e006280 --- /dev/null +++ b/src/pui/pui.dsp @@ -0,0 +1,218 @@ +# Microsoft Developer Studio Project File - Name="pui" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=pui - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pui.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pui.mak" CFG="pui - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pui - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "pui - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pui - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\fnt" /I "..\sg" /I "..\util" /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "PU_USE_NONE" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy pu.h ..\..\pu.h copy puGLUT.h "..\..\" copy puFLTK.h "..\..\" copy puSDL.h "..\..\" copy puNative.h "..\..\" copy puPW.h ..\..\ +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pui - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\fnt" /I "..\sg" /I "..\util" /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "PU_USE_NONE" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\pui_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy pu.h ..\..\pu.h copy puGLUT.h "..\..\" copy puFLTK.h "..\..\" copy puSDL.h "..\..\" copy puNative.h "..\..\" copy puPW.h ..\..\ +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "pui - Win32 Release" +# Name "pui - Win32 Debug" +# Begin Source File + +SOURCE=.\pu.cxx +# End Source File +# Begin Source File + +SOURCE=.\pu.h +# End Source File +# Begin Source File + +SOURCE=.\puArrowButton.cxx +# End Source File +# Begin Source File + +SOURCE=.\puBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puButton.cxx +# End Source File +# Begin Source File + +SOURCE=.\puButtonBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puDial.cxx +# End Source File +# Begin Source File + +SOURCE=.\puDialogBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puFLTK.h +# End Source File +# Begin Source File + +SOURCE=.\puFont.cxx +# End Source File +# Begin Source File + +SOURCE=.\puFrame.cxx +# End Source File +# Begin Source File + +SOURCE=.\puGLUT.h +# End Source File +# Begin Source File + +SOURCE=.\puGroup.cxx +# End Source File +# Begin Source File + +SOURCE=.\puInput.cxx +# End Source File +# Begin Source File + +SOURCE=.\puInputBase.cxx +# End Source File +# Begin Source File + +SOURCE=.\puInterface.cxx +# End Source File +# Begin Source File + +SOURCE=.\puListBox.cxx +# End Source File +# Begin Source File + +SOURCE=.\puLocal.h +# End Source File +# Begin Source File + +SOURCE=.\puMenuBar.cxx +# End Source File +# Begin Source File + +SOURCE=.\puNative.h +# End Source File +# Begin Source File + +SOURCE=.\puObject.cxx +# End Source File +# Begin Source File + +SOURCE=.\puOneShot.cxx +# End Source File +# Begin Source File + +SOURCE=.\puPopup.cxx +# End Source File +# Begin Source File + +SOURCE=.\puPopupMenu.cxx +# End Source File +# Begin Source File + +SOURCE=.\puPW.h +# End Source File +# Begin Source File + +SOURCE=.\puRange.cxx +# End Source File +# Begin Source File + +SOURCE=.\puSDL.h +# End Source File +# Begin Source File + +SOURCE=.\puSlider.cxx +# End Source File +# Begin Source File + +SOURCE=.\puText.cxx +# End Source File +# Begin Source File + +SOURCE=.\puValue.cxx +# End Source File +# End Target +# End Project diff --git a/src/pw/Makefile.am b/src/pw/Makefile.am new file mode 100644 index 0000000..151fbd3 --- /dev/null +++ b/src/pw/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_PW + +lib_LIBRARIES = libplibpw.a + +include_HEADERS = pw.h + +libplibpw_a_SOURCES = pw.cxx pwX11.cxx pwWindows.cxx pwMacOSX.cxx + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = pw.dsp + diff --git a/src/pw/pw.cxx b/src/pw/pw.cxx new file mode 100644 index 0000000..784363a --- /dev/null +++ b/src/pw/pw.cxx @@ -0,0 +1,6 @@ + +#include "ul.h" +#include "pw.h" + +/* This space intentionally left blank */ + diff --git a/src/pw/pw.dsp b/src/pw/pw.dsp new file mode 100644 index 0000000..d228464 --- /dev/null +++ b/src/pw/pw.dsp @@ -0,0 +1,106 @@ +# Microsoft Developer Studio Project File - Name="pw" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=pw - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pw.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pw.mak" CFG="pw - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pw - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "pw - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pw - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy pw.h ..\..\pw.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pw - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\pw_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy pw.h ..\..\pw.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "pw - Win32 Release" +# Name "pw - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\pwWindows.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\pw.h +# End Source File +# End Group +# End Target +# End Project diff --git a/src/pw/pw.h b/src/pw/pw.h new file mode 100644 index 0000000..626da03 --- /dev/null +++ b/src/pw/pw.h @@ -0,0 +1,111 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pw.h 1928 2004-04-06 23:45:19Z sjbaker $ +*/ + + +#ifndef _PW_H_ +#define _PW_H_ 1 + +#include + +#define PW_IS_PRESENT 1 + +typedef void pwResizeCB ( int w, int h ) ; +typedef void pwExitCB () ; +typedef void pwKeybdFunc ( int key, int updown, int x, int y ) ; +typedef void pwMouseFunc ( int key, int updown, int x, int y ) ; +typedef void pwMousePosFunc ( int x, int y ) ; + +void pwInit ( int multisample, int num_samples ) ; + +void pwInit ( int x, int y, int w, int h, + int multisample, + char *title, int border, int num_samples ) ; + +void pwSetCallbacks ( pwKeybdFunc *kb = NULL, pwMouseFunc *ms = NULL, + pwMousePosFunc *mp = NULL, pwResizeCB *rcb = NULL, + pwExitCB *ecb = NULL ) ; + +int pwGetModifiers () ; +void pwSwapBuffers () ; +void pwGetSize ( int *w, int *h ) ; +void pwSetSizeOrigin ( int x, int y, int w, int h ) ; +void pwSetSize ( int x, int y ) ; +void pwSetOrigin ( int w, int h ) ; +void pwSetCursor ( int c ) ; +void pwCleanup () ; +void pwSetAutoRepeatKey ( bool enable ) ; + +#define PW_CURSOR_NONE 0 +#define PW_CURSOR_RIGHT 1 +#define PW_CURSOR_LEFT 2 +#define PW_CURSOR_QUERY 3 +#define PW_CURSOR_AIM 4 +#define PW_CURSOR_CIRCLE 5 +#define PW_CURSOR_WAIT 6 +#define PW_CURSOR_CROSS 7 +#define PW_CURSOR_CUSTOM 8 + +/* + WARNING: These have to be the same as PU_KEY_whatever. +*/ + +#define PW_KEY_SPECIAL_OFFSET 256 + +#define PW_KEY_F1 (1 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F2 (2 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F3 (3 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F4 (4 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F5 (5 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F6 (6 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F7 (7 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F8 (8 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F9 (9 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F10 (10 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F11 (11 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_F12 (12 + PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_LEFT (100+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_UP (101+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_RIGHT (102+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_DOWN (103+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_PAGE_UP (104+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_PAGE_DOWN (105+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_HOME (106+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_END (107+ PW_KEY_SPECIAL_OFFSET) +#define PW_KEY_INSERT (108+ PW_KEY_SPECIAL_OFFSET) + +/* The three standard keyboard modifiers */ +#define PW_SHIFT 1 +#define PW_CTRL 2 +#define PW_ALT 4 + +/* Whether a button has been pressed (DOWN) or released (UP) */ +#define PW_DOWN 0 +#define PW_UP 1 + +/* Mouse Buttons */ +#define PW_LEFT_BUTTON 0 +#define PW_MIDDLE_BUTTON 1 +#define PW_RIGHT_BUTTON 2 + +#endif + diff --git a/src/pw/pwMacOSX.cxx b/src/pw/pwMacOSX.cxx new file mode 100644 index 0000000..ce76be0 --- /dev/null +++ b/src/pw/pwMacOSX.cxx @@ -0,0 +1,1034 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pwMacOSX.cxx 2095 2006-10-29 07:42:41Z bram $ +*/ + + +#include "ul.h" + +/* ONLY COMPILE THIS FILE FOR MACINTOSH OR MACOSX SYSTEMS */ + +/* PLEASE DON'T REMOVE THIS LINE AGAIN!!! */ +#if defined(UL_MACINTOSH) || defined(UL_MAC_OSX) +/* YES - THAT MEANS YOU! */ + + +/* +* +* - the fullscreen mode is indeed a "game mode"; +* - the fullscreen mode does not change the resolution of the screen: +* it uses the resolution (width, height and depth) in use before entering pw; +* - it is possible to toggle between modes by calling pwCleanup() +* and calling again pwInit; +* - the "border" parameter in pwInit is not used; +* - the "about" menu shows a standard alert box with the current version of plib +* and with the url to the plib official site. +* +*----------------------------------------- +* +* - with Mac OS 8.6 to 9.2: +* - must be linked with the following libraries: +* InterfaceLib, accessors.o, AppearanceLib, DrawSprocketLib +* and OpenGL (agl is in OpenGL) +* +* - ACTIVE_SLEEPTIME must be defined to 0 for the fastest execution; +* but it will not let other app to get events. +* +* - with Mac OS X: must be linked with the following frameworks: +* Carbon, AGL +* +* +* - if not using Xcode, the following compiler falgs should be set to avoid errors or warnings +* CPPFLAGS = -fpascal-strings -funsigned-char +*/ + +/* version of 2004-04-03 +* - changes from previous version: +* - fullscreen also implemented with mac OS 8.6 and 9; +* - key up events are now reported in mac OS 8.6 to 9.2 +* (these events are disabled by default...) +* - multisample added for mac OS X; +* - no auto-key repeat event by default (can be set by pwSetAutoRepeatKey(bool on_off) ); +* - better error handling and use of ulSetError; +*/ + +#if defined (UL_MAC_OSX) +# define TARGET_API_MAC_CARBON 1 +# include +# include +#elif defined (UL_MACINTOSH) +//# define TARGET_API_MAC_CARBON 0 +# undef ACCESSOR_CALLS_ARE_FUNCTIONS // already defined by ul.h !!! +# define ACCESSOR_CALLS_ARE_FUNCTIONS 1 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +//#else +//# error only for Mac OS (8.6 to OS X) operating system! +#endif + + +#include +#include +#include "pw.h" + + +/* Apple menu: */ +#define mApple 128 +#define iAbout 1 +/* File menu: */ +#ifdef UL_MAC_OSX +# define ACTIVE_SLEEPTIME 0 +# define INACTIVE_SLEEPTIME 10 +# define mFile 129 +# define iQuit 1 +#else // UL_MACINTOSH +# define ACTIVE_SLEEPTIME 1 // 0 is fastest, but does not let other app to catch events. + // set to 0 if you want your app only to have CPU time +# define INACTIVE_SLEEPTIME 10 +# define mFile 129 +# define iQuit 1 +#endif + +static bool pwInitialized = false ; +static int origin [2] = { 0, 0 } ; +static int size [2] = { 640, 480 } ; +static SInt16 horScrSze, verScrSze; // screen dimensions +static bool full_screen; +static bool auto_repeat_key = false ; +#if TARGET_API_MAC_CARBON +static bool pwQuitFlag ; +#endif +static int modifiers ; +static int click_modifiers ; +static SInt32 sleepTime = ACTIVE_SLEEPTIME; + +static pwResizeCB *resizeCB = NULL ; +static pwExitCB *exitCB = NULL ; +static pwKeybdFunc *kbCB = NULL ; +static pwMouseFunc *msCB = NULL ; +static pwMousePosFunc *mpCB = NULL ; + +#if TARGET_API_MAC_CARBON + static WindowPtr pwWindow = NULL ; +# define SETPORT(w) SetPortWindowPort(w) +# define GRAFPTR +#else + static CWindowPtr pwWindow = NULL ; +# define SETPORT(w) SetPort((GrafPtr)w) +# define GRAFPTR (GrafPtr) +#endif + +#ifdef UL_MACINTOSH +// DrawSprocket stuff: +static DSpContextReference dspContext; +#endif + +// generic OpenGL stuff +static AGLContext currContext = NULL ; + +static void HandleEvents(void); +static void Initialize(void); +static bool MakeWindow(int x, int y, int w, int h, char* title); +static void CreateContext(int multisample, int num_samples, bool fullscreen); +static void MakeMenu(void); +static void handleMenuEvent(long menuResult); +static void handleKeyEvent(EventRecord* eventPtr, int updown); +static void handleMouseMoveEvt(EventRecord* eventPtr); +#if TARGET_API_MAC_CARBON +static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon); +#endif +static void pwAboutBox(void); +static void defaultExitFunc (); +#ifdef UL_MACINTOSH +static bool InitDSP(); +#endif + + +static void CtoPcpy( Str255 pstr, char *cstr ); + + +#if TARGET_API_MAC_CARBON +static pascal OSErr +QuitAppleEventHandler( const AppleEvent*, AppleEvent*, long ) +{ + pwQuitFlag = true; + return(noErr); +} +#endif + +static void Initialize() +{ + OSErr err = noErr; + BitMap scrBits; + +#if !TARGET_API_MAC_CARBON + InitGraf(&qd.thePort); + InitFonts(); + FlushEvents(everyEvent, 0); + InitWindows(); + InitMenus(); + InitDialogs(nil); + sleepTime = ACTIVE_SLEEPTIME; + SetEventMask(everyEvent); // to enable key up events !!! +#else + pwQuitFlag = false; + err = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false ); + // this funtion is to handle the "cmd+Q" event. This key event is not send as a keyDown event with carbon! + if (err != noErr) + { + ulSetError(UL_FATAL, "Error in initialization of the system." ); + } +#endif + InitCursor(); + exitCB = defaultExitFunc; + // get screen size: + GetQDGlobalsScreenBits(&scrBits); + horScrSze = scrBits.bounds.right - scrBits.bounds.left; + verScrSze = scrBits.bounds.bottom - scrBits.bounds.top; +} + +// Copy a C string into a Pascal string +static void CtoPcpy( Str255 pstr, char* cstr ) +{ + int i = 1; + while( (*cstr) && (i < 255) ) pstr[i++] = *cstr++; + pstr[0] = i - 1; +} + +static bool MakeWindow(int x, int y, int w, int h, char* title) +{ + Rect wRect; + Str255 pTitle; + + CtoPcpy( pTitle, title ); + SetRect(&wRect, x, y, w+x, h+y); +#if TARGET_API_MAC_CARBON + pwWindow = NewCWindow(nil, &wRect, pTitle, true, zoomNoGrow, (WindowPtr) -1, true, 0); +#else + if (full_screen) // use DrawSprocket + pwWindow = (CWindowPtr) NewCWindow (nil, &wRect, pTitle, true, plainDBox, (WindowPtr)-1, 0, 0); + else + pwWindow = (CWindowPtr) NewCWindow(nil, &wRect, pTitle, true, zoomNoGrow, (WindowPtr) -1, true, 0); +#endif + if (pwWindow == NULL) + { + ulSetError(UL_WARNING, "pw: can not open a window; will exit"); + return false; + } + SETPORT(pwWindow); + return true; +} + +static void CreateContext(int multisample, int num_samples, bool fullscreen) +{ + AGLPixelFormat aglPixFmt; + // Choose pixel format: + GLint attrib[10]; + GLint i = 0; + attrib[i++] = AGL_RGBA; + attrib[i++] = AGL_DOUBLEBUFFER; +#ifdef UL_MAC_OSX + if (fullscreen) attrib[i++] = AGL_FULLSCREEN; +#endif + attrib[i++] = AGL_DEPTH_SIZE; + attrib[i++] = 32; + //attrib[i++] = AGL_ALL_RENDERERS; + attrib[i++] = AGL_NONE; + + if (multisample) + { +#ifdef UL_MAC_OSX + i--; + attrib[i++] = AGL_SAMPLE_BUFFERS_ARB ; + attrib[i++] = 1 ; + attrib[i++] = AGL_SAMPLES_ARB ; + attrib[i++] = num_samples; + attrib[i++] = AGL_NONE; + aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib); + if (aglPixFmt == NULL) + { + multisample = 0; // retry without + ulSetError(UL_WARNING, "pw: multisample pixel format not found"); + i -= 5; + attrib[i++] = AGL_NONE; + } +#else + multisample = 0; // retry without + ulSetError(UL_WARNING, "pw: multisample not implemented with MacOS 8 & 9"); +#endif + } + if (!multisample) + { + aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib); + } + if (aglPixFmt == NULL) + { + ulSetError(UL_FATAL, "pw: can not find a pixel format"); + } + + // Create an AGL context + currContext = aglCreateContext(aglPixFmt, NULL); + aglDestroyPixelFormat ( aglPixFmt ); + if (currContext == NULL) + { + ulSetError(UL_FATAL, "pw: can not create an OpenGL context\n"); + } + // swap buffers only every k vertical retrace: + //long k = 100; + //aglSetInteger(currContext, AGL_SWAP_INTERVAL, &k); +} + +void MakeMenu() +{ + SInt32 response; + MenuRef menu; + CreateNewMenu(mApple, 0, &menu); + SetMenuTitleWithCFString( menu, CFSTR("Plib") ); + + InsertMenu(menu, 0); + InsertMenuItemTextWithCFString(menu, CFSTR("About Plib..."), 0, 0, iAbout); + +#if !TARGET_API_MAC_CARBON + AppendResMenu(menu, 'DRVR'); +#endif + + // If we not running on OS X then we need to add a File:Quit command: + Gestalt( gestaltMenuMgrAttr, &response ); + if ( ( response & gestaltMenuMgrAquaLayoutMask ) == 0 ) + { + menu = NewMenu (mFile, (ConstStr255Param) "File"); // new menu + InsertMenu (menu, 0); // add menu to end + AppendMenu (menu, (ConstStr255Param) "Quit"); // add items + } + + DrawMenuBar(); +} + +static void defaultExitFunc () +{ + pwCleanup () ; + exit(0); +} + +void pwSetCallbacks( pwKeybdFunc *kb, pwMouseFunc *ms, + pwMousePosFunc *mp, pwResizeCB *rcb, + pwExitCB *ecb ) +{ + resizeCB = rcb; + exitCB = (ecb == NULL) ? defaultExitFunc : ecb ; + kbCB = kb ; + msCB = ms ; + mpCB = mp ; +} + +int pwGetModifiers() +{ + return modifiers; +} + +void pwSetAutoRepeatKey( bool on_off ) +{ + auto_repeat_key = on_off; +} + +static void pwAboutBox() +{ + // show a dialog box with info on plib: + SInt16 outItemHit; + char version[32]; + Str255 Pversion; + char* about = "for more info see "; + Str255 Pabout; + sprintf(version, "PLIB v %i.%i.%i", PLIB_MAJOR_VERSION, PLIB_MINOR_VERSION, PLIB_TINY_VERSION ); + CtoPcpy( Pversion, version ); + CtoPcpy( Pabout, about ); + StandardAlert ( kAlertPlainAlert, + Pversion, + Pabout, + NULL, + &outItemHit ); +} + +static void handleMenuEvent(long menuCmd) +{ +#if !TARGET_API_MAC_CARBON + Str255 DAName; +#endif + int menuNum = HiWord(menuCmd); + int itemNum = LoWord(menuCmd); + switch (menuNum) + { + case mApple: // apple menu: + { + switch (itemNum) + { + case iAbout: pwAboutBox(); break; + default: +#if !TARGET_API_MAC_CARBON + GetMenuItemText(GetMenuHandle(mApple), itemNum, DAName); + OpenDeskAcc(DAName); +#endif + break; + } + break; + } + case mFile: // File menu: + { + switch (itemNum) + { + case iQuit: (*exitCB)(); break; + default: ; + } + } + } + HiliteMenu(0); // remove. the highlight on the selected menu. +} + +static int translateKey(int ch, int key) +{ + /* + if ( modifiers & PW_ALT ) + { + UInt32 state = 0; + long keyResult = KeyTranslate ( (void *) GetScriptManagerVariable (smKCHRCache), + (UInt16) key, &state ); + ch = 0xFF & keyResult; + } + */ + // special characters: + switch (ch) + { + case kLeftArrowCharCode: ch = PW_KEY_LEFT; break; + case kUpArrowCharCode: ch = PW_KEY_UP; break; + case kRightArrowCharCode: ch = PW_KEY_RIGHT; break; + case kDownArrowCharCode: ch = PW_KEY_DOWN; break; + case kPageUpCharCode: ch = PW_KEY_PAGE_UP; break; + case kPageDownCharCode: ch = PW_KEY_PAGE_DOWN; break; + case kHomeCharCode: ch = PW_KEY_HOME; break; + case kEndCharCode: ch = PW_KEY_END; break; + case kHelpCharCode: ch = PW_KEY_INSERT; break; + case kFunctionKeyCharCode: + // fonction keys: + switch(key) + { + case 0x7A: ch = PW_KEY_F1; break; + case 0x78: ch = PW_KEY_F2; break; + case 0x63: ch = PW_KEY_F3; break; + case 0x76: ch = PW_KEY_F4; break; + case 0x60: ch = PW_KEY_F5; break; + case 0x61: ch = PW_KEY_F6; break; + case 0x62: ch = PW_KEY_F7; break; + case 0x64: ch = PW_KEY_F8; break; + case 0x65: ch = PW_KEY_F9; break; + case 0x6D: ch = PW_KEY_F10; break; + case 0x67: ch = PW_KEY_F11; break; + case 0x6F: ch = PW_KEY_F12; break; + default: break; + } + break; + default: ; + } + return ch; +} + +static void handleKeyEvent(EventRecord* eventPtr, int updown) +{ + GrafPtr origPort; + + if ( ! kbCB ) return; + // handle key_events: + int ch = eventPtr->message & charCodeMask ; // character pressed + int key = (eventPtr->message & keyCodeMask) >> 8 ; // key pressed + ch = translateKey(ch, key); + GetPort(&origPort); + SETPORT(pwWindow); + GlobalToLocal(&eventPtr->where); + (*kbCB) ( ch, updown, eventPtr->where.h, eventPtr->where.v ) ; + SetPort(origPort); + return; +} + + +static void handleDrag(WindowPtr window, Point mouseloc) +{ + Point loc; + GrafPtr origPort; + Rect dragBounds, windowBounds; + + GetRegionBounds(GetGrayRgn(), &dragBounds); + + DragWindow(window, mouseloc, &dragBounds); + + GetPort (&origPort); // save Port + SETPORT(pwWindow); + + GetWindowPortBounds(window, &windowBounds); + loc.h = windowBounds.left; + loc.v = windowBounds.top; + + LocalToGlobal (&loc); + origin[0] = loc.h; // save new origin + origin[1] = loc.v; + + MoveWindow(GRAFPTR pwWindow, loc.h, loc.v, false); + // update OpenGL Context: + aglUpdateContext(currContext); + + //SETPORT(pwWindow); + SetPort (origPort); // restore Port +} + +static void handleGoAwayBox(WindowPtr window, Point mouseloc) +{ + GrafPtr origPort; + + GetPort(&origPort); + SETPORT(window); + + if( TrackGoAway(window, mouseloc) ) + { + (*exitCB)(); // quit... + } + + SetPort(origPort); +} + +static int last_m_h = 0; +static int last_m_v = 0; + +static void handleMouseMoveEvt(EventRecord* eventPtr) +{ + GrafPtr origPort; + + if ( ! mpCB ) return; + + int m_h = eventPtr->where.h; + int m_v = eventPtr->where.v; + if ( (m_v != last_m_v) || (m_h != last_m_h) ) + { + last_m_v = m_v ; last_m_h = m_h ; // screen coord. + GetPort(&origPort); + SETPORT(pwWindow); + GlobalToLocal(&eventPtr->where); + (*mpCB) ( eventPtr->where.h, eventPtr->where.v ) ; + SetPort(origPort); + } + return; +} + +static void HandleEvents() +{ + EventRecord event; + GrafPtr origPort; +// bool testNextEvent = true; + +#if TARGET_API_MAC_CARBON + if (pwQuitFlag) (*exitCB)(); // quit! +#endif + +// while ( testNextEvent ) +// { + WaitNextEvent(everyEvent, &event, sleepTime, nil) ; +#ifdef UL_MACINTOSH + if (full_screen) + { + Boolean dspEventProcessed; + DSpProcessEvent(&event, &dspEventProcessed); + if (dspEventProcessed) return; + } +#endif + int updown = PW_UP ; + + modifiers = 0; + if ( event.modifiers & (shiftKey | rightShiftKey) ) + modifiers |= PW_SHIFT ; + if ( event.modifiers & (optionKey | rightOptionKey) ) + modifiers |= PW_ALT ; + if ( event.modifiers & (controlKey | rightControlKey) ) + modifiers |= PW_CTRL ; + + switch ( event.what ) + { + case keyDown: + // test for a 'command' + key (menu): + if(event.modifiers & cmdKey) + { + char achar = (char) (event.message & charCodeMask); + handleMenuEvent( MenuKey(achar) ) ; + return; + } + // FALLTHROUGH (not a command) + handleKeyEvent(&event, PW_DOWN); + return; + case autoKey: + if ( ! auto_repeat_key ) + { // report a mouse move event, if any: + handleMouseMoveEvt(&event); + return; + } + // else report a key down event: + updown = PW_DOWN ; + case keyUp: + handleKeyEvent(&event, updown); + return; + + case mouseDown: + { + short part; + WindowPtr window; + + part = FindWindow(event.where, &window); + switch(part) + { + case inMenuBar: + handleMenuEvent( MenuSelect(event.where) ); + return; + #if !TARGET_API_MAC_CARBON + case inSysWindow: + SystemClick(&event, window); + break; + #endif + case inDrag: + handleDrag(window, event.where); + return; + case inGoAway: + handleGoAwayBox(window, event.where); + return; + case inContent: + if(!window) return; + if ( GRAFPTR pwWindow != FrontWindow() ) + { + SelectWindow (GRAFPTR pwWindow); + return; + } + } + // FALLTHROUGH (not a system click) + updown = PW_DOWN ; + click_modifiers = modifiers; + } + case mouseUp: + int button; + switch( click_modifiers ) + { + case PW_ALT : button = PW_RIGHT_BUTTON ; break ; + case PW_CTRL : button = PW_MIDDLE_BUTTON ; break ; + default : button = PW_LEFT_BUTTON ; break ; + } + GetPort(&origPort); + SETPORT(pwWindow); + GlobalToLocal(&event.where); + modifiers = 0; // reset modifiers with mouse events + if (msCB) (*msCB)( button, updown, event.where.h, event.where.v ); + SetPort(origPort); + return; + + case osEvt: + { + unsigned char subcode = event.message >> 24; + if (subcode == suspendResumeMessage) // Suspend/resume event + if (resumeFlag & event.message) // Resume + { + #ifdef UL_MACINTOSH + if (full_screen) sleepTime = 0; + else + #endif + sleepTime = ACTIVE_SLEEPTIME; + return; + } + else // suspend + sleepTime = INACTIVE_SLEEPTIME; + break; + } + case activateEvt: + if (event.modifiers & activeFlag) + { + #ifdef UL_MACINTOSH + if (full_screen) sleepTime = 0; + else + #endif + sleepTime = ACTIVE_SLEEPTIME; + return; + } + else sleepTime = INACTIVE_SLEEPTIME; + break; + + case updateEvt: + case nullEvent: // likely an idle event (i.e. no events) + // report mouse location, only if mouse has moved: + // (there is no mouse move events on Mac!) + handleMouseMoveEvt(&event); + return; + + default : /* printf ("event: %i \n", event.what); */ break; + + } // switch(event.what) +// } // while ( testNextEvent ) +} + + +#ifdef UL_MACINTOSH +bool InitDSP() +{ + DSpContextAttributes dspAttributes; + DisplayIDType displayID; + + if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpStartup) + { + ulSetError(UL_WARNING, "DrawSprocket extension not found"); + return false; + } + // start DrawSprocket: + if (noErr != DSpStartup ()) + { + ulSetError(UL_WARNING, "Unable to start DrawSprocket"); + return false; + } + + // initialise to zero all fields of DSpContextAttributes: + BlockZero( &dspAttributes, sizeof (DSpContextAttributes) ); + dspAttributes.displayWidth = horScrSze; + dspAttributes.displayHeight = verScrSze; + dspAttributes.colorNeeds = kDSpColorNeeds_Require; + dspAttributes.displayDepthMask = kDSpDepthMask_16; + dspAttributes.displayBestDepth = 16; + dspAttributes.backBufferDepthMask = kDSpDepthMask_All; // must be specified even if only fornt buffer is needed + dspAttributes.backBufferBestDepth = 16; + dspAttributes.pageCount = 1; + //dspAttributes.contextOptions = 0 | kDSpContextOption_DontSyncVBL; // no page flipping and no VBL sync needed + // look for a DSp context: + dspContext = NULL; + OSStatus err = DSpFindBestContext(&dspAttributes, &dspContext); + ulSetError(UL_DEBUG, " display width = %li, height = %li, depth = %li, mask = %lX ", + dspAttributes.displayWidth, + dspAttributes.displayHeight, + dspAttributes.displayBestDepth, + dspAttributes.displayDepthMask ); + if (err != noErr) + { + if (err == kDSpContextNotFoundErr) + ulSetError(UL_WARNING, "Unable to find a DrawSprocket context"); + else + ulSetError(UL_WARNING, "DSpFindBestContext error"); + return false; + } + err = DSpContext_Reserve ( dspContext, &dspAttributes ); + if (noErr != err) + { + ulSetError(UL_WARNING, "Unable to set the display!"); + return false; + } + // activate DSp context: + //DSpContext_FadeGammaOut (NULL, NULL); // remove for debug + DSpContext_SetState (dspContext, kDSpContextState_Active); + if ( !MakeWindow( 0, 0, horScrSze, verScrSze, "" ) ) return false; + //DSpContext_FadeGammaIn (NULL, NULL); + return true; +} +#endif + +void pwInit ( int multisample, int num_samples ) +{ + pwInit ( 0,0,-1,-1, multisample, "", 0, num_samples ) ; +} + + +void pwInit ( int x, int y, int w, int h, int multisample, + char *title, int border, int num_samples ) +{ + if (pwInitialized) + { + ulSetError(UL_WARNING, "pwInit already called"); + return; + } + + full_screen = ( (w<0) || (h<0) ) ? true : false ; + Initialize(); + pwInitialized = true; + pwWindow = NULL; + + // Initialize OpenGL stuff (format, context, ...): + CreateContext(multisample, num_samples, full_screen); + + if (full_screen) + { + sleepTime = 0; +#ifdef UL_MACINTOSH + // initialisation of fullscreen mode with DrawSprocket + dspContext = NULL; + if ( ! InitDSP() ) + { + pwCleanup(); // releases GL contexts, windows, ... + exit(4); + } + // attach OpenGl to the screen: + aglSetDrawable(currContext, GetWindowPort (GRAFPTR pwWindow)); + /* + ulSetError(UL_DEBUG, "aglSetDrawable error: %s", + (const char*) aglErrorString(aglGetError()) ); + */ + // activate our GL context + aglSetCurrentContext(currContext); +#else + aglSetCurrentContext(currContext); + aglSetFullScreen (currContext, 0, 0, 0, 0); +#endif + // initialise origin and size of the window (for pwGetSize) + origin[0] = 0; + origin[1] = 0; + size[0] = horScrSze; + size[1] = verScrSze; + } + else // full_screen + { + if ( ! MakeWindow(x,y,w,h, title) ) + { + pwCleanup(); // releases GL contexts, windows, ... + exit(4); + } + // Attach the context to the window + aglSetDrawable(currContext, GetWindowPort (GRAFPTR pwWindow)); +#ifdef UL_MACINTOSH + { // because aglSetDrawable is slow... (not needed with OSX) + EventRecord event; + WaitNextEvent (everyEvent, &event, 2, NULL); + } +#endif + aglSetCurrentContext(currContext); + + // create a default menu bar (with the "quit" command): + MakeMenu(); + // initialise origin and size of the window (for pwGetSize) + origin[0] = x; + origin[1] = y; + size[0] = w; + size[1] = h; + } +} + + +void pwGetSize ( int *w, int *h ) +{ + *w = size[0]; + *h = size[1]; +} + +//special cursors: +static Cursor q_curs = // question mark cursor +{ + {0x0380, 0x07c0, 0x0c60, 0x1830, 0x1830, 0x0030, 0x0060, 0x00c0, + 0x0180, 0x0180, 0x0180, 0, 0, 0x0180, 0x0180, 0}, // data + {0x0380, 0x07e0, 0x0ff0, 0x1e38, 0x1c38, 0x1c38, 0x0078, 0x00f0, + 0x01e0, 0x01c0, 0x01c0, 0x01c0, 0, 0x01c0, 0x01c0, 0x01c0}, // mask + { 14, 7 } // hotspot +}; +static Cursor c_curs = // circle cursor +{ + {0, 0x03e0, 0x0c18, 0x1004, 0x2002, 0x2002, 0x4001, 0x4001, + 0x4001, 0x4001, 0x4001, 0x2002, 0x2002, 0x1004, 0x0c18, 0x03e0}, // data + {0, 0x03e0, 0x0ff8, 0x1c1c, 0x3006, 0x3006, 0x6003, 0x6003, + 0x6003, 0x6003, 0x6003, 0x3006, 0x3006, 0x1c1c, 0x0ff8, 0x03e0}, // mask + { 8, 8 } // hotspot +}; +static Cursor r_curs = // right arrow cursor +{ + {0, 0x0008, 0x0018, 0x0038, 0x0078, 0x00f8, 0x01f8, 0x03f8, + 0x07f8, 0x00f8, 0x00d8, 0x0188, 0x0180, 0x0300, 0x0300, 0}, // data + {0x000c, 0x001c, 0x003c, 0x007c, 0x00fc, 0x01fc, 0x03fc, 0x07fc, + 0x0ffc, 0x0ffc, 0x01fc, 0x03dc, 0x03cc, 0x0780, 0x0780, 0x0700}, // mask + { 1, 12 } // hotspot +}; +static Cursor x_curs = // circle + cross cursor +{ + {0, 0x03e0, 0x0c98, 0x1084, 0x2082, 0x2082, 0x4081, 0x4081, + 0x7fff, 0x4081, 0x4081, 0x2082, 0x2082, 0x1084, 0x0c98, 0x03e0}, // data + {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}, // mask + { 8, 8 } // hotspot +}; + + +void pwSetCursor ( int c ) +{ + UInt32 csr = 0xFFFF; + Cursor * csrPtr; + switch (c) + { + case PW_CURSOR_NONE : HideCursor(); return; + case PW_CURSOR_LEFT : csr = kThemeArrowCursor; break; + case PW_CURSOR_RIGHT : csrPtr = &r_curs; break; + case PW_CURSOR_AIM : csrPtr = &x_curs; break; + //case PW_CURSOR_CARET : csr = kThemeIBeamCursor; break; // ??? + case PW_CURSOR_WAIT : csr = kThemeWatchCursor; break; + case PW_CURSOR_CROSS : csr = kThemeCrossCursor; break; + case PW_CURSOR_QUERY : csrPtr = &q_curs; break; + case PW_CURSOR_CIRCLE : csrPtr = &c_curs; break; + case PW_CURSOR_CUSTOM : + default : csr = kThemeArrowCursor; + ulSetError(UL_WARNING, "Unknown or unavailable cursor type\n"); + } + if (csr==0xFFFF) SetCursor(csrPtr); + else SetThemeCursor(csr); + ShowCursor(); +} + + +void pwSetSize ( int w, int h ) +{ + if (!pwInitialized) + { + ulSetError(UL_WARNING, "call to pwSetSize without call to pwInit"); + return; + } + if ( full_screen ) + { + ulSetError(UL_WARNING, "call to pwSetSize in full screen mode"); + return; + } + if ( ( w == size[0] ) && ( h == size[1] ) ) return; + + GrafPtr origPort; + + if (w<50) w = 50; + if (w>horScrSze) w = horScrSze; + if (h<50) h = 50; + if (h>verScrSze) h = verScrSze; + + GetPort(&origPort); + + size[0] = w; // save size + size[1] = h; + + SizeWindow(GRAFPTR pwWindow, w, h, false); + // update OpenGL Context: + aglUpdateContext(currContext); + + if ( resizeCB != NULL ) (*resizeCB) ( w, h ) ; + + SetPort(origPort); +} + + +void pwSetOrigin ( int x, int y ) +{ + if (!pwInitialized) + { + ulSetError(UL_WARNING, "call to pwSetOrigin without call to pwInit"); + return; + } + if ( full_screen ) + { + ulSetError(UL_WARNING, "call to pwSetOrigin in full screen mode"); + return; + } + if ( ( x == origin[0] ) && ( y == origin[1] ) ) return; + + if ( (x + size[0]) < 10) x = 10 - size[0]; + if ( x > (horScrSze-10) ) x = horScrSze-10; + if ( (y + size[1]) < 60) y = 60 - size[1]; + if ( y > (verScrSze-10) ) y = verScrSze-10; + + MoveWindow(GRAFPTR pwWindow, x, y, false); + // update OpenGL Context: + aglUpdateContext(currContext); + + origin[0] = x; // save new origin + origin[1] = y; +} + + +void pwSetSizeOrigin ( int x, int y, int w, int h ) +{ + if (!pwInitialized) + { + ulSetError(UL_WARNING, "call to pwSetSizeOrigin without call to pwInit"); + return; + } + if ( full_screen ) + { + ulSetError(UL_WARNING, "call to pwSetSizeOrigin in full screen mode"); + return; + } + + pwSetOrigin( x, y ); + pwSetSize( w, h ); +} + + +void pwSwapBuffers () +{ + if (!pwInitialized) + { + ulSetError(UL_WARNING, "call to pwSwapBuffers without call to pwInit"); + return; + } + // glFlush () ; + aglSwapBuffers ( currContext ) ; + HandleEvents () ; +} + + +void pwCleanup () +{ + if (!pwInitialized) + { + ulSetError(UL_WARNING, "call to pwCleanup without call to pwInit"); + return; + } + + if (currContext != NULL) + { + aglSetDrawable (currContext, NULL); + aglSetCurrentContext (NULL); + aglDestroyContext ( currContext ); + } + if (pwWindow != NULL) DisposeWindow(GRAFPTR pwWindow); + +#ifdef UL_MACINTOSH + if (full_screen) + { + if (dspContext != NULL) + { + //DSpContext_FadeGammaOut (NULL, NULL); // remove for debug + DSpContext_SetState (dspContext, kDSpContextState_Inactive); + //DSpContext_FadeGammaIn (NULL, NULL); + + DSpContext_Release (dspContext); + } + + DSpShutdown (); + } +#endif + + pwInitialized = false; +} + +#endif diff --git a/src/pw/pwWindows.cxx b/src/pw/pwWindows.cxx new file mode 100644 index 0000000..76753e0 --- /dev/null +++ b/src/pw/pwWindows.cxx @@ -0,0 +1,490 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: pwWindows.cxx 2046 2005-09-02 21:03:43Z bram $ +*/ + + +#include "ul.h" + +/* ONLY COMPILE THIS FILE FOR MS-WINDOWS SYSTEMS */ + +#ifdef UL_WIN32 + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "pw.h" + +static int initialised = 0 ; +static int modifiers = 0 ; +static int origin [2] = { 0, 0 } ; +static int size [2] = { 640, 480 } ; + +static bool autoRepeat = false ; +static DWORD style = 0 ; + +static pwResizeCB *resizeCB = NULL ; +static pwExitCB *exitCB = NULL ; +static pwKeybdFunc *kbCB = NULL ; +static pwMouseFunc *msCB = NULL ; +static pwMousePosFunc *mpCB = NULL ; + +static HINSTANCE currInstance = NULL ; +static HWND currWnd = NULL ; +static HDC currDC = NULL ; +static HGLRC currGLRC = NULL ; + + +void defaultExitFunc () +{ + pwCleanup () ; + exit ( 0 ) ; +} + + +static void refreshModifiers () +{ + modifiers = 0 ; + + if( ( GetKeyState ( VK_SHIFT ) & 0x8000 ) != 0 ) modifiers |= PW_SHIFT ; + if( ( GetKeyState ( VK_CONTROL ) & 0x8000 ) != 0 ) modifiers |= PW_CTRL ; + if( ( GetKeyState ( VK_MENU ) & 0x8000 ) != 0 ) modifiers |= PW_ALT ; +} + +inline void clientSizeToWindowSize ( int& w, int& h, DWORD dwStyle ) +{ + RECT rect; + rect.left = 0 ; + rect.right = w ; + rect.top = 0 ; + rect.bottom = h ; + AdjustWindowRect( &rect, dwStyle, false ) ; + w = rect.right - rect.left ; + h = rect.bottom - rect.top ; +} + +LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static int key = -1 ; + static int updown = PW_UP ; + static int old_key = -1 ; // We need this because "key" changes case + int button = -1 ; + static int mb = 0 ; + static int lastx = 0 ; + static int lasty = 0 ; + + refreshModifiers () ; + + switch (uMsg) + { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + updown = PW_DOWN ; + if ( uMsg == WM_LBUTTONDOWN ) { button = PW_LEFT_BUTTON ; mb |= 1 ; } + else if ( uMsg == WM_MBUTTONDOWN ) { button = PW_MIDDLE_BUTTON ; mb |= 2 ; } + else { button = PW_RIGHT_BUTTON ; mb |= 4 ; } + SetCapture ( hWnd ) ; + lastx = SHORT ( LOWORD ( lParam ) ) ; + lasty = SHORT ( HIWORD ( lParam ) ) ; + if ( button != -1 && msCB ) + (*msCB) ( button, updown, lastx, lasty ) ; + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + updown = PW_UP ; + if ( uMsg == WM_LBUTTONUP ) { button = PW_LEFT_BUTTON ; mb &= ~1 ; } + else if ( uMsg == WM_MBUTTONUP ) { button = PW_MIDDLE_BUTTON ; mb &= ~2 ; } + else { button = PW_RIGHT_BUTTON ; mb &= ~4 ; } + if ( mb == 0 ) ReleaseCapture () ; + lastx = SHORT ( LOWORD ( lParam ) ) ; + lasty = SHORT ( HIWORD ( lParam ) ) ; + if ( button != -1 && msCB ) + (*msCB) ( button, updown, lastx, lasty ) ; + break; + + case WM_MOUSEMOVE: + lastx = SHORT ( LOWORD ( lParam ) ) ; + lasty = SHORT ( HIWORD ( lParam ) ) ; + if ( mpCB ) + (*mpCB) ( lastx, lasty ) ; + break; + + case WM_KEYDOWN: + /* If the key is already down, we are on auto-repeat. Break if the autorepeat is disabled. */ + if ( ( updown == PW_DOWN ) && ( int(wParam) == old_key ) ) + { + if ( autoRepeat ) + { + /* Disable CTRL, SHIFT, CapsLock keys from making a callback */ + if ( ( key == VK_CONTROL ) || ( key == VK_SHIFT ) || ( key == VK_CAPITAL ) ) + break ; + + if ( key != -1 && kbCB ) // Autorepeat enabled, call the callback with an "up" setting + (*kbCB) ( key, PW_UP, lastx, lasty ) ; + } + else + break ; // Autorepeat not enabled, just break + } + + updown = PW_DOWN ; + old_key = wParam ; + /* FALLTHROUGH */ + case WM_KEYUP: + if ( uMsg == WM_KEYUP ) updown = PW_UP ; + key = wParam ; + + /* Disable CTRL, SHIFT, CapsLock keys from making a callback */ + if ( ( key == VK_CONTROL ) || ( key == VK_SHIFT ) || ( key == VK_CAPITAL ) ) + break ; + + switch ( key ) + { + case VK_F1: key = PW_KEY_F1; break; + case VK_F2: key = PW_KEY_F2; break; + case VK_F3: key = PW_KEY_F3; break; + case VK_F4: key = PW_KEY_F4; break; + case VK_F5: key = PW_KEY_F5; break; + case VK_F6: key = PW_KEY_F6; break; + case VK_F7: key = PW_KEY_F7; break; + case VK_F8: key = PW_KEY_F8; break; + case VK_F9: key = PW_KEY_F9; break; + case VK_F10: key = PW_KEY_F10; break; + case VK_F11: key = PW_KEY_F11; break; + case VK_F12: key = PW_KEY_F12; break; + + case VK_LEFT: key = PW_KEY_LEFT; break; + case VK_RIGHT: key = PW_KEY_RIGHT; break; + case VK_UP: key = PW_KEY_UP; break; + case VK_DOWN: key = PW_KEY_DOWN; break; + + case VK_PRIOR: key = PW_KEY_PAGE_UP; break; + case VK_NEXT: key = PW_KEY_PAGE_DOWN; break; + case VK_HOME: key = PW_KEY_HOME; break; + case VK_END: key = PW_KEY_END; break; + case VK_INSERT: key = PW_KEY_INSERT; break; + + default: + // don't do this for WinCE + BYTE state[ 256 ]; + WORD code[ 2 ]; + + BOOL b = GetKeyboardState( state ); + assert(b); + + code [ 0 ] = 0; // WK: I need to do this, or on my Win2k box, the upper bits remain unchanged. + if( ToAscii( key, 0, state, code, 0 ) == 1 ) + if((0xFF00 & code[0]) == 0) // setting a high bit in key causes crashes later on (out of range array access) + key=code[ 0 ]; + + } + if ( key != -1 && kbCB ) + (*kbCB) ( key, updown, lastx, lasty ) ; + break; + + case WM_SIZE: + size[0] = LOWORD ( lParam ) ; + size[1] = HIWORD ( lParam ) ; + if ( resizeCB ) + (*resizeCB) ( size[0], size[1] ) ; + break; + + case WM_CLOSE: + (*exitCB)(); + break; + + default: + return ( DefWindowProc ( hWnd, uMsg, wParam, lParam ) ) ; + } + + return 0; +} + + +void pwSetAutoRepeatKey ( bool enable ) +{ + autoRepeat = enable ; +} + +void pwSetCallbacks ( pwKeybdFunc *kb, pwMouseFunc *ms, + pwMousePosFunc *mp, pwResizeCB *rcb, pwExitCB *ecb ) +{ + if ( ! initialised ) + { + fprintf ( stderr, "PW: You must not call pwSetCallbacks before pwInit.\n"); + exit ( 1 ) ; + } + + kbCB = kb ; + msCB = ms ; + mpCB = mp ; + resizeCB = rcb ; + exitCB = ecb ? ecb : defaultExitFunc ; +} + + +void pwInit ( int multisample, int num_samples ) +{ + pwInit ( 0, 0, -1, -1, multisample, "NoName", FALSE, num_samples ) ; +} + + +//typedef const char * wglGetExtensionsStringARBtype ( HDC hdc ) ; + +void pwInit ( int x, int y, int w, int h, int multisample, + char *title, int border, int num_samples ) +{ + currInstance = GetModuleHandleA( NULL ) ; + + /* Register the window class */ + WNDCLASS wc; + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW ; + wc.lpfnWndProc = WndProc ; + wc.cbClsExtra = 0 ; + wc.cbWndExtra = 0 ; + wc.hInstance = currInstance ; + wc.hIcon = LoadIcon ( NULL, IDI_APPLICATION ) ; + wc.hCursor = LoadCursor ( NULL, IDC_ARROW ) ; + wc.hbrBackground = ( HBRUSH ) ( COLOR_WINDOW + 1 ) ; + wc.lpszMenuName = NULL ; + wc.lpszClassName = "PlibAppClass" ; + if ( !RegisterClass( &wc ) ) + { + fprintf ( stderr, "PW: Can't register window class" ) ; + exit ( 1 ) ; + } + + /* Setup window style */ + bool fullscn = (w == -1 && h == -1 ) ? true : false ; + + if ( w == -1 ) w = GetSystemMetrics( SM_CXSCREEN ) ; + if ( h == -1 ) h = GetSystemMetrics( SM_CYSCREEN ) ; + + origin [ 0 ] = x ; + origin [ 1 ] = y ; + size [ 0 ] = w ; + size [ 1 ] = h ; + + RECT rect ; + rect.left = x ; + rect.top = y ; + rect.right = rect.left + w ; + rect.bottom = rect.top + h ; + + if ( !fullscn ) + style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ; + else + style = WS_POPUP ; + + AdjustWindowRect( &rect, style, false ) ; + + // The origin is really where the upper left of the window should be. + // rect is the setting for the window if the origin is for the client area. + // Adjust the window rect so that it will be located at the origin (x,y). + + int deltaX = x - rect.left ; + int deltaY = y - rect.top ; + + rect.left += deltaX ; + rect.right += deltaX ; + rect.top += deltaY ; + rect.bottom += deltaY ; + + /* Create the window */ + currWnd = CreateWindow( + "PlibAppClass", title, style, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, currInstance, NULL ) ; + + if ( !currWnd ) + { + fprintf ( stderr, "PW: Can't create window" ) ; + exit ( 1 ) ; + } + + /* Get DC for this window */ + currDC = GetDC ( currWnd ) ; + if ( !currDC ) + { + fprintf ( stderr, "PW: Can't get window DC" ) ; + exit ( 1 ) ; + } + +// wglGetExtensionsStringARBtype *wglGetExtensionsStringARB = (wglGetExtensionsStringARBtype *)wglGetProcAddress ( "wglGetExtensionsStringARB" ) ; +// const char *extensionsString = wglGetExtensionsStringARB ( currDC ) ; + +// printf ( "%s %x %s\n", glGetString ( GL_EXTENSIONS ), wglGetExtensionsStringARB, extensionsString ) ; + +// if (!GLExtensionExists("WGL_ARB_multisample ")) +// return suggestedFormat; + + /* Set pixel format for DC */ + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */ + 1, /* version number */ + PFD_DRAW_TO_WINDOW | /* support window */ + PFD_SUPPORT_OPENGL | /* support OpenGL */ + PFD_DOUBLEBUFFER, /* double buffered */ + PFD_TYPE_RGBA, /* RGBA type */ + 24, /* 24-bit color depth */ + 0, 0, 0, 0, 0, 0, /* color bits ignored */ + 0, /* no alpha buffer */ + 0, /* shift bit ignored */ + 0, /* no accumulation buffer */ + 0, 0, 0, 0, /* accum bits ignored */ + 32, /* 32-bit z-buffer */ + 0, /* no stencil buffer */ + 0, /* no auxiliary buffer */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, 0, 0 /* layer masks ignored */ + }; + + /* Get best available match of pixel format for DC */ + int iPixelFormat = ChoosePixelFormat ( currDC, &pfd ) ; + if ( iPixelFormat == 0 ) + { + fprintf ( stderr, "PW: Can't get OpenGL pixel format" ) ; + exit ( 1 ) ; + } + + /* Set the pixel format of the DC */ + if ( !SetPixelFormat( currDC, iPixelFormat, &pfd ) ) + { + fprintf ( stderr, "PW: Can't set OpenGL pixel format" ) ; + exit ( 1 ) ; + } + + /* Show the window and paint its contents */ + ShowWindow ( currWnd, SW_SHOW ) ; + UpdateWindow ( currWnd ) ; + + /* Create openGL context and make it current */ + currGLRC = wglCreateContext ( currDC ) ; + if ( !currGLRC ) + { + fprintf ( stderr, "PW: Can't create OpenGL context" ) ; + exit ( 1 ) ; + } + if ( !wglMakeCurrent ( currDC, currGLRC ) ) + { + fprintf ( stderr, "PW: Can't make OpenGL context current" ) ; + exit ( 1 ) ; + } + + kbCB = NULL ; + msCB = NULL ; + mpCB = NULL ; + resizeCB = NULL ; + exitCB = defaultExitFunc ; + initialised = 1 ; + + glClear ( GL_COLOR_BUFFER_BIT ) ; + pwSwapBuffers () ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + pwSwapBuffers () ; +} + + +void pwGetSize ( int *w, int *h ) +{ + if ( w ) *w = size[0] ; + if ( h ) *h = size[1] ; +} + + +void pwSetCursor ( int c ) +{ + LPCSTR cursor; + switch ( c ) + { + case PW_CURSOR_RIGHT : cursor = IDC_ARROW ; break ; + case PW_CURSOR_LEFT : cursor = IDC_ARROW ; break ; + case PW_CURSOR_QUERY : cursor = IDC_HELP ; break ; + /*case PW_CURSOR_AIM : cursor = ??? ; break ;*/ + case PW_CURSOR_CIRCLE : cursor = IDC_NO ; break ; + case PW_CURSOR_WAIT : cursor = IDC_WAIT ; break ; + case PW_CURSOR_CROSS : cursor = IDC_CROSS ; break ; + case PW_CURSOR_NONE : cursor = 0 ; break ; + default: + cursor = IDC_ARROW; + } + SetCursor( LoadCursor ( NULL, cursor ) ) ; +} + +void pwSetSize ( int w, int h ) +{ + clientSizeToWindowSize ( w, h, style ) ; + SetWindowPos ( currWnd, HWND_TOP, 0, 0, w, h, SWP_NOMOVE ) ; +} + + +void pwSetOrigin ( int x, int y ) +{ + SetWindowPos ( currWnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE ) ; +} + + +void pwSetSizeOrigin ( int x, int y, int w, int h ) +{ + clientSizeToWindowSize ( w, h, style ) ; + SetWindowPos ( currWnd, HWND_TOP, x, y, w, h, 0 ) ; +} + + +int pwGetModifiers () +{ + return modifiers ; +} + + +static void getEvents () +{ + MSG msg; + while ( PeekMessage( &msg, currWnd, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &msg ) ; + DispatchMessage ( &msg ) ; + } +} + + +void pwSwapBuffers () +{ + glFlush () ; + SwapBuffers ( currDC ) ; + getEvents () ; +} + + +void pwCleanup () +{ + wglMakeCurrent ( NULL, NULL ) ; + wglDeleteContext ( currGLRC ) ; + DestroyWindow ( currWnd ) ; +} + + +#endif + diff --git a/src/pw/pwX11.cxx b/src/pw/pwX11.cxx new file mode 100644 index 0000000..33488d7 --- /dev/null +++ b/src/pw/pwX11.cxx @@ -0,0 +1,651 @@ +#include "ul.h" + +/* ONLY COMPILE THIS FILE FOR GLX/X11 EQUIPPED SYSTEMS */ + +#ifdef UL_GLX + +#include "pw.h" + +#include +#include +#include +#include +#include + +extern "C" +{ +#include +#include +#include +#include +#include +#include +} + +#include +#include + +/* + These are supposed to be defined in /usr/include/GL/glxext.h + But at time of writing, they were missing. The OpenGL ARB + people have been informed by email. +*/ + +#ifndef GLX_SAMPLE_BUFFERS_ARB +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif + + +#ifdef HAVE_MOTIF + +extern "C" +{ +#include +} + +#else + +/* bit definitions for MwmHints.flags */ +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) + +/* bit definitions for MwmHints.decorations */ +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +typedef struct +{ + unsigned long flags ; + unsigned long functions ; + unsigned long decorations ; + long inputMode ; + unsigned long status ; +} PropMotifWmHints ; + +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 + +#endif + +static bool initialised = false ; +static bool insideCallback = false ; +static int modifiers = 0 ; +static int origin [2] = { 0, 0 } ; +static int size [2] = { 640, 480 } ; +static int currScreen = 0 ; +static int currConnect = 0 ; +static int currCursor = PW_CURSOR_LEFT ; + +static pwResizeCB *resizeCB = NULL ; +static pwExitCB *exitCB = NULL ; +static pwKeybdFunc *kbCB = NULL ; +static pwMouseFunc *msCB = NULL ; +static pwMousePosFunc *mpCB = NULL ; + +static Display *currDisplay = NULL ; +static XVisualInfo *visualInfo = NULL ; +static Window currHandle ; +static GLXContext currContext ; +static Window rootWindow ; +static Atom delWinAtom ; + +static bool autoRepeat = false ; + +void pwSetAutoRepeatKey ( bool enable ) +{ + autoRepeat = enable ; +} + + +struct PixelFormat +{ + int num_samples ; + int bits_per_pixel ; ; + int z_bits ; +} ; + + +static PixelFormat preferred_pixel_formats [] = +{ + /* NumSamples, RGB_bits, Z_bits */ + + { 0, 24, 24 }, /* Progressively nastier image formats */ + { 0, 16, 24 }, + { 0, 16, 16 }, + { 0, 3, 16 }, + { 0, 3, 1 }, + { -1, -1, -1 } /* Magic end marker */ +} ; + + +void defaultExitFunc () +{ + pwCleanup () ; + exit ( 0 ) ; +} + + +static void chooseVisual ( PixelFormat *pf ) +{ + int attribs [ 100 ] ; + int n = 0 ; + + attribs [n++] = GLX_RGBA ; + + switch ( pf->bits_per_pixel ) + { + case 3 : + attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 1 ; + attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 1 ; + attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 1 ; + break ; + + case 16 : + attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 5 ; + attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 6 ; + attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 5 ; + break ; + + case 24 : + attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 8 ; + attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 8 ; + attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 8 ; + break ; + } + + switch ( pf->z_bits ) + { + case 1 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 1 ; break ; + case 16 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 16 ; break ; + case 24 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 24 ; break ; + case 32 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 32 ; break ; + } + + if ( pf->num_samples > 0 ) + { + attribs [n++] = GLX_SAMPLE_BUFFERS_ARB ; attribs [n++] = 1 ; + attribs [n++] = GLX_SAMPLES_ARB ; attribs [n++] = pf->num_samples ; + } + + attribs [n++] = GLX_DOUBLEBUFFER ; + attribs [n++] = None ; + + visualInfo = glXChooseVisual ( currDisplay, currScreen, attribs ) ; +} + + +void pwInit ( int multisample, int num_samples ) +{ + pwInit ( 0, 0, -1, -1, multisample, "NoName", FALSE, num_samples ) ; +} + + +void pwSetCallbacks ( pwKeybdFunc *kb, pwMouseFunc *ms, + pwMousePosFunc *mp, pwResizeCB *rcb, + pwExitCB *ecb ) +{ + if ( ! initialised ) + { + fprintf ( stderr, "PW: You must not call pwSetCallbacks before pwInit.\n"); + exit ( 1 ) ; + } + + kbCB = kb ; + msCB = ms ; + mpCB = mp ; + resizeCB = rcb ; + exitCB = ecb ? ecb : defaultExitFunc ; +} + + +void pwInit ( int x, int y, int w, int h, int multisample, + char *title, int border, int num_samples ) +{ + char *displayName = getenv ( "DISPLAY" ) ; + + if ( displayName == NULL ) displayName = ":0.0" ; + + currDisplay = XOpenDisplay ( displayName ) ; + + if ( currDisplay == NULL ) + { + fprintf ( stderr, "PW: Can't open display '%s'", + XDisplayName ( displayName ) ) ; + exit ( 1 ) ; + } + + + /* OpenGL GLX extension availability? */ + + if ( ! glXQueryExtension ( currDisplay, NULL, NULL ) ) + { + fprintf ( stderr, "PW: GLX extension not available on display '%s'?!?", + XDisplayName ( displayName ) ) ; + exit ( 1 ) ; + } + + currScreen = DefaultScreen ( currDisplay ) ; + rootWindow = RootWindow ( currDisplay, currScreen ) ; + currConnect = ConnectionNumber ( currDisplay ) ; + delWinAtom = XInternAtom ( currDisplay, "WM_DELETE_WINDOW", 0 ) ; + + if ( w == -1 ) w = DisplayWidth ( currDisplay, currScreen ) ; + if ( h == -1 ) h = DisplayHeight ( currDisplay, currScreen ) ; + + origin [ 0 ] = x ; + origin [ 1 ] = y ; + size [ 0 ] = w ; + size [ 1 ] = h ; + + XSetWindowAttributes attribs ; + XTextProperty textProperty ; + XSizeHints sizeHints ; + XWMHints wmHints ; + unsigned int mask ; + PixelFormat pf ; + + for ( int i = 0 ; preferred_pixel_formats [ i ] . num_samples >= 0 ; i++ ) + { + pf = preferred_pixel_formats [ i ] ; + pf . num_samples = num_samples ; + + chooseVisual ( &pf ) ; + + if ( visualInfo != NULL ) + break ; + } + + if ( visualInfo == NULL ) + { + num_samples = 0 ; + + for ( int i = 0 ; preferred_pixel_formats [ i ] . num_samples >= 0 ; i++ ) + { + pf = preferred_pixel_formats [ i ] ; + pf . num_samples = num_samples ; + + chooseVisual ( &pf ) ; + + if ( visualInfo != NULL ) + break ; + } + + if ( visualInfo == NULL ) + { + fprintf ( stderr, "PW: Unable to open a suitable graphics window,\n" ) ; + exit ( 1 ) ; + } + } + + attribs.event_mask = StructureNotifyMask | ExposureMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | ButtonMotionMask | + VisibilityChangeMask ; + + attribs.background_pixmap = None ; + attribs.background_pixel = 0 ; + attribs.border_pixel = 0 ; + + attribs.colormap = XCreateColormap ( currDisplay, rootWindow, + visualInfo->visual, AllocNone ) ; + + mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask; + + currHandle = XCreateWindow ( currDisplay, rootWindow, + x, y, w, h, 0, visualInfo->depth, + InputOutput, visualInfo->visual, + mask, &attribs ) ; + + currContext = glXCreateContext ( currDisplay, visualInfo, NULL, 1 ) ; + + glXMakeCurrent ( currDisplay, currHandle, currContext ) ; + + if ( ! glXIsDirect ( currDisplay, glXGetCurrentContext() ) ) + { + fprintf ( stderr,"PW: This is an *INDIRECT* rendering context.") ; + fprintf ( stderr,"PW: That may be bad for performance." ) ; + } + + sizeHints.flags = 0 ; + + if ( x >= 0 && y >= 0 ) + sizeHints.flags |= USPosition ; + + sizeHints.flags |= USSize ; + + sizeHints.x = x ; sizeHints.y = y ; + sizeHints.width = w ; sizeHints.height = h ; + + wmHints.flags = StateHint; + wmHints.initial_state = NormalState ; + + PropMotifWmHints hints ; + Atom prop_t ; + Atom prop ; + + hints . flags = MWM_HINTS_DECORATIONS ; + hints . decorations = border ? MWM_DECOR_ALL : 0 ; + + prop_t = prop = XInternAtom ( currDisplay, "_MOTIF_WM_HINTS", True ) ; + + if ( prop != 0 ) + XChangeProperty ( currDisplay, currHandle, prop, prop_t, 32, + PropModeReplace, (unsigned char *) &hints, + PROP_MOTIF_WM_HINTS_ELEMENTS) ; + + XStringListToTextProperty ( (char **) &title, 1, &textProperty ) ; + + XSetWMProperties ( currDisplay, currHandle, + &textProperty, &textProperty, 0, 0, + &sizeHints, &wmHints, NULL ) ; + XSetWMProtocols ( currDisplay, currHandle, &delWinAtom , 1 ); + XMapWindow ( currDisplay, currHandle ) ; + glXMakeCurrent ( currDisplay, currHandle, currContext ) ; + + pwSetCursor ( PW_CURSOR_LEFT ) ; + +#ifdef GL_MULTISAMPLE_FILTER_HINT_NV + glHint ( GL_MULTISAMPLE_FILTER_HINT_NV, multisample ) ; +#endif + + kbCB = NULL ; + msCB = NULL ; + mpCB = NULL ; + resizeCB = NULL ; + exitCB = defaultExitFunc ; + + initialised = true ; + insideCallback = false ; + + glClear ( GL_COLOR_BUFFER_BIT ) ; + pwSwapBuffers () ; + glClear ( GL_COLOR_BUFFER_BIT ) ; + pwSwapBuffers () ; +} + + +void pwGetSize ( int *w, int *h ) +{ + if ( w ) *w = size[0] ; + if ( h ) *h = size[1] ; +} + + +void pwSetCursor ( int c ) +{ + switch ( c ) + { + case PW_CURSOR_RIGHT : currCursor = XC_right_ptr ; break ; + case PW_CURSOR_LEFT : currCursor = XC_left_ptr ; break ; + case PW_CURSOR_QUERY : currCursor = XC_question_arrow ; break ; + case PW_CURSOR_AIM : currCursor = XC_target ; break ; + case PW_CURSOR_CIRCLE : currCursor = XC_circle ; break ; + case PW_CURSOR_WAIT : currCursor = XC_watch ; break ; + case PW_CURSOR_CROSS : currCursor = XC_crosshair ; break ; + + case PW_CURSOR_NONE : + default: + { + Pixmap pix ; + char blank_cursor [ 16*16 ] = { 0 } ; + XColor bcol = { 0 } ; + + pix = XCreateBitmapFromData ( currDisplay, + rootWindow, + blank_cursor, 16, 16 ) ; + XDefineCursor ( currDisplay, currHandle, + XCreatePixmapCursor ( currDisplay, + pix, pix, &bcol, &bcol, 0, 0 ) ) ; + XFreePixmap ( currDisplay, pix ) ; + + currCursor = 0 ; + } + return ; + } + + XDefineCursor( currDisplay, currHandle, + XCreateFontCursor ( currDisplay, currCursor ) ) ; +} + + +void pwSetSize ( int w, int h ) +{ + XResizeWindow ( currDisplay, currHandle, w, h ) ; + XFlush ( currDisplay ) ; +} + + +void pwSetOrigin ( int x, int y ) +{ + XMoveWindow ( currDisplay, currHandle, x, y ) ; + XFlush ( currDisplay ) ; +} + + +void pwSetSizeOrigin ( int x, int y, int w, int h ) +{ + XMoveWindow ( currDisplay, currHandle, x, y ) ; + XResizeWindow ( currDisplay, currHandle, w, h ) ; + XFlush ( currDisplay ) ; +} + + +static void refreshModifiers( XEvent *event ) +{ + modifiers = 0 ; + + if( event->xkey.state & ( ShiftMask | LockMask ) ) modifiers |= PW_SHIFT ; + if( event->xkey.state & ControlMask ) modifiers |= PW_CTRL ; + if( event->xkey.state & Mod1Mask ) modifiers |= PW_ALT ; +} + + +int pwGetModifiers () +{ + return modifiers ; +} + + +static void getEvents () +{ + bool repeating = false ; + char keyflags [ 32 ] ; + XEvent event ; + + insideCallback = true ; + + while ( XPending ( currDisplay ) ) + { + int updown = PW_DOWN ; + + XNextEvent ( currDisplay, &event ) ; + + refreshModifiers ( &event ) ; + + switch ( event.type ) + { + case ClientMessage : (*exitCB)() ; break ; + case DestroyNotify : (*exitCB)() ; break ; + + case ConfigureNotify : + if ( currHandle == event.xconfigure.window && + ( size[0] != event.xconfigure.width || + size[1] != event.xconfigure.height ) ) + { + size[0] = event.xconfigure.width ; + size[1] = event.xconfigure.height ; + + glXMakeCurrent ( currDisplay, currHandle, currContext ) ; + glXWaitX () ; + + if ( resizeCB != NULL ) + (*resizeCB) ( size[0], size[1] ) ; + } + break; + + case MappingNotify: + XRefreshKeyboardMapping ( (XMappingEvent *) &event ) ; + break; + + case EnterNotify : + case LeaveNotify : + case VisibilityNotify: + case Expose : break ; + + case MotionNotify : + if ( mpCB ) + (*mpCB) ( event.xmotion.x, event.xmotion.y ) ; + break ; + + case ButtonRelease : + updown = PW_UP ; + /* FALLTHROUGH */ + + case ButtonPress : + { + int button = -1 ; + + switch ( event.xbutton.button ) + { + case 1 : button = PW_LEFT_BUTTON ; break ; + case 2 : button = PW_MIDDLE_BUTTON ; break ; + case 3 : button = PW_RIGHT_BUTTON ; break ; + } + + if ( button != -1 && msCB ) + (*msCB) ( button, updown, event.xbutton.x, event.xbutton.y ) ; + } + break ; + + case KeyRelease : + updown = PW_UP ; + + XQueryKeymap ( currDisplay, keyflags ) ; + + repeating = ( ( keyflags [ event.xkey.keycode >> 3 ] & + ( 1 << ( event.xkey.keycode & 7 ) ) ) != 0 ) ; + + /* FALLTHROUGH */ + + case KeyPress : + /* + Only generate a key up callback if the key is actually up + and not repeating. + */ + + if ( ! autoRepeat && repeating ) + break ; + + XComposeStatus composeStatus ; + char asciiCode [ 32 ] ; + KeySym keySym ; + + int len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), + &keySym, &composeStatus ) ; + int result = -1 ; + + if( len > 0 ) + result = asciiCode[ 0 ] ; + else + { + switch( keySym ) + { + case XK_F1: result = PW_KEY_F1; break; + case XK_F2: result = PW_KEY_F2; break; + case XK_F3: result = PW_KEY_F3; break; + case XK_F4: result = PW_KEY_F4; break; + case XK_F5: result = PW_KEY_F5; break; + case XK_F6: result = PW_KEY_F6; break; + case XK_F7: result = PW_KEY_F7; break; + case XK_F8: result = PW_KEY_F8; break; + case XK_F9: result = PW_KEY_F9; break; + case XK_F10: result = PW_KEY_F10; break; + case XK_F11: result = PW_KEY_F11; break; + case XK_F12: result = PW_KEY_F12; break; + + case XK_Left: result = PW_KEY_LEFT; break; + case XK_Right: result = PW_KEY_RIGHT; break; + case XK_Up: result = PW_KEY_UP; break; + case XK_Down: result = PW_KEY_DOWN; break; + + case XK_KP_Prior: + case XK_Prior: result = PW_KEY_PAGE_UP; break; + case XK_KP_Next: + case XK_Next: result = PW_KEY_PAGE_DOWN; break; + case XK_KP_Home: + case XK_Home: result = PW_KEY_HOME; break; + case XK_KP_End: + case XK_End: result = PW_KEY_END; break; + case XK_KP_Insert: + case XK_Insert: result = PW_KEY_INSERT; break; + } + } + + if ( result != -1 && kbCB != NULL ) + (*kbCB) ( result, updown, event.xkey.x, event.xkey.y ) ; + + break ; + } + } + + insideCallback = false ; + + glXMakeCurrent ( currDisplay, currHandle, currContext ) ; +} + + +void pwSwapBuffers () +{ + if ( ! initialised ) + { + fprintf ( stderr, "PLIB/PW: FATAL - Application called pwSwapBuffers" + " before pwInit.\n" ) ; + exit ( 1 ) ; + } + + if ( insideCallback ) + { + fprintf ( stderr, "PLIB/PW: FATAL - Application called pwSwapBuffers" + " from inside a callback function.\n" ) ; + exit ( 1 ) ; + } + + glFlush () ; + glXSwapBuffers ( currDisplay, currHandle ) ; + getEvents () ; +} + + +#ifdef NEED_GAMMA +void pwSetGamma ( float g ) +{ + XF86VidModeGamma gamma ; + gamma.red = gamma.green = gamma.blue = g ; + XF86VidModeSetGamma ( currDisplay, currScreen, & gamma ) ; +} +#endif + + +void pwCleanup () +{ + if ( ! initialised ) + fprintf ( stderr, "PLIB/PW: WARNING - Application called pwCleanup" + " before pwInit.\n" ) ; + + glXDestroyContext ( currDisplay, currContext ) ; + XDestroyWindow ( currDisplay, currHandle ) ; + XFlush ( currDisplay ) ; +} + + +#endif + diff --git a/src/sg/Makefile.am b/src/sg/Makefile.am new file mode 100644 index 0000000..e3e4825 --- /dev/null +++ b/src/sg/Makefile.am @@ -0,0 +1,16 @@ +if BUILD_SG + +lib_LIBRARIES = libplibsg.a + +include_HEADERS = sg.h + +libplibsg_a_SOURCES = sg.cxx sgd.cxx \ + sgIsect.cxx sgdIsect.cxx \ + sgPerlinNoise.cxx + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = sg.dsp + diff --git a/src/sg/sg.cxx b/src/sg/sg.cxx new file mode 100644 index 0000000..846e680 --- /dev/null +++ b/src/sg/sg.cxx @@ -0,0 +1,2080 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sg.cxx 2163 2010-06-01 11:48:30Z fayjf $ +*/ + + +#include "sg.h" +#ifdef UL_WIN32 +# define isnan _isnan +# define finite _finite +#endif + + +sgVec3 _sgGravity = { 0.0f, 0.0f, -9.8f } ; + +void sgVectorProductVec3 ( sgVec3 dst, const sgVec3 a, const sgVec3 b ) +{ + dst[0] = a[1] * b[2] - a[2] * b[1] ; + dst[1] = a[2] * b[0] - a[0] * b[2] ; + dst[2] = a[0] * b[1] - a[1] * b[0] ; +} + +inline SGfloat _sgClampToUnity ( const SGfloat x ) +{ + if ( x > SG_ONE ) return SG_ONE ; + if ( x < -SG_ONE ) return -SG_ONE ; + return x ; +} + +int sgCompare3DSqdDist( const sgVec3 v1, const sgVec3 v2, const SGfloat sqd_dist ) +{ + sgVec3 tmp ; + + sgSubVec3 ( tmp, v2, v1 ) ; + + SGfloat sqdist = tmp[0] * tmp[0] + tmp[1] * tmp[1] + tmp[2] * tmp[2] ; + + if ( sqdist > sqd_dist ) return 1 ; + if ( sqdist < sqd_dist ) return -1 ; + return 0 ; +} + +void sgMakeRotMat4( sgMat4 mat, const SGfloat angle, const sgVec3 axis ) +{ + sgVec3 ax ; + sgNormalizeVec3 ( ax, axis ) ; + + SGfloat temp_angle = angle * SG_DEGREES_TO_RADIANS ; + SGfloat s = (SGfloat) sin ( temp_angle ) ; + SGfloat c = (SGfloat) cos ( temp_angle ) ; + SGfloat t = SG_ONE - c ; + + mat[0][0] = t * ax[0] * ax[0] + c ; + mat[0][1] = t * ax[0] * ax[1] + s * ax[2] ; + mat[0][2] = t * ax[0] * ax[2] - s * ax[1] ; + mat[0][3] = SG_ZERO ; + + mat[1][0] = t * ax[1] * ax[0] - s * ax[2] ; + mat[1][1] = t * ax[1] * ax[1] + c ; + mat[1][2] = t * ax[1] * ax[2] + s * ax[0] ; + mat[1][3] = SG_ZERO ; + + mat[2][0] = t * ax[2] * ax[0] + s * ax[1] ; + mat[2][1] = t * ax[2] * ax[1] - s * ax[0] ; + mat[2][2] = t * ax[2] * ax[2] + c ; + mat[2][3] = SG_ZERO ; + + mat[3][0] = SG_ZERO ; + mat[3][1] = SG_ZERO ; + mat[3][2] = SG_ZERO ; + mat[3][3] = SG_ONE ; +} + + + + +void sgMakePickMatrix( sgMat4 mat, sgFloat x, sgFloat y, + sgFloat width, sgFloat height, sgVec4 viewport ) +{ + sgFloat sx = viewport[2] / width ; + sgFloat sy = viewport[3] / height ; + sgFloat tx = ( viewport[2] + SG_TWO * (viewport[0] - x) ) / width ; + sgFloat ty = ( viewport[3] + SG_TWO * (viewport[1] - y) ) / height ; + + mat[0][0] = sx ; + mat[0][1] = SG_ZERO ; + mat[0][2] = SG_ZERO ; + mat[0][3] = SG_ZERO ; + + mat[1][0] = SG_ZERO ; + mat[1][1] = sy ; + mat[1][2] = SG_ZERO ; + mat[1][3] = SG_ZERO ; + + mat[2][0] = SG_ZERO ; + mat[2][1] = SG_ZERO ; + mat[2][2] = SG_ONE ; + mat[2][3] = SG_ZERO ; + + mat[3][0] = tx ; + mat[3][1] = ty ; + mat[3][2] = SG_ZERO ; + mat[3][3] = SG_ONE ; +} + + + +void sgMakeLookAtMat4 ( sgMat4 dst, const sgVec3 eye, + const sgVec3 center, + const sgVec3 up ) +{ + // Caveats: + // 1) In order to compute the line of sight, the eye point must not be equal + // to the center point. + // 2) The up vector must not be parallel to the line of sight from the eye + // to the center point. + + /* Compute the direction vectors */ + sgVec3 x,y,z; + + /* Y vector = center - eye */ + sgSubVec3 ( y, center, eye ) ; + + /* Z vector = up */ + sgCopyVec3 ( z, up ) ; + + /* X vector = Y cross Z */ + sgVectorProductVec3 ( x, y, z ) ; + + /* Recompute Z = X cross Y */ + sgVectorProductVec3 ( z, x, y ) ; + + /* Normalize everything */ + sgNormaliseVec3 ( x ) ; + sgNormaliseVec3 ( y ) ; + sgNormaliseVec3 ( z ) ; + + /* Build the matrix */ + sgSetVec4 ( dst[0], x[0], x[1], x[2], SG_ZERO ) ; + sgSetVec4 ( dst[1], y[0], y[1], y[2], SG_ZERO ) ; + sgSetVec4 ( dst[2], z[0], z[1], z[2], SG_ZERO ) ; + sgSetVec4 ( dst[3], eye[0], eye[1], eye[2], SG_ONE ) ; +} + +// -dw- inconsistent linkage! + +float sgTriArea( sgVec3 p0, sgVec3 p1, sgVec3 p2 ) +{ + /* + From comp.graph.algorithms FAQ + + 2A(P) = abs(N.(sum_{i=0}^{n-1}(v_i x v_{i+1}))) + This is an optimized version for a triangle + but easily extended for planar polygon's with more sides + by passing in the number of sides and the vv array + sgTriArea( int nsides, float **vv ) + and changing the normal calculation and the for loop appropriately + sgMakeNormal( norm, vv[0], vv[1], vv[2] ) + for( int i=0; i 0.99999 ) + deltaAngle = SG_PI*0.5; + else + deltaAngle = (SGfloat)asin((double)myNorm); + + // deltaAngle is in the range -SG_PI*0.5 to +SG_PI*0.5 here + // However, the correct result could also be + // deltaAngleS := pi - deltaAngle + // Please note that: + // cos(deltaAngleS)=cos(pi-deltaAngle)=-cos(deltaAngle) + // So, the question is whether + or - cos(deltaAngle) + // is sgScalarProductVec3(first, second) + + if ( deltaAngle < 0 ) + deltaAngle = deltaAngle + 2*SG_PI; // unnessecary? + + SProduct = sgScalarProductVec3(first, second); + myCos = (SGfloat) cos(deltaAngle); + + abs1 = SProduct - myCos; + abs2 = SProduct + myCos; + + if ( abs1 < 0 ) abs1 = -abs1 ; + if ( abs2 < 0 ) abs2 = -abs2 ; + + assert( (abs1 < 0.1) || (abs2 < 0.1) ) ; + + if ( abs2 < abs1 ) + { + // deltaAngleS is the correct result + + if ( deltaAngle <= SG_PI ) + deltaAngle = SG_PI - deltaAngle ; + else + deltaAngle = 3*SG_PI - deltaAngle ; + } + + assert ( deltaAngle >= 0.0 ) ; + assert ( deltaAngle <= 2.0*SG_PI ) ; + + return deltaAngle * SG_RADIANS_TO_DEGREES ; +} + + +SGfloat sgAngleBetweenVec3 ( sgVec3 v1, sgVec3 v2, sgVec3 normal ) +{ + // nornmal has to be normalized. + sgVec3 nv1, nv2 ; + + sgNormalizeVec3 ( nv1, v1 ) ; + sgNormalizeVec3 ( nv2, v2 ) ; + return sgAngleBetweenNormalizedVec3 ( nv1, nv2, normal ) ; +} + + +/*********************\ +* sgBox routines * +\*********************/ + + +void sgBox::extend ( const sgVec3 v ) +{ + if ( isEmpty () ) + { + sgCopyVec3 ( min, v ) ; + sgCopyVec3 ( max, v ) ; + } + else + { + if ( v[0] < min[0] ) min[0] = v[0] ; + if ( v[1] < min[1] ) min[1] = v[1] ; + if ( v[2] < min[2] ) min[2] = v[2] ; + if ( v[0] > max[0] ) max[0] = v[0] ; + if ( v[1] > max[1] ) max[1] = v[1] ; + if ( v[2] > max[2] ) max[2] = v[2] ; + } +} + + +void sgBox::extend ( const sgBox *b ) +{ + if ( b -> isEmpty () ) + return ; + + if ( isEmpty () ) + { + sgCopyVec3 ( min, b->getMin() ) ; + sgCopyVec3 ( max, b->getMax() ) ; + } + else + { + extend ( b->getMin() ) ; + extend ( b->getMax() ) ; + } +} + + +void sgBox::extend ( const sgSphere *s ) +{ + if ( s -> isEmpty () ) + return ; + + /* + In essence, this extends around a box around the sphere - which + is still a perfect solution because both boxes are axially aligned. + */ + + sgVec3 x ; + + sgSetVec3 ( x, s->getCenter()[0]+s->getRadius(), + s->getCenter()[1]+s->getRadius(), + s->getCenter()[2]+s->getRadius() ) ; + extend ( x ) ; + + sgSetVec3 ( x, s->getCenter()[0]-s->getRadius(), + s->getCenter()[1]-s->getRadius(), + s->getCenter()[2]-s->getRadius() ) ; + extend ( x ) ; +} + + +int sgBox::intersects ( const sgVec4 plane ) const +{ + /* + Save multiplies by not redoing Ax+By+Cz+D for each point. + */ + + SGfloat Ax_min = plane[0] * min[0] ; + SGfloat By_min = plane[1] * min[1] ; + SGfloat Cz_min_plus_D = plane[2] * min[2] + plane[3] ; + + SGfloat Ax_max = plane[0] * max[0] ; + SGfloat By_max = plane[1] * max[1] ; + SGfloat Cz_max_plus_D = plane[2] * max[2] + plane[3] ; + + /* + Count the number of vertices on the positive side of the plane. + */ + + int count = ( Ax_min + By_min + Cz_min_plus_D > SG_ZERO ) + + ( Ax_min + By_min + Cz_max_plus_D > SG_ZERO ) + + ( Ax_min + By_max + Cz_min_plus_D > SG_ZERO ) + + ( Ax_min + By_max + Cz_max_plus_D > SG_ZERO ) + + ( Ax_max + By_min + Cz_min_plus_D > SG_ZERO ) + + ( Ax_max + By_min + Cz_max_plus_D > SG_ZERO ) + + ( Ax_max + By_max + Cz_min_plus_D > SG_ZERO ) + + ( Ax_max + By_max + Cz_max_plus_D > SG_ZERO ) ; + + /* + The plane intersects the box unless all 8 are positive + or none of them are positive. + */ + + return count != 0 && count != 8 ; +} + + + +/**********************\ +* sgSphere routines * +\**********************/ + +void sgSphere::extend ( const sgVec3 v ) +{ + if ( isEmpty () ) + { + sgCopyVec3 ( center, v ) ; + radius = SG_ZERO ; + return ; + } + + SGfloat d = sgDistanceVec3 ( center, v ) ; + + if ( d <= radius ) /* Point is already inside sphere */ + return ; + + SGfloat new_radius = (radius + d) / SG_TWO ; /* Grow radius */ + + SGfloat ratio = (new_radius - radius) / d ; + + center[0] += (v[0]-center[0]) * ratio ; /* Move center */ + center[1] += (v[1]-center[1]) * ratio ; + center[2] += (v[2]-center[2]) * ratio ; + + radius = new_radius ; +} + + +void sgSphere::extend ( const sgBox *b ) +{ + if ( b -> isEmpty () ) + return ; + + if ( isEmpty() ) + { + sgAddVec3 ( center, b->getMin(), b->getMax() ) ; + sgScaleVec3 ( center, SG_HALF ) ; + radius = sgDistanceVec3 ( center, b->getMax() ) ; + return ; + } + + /* + I can't think of a faster way to get an + utterly minimal sphere. + + The tighter algorithm:- enclose each + of eight vertices of the box in turn - it + looks like being pretty costly. + [8 sqrt()'s] + + The looser algorithm:- enclose the box + with an empty sphere and then do a + sphere-extend-sphere. This algorithm + does well for close-to-cube boxes, but + makes very poor spheres for long, thin + boxes. + [2 sqrt()'s] + */ + +#ifdef DONT_REALLY_NEED_A_TIGHT_SPHERE_EXTEND_BOX + + /* LOOSER/FASTER sphere-around-sphere-around-box */ + sgSphere s ; + s.empty () ; + s.enclose ( b ) ; /* Fast because s is empty */ + enclose ( s ) ; + +#else + + /* TIGHTER/EXPENSIVE sphere-around-eight-points */ + sgVec3 x ; + extend ( b->getMin() ) ; + sgSetVec3 ( x, b->getMin()[0],b->getMin()[1],b->getMax()[2] ) ; extend ( x ) ; + sgSetVec3 ( x, b->getMin()[0],b->getMax()[1],b->getMin()[2] ) ; extend ( x ) ; + sgSetVec3 ( x, b->getMin()[0],b->getMax()[1],b->getMax()[2] ) ; extend ( x ) ; + sgSetVec3 ( x, b->getMax()[0],b->getMin()[1],b->getMin()[2] ) ; extend ( x ) ; + sgSetVec3 ( x, b->getMax()[0],b->getMin()[1],b->getMax()[2] ) ; extend ( x ) ; + sgSetVec3 ( x, b->getMax()[0],b->getMax()[1],b->getMin()[2] ) ; extend ( x ) ; + extend ( b->getMax() ) ; +#endif +} + + +void sgSphere::extend ( const sgSphere *s ) +{ + if ( s->isEmpty () ) + return ; + + if ( isEmpty () ) + { + sgCopyVec3 ( center, s->getCenter() ) ; + radius = s->getRadius() ; + return ; + } + + /* + d == The distance between the sphere centers + */ + + SGfloat d = sgDistanceVec3 ( center, s->getCenter() ) ; + + if ( d + s->getRadius() <= radius ) /* New sphere is already inside this one */ + return ; + + if ( d + radius <= s->getRadius() ) /* New sphere completely contains this one */ + { + sgCopyVec3 ( center, s->getCenter() ) ; + radius = s->getRadius() ; + return ; + } + + /* + Build a new sphere that completely contains the other two: + + The center point lies halfway along the line between + the furthest points on the edges of the two spheres. + Computing those two points is ugly - so we'll use similar + triangles + */ + + SGfloat new_radius = (radius + d + s->getRadius() ) / SG_TWO ; + + SGfloat ratio = ( new_radius - radius ) / d ; + + center[0] += ( s->getCenter()[0] - center[0] ) * ratio ; + center[1] += ( s->getCenter()[1] - center[1] ) * ratio ; + center[2] += ( s->getCenter()[2] - center[2] ) * ratio ; + radius = new_radius ; +} + + +int sgSphere::intersects ( const sgBox *b ) const +{ + sgVec3 closest ; + + if ( b->getMin()[0] > center[0] ) closest[0] = b->getMin()[0] ; else + if ( b->getMax()[0] < center[0] ) closest[0] = b->getMax()[0] ; else + closest[0] = center[0] ; + + if ( b->getMin()[1] > center[1] ) closest[1] = b->getMin()[1] ; else + if ( b->getMax()[1] < center[1] ) closest[1] = b->getMax()[1] ; else + closest[1] = center[1] ; + + if ( b->getMin()[2] > center[2] ) closest[2] = b->getMin()[2] ; else + if ( b->getMax()[2] < center[2] ) closest[2] = b->getMax()[2] ; else + closest[2] = center[2] ; + + return sgCompare3DSqdDist ( closest, center, sgSquare ( radius ) ) <= 0 ; +} + + +/************************\ +* sgFrustum routines * +\************************/ + +void sgFrustum::update () +{ + if ( fabs ( ffar - nnear ) < 0.1 ) + { + ulSetError ( UL_WARNING, "sgFrustum: Can't support depth of view <0.1 units."); + return ; + } + + if ( hfov != SG_ZERO && vfov != SG_ZERO ) + { + if ( fabs ( hfov ) < 0.1 || fabs ( vfov ) < 0.1 ) + { + ulSetError ( UL_WARNING, ortho ? + "sgFrustum: Can't support width or height <0.1 units." : + "sgFrustum: Can't support fields of view narrower than 0.1 degrees." ) ; + return ; + } + + if ( ortho ) + { + right = SG_HALF * hfov ; + top = SG_HALF * vfov ; + } + else + { + right = nnear * (SGfloat) tan ( hfov * SG_DEGREES_TO_RADIANS / SG_TWO ) ; + top = nnear * (SGfloat) tan ( vfov * SG_DEGREES_TO_RADIANS / SG_TWO ) ; + } + + left = -right ; + bot = -top ; + } + + + /* Compute the projection matrix */ + + SGfloat width = right - left ; + SGfloat height = top - bot ; + SGfloat depth = ffar - nnear ; + + if ( ortho ) + { + /* orthographic */ + + mat[0][0] = SG_TWO / width ; + mat[0][1] = SG_ZERO ; + mat[0][2] = SG_ZERO ; + mat[0][3] = SG_ZERO ; + + mat[1][0] = SG_ZERO ; + mat[1][1] = SG_TWO / height ; + mat[1][2] = SG_ZERO ; + mat[1][3] = SG_ZERO ; + + mat[2][0] = SG_ZERO ; + mat[2][1] = SG_ZERO ; + mat[2][2] = -SG_TWO / depth ; + mat[2][3] = SG_ZERO ; + + mat[3][0] = -( left + right ) / width ; + mat[3][1] = -( bot + top ) / height ; + mat[3][2] = -( nnear + ffar ) / depth ; + mat[3][3] = SG_ONE ; + } + else + { + /* perspective */ + + mat[0][0] = SG_TWO * nnear / width ; + mat[0][1] = SG_ZERO ; + mat[0][2] = SG_ZERO ; + mat[0][3] = SG_ZERO ; + + mat[1][0] = SG_ZERO ; + mat[1][1] = SG_TWO * nnear / height ; + mat[1][2] = SG_ZERO ; + mat[1][3] = SG_ZERO ; + + mat[2][0] = ( right + left ) / width ; + mat[2][1] = ( top + bot ) / height ; + mat[2][2] = -( ffar + nnear ) / depth ; + mat[2][3] = -SG_ONE ; + + mat[3][0] = SG_ZERO ; + mat[3][1] = SG_ZERO ; + mat[3][2] = -SG_TWO * nnear * ffar / depth ; + mat[3][3] = SG_ZERO ; + } + + + /* + * The clip planes are derived from the projection matrix. + * + * After projection (in clip coordinates), the clip planes are simply: + * + * left: ( 1, 0, 0, 1 ) + * right: ( -1, 0, 0, 1 ) + * bottom: ( 0, 1, 0, 1 ) + * top: ( 0, -1, 0, 1 ) + * near: ( 0, 0, 1, 1 ) + * far: ( 0, 0, -1, 1 ) + * + * These can easily be transformed *backwards* by + * multiplying by the transposed projection matrix, i.e: + * + * ( A ) ( A') + * ( B ) = mat^T ( B') + * ( C ) ( C') + * ( D ) ( D') + * + * where (A',B',C',D') represents a plane in clip coordinates, + * and (A,B,C,D) is the same plane expressed in eye coordinates. + */ + + sgSetVec4( plane[ SG_LEFT_PLANE ], SG_ONE, SG_ZERO, SG_ZERO, SG_ONE ); + sgSetVec4( plane[ SG_RIGHT_PLANE ], -SG_ONE, SG_ZERO, SG_ZERO, SG_ONE ); + sgSetVec4( plane[ SG_BOT_PLANE ], SG_ZERO, SG_ONE, SG_ZERO, SG_ONE ); + sgSetVec4( plane[ SG_TOP_PLANE ], SG_ZERO, -SG_ONE, SG_ZERO, SG_ONE ); + sgSetVec4( plane[ SG_NEAR_PLANE ], SG_ZERO, SG_ZERO, SG_ONE, SG_ONE ); + sgSetVec4( plane[ SG_FAR_PLANE ], SG_ZERO, SG_ZERO, -SG_ONE, SG_ONE ); + + for ( int i = 0 ; i < 6 ; i++ ) + { + sgVec4 tmp ; + + for ( int j = 0 ; j < 4 ; j++ ) + tmp[j] = sgScalarProductVec4 ( plane[i], mat[j] ) ; + + sgScaleVec4 ( plane[i], tmp, SG_ONE / sgLengthVec3 ( tmp ) ) ; + } +} + + + +#define OC_LEFT_SHIFT 0 +#define OC_RIGHT_SHIFT 1 +#define OC_TOP_SHIFT 2 +#define OC_BOT_SHIFT 3 +#define OC_NEAR_SHIFT 4 +#define OC_FAR_SHIFT 5 + +#define OC_ALL_ON_SCREEN 0x3F +#define OC_OFF_TRF ((1<= -tmp[3] ) << OC_LEFT_SHIFT ) | + (( tmp[1] <= tmp[3] ) << OC_TOP_SHIFT ) | + (( tmp[1] >= -tmp[3] ) << OC_BOT_SHIFT ) | + (( tmp[2] <= tmp[3] ) << OC_FAR_SHIFT ) | + (( tmp[2] >= -tmp[3] ) << OC_NEAR_SHIFT ) ; +} + +int sgFrustum::contains ( const sgVec3 pt ) const +{ + return getOutcode ( pt ) == OC_ALL_ON_SCREEN ; +} + + +int sgFrustum::contains ( const sgSphere *s ) const +{ + + const SGfloat *center = s->getCenter() ; + const SGfloat radius = s->getRadius() ; + + /* + Lop off half the database (roughly) with a quick near-plane test - and + lop off a lot more with a quick far-plane test + */ + + if ( -center[2] + radius < nnear || -center[2] - radius > ffar ) + return SG_OUTSIDE ; + + /* + OK, so the sphere lies between near and far. + + Measure the distance of the center point from the four sides of the frustum, + if it's outside by more than the radius then it's history. + + It's tempting to do a quick test to see if the center point is + onscreen using sgFrustumContainsPt - but that takes a matrix transform + which is 16 multiplies and 12 adds - versus this test which does the + whole task using only 12 multiplies and 8 adds. + */ + + /* + A few operations are saved by observing that certain values in the plane + equations are zero or one. These are specific to orthographic and perspective + projections respectively. + */ + + SGfloat sp1, sp2, sp3, sp4 ; + + if ( ortho ) + { + /* + left: ( 1, 0, 0, x ) + right: ( -1, 0, 0, x ) + bottom: ( 0, 1, 0, x ) + top: ( 0, -1, 0, x ) + */ + sp1 = plane[ SG_LEFT_PLANE ][3] + center[0] ; + sp2 = plane[ SG_RIGHT_PLANE ][3] - center[0] ; + sp3 = plane[ SG_BOT_PLANE ][3] + center[1] ; + sp4 = plane[ SG_TOP_PLANE ][3] - center[1] ; + } + else + { +#if 0 + /* + left: ( x, 0, x, 0 ) + right: ( x, 0, x, 0 ) + bottom: ( 0, x, x, 0 ) + top: ( 0, x, x, 0 ) + */ + sp1 = plane[ SG_LEFT_PLANE ][0] * center[0] + plane[ SG_LEFT_PLANE ][2] * center[2] ; + sp2 = plane[ SG_RIGHT_PLANE ][0] * center[0] + plane[ SG_RIGHT_PLANE ][2] * center[2] ; + sp3 = plane[ SG_BOT_PLANE ][1] * center[1] + plane[ SG_BOT_PLANE ][2] * center[2] ; + sp4 = plane[ SG_TOP_PLANE ][1] * center[1] + plane[ SG_TOP_PLANE ][2] * center[2] ; +#else + /* + Note: in the general case, we would have to do: + + sp1 = sgScalarProductVec3 ( left_plane, center ) + left_plane[3] ; + sp2 = sgScalarProductVec3 ( right_plane, center ) + right_plane[3] ; + ... + sp6 = sgScalarProductVec3 ( far_plane, center ) + far_plane[3] ; + */ + sp1 = sgScalarProductVec3 ( plane[ SG_LEFT_PLANE ], center ) + plane[ SG_LEFT_PLANE ][3] ; + sp2 = sgScalarProductVec3 ( plane[ SG_RIGHT_PLANE ], center ) + plane[ SG_RIGHT_PLANE ][3] ; + sp3 = sgScalarProductVec3 ( plane[ SG_BOT_PLANE ], center ) + plane[ SG_BOT_PLANE ][3] ; + sp4 = sgScalarProductVec3 ( plane[ SG_TOP_PLANE ], center ) + plane[ SG_TOP_PLANE ][3] ; +#endif + } + + if ( -sp1 > radius || -sp2 > radius || -sp3 > radius || -sp4 > radius ) + return SG_OUTSIDE ; + + /* + If it's inside by more than the radius then it's *completely* inside + and we can save time elsewhere if we know that for sure. + */ + + if ( sp1 >= radius && sp2 >= radius && sp3 >= radius && sp4 >= radius + && -center[2] - radius >= nnear && -center[2] + radius <= ffar ) + return SG_INSIDE ; + + return SG_STRADDLE ; +} + + +int sgFrustum::contains ( const sgBox *b ) const +{ + sgVec3 p[8] = { + { b->getMin()[0], b->getMin()[1], b->getMin()[2] }, + { b->getMax()[0], b->getMin()[1], b->getMin()[2] }, + { b->getMin()[0], b->getMax()[1], b->getMin()[2] }, + { b->getMax()[0], b->getMax()[1], b->getMin()[2] }, + { b->getMin()[0], b->getMin()[1], b->getMax()[2] }, + { b->getMax()[0], b->getMin()[1], b->getMax()[2] }, + { b->getMin()[0], b->getMax()[1], b->getMax()[2] }, + { b->getMax()[0], b->getMax()[1], b->getMax()[2] }, + } ; + + int all = -1 ; + int one = 0 ; + + for (int i = 0 ; i < 8 ; i++ ) + { + int tmp = ~ getOutcode ( p[i] ) ; + all &= tmp ; + one |= tmp ; + } + + return ( all ? SG_OUTSIDE : one ? SG_STRADDLE : SG_INSIDE ) ; +} + + + +SGfloat sgDistSquaredToLineVec3 ( const sgLine3 line, const sgVec3 pnt ) +{ + sgVec3 r ; sgSubVec3 ( r, pnt, line.point_on_line ) ; + + float projectedDistance = sgScalarProductVec3(r,line.direction_vector); + + return sgScalarProductVec3 ( r, r ) - + projectedDistance * projectedDistance; +} + + + +SGfloat sgDistSquaredToLineSegmentVec3 ( const sgLineSegment3 line, + const sgVec3 pnt ) +{ + sgVec3 v ; sgSubVec3 ( v, line.b, line.a ) ; + sgVec3 r1 ; sgSubVec3 ( r1, pnt, line.a ) ; + + SGfloat r1_dot_v = sgScalarProductVec3 ( r1, v ) ; + + if ( r1_dot_v <= 0 ) /* Off the "A" end */ + return sgScalarProductVec3 ( r1, r1 ) ; + + sgVec3 r2 ; sgSubVec3 ( r2, pnt, line.b ) ; + + SGfloat r2_dot_v = sgScalarProductVec3 ( r2, v ) ; + + if ( r2_dot_v >= 0 ) /* Off the "B" end */ + return sgScalarProductVec3 ( r2, r2 ) ; + + /* Closest point on line is on the line segment */ + + return sgScalarProductVec3 ( r1, r1 ) - r1_dot_v * r1_dot_v / sgScalarProductVec3 ( v, v ) ; +} + + + +void sgMakeCoordMat4 ( sgMat4 m, const SGfloat x, const SGfloat y, const SGfloat z, const SGfloat h, const SGfloat p, const SGfloat r ) +{ + SGfloat ch, sh, cp, sp, cr, sr, srsp, crsp, srcp ; + + if ( h == SG_ZERO ) + { + ch = SGD_ONE ; + sh = SGD_ZERO ; + } + else + { + sh = sgSin ( h ) ; + ch = sgCos ( h ) ; + } + + if ( p == SG_ZERO ) + { + cp = SGD_ONE ; + sp = SGD_ZERO ; + } + else + { + sp = sgSin ( p ) ; + cp = sgCos ( p ) ; + } + + if ( r == SG_ZERO ) + { + cr = SGD_ONE ; + sr = SGD_ZERO ; + srsp = SGD_ZERO ; + srcp = SGD_ZERO ; + crsp = sp ; + } + else + { + sr = sgSin ( r ) ; + cr = sgCos ( r ) ; + srsp = sr * sp ; + crsp = cr * sp ; + srcp = sr * cp ; + } + + m[0][0] = (SGfloat)( ch * cr - sh * srsp ) ; + m[1][0] = (SGfloat)( -sh * cp ) ; + m[2][0] = (SGfloat)( sr * ch + sh * crsp ) ; + m[3][0] = x ; + + m[0][1] = (SGfloat)( cr * sh + srsp * ch ) ; + m[1][1] = (SGfloat)( ch * cp ) ; + m[2][1] = (SGfloat)( sr * sh - crsp * ch ) ; + m[3][1] = y ; + + m[0][2] = (SGfloat)( -srcp ) ; + m[1][2] = (SGfloat)( sp ) ; + m[2][2] = (SGfloat)( cr * cp ) ; + m[3][2] = z ; + + m[0][3] = SG_ZERO ; + m[1][3] = SG_ZERO ; + m[2][3] = SG_ZERO ; + m[3][3] = SG_ONE ; +} + + +void sgMakeTransMat4 ( sgMat4 m, const sgVec3 xyz ) +{ + m[0][1] = m[0][2] = m[0][3] = + m[1][0] = m[1][2] = m[1][3] = + m[2][0] = m[2][1] = m[2][3] = SG_ZERO ; + m[0][0] = m[1][1] = m[2][2] = m[3][3] = SG_ONE ; + sgCopyVec3 ( m[3], xyz ) ; +} + + +void sgMakeTransMat4 ( sgMat4 m, const SGfloat x, const SGfloat y, const SGfloat z ) +{ + m[0][1] = m[0][2] = m[0][3] = + m[1][0] = m[1][2] = m[1][3] = + m[2][0] = m[2][1] = m[2][3] = SG_ZERO ; + m[0][0] = m[1][1] = m[2][2] = m[3][3] = SG_ONE ; + sgSetVec3 ( m[3], x, y, z ) ; +} + + +void sgSetCoord ( sgCoord *dst, const sgMat4 src ) +{ + sgCopyVec3 ( dst->xyz, src[3] ) ; + + sgMat4 mat ; + + SGfloat s = sgLengthVec3 ( src[0] ) ; + + if ( s <= 0.00001 ) + { + ulSetError ( UL_WARNING, "sgMat4ToCoord: ERROR - Bad Matrix." ) ; + sgSetVec3 ( dst -> hpr, SG_ZERO, SG_ZERO, SG_ZERO ) ; + return ; + } + + sgScaleMat4 ( mat, src, SG_ONE / s ) ; + + dst->hpr[1] = sgASin ( _sgClampToUnity ( mat[1][2] ) ) ; + + SGfloat cp = sgCos ( dst->hpr[1] ) ; + + /* If pointing nearly vertically up - then heading is ill-defined */ + + if ( cp > -0.00001 && cp < 0.00001 ) + { + SGfloat cr = _sgClampToUnity ( mat[0][1] ) ; + SGfloat sr = _sgClampToUnity (-mat[2][1] ) ; + + dst->hpr[0] = SG_ZERO ; + dst->hpr[2] = sgATan2 ( sr, cr ) ; + } + else + { + cp = SG_ONE / cp ; + SGfloat sr = _sgClampToUnity ( -mat[0][2] * cp ) ; + SGfloat cr = _sgClampToUnity ( mat[2][2] * cp ) ; + SGfloat sh = _sgClampToUnity ( -mat[1][0] * cp ) ; + SGfloat ch = _sgClampToUnity ( mat[1][1] * cp ) ; + + if ( (sh == SG_ZERO && ch == SG_ZERO) || (sr == SG_ZERO && cr == SG_ZERO) ) + { + cr = _sgClampToUnity ( mat[0][1] ) ; + sr = _sgClampToUnity (-mat[2][1] ) ; + + dst->hpr[0] = SG_ZERO ; + } + else + dst->hpr[0] = sgATan2 ( sh, ch ) ; + + dst->hpr[2] = sgATan2 ( sr, cr ) ; + } +} + + +void sgMakeNormal(sgVec2 dst, const sgVec2 a, const sgVec2 b ) +{ + sgSubVec2 ( dst, b, a ) ; + float tmp = dst [ 0 ] ; dst [ 0 ] = -dst [ 1 ] ; dst [ 1 ] = tmp ; + sgNormaliseVec2 ( dst ) ; +} + + +void sgMakeNormal(sgVec3 dst, const sgVec3 a, const sgVec3 b, const sgVec3 c ) +{ + sgVec3 ab ; sgSubVec3 ( ab, b, a ) ; + sgVec3 ac ; sgSubVec3 ( ac, c, a ) ; + sgVectorProductVec3 ( dst, ab,ac ) ; sgNormaliseVec3 ( dst ) ; +} + + +void sgPreMultMat4( sgMat4 dst, const sgMat4 src ) +{ + sgMat4 mat ; + sgMultMat4 ( mat, dst, src ) ; + sgCopyMat4 ( dst, mat ) ; +} + +void sgPostMultMat4( sgMat4 dst, const sgMat4 src ) +{ + sgMat4 mat ; + sgMultMat4 ( mat, src, dst ) ; + sgCopyMat4 ( dst, mat ) ; +} + +void sgMultMat4( sgMat4 dst, const sgMat4 m1, const sgMat4 m2 ) +{ + for ( int j = 0 ; j < 4 ; j++ ) + { + dst[0][j] = m2[0][0] * m1[0][j] + + m2[0][1] * m1[1][j] + + m2[0][2] * m1[2][j] + + m2[0][3] * m1[3][j] ; + + dst[1][j] = m2[1][0] * m1[0][j] + + m2[1][1] * m1[1][j] + + m2[1][2] * m1[2][j] + + m2[1][3] * m1[3][j] ; + + dst[2][j] = m2[2][0] * m1[0][j] + + m2[2][1] * m1[1][j] + + m2[2][2] * m1[2][j] + + m2[2][3] * m1[3][j] ; + + dst[3][j] = m2[3][0] * m1[0][j] + + m2[3][1] * m1[1][j] + + m2[3][2] * m1[2][j] + + m2[3][3] * m1[3][j] ; + } +} + + +void sgTransposeNegateMat4 ( sgMat4 dst, const sgMat4 src ) +{ + /* Poor man's invert - can be used when matrix is a simple rotate-translate */ + + dst[0][0] = src[0][0] ; + dst[1][0] = src[0][1] ; + dst[2][0] = src[0][2] ; + dst[3][0] = - sgScalarProductVec3 ( src[3], src[0] ) ; + + dst[0][1] = src[1][0] ; + dst[1][1] = src[1][1] ; + dst[2][1] = src[1][2] ; + dst[3][1] = - sgScalarProductVec3 ( src[3], src[1] ) ; + + dst[0][2] = src[2][0] ; + dst[1][2] = src[2][1] ; + dst[2][2] = src[2][2] ; + dst[3][2] = - sgScalarProductVec3 ( src[3], src[2] ) ; + + dst[0][3] = SG_ZERO ; + dst[1][3] = SG_ZERO ; + dst[2][3] = SG_ZERO ; + dst[3][3] = SG_ONE ; +} + + +void sgTransposeNegateMat4 ( sgMat4 dst ) +{ + sgMat4 src ; + sgCopyMat4 ( src, dst ) ; + sgTransposeNegateMat4 ( dst, src ) ; +} + + + +void sgInvertMat4 ( sgMat4 dst, const sgMat4 src ) +{ + sgMat4 tmp ; + + sgCopyMat4 ( tmp, src ) ; + sgMakeIdentMat4 ( dst ) ; + + for ( int i = 0 ; i != 4 ; i++ ) + { + SGfloat val = tmp[i][i] ; + int ind = i ; + int j ; + + for ( j = i + 1 ; j != 4 ; j++ ) + { + if ( fabs ( tmp[i][j] ) > fabs(val) ) + { + ind = j; + val = tmp[i][j] ; + } + } + + if ( ind != i ) + { /* swap columns */ + for ( j = 0 ; j != 4 ; j++ ) + { + SGfloat t ; + t = dst[j][i]; dst[j][i] = dst[j][ind]; dst[j][ind] = t ; + t = tmp[j][i]; tmp[j][i] = tmp[j][ind]; tmp[j][ind] = t ; + } + } + + // if ( val == SG_ZERO) + if ( fabs(val) <= FLT_EPSILON ) + { + ulSetError ( UL_WARNING, "sg: ERROR - Singular matrix, no inverse!" ) ; + sgMakeIdentMat4 ( dst ) ; /* Do *something* */ + return; + } + + SGfloat ival = SG_ONE / val ; + + for ( j = 0 ; j != 4 ; j++ ) + { + tmp[j][i] *= ival ; + dst[j][i] *= ival ; + } + + for (j = 0; j != 4; j++) + { + if ( j == i ) + continue ; + + val = tmp[i][j] ; + + for ( int k = 0 ; k != 4 ; k++ ) + { + tmp[k][j] -= tmp[k][i] * val ; + dst[k][j] -= dst[k][i] * val ; + } + } + } +} + + + +void sgXformVec3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) +{ + SGfloat t0 = src[ 0 ] ; + SGfloat t1 = src[ 1 ] ; + SGfloat t2 = src[ 2 ] ; + + dst[0] = t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] ; + + dst[1] = t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] ; + + dst[2] = t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] ; +} + + +void sgXformPnt3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) +{ + SGfloat t0 = src[ 0 ] ; + SGfloat t1 = src[ 1 ] ; + SGfloat t2 = src[ 2 ] ; + + dst[0] = t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] + + mat[ 3 ][ 0 ] ; + + dst[1] = t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] + + mat[ 3 ][ 1 ] ; + + dst[2] = t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] + + mat[ 3 ][ 2 ] ; +} + + +void sgXformPnt4 ( sgVec4 dst, const sgVec4 src, const sgMat4 mat ) +{ + SGfloat t0 = src[ 0 ] ; + SGfloat t1 = src[ 1 ] ; + SGfloat t2 = src[ 2 ] ; + SGfloat t3 = src[ 3 ] ; + + dst[0] = t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] + + t3 * mat[ 3 ][ 0 ] ; + + dst[1] = t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] + + t3 * mat[ 3 ][ 1 ] ; + + dst[2] = t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] + + t3 * mat[ 3 ][ 2 ] ; + + dst[3] = t0 * mat[ 0 ][ 3 ] + + t1 * mat[ 1 ][ 3 ] + + t2 * mat[ 2 ][ 3 ] + + t3 * mat[ 3 ][ 3 ] ; +} + + +void sgFullXformPnt3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) +{ + sgVec4 tmp ; + + tmp [ 0 ] = src [ 0 ] ; + tmp [ 1 ] = src [ 1 ] ; + tmp [ 2 ] = src [ 2 ] ; + tmp [ 3 ] = SG_ONE ; + + sgXformPnt4 ( tmp, tmp, mat ) ; + sgScaleVec3 ( dst, tmp, SG_ONE / tmp [ 3 ] ) ; +} + +void sgHPRfromVec3 ( sgVec3 hpr, const sgVec3 src ) +{ + sgVec3 tmp ; + sgCopyVec3 ( tmp, src ) ; + sgNormaliseVec3 ( tmp ) ; + hpr[0] = - sgATan2 ( tmp [ 0 ], tmp [ 1 ] ) ; + hpr[1] = - sgATan2 ( tmp [ 2 ], sgSqrt ( sgSquare ( tmp [ 0 ] ) + + sgSquare ( tmp [ 1 ] ) ) ) ; + hpr[2] = SG_ZERO ; +} + + + +/* + Quaternion routines are Copyright (C) 1999 + Kevin B. Thompson + Modified by Sylvan W. Clebsch + Largely rewritten by "Negative0" + More added by John Fay. +*/ + + +void sgQuatToAngleAxis ( SGfloat *angle, + SGfloat *x, SGfloat *y, SGfloat *z, + const sgQuat src ) +{ + sgVec3 axis ; + + sgQuatToAngleAxis ( angle, axis, src ) ; + + *x = axis [ 0 ] ; + *y = axis [ 1 ] ; + *z = axis [ 2 ] ; +} + + +void sgQuatToAngleAxis ( SGfloat *angle, sgVec3 axis, const sgQuat src ) +{ + SGfloat a = (SGfloat) acos ( src[SG_W] ) ; + if ( isnan(a) || !finite(a) ) { + ulSetError(UL_WARNING,"sgQuatToAngleAxis: acos(%f) %f",src[SG_W],a); + if ( src[SG_W] >= 1 ) a = 0; + else if ( src[SG_W] <= -1 ) a = SG_PI; + ulSetError(UL_WARNING," - recovered to %f\n",a); + } + + SGfloat s = (SGfloat) sin ( a ) ; + + *angle = a * SG_RADIANS_TO_DEGREES * SG_TWO ; + + if ( s == SG_ZERO ) + sgSetVec3 ( axis, SG_ZERO, SG_ZERO, SG_ONE ); + else + { + sgSetVec3 ( axis, src[SG_X], src[SG_Y], src[SG_Z] ) ; + sgScaleVec3 ( axis, SG_ONE / s ) ; + } +} + + +void sgAngleAxisToQuat ( sgQuat dst, + const SGfloat angle, + const SGfloat x, const SGfloat y, const SGfloat z ) +{ + sgVec3 axis ; + sgSetVec3 ( axis, x, y, z ) ; + sgAngleAxisToQuat ( dst, angle, axis ) ; +} + + +void sgAngleAxisToQuat ( sgQuat dst, const SGfloat angle, const sgVec3 axis ) +{ + SGfloat temp_angle = angle * SG_DEGREES_TO_RADIANS / SG_TWO ; + + sgVec3 ax ; + sgNormaliseVec3 ( ax, axis ) ; + + SGfloat s = - (SGfloat) sin ( temp_angle ) ; + + dst[SG_W] = (SGfloat) cos ( temp_angle ) ; + sgScaleVec3 ( dst, ax, s ) ; +} + + +//from gamasutra.com +//by nb + +void sgMatrixToQuat( sgQuat quat, const sgMat4 m ) +{ + SGfloat tr, s, q[4] ; + int i, j, k ; + + int nxt[3] = {1, 2, 0}; + + tr = m[0][0] + m[1][1] + m[2][2]; + + // check the diagonal + if (tr > SG_ZERO ) + { + s = (SGfloat) sgSqrt (tr + SG_ONE); + quat[SG_W] = s / SG_TWO; + s = SG_HALF / s; + quat[SG_X] = (m[1][2] - m[2][1]) * s; + quat[SG_Y] = (m[2][0] - m[0][2]) * s; + quat[SG_Z] = (m[0][1] - m[1][0]) * s; + } + else + { + // diagonal is negative + i = 0; + if (m[1][1] > m[0][0]) i = 1; + if (m[2][2] > m[i][i]) i = 2; + j = nxt[i]; + k = nxt[j]; + s = (SGfloat) sgSqrt ((m[i][i] - (m[j][j] + m[k][k])) + SG_ONE); + q[i] = s * SG_HALF; + + if (s != SG_ZERO) s = SG_HALF / s; + + q[3] = (m[j][k] - m[k][j]) * s; + q[j] = (m[i][j] + m[j][i]) * s; + q[k] = (m[i][k] + m[k][i]) * s; + + quat[SG_X] = q[0]; + quat[SG_Y] = q[1]; + quat[SG_Z] = q[2]; + quat[SG_W] = q[3]; + } + + // seems to yield the inverse rotation, so: + quat[SG_W] = - quat[SG_W]; +} + + +void sgMultQuat ( sgQuat dst, const sgQuat a, const sgQuat b ) +{ + /* [ ww' - v.v', vxv' + wv' + v'w ] */ + + SGfloat t[8]; + + t[0] = (a[SG_W] + a[SG_X]) * (b[SG_W] + b[SG_X]); + t[1] = (a[SG_Z] - a[SG_Y]) * (b[SG_Y] - b[SG_Z]); + t[2] = (a[SG_X] - a[SG_W]) * (b[SG_Y] + b[SG_Z]); + t[3] = (a[SG_Y] + a[SG_Z]) * (b[SG_X] - b[SG_W]); + t[4] = (a[SG_X] + a[SG_Z]) * (b[SG_X] + b[SG_Y]); + t[5] = (a[SG_X] - a[SG_Z]) * (b[SG_X] - b[SG_Y]); + t[6] = (a[SG_W] + a[SG_Y]) * (b[SG_W] - b[SG_Z]); + t[7] = (a[SG_W] - a[SG_Y]) * (b[SG_W] + b[SG_Z]); + + dst[SG_W] = t[1] + ((-t[4] - t[5] + t[6] + t[7]) * SG_HALF); + dst[SG_X] = t[0] - (( t[4] + t[5] + t[6] + t[7]) * SG_HALF); + dst[SG_Y] = -t[2] + (( t[4] - t[5] + t[6] - t[7]) * SG_HALF); + dst[SG_Z] = -t[3] + (( t[4] - t[5] - t[6] + t[7]) * SG_HALF); +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgMultQuat2 ( sgQuat dst, const sgQuat a, const sgQuat b ) +{ + SGfloat A, B, C, D, E, F, G, H; + + A = (a[SG_W] + a[SG_X]) * (b[SG_W] + b[SG_X]) ; + B = (a[SG_Z] - a[SG_Y]) * (b[SG_Y] - b[SG_Z]) ; + C = (a[SG_X] - a[SG_W]) * (b[SG_Y] + b[SG_Z]) ; + D = (a[SG_Y] + a[SG_Z]) * (b[SG_X] - b[SG_W]) ; + E = (a[SG_X] + a[SG_Z]) * (b[SG_X] + b[SG_Y]) ; + F = (a[SG_X] - a[SG_Z]) * (b[SG_X] - b[SG_Y]) ; + G = (a[SG_W] + a[SG_Y]) * (b[SG_W] - b[SG_Z]) ; + H = (a[SG_W] - a[SG_Y]) * (b[SG_W] + b[SG_Z]) ; + + + dst[SG_W] = B + (-E - F + G + H) / SG_TWO ; + dst[SG_X] = A - ( E + F + G + H) / SG_TWO ; + dst[SG_Y] = -C + ( E - F + G - H) / SG_TWO ; + dst[SG_Z] = -D + ( E - F - G + H) / SG_TWO ; +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgEulerToQuat(sgQuat quat, const sgVec3 hpr ) +{ + SGfloat cr, cp, cy, sr, sp, sy, cpcy, spsy; + +// calculate trig identities + cr = (SGfloat) cos(hpr[2]*SG_DEGREES_TO_RADIANS/SG_TWO); + cp = (SGfloat) cos(hpr[1]*SG_DEGREES_TO_RADIANS/SG_TWO); + cy = (SGfloat) cos(hpr[0]*SG_DEGREES_TO_RADIANS/SG_TWO); + + sr = (SGfloat) sin(hpr[2]*SG_DEGREES_TO_RADIANS/SG_TWO); + sp = (SGfloat) sin(hpr[1]*SG_DEGREES_TO_RADIANS/SG_TWO); + sy = (SGfloat) sin(hpr[0]*SG_DEGREES_TO_RADIANS/SG_TWO); + + cpcy = cp * cy; + spsy = sp * sy; + + quat[SG_W] = cr * cpcy + sr * spsy; + quat[SG_X] = sr * cpcy - cr * spsy; + quat[SG_Y] = cr * sp * cy + sr * cp * sy; + quat[SG_Z] = cr * cp * sy - sr * sp * cy; +} + +//from darwin3d.com +// jeffl@darwin3d.com + +void sgQuatToEuler( sgVec3 hpr, const sgQuat quat ) +{ + SGfloat matrix[3][3]; + SGfloat cx,sx; + SGfloat cy,sy; + SGfloat cz,sz; + + // CONVERT QUATERNION TO MATRIX - I DON'T REALLY NEED ALL OF IT + + matrix[0][0] = SG_ONE - (SG_TWO * quat[SG_Y] * quat[SG_Y]) + - (SG_TWO * quat[SG_Z] * quat[SG_Z]); +//matrix[0][1] = (SG_TWO * quat->x * quat->y) - (SG_TWO * quat->w * quat->z); +//matrix[0][2] = (SG_TWO * quat->x * quat->z) + (SG_TWO * quat->w * quat->y); + + matrix[1][0] = (SG_TWO * quat[SG_X] * quat[SG_Y]) + + (SG_TWO * quat[SG_W] * quat[SG_Z]); +//matrix[1][1] = SG_ONE - (SG_TWO * quat->x * quat->x) +// - (SG_TWO * quat->z * quat->z); +//matrix[1][2] = (SG_TWO * quat->y * quat->z) - (SG_TWO * quat->w * quat->x); + + matrix[2][0] = (SG_TWO * quat[SG_X] * quat[SG_Z]) - + (SG_TWO * quat[SG_W] * quat[SG_Y]); + matrix[2][1] = (SG_TWO * quat[SG_Y] * quat[SG_Z]) + + (SG_TWO * quat[SG_W] * quat[SG_X]); + matrix[2][2] = SG_ONE - (SG_TWO * quat[SG_X] * quat[SG_X]) + - (SG_TWO * quat[SG_Y] * quat[SG_Y]); + + sy = -matrix[2][0]; + cy = (SGfloat)sgSqrt(SG_ONE - (sy * sy)); + + hpr[1] = sgATan2 ( sy, cy ) ; + + // AVOID DIVIDE BY ZERO ERROR ONLY WHERE Y= +-90 or +-270 + // NOT CHECKING cy BECAUSE OF PRECISION ERRORS + if (sy != SG_ONE && sy != -SG_ONE) + { + cx = matrix[2][2] / cy; + sx = matrix[2][1] / cy; + hpr[0] = sgATan2 ( sx, cx ) ; + + cz = matrix[0][0] / cy; + sz = matrix[1][0] / cy; + hpr[2] = sgATan2 ( sz, cz ) ; + } + else + { + // SINCE Cos(Y) IS 0, I AM SCREWED. ADOPT THE STANDARD Z = 0 + // I THINK THERE IS A WAY TO FIX THIS BUT I AM NOT SURE. EULERS SUCK + // NEED SOME MORE OF THE MATRIX TERMS NOW + + matrix[1][1] = SG_ONE - (SG_TWO * quat[SG_X] * quat[SG_X]) + - (SG_TWO * quat[SG_Z] * quat[SG_Z]); + matrix[1][2] = (SG_TWO * quat[SG_Y] * quat[SG_Z]) - + (SG_TWO * quat[SG_W] * quat[SG_X]); + + cx = matrix[1][1]; + sx = -matrix[1][2]; + hpr[0] = sgATan2 ( sx, cx ) ; + + cz = SG_ONE ; + sz = SG_ZERO ; + hpr[2] = sgATan2 ( sz, cz ) ; + } +} + + +void sgQuatToMatrix ( sgMat4 dst, const sgQuat q ) +{ + SGfloat two_xx = q[SG_X] * (q[SG_X] + q[SG_X]) ; + SGfloat two_xy = q[SG_X] * (q[SG_Y] + q[SG_Y]) ; + SGfloat two_xz = q[SG_X] * (q[SG_Z] + q[SG_Z]) ; + + SGfloat two_wx = q[SG_W] * (q[SG_X] + q[SG_X]) ; + SGfloat two_wy = q[SG_W] * (q[SG_Y] + q[SG_Y]) ; + SGfloat two_wz = q[SG_W] * (q[SG_Z] + q[SG_Z]) ; + + SGfloat two_yy = q[SG_Y] * (q[SG_Y] + q[SG_Y]) ; + SGfloat two_yz = q[SG_Y] * (q[SG_Z] + q[SG_Z]) ; + + SGfloat two_zz = q[SG_Z] * (q[SG_Z] + q[SG_Z]) ; + + sgSetVec4 ( dst[0], SG_ONE-(two_yy+two_zz), two_xy-two_wz, two_xz+two_wy, SG_ZERO ) ; + sgSetVec4 ( dst[1], two_xy+two_wz, SG_ONE-(two_xx+two_zz), two_yz-two_wx, SG_ZERO ) ; + sgSetVec4 ( dst[2], two_xz-two_wy, two_yz+two_wx, SG_ONE-(two_xx+two_yy), SG_ZERO ) ; + sgSetVec4 ( dst[3], SG_ZERO, SG_ZERO, SG_ZERO, SG_ONE ) ; +} + + +//from gamasutra.com +//by nb@netcom.ca + +/***************************** + DEPRECATED...use sgQuatToMatrix instead. +*****************************/ + +void sgMakeRotMat42( sgMat4 m, sgQuat quat ){ + SGfloat wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; + + // calculate coefficients + x2 = quat[SG_X] + quat[SG_X]; y2 = quat[SG_Y] + quat[SG_Y]; + z2 = quat[SG_Z] + quat[SG_Z]; + xx = quat[SG_X] * x2; xy = quat[SG_X] * y2; xz = quat[SG_X] * z2; + yy = quat[SG_Y] * y2; yz = quat[SG_Y] * z2; zz = quat[SG_Z] * z2; + wx = quat[SG_W] * x2; wy = quat[SG_W] * y2; wz = quat[SG_W] * z2; + + m[0][0] = SG_ONE- (yy + zz); m[0][1] = xy - wz; + m[0][2] = xz + wy; m[0][3] = SG_ZERO ; + + m[1][0] = xy + wz; m[1][1] = SG_ONE- (xx + zz); + m[1][2] = yz - wx; m[1][3] = SG_ZERO ; + + m[2][0] = xz - wy; m[2][1] = yz + wx; + m[2][2] = SG_ONE- (xx + yy); m[2][3] = SG_ZERO ; + + m[3][0] = 0; m[3][1] = 0; + m[3][2] = 0; m[3][3] = 1; +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgSlerpQuat2( sgQuat dst, const sgQuat from, const sgQuat to, const SGfloat t ) +{ + SGfloat to1[4]; + SGfloat omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = from[SG_X] * to[SG_X] + + from[SG_Y] * to[SG_Y] + + from[SG_Z] * to[SG_Z] + + from[SG_W] * to[SG_W]; + + // adjust signs (if necessary) + + if ( cosom < SG_ZERO ) + { + cosom = -cosom; + to1[0] = - to[SG_X]; + to1[1] = - to[SG_Y]; + to1[2] = - to[SG_Z]; + to1[3] = - to[SG_W]; + } + else + { + to1[0] = to[SG_X]; + to1[1] = to[SG_Y]; + to1[2] = to[SG_Z]; + to1[3] = to[SG_W]; + } + + // calculate coefficients +#define DELTA SG_ZERO + if ( (SG_ONE- cosom) > DELTA ) + { + // standard case (slerp) + omega = (SGfloat) acos(cosom); + sinom = (SGfloat) sin(omega); + scale0 = (SGfloat) sin((SG_ONE- t) * omega) / sinom; + scale1 = (SGfloat) sin(t * omega) / sinom; + } + else + { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = SG_ONE- t; + scale1 = t; + } + + // calculate final values + dst[SG_X] = scale0 * from[SG_X] + scale1 * to1[0]; + dst[SG_Y] = scale0 * from[SG_Y] + scale1 * to1[1]; + dst[SG_Z] = scale0 * from[SG_Z] + scale1 * to1[2]; + dst[SG_W] = scale0 * from[SG_W] + scale1 * to1[3]; +} + +void sgSlerpQuat( sgQuat dst, const sgQuat from, const sgQuat to, const SGfloat t ) +{ + SGfloat co, scale0, scale1; + bool flip = false ; + + /* SWC - Interpolate between to quaternions */ + + co = sgScalarProductVec4 ( from, to ) ; + + if ( co < SG_ZERO ) + { + co = -co; + flip = true ; + } + + if ( co < SG_ONE - (SGfloat) 1e-6 ) + { + SGfloat o = (SGfloat) acos ( co ); + SGfloat so = SG_ONE / (SGfloat) sin ( o ); + + scale0 = (SGfloat) sin ( (SG_ONE - t) * o ) * so; + scale1 = (SGfloat) sin ( t * o ) * so; + } + else + { + scale0 = SG_ONE - t; + scale1 = t; + } + + if ( flip ) + { + scale1 = -scale1 ; + } + + dst[SG_X] = scale0 * from[SG_X] + scale1 * to[SG_X] ; + dst[SG_Y] = scale0 * from[SG_Y] + scale1 * to[SG_Y] ; + dst[SG_Z] = scale0 * from[SG_Z] + scale1 * to[SG_Z] ; + dst[SG_W] = scale0 * from[SG_W] + scale1 * to[SG_W] ; +} + + + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q r Q-1 -- this gives the components of a ROTATED vector in a STATIONARY + * coordinate system. We assume that Q is a unit quaternion. + */ +void sgRotateVecQuat ( sgVec3 vec, sgQuat q ) +{ + sgVec3 rot ; + sgFloat qwqw = q[SG_W] * q[SG_W] ; + sgFloat qwqx = q[SG_W] * q[SG_X] ; + sgFloat qwqy = q[SG_W] * q[SG_Y] ; + sgFloat qwqz = q[SG_W] * q[SG_Z] ; + sgFloat qxqx = q[SG_X] * q[SG_X] ; + sgFloat qxqy = q[SG_X] * q[SG_Y] ; + sgFloat qxqz = q[SG_X] * q[SG_Z] ; + sgFloat qyqy = q[SG_Y] * q[SG_Y] ; + sgFloat qyqz = q[SG_Y] * q[SG_Z] ; + sgFloat qzqz = q[SG_Z] * q[SG_Z] ; + rot[SG_X] = ( qwqw + qxqx - qyqy - qzqz ) * vec[SG_X] + 2.0f * ( qxqy - qwqz ) * vec[SG_Y] + 2.0f * ( qxqz + qwqy ) * vec[SG_Z] ; + rot[SG_Y] = ( qwqw - qxqx + qyqy - qzqz ) * vec[SG_Y] + 2.0f * ( qyqz - qwqx ) * vec[SG_Z] + 2.0f * ( qxqy + qwqz ) * vec[SG_X] ; + rot[SG_Z] = ( qwqw - qxqx - qyqy + qzqz ) * vec[SG_Z] + 2.0f * ( qxqz - qwqy ) * vec[SG_X] + 2.0f * ( qyqz + qwqx ) * vec[SG_Y] ; + sgCopyVec3 ( vec, rot ) ; +} + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q-1 r Q -- this gives the components of a STATIONARY vector in a ROTATED + * coordinate system. We assume that Q is a unit quaternion. + */ +void sgRotateCoordQuat ( sgVec3 vec, sgQuat q ) +{ + sgVec3 rot ; + sgFloat qwqw = q[SG_W] * q[SG_W] ; + sgFloat qwqx = q[SG_W] * q[SG_X] ; + sgFloat qwqy = q[SG_W] * q[SG_Y] ; + sgFloat qwqz = q[SG_W] * q[SG_Z] ; + sgFloat qxqx = q[SG_X] * q[SG_X] ; + sgFloat qxqy = q[SG_X] * q[SG_Y] ; + sgFloat qxqz = q[SG_X] * q[SG_Z] ; + sgFloat qyqy = q[SG_Y] * q[SG_Y] ; + sgFloat qyqz = q[SG_Y] * q[SG_Z] ; + sgFloat qzqz = q[SG_Z] * q[SG_Z] ; + rot[SG_X] = ( qwqw + qxqx - qyqy - qzqz ) * vec[SG_X] + 2.0f * ( qxqy + qwqz ) * vec[SG_Y] + 2.0f * ( qxqz - qwqy ) * vec[SG_Z] ; + rot[SG_Y] = ( qwqw - qxqx + qyqy - qzqz ) * vec[SG_Y] + 2.0f * ( qyqz + qwqx ) * vec[SG_Z] + 2.0f * ( qxqy - qwqz ) * vec[SG_X] ; + rot[SG_Z] = ( qwqw - qxqx - qyqy + qzqz ) * vec[SG_Z] + 2.0f * ( qxqz + qwqy ) * vec[SG_X] + 2.0f * ( qyqz - qwqx ) * vec[SG_Y] ; + sgCopyVec3 ( vec, rot ) ; +} + + +sgFloat sgDistSquaredToLineLineSegment ( const sgLineSegment3 seg, const sgLine3 line ) +{ + /* Convert the line segment to a line. We will deal with the segment limits later. */ + sgLine3 line2 ; + sgLineSegment3ToLine3 ( &line2, seg ) ; + + /* Get the dot product of the two direction vectors */ + sgFloat t1_dot_t2 = sgScalarProductVec3 ( line.direction_vector, line2.direction_vector ) ; + + /* If the lines are parallel, distance is just the distance from a point to the other line */ + if ( fabs ( t1_dot_t2 ) >= 1.0 ) + return sgDistSquaredToLineVec3 ( line, seg.a ) ; + + /* Get the parametric coordinates of the closest points on the two lines. The first line + * is parameterized by r = r1 + t1 u while the second is parameterized by r = r2 + t2 v. + * The square of the distance between them is [ ( r1 + t1 u ) - ( r2 + t2 v ) ] dot + * [ ( r1 + t1 u ) - ( r2 + t2 v ) ]. Differentiating this dot product with respect to + * u and v and setting the derivatives to zero gives a matrix equation: + * [ 1 -(t1 dot t2) ] [ u ] = [ ( r1 - r2 ) dot t1 ] + * [ -(t1 dot t2) 1 ] [ v ] [ -( r1 - r2 ) dot t2 ] + * We invert the matrix to get the equations below. + */ + sgVec3 r1_minus_r2 ; + sgSubVec3 ( r1_minus_r2, line.point_on_line, line2.point_on_line ) ; + + /* t1_t2_t2_minus_t1 = ( t1 dot t2 ) t2 - t1 + * t2_minus_t1_t2_t1 = t2 - ( t1 dot t2 ) t1 + */ + sgVec3 t1_t2_t2_minus_t1, t2_minus_t1_t2_t1 ; + sgAddScaledVec3 ( t1_t2_t2_minus_t1, line.direction_vector, line2.direction_vector, -t1_dot_t2 ) ; + sgNegateVec3 ( t1_t2_t2_minus_t1 ) ; + sgAddScaledVec3 ( t2_minus_t1_t2_t1, line2.direction_vector, line.direction_vector, -t1_dot_t2 ) ; + + sgFloat u = sgScalarProductVec3 ( r1_minus_r2, t1_t2_t2_minus_t1 ) / ( 1.0f - t1_dot_t2 * t1_dot_t2 ) ; + sgFloat v = sgScalarProductVec3 ( r1_minus_r2, t2_minus_t1_t2_t1 ) / ( 1.0f - t1_dot_t2 * t1_dot_t2 ) ; + + /* Since line 2 is a line segment, we limit "v" to between 0 and the distance between the points. */ + sgFloat length = sgDistanceVec3 ( seg.a, seg.b ) ; + if ( v < 0.0 ) v = 0.0 ; + if ( v > length ) v = length ; + + sgVec3 point1, point2 ; + sgAddScaledVec3 ( point1, line.point_on_line, line.direction_vector, u ) ; + sgAddScaledVec3 ( point2, line2.point_on_line, line2.direction_vector, v ) ; + return sgDistanceSquaredVec3 ( point1, point2 ) ; +} + + +void sgReflectInPlaneVec3 ( sgVec3 dst, const sgVec3 src, const sgVec4 plane ) +{ + SGfloat src_dot_norm = sgScalarProductVec3 ( src, plane ) ; + + sgVec3 tmp ; + + sgScaleVec3 ( tmp, plane, SG_TWO * src_dot_norm ) ; + sgSubVec3 ( dst, src, tmp ) ; +} + + + +int sgClassifyMat4 ( const sgMat4 m ) +{ + const SGfloat epsilon = 1e-6f ; + + int flags = 0 ; + + + SGfloat sx, sy, sz ; + + if ( m[0][1] == SG_ZERO && m[0][2] == SG_ZERO && + m[1][0] == SG_ZERO && m[1][2] == SG_ZERO && + m[2][0] == SG_ZERO && m[2][1] == SG_ZERO ) + { + + int n = ( m[0][0] < 0 ) + ( m[1][1] < 0 ) + ( m[2][2] < 0 ) ; + + if ( n > 1 ) + flags |= SG_ROTATION ; + + if ( n % 2 != 0 ) + flags |= SG_MIRROR ; + + sx = m[0][0] * m[0][0] ; + sy = m[1][1] * m[1][1] ; + sz = m[2][2] * m[2][2] ; + + } + else + { + + flags |= SG_ROTATION ; + + if ( sgAbs ( sgScalarProductVec3 ( m[1], m[2] ) ) > epsilon || + sgAbs ( sgScalarProductVec3 ( m[2], m[0] ) ) > epsilon || + sgAbs ( sgScalarProductVec3 ( m[0], m[1] ) ) > epsilon ) + { + flags |= SG_NONORTHO ; + } + + sgVec3 temp ; + sgVectorProductVec3 ( temp, m[0], m[1] ) ; + SGfloat det = sgScalarProductVec3 ( temp, m[2] ) ; + + if ( det < 0 ) + flags |= SG_MIRROR ; + + sx = sgScalarProductVec3 ( m[0], m[0] ) ; + sy = sgScalarProductVec3 ( m[1], m[1] ) ; + sz = sgScalarProductVec3 ( m[2], m[2] ) ; + + } + + + if ( sgAbs ( sx - sy ) > epsilon || + sgAbs ( sx - sz ) > epsilon ) + { + flags |= SG_NONORTHO ; + flags |= SG_GENERAL_SCALE ; // also set general scale bit, though it may be deleted in the future + } + else + { + if ( sgAbs ( sx - SG_ONE ) > epsilon ) + flags |= SG_SCALE ; + } + + + if ( m[3][0] != SG_ZERO || m[3][1] != SG_ZERO || m[3][2] != SG_ZERO ) + { + flags |= SG_TRANSLATION ; + } + + + if ( m[0][3] != SG_ZERO || m[1][3] != SG_ZERO || m[2][3] != SG_ZERO || + m[3][3] != SG_ONE ) + { + flags |= SG_PROJECTION ; + } + + + return flags ; +} + + +SGfloat sgTriangleSolver_ASAtoArea ( SGfloat angA, SGfloat lenB, SGfloat angC ) +{ + /* Get the third angle */ + + SGfloat angB = SG_180 - (angA + angC) ; + + /* Use Sine Rule to get length of a second side - then use SAStoArea. */ + + SGfloat sinB = sgSin ( angB ) ; + + if ( sinB == SG_ZERO ) + return SG_ZERO ; + + SGfloat lenA = lenB * sgSin(angA) / sinB ; + + return sgTriangleSolver_SAStoArea ( lenA, angC, lenB ) ; +} + + +SGfloat sgTriangleSolver_SAStoArea ( SGfloat lenA, SGfloat angB, SGfloat lenC ) +{ + return SG_HALF * lenC * lenA * sgSin ( angB ) ; +} + + +SGfloat sgTriangleSolver_SSStoArea ( SGfloat lenA, SGfloat lenB, SGfloat lenC ) +{ + /* Heron's formula */ + + SGfloat s = ( lenA + lenB + lenC ) / SG_TWO ; + SGfloat q = s * (s-lenA) * (s-lenB) * (s-lenC) ; + + /* Ikky illegal triangles generate zero areas. */ + + return ( q <= SG_ZERO ) ? SG_ZERO : sgSqrt ( q ) ; +} + + +SGfloat sgTriangleSolver_ASStoArea ( SGfloat angB, SGfloat lenA, SGfloat lenB, + int angA_is_obtuse ) +{ + SGfloat lenC ; + + sgTriangleSolver_ASStoSAA ( angB, lenA, lenB, angA_is_obtuse, + &lenC, NULL, NULL ) ; + + return sgTriangleSolver_SAStoArea ( lenA, angB, lenC ) ; +} + +SGfloat sgTriangleSolver_SAAtoArea ( SGfloat lenA, SGfloat angB, SGfloat angA ) +{ + SGfloat lenC ; + + sgTriangleSolver_SAAtoASS ( lenA, angB, angA, NULL, NULL, &lenC ) ; + + return sgTriangleSolver_SAStoArea ( lenA, angB, lenC ) ; +} + +void sgTriangleSolver_SSStoAAA ( SGfloat lenA, SGfloat lenB, SGfloat lenC, + SGfloat *angA, SGfloat *angB, SGfloat *angC ) +{ + SGfloat aa, bb, cc ; + + int flag = ( lenA == SG_ZERO ) | + (( lenB == SG_ZERO )<<1) | + (( lenC == SG_ZERO )<<2) ; + + /* Ikky zero-sized triangles generate zero/90 angles appropriately. */ + /* Ikky triangles with all lengths zero generate 60 degree angles. */ + /* Ikky impossible triangles generate all zero angles. */ + + switch ( flag ) + { + case 0 : /* no zero-lengthed sides */ + /* Cosine law */ + aa = sgACos (( lenB*lenB + lenC*lenC - lenA*lenA )/(SG_TWO*lenB*lenC)) ; + bb = sgACos (( lenA*lenA + lenC*lenC - lenB*lenB )/(SG_TWO*lenA*lenC)) ; + cc = sgACos (( lenA*lenA + lenB*lenB - lenC*lenC )/(SG_TWO*lenA*lenB)) ; + break ; + + case 1 : /* lenA is zero */ + aa = SG_ZERO ; + bb = cc = SG_90 ; + break ; + + case 2 : /* lenB is zero */ + bb = SG_ZERO ; + aa = cc = SG_90 ; + break ; + + case 4 : /* lenC is zero */ + cc = SG_ZERO ; + aa = bb = SG_90 ; + break ; + + case 3 : /* Two lengths are zero and the third isn't?!? */ + case 5 : + case 6 : + aa = bb = cc = SG_ZERO ; + break ; + + default : /* All three sides are zero length */ + aa = bb = cc = SG_60 ; + break ; + } + + if ( angA ) *angA = aa ; + if ( angB ) *angB = bb ; + if ( angC ) *angC = cc ; +} + +void sgTriangleSolver_SAStoASA ( SGfloat lenA, SGfloat angB, SGfloat lenC, + SGfloat *angA, SGfloat *lenB, SGfloat *angC ) +{ + /* Get third side using Cosine Rule */ + + SGfloat s = lenC * lenC + + lenA * lenA - SG_TWO * lenC * lenA * sgCos( angB ) ; + + SGfloat lb = ( s <= SG_ZERO ) ? SG_ZERO : (SGfloat) sqrt ( s ) ; + + if ( lenB ) *lenB = lb ; + + sgTriangleSolver_SSStoAAA ( lenA, lb, lenC, angA, NULL, angC ) ; +} + + +void sgTriangleSolver_ASAtoSAS ( SGfloat angA, SGfloat lenB, SGfloat angC, + SGfloat *lenA, SGfloat *angB, SGfloat *lenC ) +{ + /* Find the missing angle */ + + SGfloat bb = SG_180 - (angA + angC) ; + + if ( angB ) *angB = bb ; + + /* Use Sine Rule */ + + SGfloat sinB = sgSin ( bb ) ; + + if ( sinB == SG_ZERO ) + { + if ( lenA ) *lenA = lenB / SG_TWO ; /* One valid interpretation */ + if ( lenC ) *lenC = lenB / SG_TWO ; + } + else + { + if ( lenA ) *lenA = lenB * sgSin(angA) / sinB ; + if ( lenC ) *lenC = lenB * sgSin(angC) / sinB ; + } +} + + +void sgTriangleSolver_ASStoSAA ( SGfloat angB, SGfloat lenA, SGfloat lenB, + int angA_is_obtuse, + SGfloat *lenC, SGfloat *angA, SGfloat *angC ) +{ + /* Sine law */ + + SGfloat aa = (lenB == SG_ZERO ) ? SG_ZERO : sgASin (lenA * sgSin(angB)/lenB) ; + + if ( angA_is_obtuse ) + aa = SG_180 - aa ; + + if ( angA ) *angA = aa ; + + /* Find the missing angle */ + + SGfloat cc = SG_180 - (aa + angB) ; + + if ( angC ) *angC = cc ; + + /* Use SAStoASA to get the last length */ + + sgTriangleSolver_SAStoASA ( lenA, cc, lenB, NULL, lenC, NULL ) ; +} + + +void sgTriangleSolver_SAAtoASS ( SGfloat lenA, SGfloat angB, SGfloat angA, + SGfloat *angC, SGfloat *lenB, SGfloat *lenC ) +{ + /* Find the missing angle */ + + SGfloat cc = SG_180 - (angB + angA) ; + + if ( angC ) *angC = cc ; + + sgTriangleSolver_ASAtoSAS ( cc, lenA, angB, lenC, NULL, lenB ) ; +} + + diff --git a/src/sg/sg.dsp b/src/sg/sg.dsp new file mode 100644 index 0000000..ade896f --- /dev/null +++ b/src/sg/sg.dsp @@ -0,0 +1,118 @@ +# Microsoft Developer Studio Project File - Name="sg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=sg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sg.mak" CFG="sg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sg - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "sg - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\util" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy sg.h ..\..\sg.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "sg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\util" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\sg_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy sg.h ..\..\sg.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "sg - Win32 Release" +# Name "sg - Win32 Debug" +# Begin Source File + +SOURCE=.\sg.cxx +# End Source File +# Begin Source File + +SOURCE=.\sg.h +# End Source File +# Begin Source File + +SOURCE=.\sgd.cxx +# End Source File +# Begin Source File + +SOURCE=.\sgdIsect.cxx +# End Source File +# Begin Source File + +SOURCE=.\sgIsect.cxx +# End Source File +# Begin Source File + +SOURCE=.\sgPerlinNoise.cxx +# End Source File +# End Target +# End Project diff --git a/src/sg/sg.h b/src/sg/sg.h new file mode 100644 index 0000000..8b339a3 --- /dev/null +++ b/src/sg/sg.h @@ -0,0 +1,3102 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sg.h 2121 2007-09-15 03:36:21Z fayjf $ +*/ + + +#ifndef SG_H +#define SG_H 1 + +#include +#include "ul.h" + +#define sgFloat float +#define SGfloat float + +#define SG_ZERO 0.0f +#define SG_HALF 0.5f +#define SG_ONE 1.0f +#define SG_TWO 2.0f +#define SG_THREE 3.0f +#define SG_FOUR 4.0f +#define SG_45 45.0f +#define SG_60 60.0f +#define SG_90 90.0f +#define SG_180 180.0f +#define SG_MAX FLT_MAX + +#define SG_X 0 +#define SG_Y 1 +#define SG_Z 2 +#define SG_W 3 + +#ifndef M_PI +#define SG_PI 3.1415926535f +#else +#define SG_PI ((SGfloat) M_PI) +#endif + +#define SG_DEGREES_TO_RADIANS (SG_PI/SG_180) +#define SG_RADIANS_TO_DEGREES (SG_180/SG_PI) + +/* + These are just convenient redefinitions of standard + math library functions to stop float/double warnings. +*/ + +inline SGfloat sgSqrt ( const SGfloat x ) { return (SGfloat) sqrt ( x ) ; } +inline SGfloat sgSquare ( const SGfloat x ) { return x * x ; } +inline SGfloat sgAbs ( const SGfloat a ) { return (axyz, src->hpr ) ; +} + +extern void sgMakeLookAtMat4 ( sgMat4 dst, + const sgVec3 eye, const sgVec3 center, const sgVec3 up ) ; + +extern void sgMakeRotMat4 ( sgMat4 dst, const SGfloat angle, const sgVec3 axis ) ; + +inline void sgMakeRotMat4 ( sgMat4 dst, const sgVec3 hpr ) +{ + sgMakeCoordMat4 ( dst, SG_ZERO, SG_ZERO, SG_ZERO, hpr[0], hpr[1], hpr[2] ) ; +} + +inline void sgMakeRotMat4 ( sgMat4 dst,const SGfloat h, const SGfloat p, const SGfloat r ) +{ + sgMakeCoordMat4 ( dst, SG_ZERO, SG_ZERO, SG_ZERO, h, p, r ) ; +} + +extern void sgMakeTransMat4 ( sgMat4 dst, const sgVec3 xyz ) ; +extern void sgMakeTransMat4 ( sgMat4 dst, const SGfloat x, const SGfloat y, const SGfloat z ) ; + + +extern void sgSetCoord ( sgCoord *coord, const sgMat4 src ) ; + +extern void sgMultMat4 ( sgMat4 dst, const sgMat4 a, const sgMat4 b ) ; +extern void sgPostMultMat4 ( sgMat4 dst, const sgMat4 a ) ; +extern void sgPreMultMat4 ( sgMat4 dst, const sgMat4 a ) ; + +extern void sgTransposeNegateMat4 ( sgMat4 dst ) ; +extern void sgTransposeNegateMat4 ( sgMat4 dst, const sgMat4 src ) ; +extern void sgInvertMat4 ( sgMat4 dst, const sgMat4 src ) ; +inline void sgInvertMat4 ( sgMat4 dst ) { sgInvertMat4 ( dst, dst ) ; } + +extern void sgXformVec3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) ; +extern void sgXformPnt3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) ; +extern void sgXformVec4 ( sgVec4 dst, const sgVec4 src, const sgMat4 mat ) ; +extern void sgXformPnt4 ( sgVec4 dst, const sgVec4 src, const sgMat4 mat ) ; +extern void sgFullXformPnt3 ( sgVec3 dst, const sgVec3 src, const sgMat4 mat ) ; +extern void sgFullXformPnt4 ( sgVec4 dst, const sgVec4 src, const sgMat4 mat ) ; + +inline void sgXformVec3 ( sgVec3 dst, const sgMat4 mat ) { sgXformVec3 ( dst, dst, mat ) ; } +inline void sgXformPnt3 ( sgVec3 dst, const sgMat4 mat ) { sgXformPnt3 ( dst, dst, mat ) ; } +inline void sgXformVec4 ( sgVec4 dst, const sgMat4 mat ) { sgXformVec4 ( dst, dst, mat ) ; } +inline void sgXformPnt4 ( sgVec4 dst, const sgMat4 mat ) { sgXformPnt4 ( dst, dst, mat ) ; } +inline void sgFullXformPnt3 ( sgVec3 dst, const sgMat4 mat ) { sgFullXformPnt3 ( dst, dst, mat ) ; } +inline void sgFullXformPnt4 ( sgVec4 dst, const sgMat4 mat ) { sgFullXformPnt4 ( dst, dst, mat ) ; } + + +/* Bits returned by sgClassifyMat4 */ + +#define SG_IDENTITY 0x00 // for clarity +#define SG_ROTATION 0x01 // includes a rotational component +#define SG_MIRROR 0x02 // changes handedness (det < 0) +#define SG_SCALE 0x04 // uniform scaling +#define SG_NONORTHO 0x10 // 3x3 not orthogonal +#define SG_TRANSLATION 0x20 // translates +#define SG_PROJECTION 0x40 // forth column not 0,0,0,1 + +/* Are these needed? sgClassifyMat4() does set the general scale bit for some matrices, + * but it is not easily defined. Use SG_NONORTHO instead (which is also set). */ +#define SG_UNIFORM_SCALE SG_SCALE +#define SG_GENERAL_SCALE 0x08 // x, y and z scaled differently + +extern int sgClassifyMat4 ( const sgMat4 mat ) ; + + + +/* + Basic low-level vector functions. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For each of Vec2, Vec3 and Vec4, we provide inlines for + + Zero - set all elements to zero. + Set - set each element individually. + Add - add vectors element by element. + Sub - subtract vectors element by element. + Scale - multiply each element of a vector by a variable. + AddScaled - multiply second vector by a constant and add the + result to the first vector. + Negate - negate each element of a vector. + Compare - compare vectors element by element with optional tolerance. + (return TRUE if vectors are equal - within tolerances) + Equal - return TRUE if vectors are exactly equal. + Length - compute length of a vector. + Distance - compute distance between two points. + ScalarProduct - scalar (dot) product. + VectorProduct - vector (cross) product (3-element vectors ONLY!). + Normalise/Normalize - make vector be one unit long. +*/ + +inline void sgZeroVec2 ( sgVec2 dst ) { dst[0]=dst[1]=SG_ZERO ; } +inline void sgZeroVec3 ( sgVec3 dst ) { dst[0]=dst[1]=dst[2]=SG_ZERO ; } +inline void sgZeroVec4 ( sgVec4 dst ) { dst[0]=dst[1]=dst[2]=dst[3]=SG_ZERO ; } + + +inline void sgSetVec2 ( sgVec2 dst, const SGfloat x, const SGfloat y ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; +} + +inline void sgSetVec3 ( sgVec3 dst, const SGfloat x, const SGfloat y, const SGfloat z ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; + dst [ 2 ] = z ; +} + +inline void sgSetVec4 ( sgVec4 dst, const SGfloat x, const SGfloat y, const SGfloat z, const SGfloat w ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; + dst [ 2 ] = z ; + dst [ 3 ] = w ; +} + + +inline void sgCopyVec2 ( sgVec2 dst, const sgVec2 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; +} + +inline void sgCopyVec3 ( sgVec3 dst, const sgVec3 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; + dst [ 2 ] = src [ 2 ] ; +} + +inline void sgCopyVec4 ( sgVec4 dst, const sgVec4 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; + dst [ 2 ] = src [ 2 ] ; + dst [ 3 ] = src [ 3 ] ; +} + + +inline void sgAddVec2 ( sgVec2 dst, const sgVec2 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; +} + +inline void sgAddVec3 ( sgVec3 dst, const sgVec3 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; + dst [ 2 ] += src [ 2 ] ; +} + +inline void sgAddVec4 ( sgVec4 dst, const sgVec4 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; + dst [ 2 ] += src [ 2 ] ; + dst [ 3 ] += src [ 3 ] ; +} + + +inline void sgAddVec2 ( sgVec2 dst, const sgVec2 src1, const sgVec2 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; +} + +inline void sgAddVec3 ( sgVec3 dst, const sgVec3 src1, const sgVec3 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] ; +} + +inline void sgAddVec4 ( sgVec4 dst, const sgVec4 src1, const sgVec4 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] ; + dst [ 3 ] = src1 [ 3 ] + src2 [ 3 ] ; +} + + +inline void sgSubVec2 ( sgVec2 dst, const sgVec2 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; +} + +inline void sgSubVec3 ( sgVec3 dst, const sgVec3 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; + dst [ 2 ] -= src [ 2 ] ; +} + +inline void sgSubVec4 ( sgVec4 dst, const sgVec4 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; + dst [ 2 ] -= src [ 2 ] ; + dst [ 3 ] -= src [ 3 ] ; +} + +inline void sgSubVec2 ( sgVec2 dst, const sgVec2 src1, const sgVec2 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; +} + +inline void sgSubVec3 ( sgVec3 dst, const sgVec3 src1, const sgVec3 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] - src2 [ 2 ] ; +} + +inline void sgSubVec4 ( sgVec4 dst, const sgVec4 src1, const sgVec4 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] - src2 [ 2 ] ; + dst [ 3 ] = src1 [ 3 ] - src2 [ 3 ] ; +} + + +inline void sgNegateVec2 ( sgVec2 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; +} + +inline void sgNegateVec3 ( sgVec3 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; + dst [ 2 ] = -dst [ 2 ] ; +} + +inline void sgNegateVec4 ( sgVec4 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; + dst [ 2 ] = -dst [ 2 ] ; + dst [ 3 ] = -dst [ 3 ] ; +} + + +inline void sgNegateVec2 ( sgVec2 dst, const sgVec2 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; +} + +inline void sgNegateVec3 ( sgVec3 dst, const sgVec3 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; + dst [ 2 ] = -src [ 2 ] ; +} + +inline void sgNegateVec4 ( sgVec4 dst, const sgVec4 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; + dst [ 2 ] = -src [ 2 ] ; + dst [ 3 ] = -src [ 3 ] ; +} + + +inline void sgScaleVec2 ( sgVec2 dst, const SGfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; +} + +inline void sgScaleVec3 ( sgVec3 dst, const SGfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; + dst [ 2 ] *= s ; +} + +inline void sgScaleVec4 ( sgVec4 dst, const SGfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; + dst [ 2 ] *= s ; + dst [ 3 ] *= s ; +} + +inline void sgScaleVec2 ( sgVec2 dst, const sgVec2 src, const SGfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; +} + +inline void sgScaleVec3 ( sgVec3 dst, const sgVec3 src, const SGfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; + dst [ 2 ] = src [ 2 ] * s ; +} + +inline void sgScaleVec4 ( sgVec4 dst, const sgVec4 src, const SGfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; + dst [ 2 ] = src [ 2 ] * s ; + dst [ 3 ] = src [ 3 ] * s ; +} + + +inline void sgAddScaledVec2 ( sgVec2 dst, const sgVec2 src, const SGfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; +} + +inline void sgAddScaledVec3 ( sgVec3 dst, const sgVec3 src, const SGfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; + dst [ 2 ] += src [ 2 ] * s ; +} + +inline void sgAddScaledVec4 ( sgVec4 dst, const sgVec4 src, const SGfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; + dst [ 2 ] += src [ 2 ] * s ; + dst [ 3 ] += src [ 3 ] * s ; +} + + +inline void sgAddScaledVec2 ( sgVec2 dst, const sgVec2 src1, const sgVec2 src2, const SGfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; +} + +inline void sgAddScaledVec3 ( sgVec3 dst, const sgVec3 src1, const sgVec3 src2, const SGfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] * s ; +} + +inline void sgAddScaledVec4 ( sgVec4 dst, const sgVec4 src1, const sgVec4 src2, const SGfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] * s ; + dst [ 3 ] = src1 [ 3 ] + src2 [ 3 ] * s ; +} + + +inline int sgCompareVec2 ( const sgVec2 a, const sgVec2 b, const SGfloat tol ) +{ + if ( sgCompareFloat( a[0], b[0], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[1], b[1], tol ) != 0 ) return FALSE ; + + return TRUE ; +} + +inline int sgCompareVec3 ( const sgVec3 a, const sgVec3 b, const SGfloat tol ) +{ + if ( sgCompareFloat( a[0], b[0], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[1], b[1], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[2], b[2], tol ) != 0 ) return FALSE ; + + return TRUE ; +} + +inline int sgCompareVec4 ( const sgVec4 a, const sgVec4 b, const SGfloat tol ) +{ + if ( sgCompareFloat( a[0], b[0], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[1], b[1], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[2], b[2], tol ) != 0 ) return FALSE ; + if ( sgCompareFloat( a[3], b[3], tol ) != 0 ) return FALSE ; + + return TRUE ; +} + + +inline int sgEqualVec2 ( const sgVec2 a, const sgVec2 b ) +{ + return a[0] == b[0] && + a[1] == b[1] ; +} + +inline int sgEqualVec3 ( const sgVec3 a, const sgVec3 b ) +{ + return a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] ; +} + +inline int sgEqualVec4 ( const sgVec4 a, const sgVec4 b ) +{ + return a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3] ; +} + + +inline SGfloat sgScalarProductVec2 ( const sgVec2 a, const sgVec2 b ) +{ + return a[0]*b[0] + a[1]*b[1] ; +} + +inline SGfloat sgScalarProductVec3 ( const sgVec3 a, const sgVec3 b ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] ; +} + +inline SGfloat sgScalarProductVec4 ( const sgVec4 a, const sgVec4 b ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3] ; +} + + +extern void sgVectorProductVec3 ( sgVec3 dst, const sgVec3 a, const sgVec3 b ) ; + +inline SGfloat sgLerp ( const SGfloat a, const SGfloat b, const SGfloat f ) +{ + return a + f * ( b - a ) ; +} + +inline void sgLerpVec4 ( sgVec4 dst, const sgVec4 a, const sgVec4 b, const SGfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; + dst[2] = a[2] + f * ( b[2] - a[2] ) ; + dst[3] = a[3] + f * ( b[3] - a[3] ) ; +} + + +inline void sgLerpVec3 ( sgVec3 dst, const sgVec3 a, const sgVec3 b, const SGfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; + dst[2] = a[2] + f * ( b[2] - a[2] ) ; +} + + +inline void sgLerpVec2 ( sgVec2 dst, const sgVec2 a, const sgVec2 b, const SGfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; +} + + +inline void sgLerpAnglesVec3 ( sgVec3 dst, const sgVec3 a, + const sgVec3 b, + const SGfloat f ) +{ + sgVec3 tmp ; + + if ( b[0] - a[0] > 180.0f ) tmp[0] = a[0] + 360.0f ; else + if ( b[0] - a[0] < -180.0f ) tmp[0] = a[0] - 360.0f ; else tmp[0] = a[0] ; + + if ( b[1] - a[1] > 180.0f ) tmp[1] = a[1] + 360.0f ; else + if ( b[1] - a[1] < -180.0f ) tmp[1] = a[1] - 360.0f ; else tmp[1] = a[1] ; + + if ( b[2] - a[2] > 180.0f ) tmp[2] = a[2] + 360.0f ; else + if ( b[2] - a[2] < -180.0f ) tmp[2] = a[2] - 360.0f ; else tmp[2] = a[2] ; + + dst[0] = tmp[0] + f * ( b[0] - tmp[0] ) ; + dst[1] = tmp[1] + f * ( b[1] - tmp[1] ) ; + dst[2] = tmp[2] + f * ( b[2] - tmp[2] ) ; +} + + + +inline SGfloat sgDistanceSquaredVec2 ( const sgVec2 a, const sgVec2 b ) +{ + return sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) ; +} + +inline SGfloat sgDistanceSquaredVec3 ( const sgVec3 a, const sgVec3 b ) +{ + return sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) + + sgSquare ( a[2]-b[2] ) ; +} + +inline SGfloat sgDistanceSquaredVec4 ( const sgVec4 a, const sgVec4 b ) +{ + return sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) + + sgSquare ( a[2]-b[2] ) + sgSquare ( a[3]-b[3] ) ; +} + +inline SGfloat sgDistanceVec2 ( const sgVec2 a, const sgVec2 b ) +{ + return sgSqrt ( sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) ) ; +} + +inline SGfloat sgDistanceVec3 ( const sgVec3 a, const sgVec3 b ) +{ + return sgSqrt ( sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) + + sgSquare ( a[2]-b[2] ) ) ; +} + +inline SGfloat sgDistanceVec4 ( const sgVec4 a, const sgVec4 b ) +{ + return sgSqrt ( sgSquare ( a[0]-b[0] ) + sgSquare ( a[1]-b[1] ) + + sgSquare ( a[2]-b[2] ) + sgSquare ( a[3]-b[3] ) ) ; +} + + +inline SGfloat sgLengthVec2 ( const sgVec2 src ) +{ + return sgSqrt ( sgScalarProductVec2 ( src, src ) ) ; +} + +inline SGfloat sgLengthVec3 ( const sgVec3 src ) +{ + return sgSqrt ( sgScalarProductVec3 ( src, src ) ) ; +} + +inline SGfloat sgLengthVec4 ( const sgVec4 src ) +{ + return sgSqrt ( sgScalarProductVec4 ( src, src ) ) ; +} + +inline SGfloat sgLengthSquaredVec2 ( sgVec2 const src ) +{ + return sgScalarProductVec2 ( src, src ) ; +} + +inline SGfloat sgLengthSquaredVec3 ( sgVec3 const src ) +{ + return sgScalarProductVec3 ( src, src ) ; +} + +inline SGfloat sgLengthSquaredVec4 ( sgVec4 const src ) +{ + return sgScalarProductVec4 ( src, src ) ; +} + + +/* Anglo-US spelling issues. */ +#define sgNormalizeVec2 sgNormaliseVec2 +#define sgNormalizeVec3 sgNormaliseVec3 +#define sgNormalizeVec4 sgNormaliseVec4 +#define sgNormalizeQuat sgNormaliseQuat + +inline void sgNormaliseVec2 ( sgVec2 dst ) +{ + sgScaleVec2 ( dst, SG_ONE / sgLengthVec2 ( dst ) ) ; +} + +inline void sgNormaliseVec3 ( sgVec3 dst ) +{ + sgScaleVec3 ( dst, SG_ONE / sgLengthVec3 ( dst ) ) ; +} + +inline void sgNormaliseVec4 ( sgVec4 dst ) +{ + sgScaleVec4 ( dst, SG_ONE / sgLengthVec4 ( dst ) ) ; +} + +inline void sgNormaliseVec2 ( sgVec2 dst, const sgVec2 src ) +{ + sgScaleVec2 ( dst, src, SG_ONE / sgLengthVec2 ( src ) ) ; +} + +inline void sgNormaliseVec3 ( sgVec3 dst, const sgVec3 src ) +{ + sgScaleVec3 ( dst, src, SG_ONE / sgLengthVec3 ( src ) ) ; +} + +inline void sgNormaliseVec4 ( sgVec4 dst, const sgVec4 src ) +{ + sgScaleVec4 ( dst, src, SG_ONE / sgLengthVec4 ( src ) ) ; +} + + +inline void sgZeroCoord ( sgCoord *dst ) +{ + sgSetVec3 ( dst->xyz, SG_ZERO, SG_ZERO, SG_ZERO ) ; + sgSetVec3 ( dst->hpr, SG_ZERO, SG_ZERO, SG_ZERO ) ; +} + +inline void sgSetCoord ( sgCoord *dst, const SGfloat x, const SGfloat y, const SGfloat z, + const SGfloat h, const SGfloat p, const SGfloat r ) +{ + sgSetVec3 ( dst->xyz, x, y, z ) ; + sgSetVec3 ( dst->hpr, h, p, r ) ; +} + +inline void sgSetCoord ( sgCoord *dst, const sgVec3 xyz, const sgVec3 hpr ) +{ + sgCopyVec3 ( dst->xyz, xyz ) ; + sgCopyVec3 ( dst->hpr, hpr ) ; +} + +inline void sgCopyCoord ( sgCoord *dst, const sgCoord *src ) +{ + sgCopyVec3 ( dst->xyz, src->xyz ) ; + sgCopyVec3 ( dst->hpr, src->hpr ) ; +} + + + +inline void sgCopyMat4 ( sgMat4 dst, const sgMat4 src ) +{ + sgCopyVec4 ( dst[ 0 ], src[ 0 ] ) ; + sgCopyVec4 ( dst[ 1 ], src[ 1 ] ) ; + sgCopyVec4 ( dst[ 2 ], src[ 2 ] ) ; + sgCopyVec4 ( dst[ 3 ], src[ 3 ] ) ; +} + + +inline void sgScaleMat4 ( sgMat4 dst, const sgMat4 src, const SGfloat scale ) +{ + sgScaleVec4 ( dst[0], src[0], scale ) ; + sgScaleVec4 ( dst[1], src[1], scale ) ; + sgScaleVec4 ( dst[2], src[2], scale ) ; + sgScaleVec4 ( dst[3], src[3], scale ) ; +} + + +inline void sgMakeIdentMat4 ( sgMat4 dst ) +{ + sgSetVec4 ( dst[0], SG_ONE , SG_ZERO, SG_ZERO, SG_ZERO ) ; + sgSetVec4 ( dst[1], SG_ZERO, SG_ONE , SG_ZERO, SG_ZERO ) ; + sgSetVec4 ( dst[2], SG_ZERO, SG_ZERO, SG_ONE , SG_ZERO ) ; + sgSetVec4 ( dst[3], SG_ZERO, SG_ZERO, SG_ZERO, SG_ONE ) ; +} + + +extern void sgMakePickMatrix( sgMat4 mat, sgFloat x, sgFloat y, + sgFloat width, sgFloat height, sgVec4 viewport ) ; + +extern int sgCompare3DSqdDist ( const sgVec3 a, const sgVec3 b, const SGfloat sqd_dist ) ; + +inline SGfloat sgDistToLineVec2 ( const sgVec3 line, const sgVec2 pnt ) +{ + return sgScalarProductVec2 ( line, pnt ) + line[2] ; +} + + +struct sgLineSegment3 /* Bounded line segment */ +{ + sgVec3 a ; + sgVec3 b ; +} ; + +struct sgLine3 /* Infinite line */ +{ + sgVec3 point_on_line ; + sgVec3 direction_vector ; /* Should be a unit vector */ +} ; + + +inline void sgLineSegment3ToLine3 ( sgLine3 *line, + const sgLineSegment3 lineseg ) +{ + sgCopyVec3 ( line->point_on_line , lineseg.a ) ; + sgSubVec3 ( line->direction_vector, lineseg.b, lineseg.a ) ; + sgNormaliseVec3 ( line->direction_vector ) ; +} + + +SGfloat sgDistSquaredToLineVec3 ( const sgLine3 line, + const sgVec3 pnt ) ; +SGfloat sgDistSquaredToLineSegmentVec3 ( const sgLineSegment3 line, + const sgVec3 pnt ) ; + + +inline SGfloat sgDistToLineVec3 ( const sgLine3 line, + const sgVec3 pnt ) +{ + return sgSqrt ( sgDistSquaredToLineVec3 ( line, pnt ) ); +} + + +inline SGfloat sgDistToLineSegmentVec3 ( const sgLineSegment3 line, + const sgVec3 pnt ) +{ + return sgSqrt ( sgDistSquaredToLineSegmentVec3(line,pnt) ) ; +} + + +inline SGfloat sgDistToPlaneVec3 ( const sgVec4 plane, const sgVec3 pnt ) +{ + return sgScalarProductVec3 ( plane, pnt ) + plane[3] ; +} + + +inline SGfloat sgHeightAbovePlaneVec3 ( const sgVec4 plane, const sgVec3 pnt ) +{ + return pnt[2] - sgHeightOfPlaneVec2 ( plane, pnt ) ; +} + +extern void sgReflectInPlaneVec3 ( sgVec3 dst, const sgVec3 src, const sgVec4 plane ) ; + +inline void sgReflectInPlaneVec3 ( sgVec3 dst, const sgVec4 plane ) +{ + sgReflectInPlaneVec3 ( dst, dst, plane ) ; +} + +extern void sgMakeNormal ( sgVec2 dst, const sgVec3 a, const sgVec3 b ) ; + + +extern void sgMakeNormal ( sgVec3 dst, const sgVec3 a, const sgVec3 b, const sgVec3 c ) ; + + +inline void sgMake2DLine ( sgVec3 dst, const sgVec2 a, const sgVec2 b ) +{ + dst[0] = b[1]-a[1] ; + dst[1] = -(b[0]-a[0]) ; + sgNormalizeVec2 ( dst ) ; + dst[2] = - ( dst[0]*a[0] + dst[1]*a[1] ) ; +} + +inline void sgMakePlane ( sgVec4 dst, const sgVec3 normal, const sgVec3 pnt ) +{ + sgCopyVec3 ( dst, normal ) ; + dst [ 3 ] = - sgScalarProductVec3 ( normal, pnt ) ; +} + +inline void sgMakePlane ( sgVec4 dst, const sgVec3 a, const sgVec3 b, const sgVec3 c ) +{ + /* + Ax + By + Cz + D == 0 ; + D = - ( Ax + By + Cz ) + = - ( A*a[0] + B*a[1] + C*a[2] ) + = - sgScalarProductVec3 ( normal, a ) ; + */ + + sgMakeNormal ( dst, a, b, c ) ; + + dst [ 3 ] = - sgScalarProductVec3 ( dst, a ) ; +} + +float sgTriArea( sgVec3 p0, sgVec3 p1, sgVec3 p2 ); + + +// Fast code. Result is in the range 0..180: +inline SGfloat sgAngleBetweenNormalizedVec3 ( sgVec3 v1, sgVec3 v2 ) +{ + float f = sgScalarProductVec3 ( v1, v2 ) ; + + return (float) acos ( ( f >= 1.0f ) ? 1.0f : + ( f <= -1.0f ) ? -1.0f : f ) * + SG_RADIANS_TO_DEGREES ; +} + +// Fast code. Result is in the range 0..180: + +SGfloat sgAngleBetweenVec3 ( sgVec3 v1, sgVec3 v2 ); + +// All three have to be normalized. Slow code. Result is in the range 0..360: + +SGfloat sgAngleBetweenNormalizedVec3 (sgVec3 first, sgVec3 second, sgVec3 normal); + +// Normal has to be normalized. Slow code. Result is in the range 0..360: + +SGfloat sgAngleBetweenVec3 ( sgVec3 v1, sgVec3 v2, sgVec3 normal ); + +class sgSphere +{ +public: + sgVec3 center ; + SGfloat radius ; + + + sgSphere () { empty () ; } + + const SGfloat *getCenter (void) const { return center ; } + + void setCenter ( const sgVec3 c ) + { + sgCopyVec3 ( center, c ) ; + } + + void setCenter ( const SGfloat x, const SGfloat y, const SGfloat z ) + { + sgSetVec3 ( center, x, y, z ) ; + } + + SGfloat getRadius (void) const { return radius ; } + void setRadius ( const SGfloat r ) { radius = r ; } + + int isEmpty (void) const { return radius < SG_ZERO ; } + void empty (void) { radius = - SG_ONE ; } + + void orthoXform ( const sgMat4 m ) + { + sgXformPnt3 ( center, center, m ) ; + // radius *= sgLengthVec3 ( m[0] ) ; -- degrades performance for non-scaled matrices ... + } + + void extend ( const sgSphere *s ) ; + void extend ( const sgBox *b ) ; + void extend ( const sgVec3 v ) ; + + int intersects ( const sgSphere *s ) const + { + return sgCompare3DSqdDist ( center, s->getCenter(), + sgSquare ( radius + s->getRadius() ) ) <= 0 ; + } + + int intersects ( const sgVec4 plane ) const + { + return sgAbs ( sgDistToPlaneVec3 ( plane, center ) ) <= radius ; + } + + int intersects ( const sgBox *b ) const ; +} ; + + +class sgBox +{ +public: + sgVec3 min ; + sgVec3 max ; + + + sgBox () { empty () ; } + + const SGfloat *getMin (void) const { return min ; } + const SGfloat *getMax (void) const { return max ; } + + void setMin ( const SGfloat x, const SGfloat y, const SGfloat z ) + { + sgSetVec3 ( min, x, y, z ) ; + } + + void setMin ( const sgVec3 src ) + { + sgCopyVec3 ( min, src ) ; + } + + void setMax ( const SGfloat x, const SGfloat y, const SGfloat z ) + { + sgSetVec3 ( max, x, y, z ) ; + } + + void setMax ( const sgVec3 src ) + { + sgCopyVec3 ( max, src ) ; + } + + int isEmpty(void) const + { + return ( min[0] > max[0] || + min[1] > max[1] || + min[2] > max[2] ) ; + } + + void empty (void) + { + sgSetVec3 ( min, SG_MAX, SG_MAX, SG_MAX ) ; + sgSetVec3 ( max, -SG_MAX, -SG_MAX, -SG_MAX ) ; + } + + void extend ( const sgSphere *s ) ; + void extend ( const sgBox *b ) ; + void extend ( const sgVec3 v ) ; + + int intersects ( const sgSphere *s ) const + { + return s -> intersects ( this ) ; + } + + int intersects ( const sgBox *b ) const + { + return min[0] <= b->getMax()[0] && max[0] >= b->getMin()[0] && + min[1] <= b->getMax()[1] && max[1] >= b->getMin()[1] && + min[2] <= b->getMax()[2] && max[2] >= b->getMin()[2] ; + } + + int intersects ( const sgVec4 plane ) const ; +} ; + + +#define SG_LEFT_PLANE 0 +#define SG_RIGHT_PLANE 1 +#define SG_BOT_PLANE 2 +#define SG_TOP_PLANE 3 +#define SG_NEAR_PLANE 4 +#define SG_FAR_PLANE 5 + +class sgFrustum +{ + /* Is the projection orthographic (or perspective)? */ + int ortho ; + + /* The parameters for glFrustum/glOrtho */ + + SGfloat left, right, bot, top, nnear, ffar ; + + /* The computed projection matrix for this frustum */ + + sgMat4 mat ; + + /* The A,B,C,D terms of the plane equations of the clip planes */ + /* A point (x,y,z) is inside the frustum iff Ax + By + Cz + D >= 0 for all planes */ + + sgVec4 plane [ 6 ] ; + + /* These two are only valid for simple frusta */ + /* Note that to convert to parameters for gluPerspective you do the following: + * "fovy" is "vfov" + * "aspect" is the ratio "hfov" / "vfov" + * Note also that the "hfov" and "vfov" variables are NOT automatically set. + */ + + SGfloat hfov ; /* Horizontal Field of View -or- Orthographic Width */ + SGfloat vfov ; /* Vertical Field of View -or- Orthographic Height */ + + void update (void) ; + int getOutcode ( const sgVec4 src ) const ; + +public: + + sgFrustum (void) + { + ortho = FALSE ; + nnear = SG_ONE ; + ffar = 1000000.0f ; + hfov = SG_45 ; + vfov = SG_45 ; + update () ; + } + + void setFrustum ( const SGfloat l, const SGfloat r, + const SGfloat b, const SGfloat t, + const SGfloat n, const SGfloat f ) + { + ortho = FALSE ; + left = l ; + right = r ; + bot = b ; + top = t ; + nnear = n ; + ffar = f ; + hfov = vfov = SG_ZERO ; + update () ; + } + + void setOrtho ( const SGfloat l, const SGfloat r, + const SGfloat b, const SGfloat t, + const SGfloat n, const SGfloat f ) + { + ortho = TRUE ; + left = l ; + right = r ; + bot = b ; + top = t ; + nnear = n ; + ffar = f ; + hfov = vfov = SG_ZERO ; + update () ; + } + + void getMat4 ( sgMat4 dst ) { sgCopyMat4 ( dst, mat ) ; } + + SGfloat getLeft (void) const { return left ; } + SGfloat getRight(void) const { return right ; } + SGfloat getBot (void) const { return bot ; } + SGfloat getTop (void) const { return top ; } + SGfloat getNear (void) const { return nnear ; } + SGfloat getFar (void) const { return ffar ; } + + const SGfloat *getPlane ( int i ) const { return plane [ i ] ; } + + SGfloat getHFOV (void) const { return hfov ; } + SGfloat getVFOV (void) const { return vfov ; } + + void getFOV ( SGfloat *h, SGfloat *v ) const + { + if ( h != (SGfloat *) 0 ) *h = hfov ; + if ( v != (SGfloat *) 0 ) *v = vfov ; + } + + void setFOV ( const SGfloat h, const SGfloat v ) + { + ortho = FALSE ; + hfov = ( h <= 0 ) ? ( v * SG_FOUR / SG_THREE ) : h ; + vfov = ( v <= 0 ) ? ( h * SG_THREE / SG_FOUR ) : v ; + update () ; + } + + void getOrtho ( SGfloat *w, SGfloat *h ) const + { + if ( w != (SGfloat *) 0 ) *w = right - left ; + if ( h != (SGfloat *) 0 ) *h = top - bot ; + } + + void setOrtho ( const SGfloat w, const SGfloat h ) + { + ortho = TRUE ; + hfov = ( w <= 0 ) ? ( h * SG_FOUR / SG_THREE ) : w ; + vfov = ( h <= 0 ) ? ( w * SG_FOUR / SG_THREE ) : h ; + update () ; + } + + void getNearFar ( SGfloat *n, SGfloat *f ) const + { + if ( n != (SGfloat *) 0 ) *n = nnear ; + if ( f != (SGfloat *) 0 ) *f = ffar ; + } + + void setNearFar ( const SGfloat n, const SGfloat f ) + { + nnear = n ; + ffar = f ; + update () ; + } + + int isOrtho (void) const { return ortho ; } + + int contains ( const sgVec3 p ) const ; + int contains ( const sgSphere *s ) const ; + int contains ( const sgBox *b ) const ; +} ; + + +/* + Quaternion routines are Copyright (C) 1999 + Kevin B. Thompson + Modified by Sylvan W. Clebsch + Largely rewritten by "Negative0" + Added to by John Fay +*/ + +/* + Quaternion structure w = real, (x, y, z) = vector + CHANGED sqQuat to float array so that syntax matches + vector and matrix routines +*/ + + +inline void sgMakeIdentQuat ( sgQuat dst ) +{ + sgSetVec4 ( dst, SG_ZERO, SG_ZERO, SG_ZERO, SG_ONE ) ; +} + + +inline void sgSetQuat ( sgQuat dst, + const SGfloat w, const SGfloat x, + const SGfloat y, const SGfloat z ) +{ + sgSetVec4 ( dst, x, y, z, w ) ; +} + +inline void sgCopyQuat ( sgQuat dst, const sgQuat src ) +{ + sgCopyVec4 ( dst, src ) ; +} + + +/* Construct a unit quaternion (length==1) */ + +inline void sgNormaliseQuat ( sgQuat dst, const sgQuat src ) +{ + SGfloat d = sgScalarProductVec4 ( src, src ) ; + + d = (d > SG_ZERO) ? (SG_ONE / sgSqrt ( d )) : SG_ONE ; + + sgScaleVec4 ( dst, src, d ) ; +} + + + +inline void sgNormaliseQuat ( sgQuat dst ) { sgNormaliseQuat ( dst, dst ) ; } + + +inline void sgInvertQuat ( sgQuat dst, const sgQuat src ) +{ + SGfloat d = sgScalarProductVec4 ( src, src ) ; + + d = ( d == SG_ZERO ) ? SG_ONE : ( SG_ONE / d ) ; + + dst[SG_W] = src[SG_W] * d ; + dst[SG_X] = -src[SG_X] * d ; + dst[SG_Y] = -src[SG_Y] * d ; + dst[SG_Z] = -src[SG_Z] * d ; +} + +inline void sgInvertQuat ( sgQuat dst ) { sgInvertQuat ( dst, dst ) ; } + + +/* Make an angle and axis of rotation from a Quaternion. */ + +void sgQuatToAngleAxis ( SGfloat *angle, sgVec3 axis, const sgQuat src ) ; +void sgQuatToAngleAxis ( SGfloat *angle, + SGfloat *x, SGfloat *y, SGfloat *z, + const sgQuat src ) ; + +/* Make a quaternion from a given angle and axis of rotation */ + +void sgAngleAxisToQuat ( sgQuat dst, const SGfloat angle, const sgVec3 axis ) ; +void sgAngleAxisToQuat ( sgQuat dst, + const SGfloat angle, + const SGfloat x, const SGfloat y, const SGfloat z ) ; + +/* Convert a matrix to/from a quat */ + +void sgMatrixToQuat ( sgQuat quat, const sgMat4 m ) ; +void sgQuatToMatrix ( sgMat4 m, const sgQuat quat ) ; + +/* Convert a set of eulers to/from a quat */ + +void sgQuatToEuler( sgVec3 hpr, const sgQuat quat ) ; +void sgEulerToQuat( sgQuat quat, const sgVec3 hpr ) ; + +inline void sgEulerToQuat( sgQuat dst, + SGfloat h, SGfloat p, SGfloat r ) +{ + sgVec3 hpr ; + + sgSetVec3 ( hpr, h, p, r ) ; + + sgEulerToQuat( dst, hpr ) ; +} + +inline void sgHPRToQuat ( sgQuat dst, SGfloat h, SGfloat p, SGfloat r ) +{ + sgVec3 hpr; + + hpr[0] = h * SG_DEGREES_TO_RADIANS ; + hpr[1] = p * SG_DEGREES_TO_RADIANS ; + hpr[2] = r * SG_DEGREES_TO_RADIANS ; + + sgEulerToQuat( dst, hpr ) ; +} + +inline void sgHPRToQuat ( sgQuat dst, const sgVec3 hpr ) +{ + sgVec3 tmp ; + + sgScaleVec3 ( tmp, hpr, SG_DEGREES_TO_RADIANS ) ; + + sgEulerToQuat ( dst, tmp ) ; +} + + +/* Multiply quaternions together (concatenate rotations) */ + +void sgMultQuat ( sgQuat dst, const sgQuat a, const sgQuat b ) ; + +inline void sgPostMultQuat ( sgQuat dst, const sgQuat q ) +{ + sgQuat r ; + + sgCopyQuat ( r, dst ) ; + sgMultQuat ( dst, r, q ) ; +} + +inline void sgPreMultQuat ( sgQuat dst, const sgQuat q ) +{ + sgQuat r ; + + sgCopyQuat ( r, dst ) ; + sgMultQuat ( dst, q, r ) ; +} + + +/* Rotate a quaternion by a given angle and axis (convenience function) */ + + +inline void sgPreRotQuat ( sgQuat dst, const SGfloat angle, const sgVec3 axis ) +{ + sgQuat q ; + sgAngleAxisToQuat ( q, angle, axis ) ; + sgPreMultQuat ( dst, q ) ; + sgNormaliseQuat ( dst ) ; +} + + +inline void sgPostRotQuat ( sgQuat dst, const SGfloat angle, const sgVec3 axis ) +{ + sgQuat q ; + sgAngleAxisToQuat ( q, angle, axis ) ; + sgPostMultQuat ( dst, q ) ; + sgNormaliseQuat ( dst ) ; +} + + +inline void sgPreRotQuat ( sgQuat dst, + const SGfloat angle, + const SGfloat x, const SGfloat y, const SGfloat z ) +{ + sgVec3 axis ; + + sgSetVec3 ( axis, x, y, z ) ; + sgPreRotQuat ( dst, angle, axis ) ; +} + +inline void sgPostRotQuat ( sgQuat dst, + const SGfloat angle, + const SGfloat x, const SGfloat y, const SGfloat z ) +{ + sgVec3 axis ; + + sgSetVec3 ( axis, x, y, z ) ; + sgPostRotQuat ( dst, angle, axis ) ; +} + + +/* more meaningful names */ +#define sgWorldRotQuat sgPostRotQuat +#define sgLocalRotQuat sgPreRotQuat + +/* for backwards compatibility */ +#define sgRotQuat sgPostRotQuat + + +/* SWC - Interpolate between to quaternions */ + +extern void sgSlerpQuat ( sgQuat dst, + const sgQuat from, const sgQuat to, + const SGfloat t ) ; + + + +/* + Intersection testing. +*/ + +int sgIsectPlanePlane ( sgVec3 point, sgVec3 dir, + sgVec4 plane1, sgVec4 plane2 ) ; +int sgIsectInfLinePlane ( sgVec3 dst, + sgVec3 l_org, sgVec3 l_vec, + sgVec4 plane ) ; +int sgIsectInfLineInfLine ( sgVec3 dst, + sgVec3 l1_org, sgVec3 l1_vec, + sgVec3 l2_org, sgVec3 l2_vec ) ; +SGfloat sgIsectLinesegPlane ( sgVec3 dst, + sgVec3 v1, sgVec3 v2, + sgVec4 plane ) ; + +bool sgPointInTriangle3 ( sgVec3 point, sgVec3 tri[3] ) ; +bool sgPointInTriangle2 ( sgVec2 point, sgVec2 tri[3] ) ; + +inline bool sgPointInTriangle ( sgVec3 point, sgVec3 tri[3] ) +{ + return sgPointInTriangle3 ( point, tri ) ; +} + + + +/**********************************************************************/ + +#define sgdFloat double +#define SGDfloat double + +#define SGD_ZERO 0.0 +#define SGD_HALF 0.5 +#define SGD_ONE 1.0 +#define SGD_TWO 2.0 +#define SGD_THREE 3.0 +#define SGD_FOUR 4.0 +#define SGD_45 45.0 +#define SGD_60 60.0 +#define SGD_90 90.0 +#define SGD_180 180.0 +#define SGD_MAX DBL_MAX + + +#define SGD_X 0 +#define SGD_Y 1 +#define SGD_Z 2 +#define SGD_W 3 + +#ifndef M_PI +#define SGD_PI 3.14159265358979323846 /* From M_PI under Linux/X86 */ +#else +#define SGD_PI M_PI +#endif + +#define SGD_DEGREES_TO_RADIANS (SGD_PI/SGD_180) +#define SGD_RADIANS_TO_DEGREES (SGD_180/SGD_PI) + +/* + These are just convenient redefinitions of standard + math library functions to stop float/double warnings. +*/ + +inline SGDfloat sgdSqrt ( const SGDfloat x ) { return sqrt ( x ) ; } +inline SGDfloat sgdSquare ( const SGDfloat x ) { return x * x ; } +inline SGDfloat sgdAbs ( const SGDfloat a ) { return ( a < SGD_ZERO ) ? -a : a ; } + +inline SGDfloat sgdASin ( SGDfloat s ) + { return (SGDfloat) asin ( s ) * SGD_RADIANS_TO_DEGREES ; } +inline SGDfloat sgdACos ( SGDfloat s ) + { return (SGDfloat) acos ( s ) * SGD_RADIANS_TO_DEGREES ; } +inline SGDfloat sgdATan ( SGDfloat s ) + { return (SGDfloat) atan ( s ) * SGD_RADIANS_TO_DEGREES ; } +inline SGDfloat sgdATan2 ( SGDfloat y, SGDfloat x ) + { return (SGDfloat) atan2 ( y,x ) * SGD_RADIANS_TO_DEGREES ; } +inline SGDfloat sgdSin ( SGDfloat s ) + { return (SGDfloat) sin ( s * SGD_DEGREES_TO_RADIANS ) ; } +inline SGDfloat sgdCos ( SGDfloat s ) + { return (SGDfloat) cos ( s * SGD_DEGREES_TO_RADIANS ) ; } +inline SGDfloat sgdTan ( SGDfloat s ) + { return (SGDfloat) tan ( s * SGD_DEGREES_TO_RADIANS ) ; } + +inline int sgdCompareFloat ( const SGDfloat a, const SGDfloat b, const SGDfloat tol ) +{ + if ( ( a + tol ) < b ) return -1 ; + if ( ( b + tol ) < a ) return 1 ; + return 0 ; +} + + +/* + Types used in SGD. +*/ + +typedef SGDfloat sgdVec2 [ 2 ] ; +typedef SGDfloat sgdVec3 [ 3 ] ; +typedef SGDfloat sgdVec4 [ 4 ] ; + +typedef sgdVec4 sgdQuat ; + +typedef SGDfloat sgdMat3 [3][3] ; +typedef SGDfloat sgdMat4 [4][4] ; + +struct sgdCoord +{ + sgdVec3 xyz ; + sgdVec3 hpr ; +} ; + +class sgdSphere ; +class sgdBox ; +class sgdFrustum ; + +/* + Some handy constants +*/ + +#define SGD_OUTSIDE FALSE +#define SGD_INSIDE TRUE +#define SGD_STRADDLE 2 + +inline SGDfloat sgdHeightOfPlaneVec2 ( const sgdVec4 plane, const sgdVec2 pnt ) +{ + if ( plane[2] == SGD_ZERO ) + return SGD_ZERO ; + else + return -( plane[0] * pnt[0] + plane[1] * pnt[1] + plane[3] ) / plane[2] ; +} + +/* + Convert a direction vector into a set of euler angles, + (with zero roll) +*/ + +extern void sgdHPRfromVec3 ( sgdVec3 hpr, const sgdVec3 src ) ; + +extern void sgdMakeCoordMat4 ( sgdMat4 dst, const SGDfloat x, const SGDfloat y, const SGDfloat z, + const SGDfloat h, const SGDfloat p, const SGDfloat r ) ; + +inline void sgdMakeCoordMat4( sgdMat4 dst, const sgdVec3 xyz, const sgdVec3 hpr ) +{ + sgdMakeCoordMat4 ( dst, xyz[0], xyz[1], xyz[2], + hpr[0], hpr[1], hpr[2] ) ; +} + +inline void sgdMakeCoordMat4( sgdMat4 dst, const sgdCoord *src ) +{ + sgdMakeCoordMat4 ( dst, src->xyz, src->hpr ) ; +} + +extern void sgdMakeRotMat4 ( sgdMat4 dst, const SGDfloat angle, const sgdVec3 axis ) ; + +inline void sgdMakeRotMat4 ( sgdMat4 dst, const sgdVec3 hpr ) +{ + sgdMakeCoordMat4 ( dst, SGD_ZERO, SGD_ZERO, SGD_ZERO, hpr[0], hpr[1], hpr[2] ) ; +} + +inline void sgdMakeRotMat4 ( sgdMat4 dst, const SGDfloat h, const SGDfloat p, const SGDfloat r ) +{ + sgdMakeCoordMat4 ( dst, SGD_ZERO, SGD_ZERO, SGD_ZERO, h, p, r ) ; +} + +extern void sgdMakeTransMat4 ( sgdMat4 dst, const sgdVec3 xyz ) ; +extern void sgdMakeTransMat4 ( sgdMat4 dst, const SGDfloat x, const SGDfloat y, const SGDfloat z ) ; + + +extern void sgdSetCoord ( sgdCoord *coord, const sgdMat4 src ) ; + +extern void sgdMultMat4 ( sgdMat4 dst, const sgdMat4 a, const sgdMat4 b ) ; +extern void sgdPostMultMat4 ( sgdMat4 dst, const sgdMat4 a ) ; +extern void sgdPreMultMat4 ( sgdMat4 dst, const sgdMat4 a ) ; + +extern void sgdTransposeNegateMat4 ( sgdMat4 dst ) ; +extern void sgdTransposeNegateMat4 ( sgdMat4 dst, const sgdMat4 src ) ; + +extern void sgdInvertMat4 ( sgdMat4 dst, const sgdMat4 src ) ; +inline void sgdInvertMat4 ( sgdMat4 dst ) { sgdInvertMat4 ( dst, dst ) ; } + +extern void sgdXformVec3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) ; +extern void sgdXformPnt3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) ; +extern void sgdXformVec4 ( sgdVec4 dst, const sgdVec4 src, const sgdMat4 mat ) ; +extern void sgdXformPnt4 ( sgdVec4 dst, const sgdVec4 src, const sgdMat4 mat ) ; +extern void sgdFullXformPnt3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) ; +extern void sgdFullXformPnt4 ( sgdVec4 dst, const sgdVec4 src, const sgdMat4 mat ) ; + +inline void sgdXformVec3 ( sgdVec3 dst, const sgdMat4 mat ) { sgdXformVec3 ( dst, dst, mat ) ; } +inline void sgdXformPnt3 ( sgdVec3 dst, const sgdMat4 mat ) { sgdXformPnt3 ( dst, dst, mat ) ; } +inline void sgdXformVec4 ( sgdVec4 dst, const sgdMat4 mat ) { sgdXformVec4 ( dst, dst, mat ) ; } +inline void sgdXformPnt4 ( sgdVec4 dst, const sgdMat4 mat ) { sgdXformPnt4 ( dst, dst, mat ) ; } +inline void sgdFullXformPnt3 ( sgdVec3 dst, const sgdMat4 mat ) { sgdFullXformPnt3 ( dst, dst, mat ) ; } +inline void sgdFullXformPnt4 ( sgdVec4 dst, const sgdMat4 mat ) { sgdFullXformPnt4 ( dst, dst, mat ) ; } + +/* + Basic low-level vector functions. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For each of Vec2, Vec3 and Vec4, we provide inlines for + + Zero - set all elements to zero. + Set - set each element individually. + Add - add vectors element by element. + Sub - subtract vectors element by element. + Scale - multiply each element of a vector by a variable. + Negate - negate each element of a vector. + Compare - compare vectors element by element with optional tolerance. + (return TRUE if vectors are equal - within tolerances) + Equal - return TRUE if vectors are exactly equal. + Length - compute length of a vector. + Distance - compute distance between two points. + LengthSquared - compute the square of the length of a vector. + DistanceSquared - compute the square of the distance between two points. + ScalarProduct - scalar (dot) product. + VectorProduct - vector (cross) product (3-element vectors ONLY!). + Normalise/Normalize - make vector be one unit long. + Lerp - linear interpolation by a fraction 'f'. +*/ + +inline void sgdZeroVec2 ( sgdVec2 dst ) { dst[0]=dst[1]=SGD_ZERO ; } +inline void sgdZeroVec3 ( sgdVec3 dst ) { dst[0]=dst[1]=dst[2]=SGD_ZERO ; } +inline void sgdZeroVec4 ( sgdVec4 dst ) { dst[0]=dst[1]=dst[2]=dst[3]=SGD_ZERO ; } + + +inline void sgdSetVec2 ( sgdVec2 dst, const SGDfloat x, const SGDfloat y ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; +} + +inline void sgdSetVec3 ( sgdVec3 dst, const SGDfloat x, const SGDfloat y, const SGDfloat z ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; + dst [ 2 ] = z ; +} + +inline void sgdSetVec4 ( sgdVec4 dst, const SGDfloat x, const SGDfloat y, const SGDfloat z, const SGDfloat w ) +{ + dst [ 0 ] = x ; + dst [ 1 ] = y ; + dst [ 2 ] = z ; + dst [ 3 ] = w ; +} + + +inline void sgdCopyVec2 ( sgdVec2 dst, const sgdVec2 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; +} + +inline void sgdCopyVec3 ( sgdVec3 dst, const sgdVec3 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; + dst [ 2 ] = src [ 2 ] ; +} + +inline void sgdCopyVec4 ( sgdVec4 dst, const sgdVec4 src ) +{ + dst [ 0 ] = src [ 0 ] ; + dst [ 1 ] = src [ 1 ] ; + dst [ 2 ] = src [ 2 ] ; + dst [ 3 ] = src [ 3 ] ; +} + + +inline void sgdAddVec2 ( sgdVec2 dst, const sgdVec2 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; +} + +inline void sgdAddVec3 ( sgdVec3 dst, const sgdVec3 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; + dst [ 2 ] += src [ 2 ] ; +} + +inline void sgdAddVec4 ( sgdVec4 dst, const sgdVec4 src ) +{ + dst [ 0 ] += src [ 0 ] ; + dst [ 1 ] += src [ 1 ] ; + dst [ 2 ] += src [ 2 ] ; + dst [ 3 ] += src [ 3 ] ; +} + + +inline void sgdAddVec2 ( sgdVec2 dst, const sgdVec2 src1, const sgdVec2 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; +} + +inline void sgdAddVec3 ( sgdVec3 dst, const sgdVec3 src1, const sgdVec3 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] ; +} + +inline void sgdAddVec4 ( sgdVec4 dst, const sgdVec4 src1, const sgdVec4 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] ; + dst [ 3 ] = src1 [ 3 ] + src2 [ 3 ] ; +} + + +inline void sgdSubVec2 ( sgdVec2 dst, const sgdVec2 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; +} + +inline void sgdSubVec3 ( sgdVec3 dst, const sgdVec3 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; + dst [ 2 ] -= src [ 2 ] ; +} + +inline void sgdSubVec4 ( sgdVec4 dst, const sgdVec4 src ) +{ + dst [ 0 ] -= src [ 0 ] ; + dst [ 1 ] -= src [ 1 ] ; + dst [ 2 ] -= src [ 2 ] ; + dst [ 3 ] -= src [ 3 ] ; +} + +inline void sgdSubVec2 ( sgdVec2 dst, const sgdVec2 src1, const sgdVec2 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; +} + +inline void sgdSubVec3 ( sgdVec3 dst, const sgdVec3 src1, const sgdVec3 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] - src2 [ 2 ] ; +} + +inline void sgdSubVec4 ( sgdVec4 dst, const sgdVec4 src1, const sgdVec4 src2 ) +{ + dst [ 0 ] = src1 [ 0 ] - src2 [ 0 ] ; + dst [ 1 ] = src1 [ 1 ] - src2 [ 1 ] ; + dst [ 2 ] = src1 [ 2 ] - src2 [ 2 ] ; + dst [ 3 ] = src1 [ 3 ] - src2 [ 3 ] ; +} + + +inline void sgdNegateVec2 ( sgdVec2 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; +} + +inline void sgdNegateVec3 ( sgdVec3 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; + dst [ 2 ] = -dst [ 2 ] ; +} + +inline void sgdNegateVec4 ( sgdVec4 dst ) +{ + dst [ 0 ] = -dst [ 0 ] ; + dst [ 1 ] = -dst [ 1 ] ; + dst [ 2 ] = -dst [ 2 ] ; + dst [ 3 ] = -dst [ 3 ] ; +} + + +inline void sgdNegateVec2 ( sgdVec2 dst, const sgdVec2 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; +} + +inline void sgdNegateVec3 ( sgdVec3 dst, const sgdVec3 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; + dst [ 2 ] = -src [ 2 ] ; +} + +inline void sgdNegateVec4 ( sgdVec4 dst, const sgdVec4 src ) +{ + dst [ 0 ] = -src [ 0 ] ; + dst [ 1 ] = -src [ 1 ] ; + dst [ 2 ] = -src [ 2 ] ; + dst [ 3 ] = -src [ 3 ] ; +} + + +inline void sgdScaleVec2 ( sgdVec2 dst, const SGDfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; +} + +inline void sgdScaleVec3 ( sgdVec3 dst, const SGDfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; + dst [ 2 ] *= s ; +} + +inline void sgdScaleVec4 ( sgdVec4 dst, const SGDfloat s ) +{ + dst [ 0 ] *= s ; + dst [ 1 ] *= s ; + dst [ 2 ] *= s ; + dst [ 3 ] *= s ; +} + +inline void sgdScaleVec2 ( sgdVec2 dst, const sgdVec2 src, const SGDfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; +} + +inline void sgdScaleVec3 ( sgdVec3 dst, const sgdVec3 src, const SGDfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; + dst [ 2 ] = src [ 2 ] * s ; +} + +inline void sgdScaleVec4 ( sgdVec4 dst, const sgdVec4 src, const SGDfloat s ) +{ + dst [ 0 ] = src [ 0 ] * s ; + dst [ 1 ] = src [ 1 ] * s ; + dst [ 2 ] = src [ 2 ] * s ; + dst [ 3 ] = src [ 3 ] * s ; +} + + +inline void sgdAddScaledVec2 ( sgdVec2 dst, const sgdVec2 src, const SGDfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; +} + +inline void sgdAddScaledVec3 ( sgdVec3 dst, const sgdVec3 src, const SGDfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; + dst [ 2 ] += src [ 2 ] * s ; +} + +inline void sgdAddScaledVec4 ( sgdVec4 dst, const sgdVec4 src, const SGDfloat s ) +{ + dst [ 0 ] += src [ 0 ] * s ; + dst [ 1 ] += src [ 1 ] * s ; + dst [ 2 ] += src [ 2 ] * s ; + dst [ 3 ] += src [ 3 ] * s ; +} + + +inline void sgdAddScaledVec2 ( sgdVec2 dst, const sgdVec2 src1, const sgdVec2 src2, const SGDfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; +} + +inline void sgdAddScaledVec3 ( sgdVec3 dst, const sgdVec3 src1, const sgdVec3 src2, const SGDfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] * s ; +} + +inline void sgdAddScaledVec4 ( sgdVec4 dst, const sgdVec4 src1, const sgdVec4 src2, const SGDfloat s ) +{ + dst [ 0 ] = src1 [ 0 ] + src2 [ 0 ] * s ; + dst [ 1 ] = src1 [ 1 ] + src2 [ 1 ] * s ; + dst [ 2 ] = src1 [ 2 ] + src2 [ 2 ] * s ; + dst [ 3 ] = src1 [ 3 ] + src2 [ 3 ] * s ; +} + + + +inline int sgdCompareVec2 ( const sgdVec2 a, const sgdVec2 b, const SGDfloat tol ) +{ + int val = 0 ; + + if ( ( val = sgdCompareFloat( a[0], b[0], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[1], b[1], tol ) ) != 0 ) return val ; + + return 0 ; +} + +inline int sgdCompareVec3 ( const sgdVec3 a, const sgdVec3 b, const SGDfloat tol ) +{ + int val = 0 ; + + if ( ( val = sgdCompareFloat( a[0], b[0], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[1], b[1], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[2], b[2], tol ) ) != 0 ) return val ; + + return 0 ; +} + +inline int sgdCompareVec4 ( const sgdVec4 a, const sgdVec4 b, const SGDfloat tol ) +{ + int val = 0 ; + + if ( ( val = sgdCompareFloat( a[0], b[0], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[1], b[1], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[2], b[2], tol ) ) != 0 ) return val ; + if ( ( val = sgdCompareFloat( a[3], b[3], tol ) ) != 0 ) return val ; + + return 0 ; +} + + +inline int sgdEqualVec2 ( const sgdVec2 a, const sgdVec2 b ) +{ + return a[0] == b[0] && + a[1] == b[1] ; +} + +inline int sgdEqualVec3 ( const sgdVec3 a, const sgdVec3 b ) +{ + return a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] ; +} + +inline int sgdEqualVec4 ( const sgdVec4 a, const sgdVec4 b ) +{ + return a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3] ; +} + + +inline SGDfloat sgdScalarProductVec2 ( const sgdVec2 a, const sgdVec2 b ) +{ + return a[0]*b[0] + a[1]*b[1] ; +} + +inline SGDfloat sgdScalarProductVec3 ( const sgdVec3 a, const sgdVec3 b ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] ; +} + +inline SGDfloat sgdScalarProductVec4 ( const sgdVec4 a, const sgdVec4 b ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3] ; +} + + +extern void sgdVectorProductVec3 ( sgdVec3 dst, const sgdVec3 a, const sgdVec3 b ) ; + + +inline void sgdLerpVec4 ( sgdVec4 dst, const sgdVec4 a, const sgdVec4 b, const SGDfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; + dst[2] = a[2] + f * ( b[2] - a[2] ) ; + dst[3] = a[3] + f * ( b[3] - a[3] ) ; +} + +inline void sgdLerpVec3 ( sgdVec3 dst, const sgdVec3 a, const sgdVec3 b, const SGDfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; + dst[2] = a[2] + f * ( b[2] - a[2] ) ; +} + +inline void sgdLerpVec2 ( sgdVec2 dst, const sgdVec2 a, const sgdVec2 b, const SGDfloat f ) +{ + dst[0] = a[0] + f * ( b[0] - a[0] ) ; + dst[1] = a[1] + f * ( b[1] - a[1] ) ; +} + +inline void sgdLerpAnglesVec3 ( sgdVec3 dst, const sgdVec3 a, + const sgdVec3 b, + const SGDfloat f ) +{ + sgdVec3 tmp ; + + if ( b[0] - a[0] > 180.0 ) tmp[0] = a[0] + 360.0 ; else + if ( b[0] - a[0] < -180.0 ) tmp[0] = a[0] - 360.0 ; else tmp[0] = a[0] ; + + if ( b[1] - a[1] > 180.0 ) tmp[1] = a[1] + 360.0 ; else + if ( b[1] - a[1] < -180.0 ) tmp[1] = a[1] - 360.0 ; else tmp[1] = a[1] ; + + if ( b[2] - a[2] > 180.0 ) tmp[2] = a[2] + 360.0 ; else + if ( b[2] - a[2] < -180.0 ) tmp[2] = a[2] - 360.0 ; else tmp[2] = a[2] ; + + dst[0] = tmp[0] + f * ( b[0] - tmp[0] ) ; + dst[1] = tmp[1] + f * ( b[1] - tmp[1] ) ; + dst[2] = tmp[2] + f * ( b[2] - tmp[2] ) ; +} + + + + +inline SGDfloat sgdDistanceSquaredVec2 ( const sgdVec2 a, const sgdVec2 b ) +{ + return sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) ; +} + +inline SGDfloat sgdDistanceSquaredVec3 ( const sgdVec3 a, const sgdVec3 b ) +{ + return sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) + + sgdSquare ( a[2]-b[2] ) ; +} + +inline SGDfloat sgdDistanceSquaredVec4 ( const sgdVec4 a, const sgdVec4 b ) +{ + return sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) + + sgdSquare ( a[2]-b[2] ) + sgdSquare ( a[3]-b[3] ) ; +} + + +inline SGDfloat sgdDistanceVec2 ( const sgdVec2 a, const sgdVec2 b ) +{ + return sgdSqrt ( sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) ) ; +} + +inline SGDfloat sgdDistanceVec3 ( const sgdVec3 a, const sgdVec3 b ) +{ + return sgdSqrt ( sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) + + sgdSquare ( a[2]-b[2] ) ) ; +} + +inline SGDfloat sgdDistanceVec4 ( const sgdVec4 a, const sgdVec4 b ) +{ + return sgdSqrt ( sgdSquare ( a[0]-b[0] ) + sgdSquare ( a[1]-b[1] ) + + sgdSquare ( a[2]-b[2] ) + sgdSquare ( a[3]-b[3] ) ) ; +} + + +inline SGDfloat sgdLengthVec2 ( sgdVec2 const src ) +{ + return sgdSqrt ( sgdScalarProductVec2 ( src, src ) ) ; +} + +inline SGDfloat sgdLengthVec3 ( sgdVec3 const src ) +{ + return sgdSqrt ( sgdScalarProductVec3 ( src, src ) ) ; +} + +inline SGDfloat sgdLengthVec4 ( sgdVec4 const src ) +{ + return sgdSqrt ( sgdScalarProductVec4 ( src, src ) ) ; +} + +inline SGDfloat sgdLengthSquaredVec2 ( sgdVec2 const src ) +{ + return sgdScalarProductVec2 ( src, src ) ; +} + +inline SGDfloat sgdLengthSquaredVec3 ( sgdVec3 const src ) +{ + return sgdScalarProductVec3 ( src, src ) ; +} + +inline SGDfloat sgdLengthSquaredVec4 ( sgdVec4 const src ) +{ + return sgdScalarProductVec4 ( src, src ) ; +} + +/* Anglo-US spelling issues. */ +#define sgdNormalizeVec2 sgdNormaliseVec2 +#define sgdNormalizeVec3 sgdNormaliseVec3 +#define sgdNormalizeVec4 sgdNormaliseVec4 +#define sgdNormalizeQuat sgdNormaliseQuat + +inline void sgdNormaliseVec2 ( sgdVec2 dst ) +{ + sgdScaleVec2 ( dst, SGD_ONE / sgdLengthVec2 ( dst ) ) ; +} + +inline void sgdNormaliseVec3 ( sgdVec3 dst ) +{ + sgdScaleVec3 ( dst, SGD_ONE / sgdLengthVec3 ( dst ) ) ; +} + +inline void sgdNormaliseVec4 ( sgdVec4 dst ) +{ + sgdScaleVec4 ( dst, SGD_ONE / sgdLengthVec4 ( dst ) ) ; +} + +inline void sgdNormaliseVec2 ( sgdVec2 dst, const sgdVec2 src ) +{ + sgdScaleVec2 ( dst, src, SGD_ONE / sgdLengthVec2 ( src ) ) ; +} + +inline void sgdNormaliseVec3 ( sgdVec3 dst, const sgdVec3 src ) +{ + sgdScaleVec3 ( dst, src, SGD_ONE / sgdLengthVec3 ( src ) ) ; +} + +inline void sgdNormaliseVec4 ( sgdVec4 dst, const sgdVec4 src ) +{ + sgdScaleVec4 ( dst, src, SGD_ONE / sgdLengthVec4 ( src ) ) ; +} + + +inline void sgdZeroCoord ( sgdCoord *dst ) +{ + sgdSetVec3 ( dst->xyz, SGD_ZERO, SGD_ZERO, SGD_ZERO ) ; + sgdSetVec3 ( dst->hpr, SGD_ZERO, SGD_ZERO, SGD_ZERO ) ; +} + +inline void sgdSetCoord ( sgdCoord *dst, const SGDfloat x, const SGDfloat y, const SGDfloat z, + const SGDfloat h, const SGDfloat p, const SGDfloat r ) +{ + sgdSetVec3 ( dst->xyz, x, y, z ) ; + sgdSetVec3 ( dst->hpr, h, p, r ) ; +} + +inline void sgdSetCoord ( sgdCoord *dst, const sgdVec3 xyz, const sgdVec3 hpr ) +{ + sgdCopyVec3 ( dst->xyz, xyz ) ; + sgdCopyVec3 ( dst->hpr, hpr ) ; +} + +inline void sgdCopyCoord ( sgdCoord *dst, const sgdCoord *src ) +{ + sgdCopyVec3 ( dst->xyz, src->xyz ) ; + sgdCopyVec3 ( dst->hpr, src->hpr ) ; +} + + + +inline void sgdCopyMat4 ( sgdMat4 dst, const sgdMat4 src ) +{ + sgdCopyVec4 ( dst[ 0 ], src[ 0 ] ) ; + sgdCopyVec4 ( dst[ 1 ], src[ 1 ] ) ; + sgdCopyVec4 ( dst[ 2 ], src[ 2 ] ) ; + sgdCopyVec4 ( dst[ 3 ], src[ 3 ] ) ; +} + + +inline void sgdScaleMat4 ( sgdMat4 dst, const sgdMat4 src, const SGDfloat scale ) +{ + sgdScaleVec4 ( dst[0], src[0], scale ) ; + sgdScaleVec4 ( dst[1], src[1], scale ) ; + sgdScaleVec4 ( dst[2], src[2], scale ) ; + sgdScaleVec4 ( dst[3], src[3], scale ) ; +} + + +inline void sgdMakeIdentMat4 ( sgdMat4 dst ) +{ + sgdSetVec4 ( dst[0], SGD_ONE , SGD_ZERO, SGD_ZERO, SGD_ZERO ) ; + sgdSetVec4 ( dst[1], SGD_ZERO, SGD_ONE , SGD_ZERO, SGD_ZERO ) ; + sgdSetVec4 ( dst[2], SGD_ZERO, SGD_ZERO, SGD_ONE , SGD_ZERO ) ; + sgdSetVec4 ( dst[3], SGD_ZERO, SGD_ZERO, SGD_ZERO, SGD_ONE ) ; +} + + +extern void sgdMakeTransMat4 ( sgdMat4 m, const SGDfloat x, const SGDfloat y, const SGDfloat z ) ; +extern void sgdMakeTransMat4 ( sgdMat4 m, const sgdVec3 xyz ) ; +extern void sgdMakeCoordMat4 ( sgdMat4 m, const SGDfloat x, const SGDfloat y, const SGDfloat z, + const SGDfloat h, const SGDfloat p, const SGDfloat r ) ; +extern void sgdMakeCoordMat4 ( sgdMat4 m, const sgdCoord *c ) ; + +extern int sgdCompare3DSqdDist ( const sgdVec3 a, const sgdVec3 b, const SGDfloat sqd_dist ) ; + +inline SGDfloat sgdDistToLineVec2 ( const sgdVec3 line, const sgdVec2 pnt ) +{ + return sgdScalarProductVec2 ( line, pnt ) + line[2] ; +} + + +struct sgdLineSegment3 /* Bounded line segment */ +{ + sgdVec3 a ; + sgdVec3 b ; +} ; + +struct sgdLine3 /* Infinite line */ +{ + sgdVec3 point_on_line ; + sgdVec3 direction_vector ; /* Should be a unit vector */ +} ; + + +inline void sgdLineSegment3ToLine3 ( sgdLine3 *line, + const sgdLineSegment3 lineseg ) +{ + sgdCopyVec3 ( line->point_on_line , lineseg.a ) ; + sgdSubVec3 ( line->direction_vector, lineseg.b, lineseg.a ) ; + sgdNormaliseVec3 ( line->direction_vector ) ; +} + + +SGDfloat sgdDistSquaredToLineVec3 ( const sgdLine3 line, + const sgdVec3 pnt ) ; +SGDfloat sgdDistSquaredToLineSegmentVec3 ( const sgdLineSegment3 line, + const sgdVec3 pnt ) ; + + +inline SGDfloat sgdDistToLineVec3 ( const sgdLine3 line, + const sgdVec3 pnt ) +{ + return sgdSqrt ( sgdDistSquaredToLineVec3 ( line, pnt ) ); +} + + +inline SGDfloat sgdDistToLineSegmentVec3 ( const sgdLineSegment3 line, + const sgdVec3 pnt ) +{ + return sgdSqrt ( sgdDistSquaredToLineSegmentVec3(line,pnt) ) ; +} + +inline SGDfloat sgdDistToPlaneVec3 ( const sgdVec4 plane, const sgdVec3 pnt ) +{ + return sgdScalarProductVec3 ( plane, pnt ) + plane[3] ; +} + +inline SGDfloat sgdHeightAbovePlaneVec3 ( const sgdVec4 plane, const sgdVec3 pnt ) +{ + return pnt[2] - sgdHeightOfPlaneVec2 ( plane, pnt ) ; +} + +extern void sgdReflectInPlaneVec3 ( sgdVec3 dst, const sgdVec3 src, const sgdVec4 plane ) ; + +inline void sgdReflectInPlaneVec3 ( sgdVec3 dst, const sgdVec4 plane ) +{ + sgdReflectInPlaneVec3 ( dst, dst, plane ) ; +} + +extern void sgdMakeNormal ( sgdVec3 dst, const sgdVec3 a, const sgdVec3 b, const sgdVec3 c ) ; + +inline void sgdMake2DLine ( sgdVec3 dst, const sgdVec2 a, const sgdVec2 b ) +{ + dst[0] = b[1]-a[1] ; + dst[1] = -b[0]-a[0] ; + sgdNormalizeVec2 ( dst ) ; + dst[2] = - ( dst[0]*a[0] + dst[1]*a[1] ) ; +} + +inline void sgdMakePlane ( sgdVec4 dst, const sgdVec3 normal, const sgdVec3 pnt ) +{ + sgdCopyVec3 ( dst, normal ) ; + dst [ 3 ] = - sgdScalarProductVec3 ( normal, pnt ) ; +} + +inline void sgdMakePlane ( sgdVec4 dst, const sgdVec3 a, const sgdVec3 b, const sgdVec3 c ) +{ + /* + Ax + By + Cz + D == 0 ; + D = - ( Ax + By + Cz ) + = - ( A*a[0] + B*a[1] + C*a[2] ) + = - sgdScalarProductVec3 ( normal, a ) ; + */ + + sgdMakeNormal ( dst, a, b, c ) ; + + dst [ 3 ] = - sgdScalarProductVec3 ( dst, a ) ; +} + +SGDfloat sgdTriArea( sgdVec3 p0, sgdVec3 p1, sgdVec3 p2 ); + + +// Fast code. Result is in the range 0..180: +inline SGDfloat sgdAngleBetweenNormalizedVec3 ( sgdVec3 v1, sgdVec3 v2 ) +{ + SGDfloat f = sgdScalarProductVec3 ( v1, v2 ) ; + + return (float) acos ( ( f >= 1.0f ) ? 1.0f : + ( f <= -1.0f ) ? -1.0f : f ) * + SGD_RADIANS_TO_DEGREES ; +} + +// Fast code. Result is in the range 0..180: + +SGDfloat sgdAngleBetweenVec3 ( sgdVec3 v1, sgdVec3 v2 ); + +// All three have to be normalized. Slow code. Result is in the range 0..360: + +SGDfloat sgdAngleBetweenNormalizedVec3 (sgdVec3 first, sgdVec3 second, sgdVec3 normal); + +// Normal has to be normalized. Slow code. Result is in the range 0..360: + +SGDfloat sgdAngleBetweenVec3 ( sgdVec3 v1, sgdVec3 v2, sgdVec3 normal ); + + + +class sgdSphere +{ +public: + sgdVec3 center ; + SGDfloat radius ; + + + const SGDfloat *getCenter (void) const { return center ; } + + void setCenter ( const sgdVec3 c ) + { + sgdCopyVec3 ( center, c ) ; + } + + void setCenter ( const SGDfloat x, const SGDfloat y, const SGDfloat z ) + { + sgdSetVec3 ( center, x, y, z ) ; + } + + SGDfloat getRadius (void) const { return radius ; } + void setRadius ( const SGDfloat r ) { radius = r ; } + + int isEmpty (void) const { return radius < SGD_ZERO ; } + void empty (void) { radius = - SGD_ONE ; } + + void orthoXform ( const sgdMat4 m ) + { + sgdXformPnt3 ( center, center, m ) ; + // radius *= sgdLengthVec3 ( m[0] ) ; + } + + void extend ( const sgdSphere *s ) ; + void extend ( const sgdBox *b ) ; + void extend ( const sgdVec3 v ) ; + + int intersects ( const sgdSphere *s ) const + { + return sgdCompare3DSqdDist ( center, s->getCenter(), + sgdSquare ( radius + s->getRadius() ) ) <= 0 ; + } + + int intersects ( const sgdVec4 plane ) const + { + return sgdAbs ( sgdDistToPlaneVec3 ( plane, center ) ) <= radius ; + } + + int intersects ( const sgdBox *b ) const ; +} ; + + +class sgdBox +{ +public: + sgdVec3 min ; + sgdVec3 max ; + + + const SGDfloat *getMin (void) const { return min ; } + const SGDfloat *getMax (void) const { return max ; } + + void setMin ( const SGDfloat x, const SGDfloat y, const SGDfloat z ) + { + sgdSetVec3 ( min, x, y, z ) ; + } + + void setMin ( const sgdVec3 src ) + { + sgdCopyVec3 ( min, src ) ; + } + + void setMax ( const SGDfloat x, const SGDfloat y, const SGDfloat z ) + { + sgdSetVec3 ( max, x, y, z ) ; + } + + void setMax ( const sgdVec3 src ) + { + sgdCopyVec3 ( max, src ) ; + } + + int isEmpty(void) const + { + return ( min[0] > max[0] || + min[1] > max[1] || + min[2] > max[2] ) ; + } + + void empty (void) + { + sgdSetVec3 ( min, SGD_MAX, SGD_MAX, SGD_MAX ) ; + sgdSetVec3 ( max, -SGD_MAX, -SGD_MAX, -SGD_MAX ) ; + } + + void extend ( const sgdSphere *s ) ; + void extend ( const sgdBox *b ) ; + void extend ( const sgdVec3 v ) ; + + int intersects ( const sgdSphere *s ) const + { + return s -> intersects ( this ) ; + } + + int intersects ( const sgdBox *b ) const + { + return min[0] <= b->getMax()[0] && max[0] >= b->getMin()[0] && + min[1] <= b->getMax()[1] && max[1] >= b->getMin()[1] && + min[2] <= b->getMax()[2] && max[2] >= b->getMin()[2] ; + } + + int intersects ( const sgdVec4 plane ) const ; +} ; + + +class sgdFrustum +{ + /* Is the projection orthographic (or perspective)? */ + int ortho ; + + /* The parameters for glFrustum/glOrtho */ + + SGDfloat left, right, bot, top, nnear, ffar ; + + /* The computed projection matrix for this frustum */ + + sgdMat4 mat ; + + /* The A,B,C,D terms of the plane equations of the clip planes */ + /* A point (x,y,z) is inside the frustum iff Ax + By + Cz + D >= 0 for all planes */ + + sgdVec4 plane [ 6 ] ; + + /* These two are only valid for simple frusta */ + + SGDfloat hfov ; /* Horizontal Field of View -or- Orthographic Width */ + SGDfloat vfov ; /* Vertical Field of View -or- Orthographic Height */ + + void update (void) ; + int getOutcode ( const sgdVec4 src ) const ; + +public: + + sgdFrustum (void) + { + ortho = FALSE ; + nnear = SGD_ONE ; + ffar = 1000000.0f ; + hfov = SGD_45 ; + vfov = SGD_45 ; + update () ; + } + + void setFrustum ( const SGDfloat l, const SGDfloat r, + const SGDfloat b, const SGDfloat t, + const SGDfloat n, const SGDfloat f ) + { + ortho = FALSE ; + left = l ; + right = r ; + bot = b ; + top = t ; + nnear = n ; + ffar = f ; + hfov = vfov = SGD_ZERO ; + update () ; + } + + void setOrtho ( const SGDfloat l, const SGDfloat r, + const SGDfloat b, const SGDfloat t, + const SGDfloat n, const SGDfloat f ) + { + ortho = TRUE ; + left = l ; + right = r ; + bot = b ; + top = t ; + nnear = n ; + ffar = f ; + hfov = vfov = SGD_ZERO ; + update () ; + } + + void getMat4 ( sgdMat4 dst ) { sgdCopyMat4 ( dst, mat ) ; } + + SGDfloat getLeft (void) const { return left ; } + SGDfloat getRight(void) const { return right ; } + SGDfloat getBot (void) const { return bot ; } + SGDfloat getTop (void) const { return top ; } + SGDfloat getNear (void) const { return nnear ; } + SGDfloat getFar (void) const { return ffar ; } + + const SGDfloat *getPlane ( int i ) const { return plane [ i ] ; } + + SGDfloat getHFOV (void) const { return hfov ; } + SGDfloat getVFOV (void) const { return vfov ; } + + void getFOV ( SGDfloat *h, SGDfloat *v ) const + { + if ( h != (SGDfloat *) 0 ) *h = hfov ; + if ( v != (SGDfloat *) 0 ) *v = vfov ; + } + + void setFOV ( const SGDfloat h, const SGDfloat v ) + { + ortho = FALSE ; + hfov = ( h <= 0 ) ? ( v * SGD_FOUR / SGD_THREE ) : h ; + vfov = ( v <= 0 ) ? ( h * SGD_THREE / SGD_FOUR ) : v ; + update () ; + } + + void getOrtho ( SGDfloat *w, SGDfloat *h ) const + { + if ( w != (SGDfloat *) 0 ) *w = right - left ; + if ( h != (SGDfloat *) 0 ) *h = top - bot ; + } + + void setOrtho ( const SGDfloat w, const SGDfloat h ) + { + ortho = TRUE ; + hfov = ( w <= 0 ) ? ( h * SGD_FOUR / SGD_THREE ) : w ; + vfov = ( h <= 0 ) ? ( w * SGD_FOUR / SGD_THREE ) : h ; + update () ; + } + + void getNearFar ( SGDfloat *n, SGDfloat *f ) const + { + if ( n != (SGDfloat *) 0 ) *n = nnear ; + if ( f != (SGDfloat *) 0 ) *f = ffar ; + } + + void setNearFar ( const SGDfloat n, const SGDfloat f ) + { + nnear = n ; + ffar = f ; + update () ; + } + + int isOrtho (void) const { return ortho ; } + + int contains ( const sgdVec3 p ) const ; + int contains ( const sgdSphere *s ) const ; + int contains ( const sgdBox *b ) const ; +} ; + + +/* + Quaternion routines are Copyright (C) 1999 + Kevin B. Thompson + Modified by Sylvan W. Clebsch + Largely rewritten by "Negative0" +*/ + +/* + Quaternion structure w = real, (x, y, z) = vector + CHANGED sqQuat to float array so that syntax matches + vector and matrix routines +*/ + + +inline void sgdMakeIdentQuat ( sgdQuat dst ) +{ + sgdSetVec4 ( dst, SGD_ZERO, SGD_ZERO, SGD_ZERO, SGD_ONE ) ; +} + + +inline void sgdSetQuat ( sgdQuat dst, + const SGDfloat w, const SGDfloat x, + const SGDfloat y, const SGDfloat z ) +{ + sgdSetVec4 ( dst, x, y, z, w ) ; +} + +inline void sgdCopyQuat ( sgdQuat dst, const sgdQuat src ) +{ + sgdCopyVec4 ( dst, src ) ; +} + + +/* Construct a unit quaternion (length==1) */ + +inline void sgdNormaliseQuat ( sgdQuat dst, const sgdQuat src ) +{ + SGDfloat d = sgdScalarProductVec4 ( src, src ) ; + + d = (d > SGD_ZERO) ? (SGD_ONE / sgdSqrt ( d )) : SGD_ONE ; + + sgdScaleVec4 ( dst, src, d ) ; +} + + + +inline void sgdNormaliseQuat ( sgdQuat dst ) { sgdNormaliseQuat ( dst, dst ) ; } + + +inline void sgdInvertQuat ( sgdQuat dst, const sgdQuat src ) +{ + SGDfloat d = sgdScalarProductVec4 ( src, src ) ; + + d = ( d == SGD_ZERO ) ? SGD_ONE : ( SGD_ONE / d ) ; + + dst[SG_W] = src[SG_W] * d ; + dst[SG_X] = -src[SG_X] * d ; + dst[SG_Y] = -src[SG_Y] * d ; + dst[SG_Z] = -src[SG_Z] * d ; +} + +inline void sgdInvertQuat ( sgdQuat dst ) { sgdInvertQuat ( dst, dst ) ; } + + +/* Make an angle and axis of rotation from a Quaternion. */ + +void sgdQuatToAngleAxis ( SGDfloat *angle, sgdVec3 axis, const sgdQuat src ) ; +void sgdQuatToAngleAxis ( SGDfloat *angle, + SGDfloat *x, SGDfloat *y, SGDfloat *z, + const sgdQuat src ) ; + +/* Make a quaternion from a given angle and axis of rotation */ + +void sgdAngleAxisToQuat ( sgdQuat dst, + const SGDfloat angle, const sgdVec3 axis ) ; +void sgdAngleAxisToQuat ( sgdQuat dst, + const SGDfloat angle, + const SGDfloat x, const SGDfloat y, const SGDfloat z ); + +/* Convert a matrix to/from a quat */ + +void sgdMatrixToQuat ( sgdQuat quat, const sgdMat4 m ) ; +void sgdQuatToMatrix ( sgdMat4 m, const sgdQuat quat ) ; + +/* Convert a set of eulers to/from a quat */ + +void sgdQuatToEuler( sgdVec3 hpr, const sgdQuat quat ) ; +void sgdEulerToQuat( sgdQuat quat, const sgdVec3 hpr ) ; + +inline void sgdEulerToQuat( sgdQuat dst, + SGDfloat h, SGDfloat p, SGDfloat r ) +{ + sgdVec3 hpr ; + + sgdSetVec3 ( hpr, h, p, r ) ; + + sgdEulerToQuat( dst, hpr ) ; +} + +inline void sgdHPRToQuat ( sgdQuat dst, SGDfloat h, SGDfloat p, SGDfloat r ) +{ + sgdVec3 hpr; + + hpr[0] = h * SGD_DEGREES_TO_RADIANS ; + hpr[1] = p * SGD_DEGREES_TO_RADIANS ; + hpr[2] = r * SGD_DEGREES_TO_RADIANS ; + + sgdEulerToQuat( dst, hpr ) ; +} + +inline void sgdHPRToQuat ( sgdQuat dst, const sgdVec3 hpr ) +{ + sgdVec3 tmp ; + + sgdScaleVec3 ( tmp, hpr, SGD_DEGREES_TO_RADIANS ) ; + + sgdEulerToQuat ( dst, tmp ) ; +} + +/* Multiply quaternions together (concatenate rotations) */ + +void sgdMultQuat ( sgdQuat dst, const sgdQuat a, const sgdQuat b ) ; + +inline void sgdPostMultQuat ( sgdQuat dst, const sgdQuat q ) +{ + sgdQuat r ; + + sgdCopyQuat ( r, dst ) ; + sgdMultQuat ( dst, r, q ) ; +} + +inline void sgdPreMultQuat ( sgdQuat dst, const sgdQuat q ) +{ + sgdQuat r ; + + sgdCopyQuat ( r, dst ) ; + sgdMultQuat ( dst, q, r ) ; +} + + +/* Rotate a quaternion by a given angle and axis (convenience function) */ + + +inline void sgdRotQuat ( sgdQuat dst, const SGDfloat angle, const sgdVec3 axis ) +{ + sgdQuat q ; + + sgdAngleAxisToQuat ( q, angle, axis ) ; + sgdPostMultQuat ( dst, q ) ; + sgdNormaliseQuat ( dst ) ; +} + + +inline void sgdRotQuat ( sgdQuat dst, + const SGDfloat angle, + const SGDfloat x, const SGDfloat y, const SGDfloat z ) +{ + sgdVec3 axis ; + + sgdSetVec3 ( axis, x, y, z ) ; + sgdRotQuat ( dst, angle, axis ) ; +} + +/* SWC - Interpolate between to quaternions */ + +extern void sgdSlerpQuat ( sgdQuat dst, + const sgdQuat from, const sgdQuat to, + const SGDfloat t ) ; + + +/* Conversions between sg and sgd types. */ + +inline void sgSetVec2 ( sgVec2 dst, sgdVec2 src ) +{ + dst [ 0 ] = (SGfloat) src [ 0 ] ; + dst [ 1 ] = (SGfloat) src [ 1 ] ; +} + +inline void sgSetVec3 ( sgVec3 dst, sgdVec3 src ) +{ + dst [ 0 ] = (SGfloat) src [ 0 ] ; + dst [ 1 ] = (SGfloat) src [ 1 ] ; + dst [ 2 ] = (SGfloat) src [ 2 ] ; +} + +inline void sgSetVec4 ( sgVec4 dst, sgdVec4 src ) +{ + dst [ 0 ] = (SGfloat) src [ 0 ] ; + dst [ 1 ] = (SGfloat) src [ 1 ] ; + dst [ 2 ] = (SGfloat) src [ 2 ] ; + dst [ 3 ] = (SGfloat) src [ 3 ] ; +} + +inline void sgdSetVec2 ( sgdVec2 dst, sgVec2 src ) +{ + dst [ 0 ] = (SGDfloat) src [ 0 ] ; + dst [ 1 ] = (SGDfloat) src [ 1 ] ; +} + +inline void sgdSetVec3 ( sgdVec3 dst, sgVec3 src ) +{ + dst [ 0 ] = (SGDfloat) src [ 0 ] ; + dst [ 1 ] = (SGDfloat) src [ 1 ] ; + dst [ 2 ] = (SGDfloat) src [ 2 ] ; +} + +inline void sgdSetVec4 ( sgdVec4 dst, sgVec4 src ) +{ + dst [ 0 ] = (SGDfloat) src [ 0 ] ; + dst [ 1 ] = (SGDfloat) src [ 1 ] ; + dst [ 2 ] = (SGDfloat) src [ 2 ] ; + dst [ 3 ] = (SGDfloat) src [ 3 ] ; +} + + +inline void sgSetMat4 ( sgMat4 dst, sgdMat4 src ) +{ + sgSetVec4 ( dst [ 0 ], src [ 0 ] ) ; + sgSetVec4 ( dst [ 1 ], src [ 1 ] ) ; + sgSetVec4 ( dst [ 2 ], src [ 2 ] ) ; + sgSetVec4 ( dst [ 3 ], src [ 3 ] ) ; +} + + +inline void sgdSetMat4 ( sgdMat4 dst, sgMat4 src ) +{ + sgdSetVec4 ( dst [ 0 ], src [ 0 ] ) ; + sgdSetVec4 ( dst [ 1 ], src [ 1 ] ) ; + sgdSetVec4 ( dst [ 2 ], src [ 2 ] ) ; + sgdSetVec4 ( dst [ 3 ], src [ 3 ] ) ; +} + + +inline void sgSetCoord ( sgCoord *dst, sgdCoord *src ) +{ + sgSetVec3 ( dst->xyz, src->xyz ) ; + sgSetVec3 ( dst->hpr, src->hpr ) ; +} + + +inline void sgdSetCoord ( sgdCoord *dst, sgCoord *src ) +{ + sgdSetVec3 ( dst->xyz, src->xyz ) ; + sgdSetVec3 ( dst->hpr, src->hpr ) ; +} + +inline void sgSetQuat ( sgQuat dst, sgdQuat src ) +{ + sgSetVec4 ( dst, src ) ; +} + + + +inline void sgdSetQuat ( sgdQuat dst, sgQuat src ) +{ + sgdSetVec4 ( dst, src ) ; +} + + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q r Q-1 -- this gives the components of a ROTATED vector in a STATIONARY + * coordinate system. We assume that Q is a unit quaternion. + */ + +void sgRotateVecQuat ( sgVec3 vec, sgQuat q ) ; +void sgdRotateVecQuat ( sgdVec3 vec, sgdQuat q ) ; + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q-1 r Q -- this gives the components of a STATIONARY vector in a ROTATED + * coordinate system. We assume that Q is a unit quaternion. + */ + +void sgRotateCoordQuat ( sgVec3 vec, sgQuat q ) ; +void sgdRotateCoordQuat ( sgdVec3 vec, sgdQuat q ) ; + +sgFloat sgDistSquaredToLineLineSegment ( const sgLineSegment3 seg, const sgLine3 line ) ; +sgdFloat sgdDistSquaredToLineLineSegment ( const sgdLineSegment3 seg, const sgdLine3 line ) ; + + + +/* + Intersection testing. +*/ + +int sgdIsectPlanePlane ( sgdVec3 point, sgdVec3 dir, + sgdVec4 plane1, sgdVec4 plane2 ) ; +int sgdIsectInfLinePlane ( sgdVec3 dst, + sgdVec3 l_org, sgdVec3 l_vec, + sgdVec4 plane ) ; +int sgdIsectInfLineInfLine ( sgdVec3 dst, + sgdVec3 l1_org, sgdVec3 l1_vec, + sgdVec3 l2_org, sgdVec3 l2_vec ) ; +SGDfloat sgdIsectLinesegPlane ( sgdVec3 dst, + sgdVec3 v1, sgdVec3 v2, + sgdVec4 plane ) ; +bool sgdPointInTriangle ( sgdVec3 point, sgdVec3 tri[3] ); + + +/* + TRIANGLE SOLVERS - These work for any triangle. + + SSS == Side-lengths for all three sides. + SAS == Side-lengths for two sides - plus the angle between them. + ASA == Two angles plus the length of the Side between them. + Area == The area of the triangle. +*/ + +SGfloat sgTriangleSolver_ASAtoArea ( SGfloat angA, SGfloat lenB, SGfloat angC ); +SGfloat sgTriangleSolver_SAStoArea ( SGfloat lenA, SGfloat angB, SGfloat lenC ); +SGfloat sgTriangleSolver_SSStoArea ( SGfloat lenA, SGfloat lenB, SGfloat lenC ); +SGfloat sgTriangleSolver_SAAtoArea ( SGfloat lenA, SGfloat angB, SGfloat angA ); +SGfloat sgTriangleSolver_ASStoArea ( SGfloat angB, SGfloat lenA, SGfloat lenB, + int angA_is_obtuse ); + +void sgTriangleSolver_SSStoAAA ( SGfloat lenA, SGfloat lenB, SGfloat lenC, + SGfloat *angA, SGfloat *angB, SGfloat *angC ) ; +void sgTriangleSolver_SAStoASA ( SGfloat lenA, SGfloat angB, SGfloat lenC, + SGfloat *angA, SGfloat *lenB, SGfloat *angC ) ; +void sgTriangleSolver_ASAtoSAS ( SGfloat angA, SGfloat lenB, SGfloat angC, + SGfloat *lenA, SGfloat *angB, SGfloat *lenC ) ; +void sgTriangleSolver_SAAtoASS ( SGfloat lenA, SGfloat angB, SGfloat angA, + SGfloat *angC, SGfloat *lenB, SGfloat *lenC ) ; +void sgTriangleSolver_ASStoSAA ( SGfloat angB, SGfloat lenA, SGfloat lenB, + int angA_is_obtuse, + SGfloat *lenC, SGfloat *angA, SGfloat *angC ) ; + + +SGDfloat sgdTriangleSolver_ASAtoArea ( SGDfloat angA, SGDfloat lenB, SGDfloat angC ); +SGDfloat sgdTriangleSolver_SAStoArea ( SGDfloat lenA, SGDfloat angB, SGDfloat lenC ); +SGDfloat sgdTriangleSolver_SSStoArea ( SGDfloat lenA, SGDfloat lenB, SGDfloat lenC ); +SGDfloat sgdTriangleSolver_SAAtoArea ( SGDfloat lenA, SGDfloat angB, SGDfloat angA ); +SGDfloat sgdTriangleSolver_ASStoArea ( SGDfloat angB, SGDfloat lenA, SGDfloat lenB, + int angA_is_obtuse ); + +void sgdTriangleSolver_SSStoAAA ( SGDfloat lenA, SGDfloat lenB, SGDfloat lenC, + SGDfloat *angA, SGDfloat *angB, SGDfloat *angC ) ; +void sgdTriangleSolver_SAStoASA ( SGDfloat lenA, SGDfloat angB, SGDfloat lenC, + SGDfloat *angA, SGDfloat *lenB, SGDfloat *angC ) ; +void sgdTriangleSolver_ASAtoSAS ( SGDfloat angA, SGDfloat lenB, SGDfloat angC, + SGDfloat *lenA, SGDfloat *angB, SGDfloat *lenC ) ; +void sgdTriangleSolver_SAAtoASS ( SGDfloat lenA, SGDfloat angB, SGDfloat angA, + SGDfloat *angC, SGDfloat *lenB, SGDfloat *lenC ) ; +void sgdTriangleSolver_ASStoSAA ( SGDfloat angB, SGDfloat lenA, SGDfloat lenB, + int angA_is_obtuse, + SGDfloat *lenC, SGDfloat *angA, SGDfloat *angC ) ; + +/* + SPRING-MASS-DAMPER (with simple Euler integrator) +*/ + + +extern sgVec3 _sgGravity ; + +inline void sgSetGravity ( float g ) { sgSetVec3 ( _sgGravity, 0.0f, 0.0f, -g ) ; } +inline void sgSetGravityVec3 ( sgVec3 g ) { sgCopyVec3 ( _sgGravity, g ) ; } +inline float *sgGetGravityVec3 () { return _sgGravity ; } +inline float sgGetGravity () { return - _sgGravity[2] ; } + + +class sgParticle +{ + float ooMass ; /* One-over-mass */ + sgVec3 pos ; + sgVec3 vel ; + sgVec3 force ; + +public: + + sgParticle ( float mass, sgVec3 _pos ) + { + setMass ( mass ) ; + sgCopyVec3 ( pos, _pos ) ; + sgZeroVec3 ( vel ) ; + sgZeroVec3 ( force ) ; + } + + sgParticle ( float mass, float x = 0.0f, float y = 0.0f, float z = 0.0f ) + { + setMass ( mass ) ; + sgSetVec3 ( pos, x, y, z ) ; + sgZeroVec3 ( vel ) ; + sgZeroVec3 ( force ) ; + } + + SGfloat *getPos () { return pos ; } + SGfloat *getVel () { return vel ; } + SGfloat *getForce () { return force ; } + + const SGfloat *getPos () const { return pos ; } + const SGfloat *getVel () const { return vel ; } + const SGfloat *getForce () const { return force ; } + + float getOneOverMass () { return ooMass ; } + float getMass () { return 1.0f / ooMass ; } + + void setPos ( sgVec3 p ) { sgCopyVec3 ( pos , p ) ; } + void setVel ( sgVec3 v ) { sgCopyVec3 ( vel , v ) ; } + void setForce ( sgVec3 f ) { sgCopyVec3 ( force, f ) ; } + + void setPos ( float x, float y, float z ) { sgSetVec3 ( pos ,x,y,z ) ; } + void setVel ( float x, float y, float z ) { sgSetVec3 ( vel ,x,y,z ) ; } + void setForce ( float x, float y, float z ) { sgSetVec3 ( force,x,y,z ) ; } + + void setOneOverMass ( float oom ) { ooMass = oom ; } + + void setMass ( float m ) + { + assert ( m > 0.0f ) ; + ooMass = 1.0f / m ; + } + + void zeroForce () { sgZeroVec3 ( force ) ; } + void addForce ( sgVec3 f ) { sgAddVec3 ( force, f ) ; } + void subForce ( sgVec3 f ) { sgSubVec3 ( force, f ) ; } + void gravityOnly () { sgScaleVec3 ( force, sgGetGravityVec3 (), ooMass ) ; } + + void bounce ( sgVec3 normal, float coefRestitution ) + { + sgVec3 vn, vt ; + sgScaleVec3 ( vn, normal, + sgScalarProductVec3 ( normal, vel ) ) ; + sgSubVec3 ( vt, vel, vn ) ; + sgAddScaledVec3 ( vel, vt, vn, -coefRestitution ) ; + } + + void update ( float dt ) + { + sgAddScaledVec3 ( vel, force, dt * ooMass ) ; + sgAddScaledVec3 ( pos, vel, dt ) ; + } +} ; + + +class sgSpringDamper +{ + sgParticle *p0 ; + sgParticle *p1 ; + + float restLength ; + float stiffness ; + float damping ; + +public: + + sgSpringDamper () + { + p0 = p1 = NULL ; + stiffness = 1.0f ; + damping = 1.0f ; + restLength = 1.0f ; + } + + sgSpringDamper ( sgParticle *_p0, sgParticle *_p1, + float _stiffness, float _damping, + float _restLength = -1.0f ) + { + p0 = _p0 ; + p1 = _p1 ; + stiffness = _stiffness ; + damping = _damping ; + + if ( _restLength < 0.0f ) + { + if ( p0 != NULL && p1 != NULL ) + restLength = sgDistanceVec3 ( p0->getPos(), p1->getPos() ) ; + else + restLength = _restLength ; + } + else + restLength = 1.0f ; + } + + float getRestLength () { return restLength ; } + float getStiffness () { return stiffness ; } + float getDamping () { return damping ; } + + sgParticle *getParticle ( int which ) { return ( which == 0 ) ? p0 : p1 ; } + + + void setParticles ( sgParticle *_p0, sgParticle *_p1 ) { p0 = _p0 ; p1 = _p1 ; } + void setParticle ( int which, sgParticle *p ) { if ( which == 0 ) p0 = p ; else p1 = p ; } + + void setRestLength () { restLength = sgDistanceVec3 ( p0->getPos(), p1->getPos() ) ; } + + void setRestLength ( float l ) { restLength = l ; } + void setStiffness ( float s ) { stiffness = s ; } + void setDamping ( float d ) { damping = d ; } + + void update () + { + sgVec3 dP ; sgSubVec3 ( dP, p0->getPos(), p1->getPos() ) ; + sgVec3 dV ; sgSubVec3 ( dV, p0->getVel(), p1->getVel() ) ; + + float L = sgLengthVec3 ( dP ) ; if ( L == 0.0f ) L = 0.0000001f ; + float H = ( L - restLength ) * stiffness ; + float D = sgScalarProductVec3 ( dV, dP ) * damping / L ; + + sgVec3 F ; sgScaleVec3 ( F, dP, - ( H + D ) / L ) ; + + p0 -> addForce ( F ) ; + p1 -> subForce ( F ) ; + } + +} ; + + +/* + It must be true that (x % NOISE_WRAP_INDEX) == (x & NOISE_MOD_MASK) + so NOISE_WRAP_INDEX must be a power of two, and NOISE_MOD_MASK must be + that power of 2 - 1. as indices are implemented, as unsigned chars, + NOISE_WRAP_INDEX shoud be less than or equal to 256. + There's no good reason to change it from 256, really. + + NOISE_LARGE_PWR2 is a large power of 2, we'll go for 4096, to add to + negative numbers in order to make them positive +*/ + +#define SG_PERLIN_NOISE_WRAP_INDEX 256 +#define SG_PERLIN_NOISE_MOD_MASK 255 +#define SG_PERLIN_NOISE_LARGE_PWR2 4096 + + + +class sgPerlinNoise_1D +{ +private: + + SGfloat gradTable [ SG_PERLIN_NOISE_WRAP_INDEX * 2 + 2 ] ; + +public: + + sgPerlinNoise_1D () ; + + void regenerate () ; + + SGfloat getNoise ( SGfloat x ) ; +} ; + + + +class sgPerlinNoise_2D +{ +private: + + sgVec2 gradTable [ SG_PERLIN_NOISE_WRAP_INDEX * 2 + 2 ] ; + +public: + + sgPerlinNoise_2D () ; + + void regenerate () ; + + SGfloat getNoise ( sgVec2 pos ) ; + SGfloat getNoise ( SGfloat x, SGfloat y ) + { + sgVec2 p ; + sgSetVec2 ( p, x, y ) ; + return getNoise ( p ) ; + } +} ; + + + +class sgPerlinNoise_3D +{ +private: + + sgVec3 gradTable [ SG_PERLIN_NOISE_WRAP_INDEX * 2 + 2 ] ; + +public: + + sgPerlinNoise_3D () ; + + void regenerate () ; + + SGfloat getNoise ( sgVec3 pos ) ; + SGfloat getNoise ( SGfloat x, SGfloat y, SGfloat z ) + { + sgVec3 p ; + sgSetVec3 ( p, x, y, z ) ; + return getNoise ( p ) ; + } +} ; + + + +#endif + diff --git a/src/sg/sgIsect.cxx b/src/sg/sgIsect.cxx new file mode 100644 index 0000000..d7c4bd9 --- /dev/null +++ b/src/sg/sgIsect.cxx @@ -0,0 +1,390 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sgIsect.cxx 1851 2004-02-02 01:35:15Z sjbaker $ +*/ + + +#include "sg.h" + +/* + Determine the origin and unit direction vector of a line + formed by the intersection of two planes. + Returned point will lie on axis that was used to + determine intersection. + Norman Vine - nhv@yahoo.com +*/ + +int sgIsectPlanePlane ( sgVec3 point, sgVec3 dir, + sgVec4 plane1, sgVec4 plane2 ) +{ + /* Determine intersection line direction. */ + + sgVectorProductVec3 ( dir, plane1, plane2 ) ; + + SGfloat dnorm = sgLengthVec3 ( dir ) ; + + /* If planes are parallel then fail */ + + if ( dnorm < FLT_EPSILON ) + { + sgZeroVec3 ( point ) ; + sgZeroVec3 ( dir ) ; + return FALSE ; + } + + /* Determine intersection point with the best suited coordinate plane. */ + + SGfloat abs ; + SGfloat maxabs = sgAbs(dir[0]); + int index = 0; + + if ((abs = sgAbs(dir[1])) > maxabs) { maxabs = abs ; index = 1; } + if ((abs = sgAbs(dir[2])) > maxabs) { maxabs = abs ; index = 2; } + + switch ( index ) + { + case 0: + sgSetVec3( point, + SG_ZERO, + (plane1[2] * plane2[3] - plane2[2] * plane1[3]) / dir[0], + (plane2[1] * plane1[3] - plane1[1] * plane2[3]) / dir[0] ); + break; + case 1: + sgSetVec3( point, + (plane2[2] * plane1[3] - plane1[2] * plane2[3]) / dir[1], + SG_ZERO, + (plane1[0] * plane2[3] - plane2[0] * plane1[3]) / dir[1] ); + break; + case 2: + sgSetVec3( point, + (plane1[1] * plane2[3] - plane2[1] * plane1[3]) / dir[2], + (plane2[0] * plane1[3] - plane1[0] * plane2[3]) / dir[2], + SG_ZERO ); + break; + default: return FALSE ; /* Impossible */ + } + + /* Normalize the direction */ + + sgScaleVec3( dir, SG_ONE / dnorm ); + return TRUE; +} + + + +/* + Find the intersection of an infinite line with a plane + (the line being defined by a point and direction). + + Norman Vine -- nhv@yahoo.com (with hacks by Steve) +*/ + +int sgIsectInfLinePlane( sgVec3 dst, sgVec3 l_org, sgVec3 l_vec, sgVec4 plane ) +{ + SGfloat tmp = sgScalarProductVec3 ( l_vec, plane ) ; + + /* Is line parallel to plane? */ + + if ( sgAbs ( tmp ) < FLT_EPSILON ) + return FALSE ; + + sgScaleVec3 ( dst, l_vec, -( sgScalarProductVec3 ( l_org, plane ) + + plane[3] ) / tmp ) ; + sgAddVec3 ( dst, l_org ) ; + + return TRUE ; +} + + + +/* + Given the origin and direction vector for two lines + find their intersection - or the point closest to + both lines if they don't intersect. + + Norman Vine -- nhv@yahoo.com +*/ + +int sgIsectInfLineInfLine( sgVec3 dst, + sgVec3 l1_org, sgVec3 l1_vec, + sgVec3 l2_org, sgVec3 l2_vec ) +{ + sgVec3 vec_l1, vec_l2 ; + + sgNormalizeVec3 ( vec_l1, l1_vec ) ; + sgNormalizeVec3 ( vec_l2, l2_vec ) ; + + /* Connecting line 'C' is perpendicular to both */ + + sgVec3 perp ; + sgVectorProductVec3 ( perp, vec_l1, vec_l2 ) ; + + /* check for near-parallel lines */ + + SGfloat dist = sgScalarProductVec3 ( perp, perp ) ; + + if ( dist < FLT_EPSILON ) + { + /* degenerate: lines parallel - any point will do. */ + + sgCopyVec3 ( dst, l2_org ) ; + return TRUE ; + } + + /* + Form a plane containing the line A and C, + and another containing B and C + */ + + sgScaleVec3 ( perp, SG_ONE / sgSqrt( dist ) ) ; + + sgVec4 pa ; + sgVec4 pb ; + sgVec3 tmp ; + + sgVectorProductVec3 ( tmp, perp, vec_l1 ) ; + sgNormalizeVec3 ( tmp ); + sgMakePlane ( pa, tmp, l1_org ) ; + + sgVectorProductVec3 ( tmp, perp, vec_l2 ) ; + sgNormalizeVec3 ( tmp ); + sgMakePlane ( pb, tmp, l2_org ) ; + + sgVec3 tmp_org, tmp_vec ; + + if ( ! sgIsectPlanePlane( tmp_org, tmp_vec, pa, pb ) ) + { + /* This *shouldn't* ever happen because we already + tested for parallel lines - but with roundoff + errors, it *might* in borderline cases so... + */ + + sgCopyVec3 ( dst, l2_org ) ; + return FALSE ; + } + + if ( ! sgIsectInfLinePlane ( dst, l2_org, vec_l2, pa ) ) + { + sgCopyVec3 ( dst, l2_org ) ; + return FALSE ; + } + + return TRUE ; +} + + +/* + Intersect the line segment from v1->v2 with the 'plane'. + 'dst' is the intersection point and the return + result is in the range 0..1 if the intersection lies + between v1 and v2, >1 if beyond v2 and <0 if before v1. + FLT_MAX is returned if the vector does not intersect + the plane. + + Steve Baker +*/ + +SGfloat sgIsectLinesegPlane ( sgVec3 dst, sgVec3 v1, sgVec3 v2, sgVec4 plane ) +{ + sgVec3 delta ; + + sgSubVec3 ( delta, v2, v1 ) ; + + SGfloat p = sgScalarProductVec3 ( plane, delta ) ; + + if ( p == SG_ZERO ) + { + dst [ 0 ] = dst [ 1 ] = dst [ 2 ] = FLT_MAX ; + return FLT_MAX ; + } + + float s = - ( sgScalarProductVec3 ( plane, v1 ) + plane[3] ) / p ; + + sgScaleVec3 ( dst, delta, s ) ; + sgAddVec3 ( dst, dst, v1 ) ; + + return s ; +} + + +// return the sign of a value + +static inline const int SG_SIGN( const SGfloat x ) +{ + return x < 0 ? -1 : 1; +} + + +// return the minimum of two values + +static inline SGfloat SG_MIN2( const SGfloat a, const SGfloat b ) +{ + return a < b ? a : b ; +} + + +// return the minimum of three values + +static inline SGfloat SG_MIN3 ( const SGfloat a, + const SGfloat b, const SGfloat c ) +{ + return ( a < b ? SG_MIN2(a, c) : SG_MIN2(b, c) ); +} + + +// return the minimum and maximum of three values + +static void SG_MIN_MAX3 ( SGfloat &min, SGfloat &max, const SGfloat &a, + const SGfloat &b, const SGfloat &c ) +{ + if( a > b ) + { + if( a > c ) { max = a; min = SG_MIN2( b, c ); } else + { max = c; min = SG_MIN2( a, b ); } + } + else + { + if( b > c ) { max = b; min = SG_MIN2( a, c ); } else + { max = c; min = SG_MIN2( a, b ); } + } +} + +/* + * Given a point and a triangle lying on the same plane + * check to see if the point is inside the triangle + */ + +bool sgPointInTriangle3 ( sgVec3 point, sgVec3 tri[3] ) +{ + sgVec3 dif ; + + /* Early-out if point is not in bounding cube of triangle */ + + for( int i = 0 ; i < 3 ; i++ ) + { + SGfloat min, max ; + + SG_MIN_MAX3 ( min, max, tri[0][i], tri[1][i], tri[2][i] ) ; + + if( point[i] < min || point[i] > max ) + return false ; + + dif[i] = max - min ; /* Calc size of bounding cube */ + } + + /* Drop the smallest dimension so we only have to work in 2d. */ + + sgVec2 p, a, b, c ; + + if ( dif[0] <= dif[1] && dif[0] <= dif[2] ) + { + p[0] = point[1] ; p[1] = point[2] ; + a[0] = tri[0][1] ; a[1] = tri[0][2] ; + b[0] = tri[1][1] ; b[1] = tri[1][2] ; + c[0] = tri[2][1] ; c[1] = tri[2][2] ; + } + else + if ( dif[1] <= dif[0] && dif[1] <= dif[2] ) + { + p[0] = point[0] ; p[1] = point[2] ; + a[0] = tri[0][0] ; a[1] = tri[0][2] ; + b[0] = tri[1][0] ; b[1] = tri[1][2] ; + c[0] = tri[2][0] ; c[1] = tri[2][2] ; + } + else + { + p[0] = point[0] ; p[1] = point[1] ; + a[0] = tri[0][0] ; a[1] = tri[0][1] ; + b[0] = tri[1][0] ; b[1] = tri[1][1] ; + c[0] = tri[2][0] ; c[1] = tri[2][1] ; + } + + /* + Check if the intersection point is on the same side of + each edge of the triangle as the remaining point. + */ + + sgVec3 le ; /* Line Equation */ + + sgMake2DLine ( le, a, b ) ; + + if ( SG_SIGN ( le[0]*c[0] + le[1]*c[1] + le[2] ) != + SG_SIGN ( le[0]*p[0] + le[1]*p[1] + le[2] ) ) return false ; + + sgMake2DLine ( le, b, c ) ; + + if ( SG_SIGN ( le[0]*a[0] + le[1]*a[1] + le[2] ) != + SG_SIGN ( le[0]*p[0] + le[1]*p[1] + le[2] ) ) return false ; + + sgMake2DLine ( le, c, a ) ; + + if ( SG_SIGN ( le[0]*b[0] + le[1]*b[1] + le[2] ) != + SG_SIGN ( le[0]*p[0] + le[1]*p[1] + le[2] ) ) return false ; + + return true ; +} + + + +bool sgPointInTriangle2 ( sgVec2 point, sgVec2 tri[3] ) +{ + SGfloat min, max ; + + /* Early-out if outside bounding box */ + + SG_MIN_MAX3 ( min, max, tri[0][0], tri[1][0], tri[2][0] ); + if ( point[0] < min || point[0] > max ) return false ; + + SG_MIN_MAX3 ( min, max, tri[0][1], tri[1][1], tri[2][1] ); + if ( point[1] < min || point[1] > max ) return false ; + + sgVec2 a, b, c ; + + sgCopyVec2 ( a, tri[0] ) ; + sgCopyVec2 ( b, tri[1] ) ; + sgCopyVec2 ( c, tri[2] ) ; + + /* + Check if the intersection point is on the same side of + each edge of the triangle as the remaining point. + */ + + sgVec3 le ; /* Line Equation */ + + sgMake2DLine ( le, a, b ) ; + + if ( SG_SIGN ( le[0]*c[0] + le[1]*c[1] + le[2] ) != + SG_SIGN ( le[0]*point[0] + le[1]*point[1] + le[2] ) ) return false ; + + sgMake2DLine ( le, b, c ) ; + + if ( SG_SIGN ( le[0]*a[0] + le[1]*a[1] + le[2] ) != + SG_SIGN ( le[0]*point[0] + le[1]*point[1] + le[2] ) ) return false ; + + sgMake2DLine ( le, c, a ) ; + + if ( SG_SIGN ( le[0]*b[0] + le[1]*b[1] + le[2] ) != + SG_SIGN ( le[0]*point[0] + le[1]*point[1] + le[2] ) ) return false ; + + return true ; +} + + diff --git a/src/sg/sgPerlinNoise.cxx b/src/sg/sgPerlinNoise.cxx new file mode 100644 index 0000000..bd7cbb0 --- /dev/null +++ b/src/sg/sgPerlinNoise.cxx @@ -0,0 +1,315 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + + +/* + sgPerlinNoise + + This is a class to implement coherent noise over 1, 2, or 3 + dimensions. The implementation is based on the Ken Perlin's + noise function and borrows heavily from Matt Zucker's implementation. +*/ + + +#include +#include +#include +#include "sg.h" + +static unsigned int *permTable = NULL ; + + +static void initPermTable () +{ + int i ; + + if ( permTable != NULL ) + return ; + + permTable = new unsigned int [ SG_PERLIN_NOISE_WRAP_INDEX * 2 + 2 ] ; + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX ; i++ ) + permTable[i] = i ; + + /* Shuffle permutation table up to SG_PERLIN_NOISE_WRAP_INDEX */ + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX ; i++ ) + { + int j = rand () & SG_PERLIN_NOISE_MOD_MASK ; + + int temp = permTable [ i ] ; + permTable [ i ] = permTable [ j ] ; + permTable [ j ] = temp ; + } + + /* + Add the rest of the table entries in, duplicating + indices and entries so that they can effectively be indexed + by unsigned chars. I think. Ask Perlin what this is really doing. + */ + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX + 2 ; i++ ) + permTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ] = permTable [ i ] ; +} + + + + + + +/* + S curve is (3x^2 - 2x^3) because it's quick to calculate + though -cos(x * PI) * 0.5 + 0.5 would work too +*/ + +inline SGfloat easeCurve ( SGfloat t ) { return (SGfloat) (t * t * (3.0 - 2.0 * t)) ; } + +inline SGfloat dot2 ( SGfloat rx, SGfloat ry, sgVec2 q ) +{ + return rx * q[0] + ry * q[1] ; +} + +inline SGfloat dot3 ( SGfloat rx, SGfloat ry, SGfloat rz, sgVec3 q ) +{ + return rx * q[0] + ry * q[1] + rz * q[2] ; +} + + +inline SGfloat randFloat () +{ + return (SGfloat)(( rand() % ( SG_PERLIN_NOISE_WRAP_INDEX + + SG_PERLIN_NOISE_WRAP_INDEX ) ) - + SG_PERLIN_NOISE_WRAP_INDEX ) / + SG_PERLIN_NOISE_WRAP_INDEX ; +} + + +inline void setupValues ( int axis, int *g0, int *g1, + SGfloat *d0, SGfloat *d1, SGfloat *pos ) +{ + SGfloat t = pos[axis] + SG_PERLIN_NOISE_LARGE_PWR2 ; + int it = (int) t ; + + *g0 = it & SG_PERLIN_NOISE_MOD_MASK ; + *g1 = (*g0 + 1) & SG_PERLIN_NOISE_MOD_MASK ; + *d0 = t - it ; + *d1 = *d0 - 1.0f ; +} + + + +SGfloat sgPerlinNoise_1D::getNoise ( SGfloat pos ) +{ + int gridL, gridR ; + SGfloat distL, distR ; + + /* + Find out neighboring grid points to pos and + signed distances from pos to them. + */ + + setupValues ( 0, &gridL, &gridR, + &distL , &distR , &pos ) ; + + return sgLerp ( distL * gradTable [ permTable [ gridL ] ], + distR * gradTable [ permTable [ gridR ] ], + easeCurve ( distL ) ) ; +} + + + +SGfloat sgPerlinNoise_2D::getNoise ( sgVec2 pos ) +{ + int gridL, gridR, + gridD, gridU ; + + SGfloat distL, distR, + distD, distU ; + + /* + Find out neighboring grid points to pos and + signed distances from pos to them. + */ + + setupValues ( 0, &gridL, &gridR, &distL, &distR, pos ) ; + setupValues ( 1, &gridD, &gridU, &distD, &distU, pos ) ; + + /* + Generate some temporary indexes associated with the left + and right grid values + */ + + int indexL = permTable [ gridL ] ; + int indexR = permTable [ gridR ] ; + + /* Generate indexes in the permutation table for all 4 corners */ + + int indexLD = permTable [ indexL + gridD ] ; + int indexRD = permTable [ indexR + gridD ] ; + int indexLU = permTable [ indexL + gridU ] ; + int indexRU = permTable [ indexR + gridU ] ; + + /* Get the s curves at the proper values */ + + SGfloat sX = easeCurve ( distL ) ; + SGfloat sY = easeCurve ( distD ) ; + + return sgLerp ( + sgLerp ( + dot2 ( distL, distD, gradTable [ indexLD ] ), + dot2 ( distR, distD, gradTable [ indexRD ] ), sX ), + sgLerp ( + dot2 ( distL, distU, gradTable [ indexLU ] ), + dot2 ( distR, distU, gradTable [ indexRU ] ), sX ), + sY ) ; +} + + +SGfloat sgPerlinNoise_3D::getNoise ( sgVec3 pos ) +{ + int gridL, gridR, + gridD, gridU, + gridB, gridF ; + + SGfloat distL, distR, + distD, distU, + distB, distF ; + + /* + Find out neighboring grid points to pos and signed + distances from pos to them. + */ + + setupValues ( 0, &gridL, &gridR, &distL, &distR, pos ) ; + setupValues ( 1, &gridD, &gridU, &distD, &distU, pos ) ; + setupValues ( 2, &gridB, &gridF, &distB, &distF, pos ) ; + + int indexL = permTable [ gridL ] ; + int indexR = permTable [ gridR ] ; + + int indexLD = permTable [ indexL + gridD ] ; + int indexRD = permTable [ indexR + gridD ] ; + int indexLU = permTable [ indexL + gridU ] ; + int indexRU = permTable [ indexR + gridU ] ; + + SGfloat sX = easeCurve ( distL ) ; + SGfloat sY = easeCurve ( distD ) ; + SGfloat sZ = easeCurve ( distB ) ; + + return sgLerp ( + sgLerp ( + sgLerp ( + dot3 ( distL, distD, distB, gradTable [ indexLD + gridB ] ), + dot3 ( distR, distD, distB, gradTable [ indexRD + gridB ] ), sX ), + sgLerp ( + dot3 ( distL, distU, distB, gradTable [ indexLU + gridB ] ), + dot3 ( distR, distU, distB, gradTable [ indexRU + gridB ] ), sX ), + sY ), + sgLerp ( + sgLerp ( + dot3 ( distL, distD, distF, gradTable [ indexLD + gridF ] ), + dot3 ( distR, distD, distF, gradTable [ indexRD + gridF ] ), sX ), + sgLerp ( + dot3 ( distL, distU, distF, gradTable [ indexLU + gridF ] ), + dot3 ( distR, distU, distF, gradTable [ indexRU + gridF ] ), sX ), + sY ), + sZ ) ; +} + + + + +sgPerlinNoise_1D::sgPerlinNoise_1D () +{ + regenerate () ; +} + + +void sgPerlinNoise_1D::regenerate () +{ + int i ; + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX ; i++ ) + gradTable [ i ] = randFloat () ; + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX + 2 ; i++ ) + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ] = gradTable [ i ] ; + + initPermTable () ; +} + + + +sgPerlinNoise_2D::sgPerlinNoise_2D () +{ + regenerate () ; +} + + +void sgPerlinNoise_2D::regenerate () +{ + int i ; + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX ; i++ ) + { + sgSetVec2 ( gradTable [ i ], randFloat(), randFloat() ) ; + sgNormalizeVec2 ( gradTable [ i ] ) ; + } + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX + 2 ; i++ ) + { + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ][ 0 ] = gradTable [ i ][ 0 ] ; + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ][ 1 ] = gradTable [ i ][ 1 ] ; + } + + initPermTable () ; +} + + + +sgPerlinNoise_3D::sgPerlinNoise_3D () +{ + regenerate () ; +} + + +void sgPerlinNoise_3D::regenerate () +{ + int i ; + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX ; i++ ) + { + sgSetVec3 ( gradTable [ i ], randFloat(), randFloat(), randFloat() ) ; + sgNormalizeVec3 ( gradTable [ i ] ) ; + } + + for ( i = 0 ; i < SG_PERLIN_NOISE_WRAP_INDEX + 2 ; i++ ) + { + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ][ 0 ] = gradTable [ i ][ 0 ] ; + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ][ 1 ] = gradTable [ i ][ 1 ] ; + gradTable [ SG_PERLIN_NOISE_WRAP_INDEX + i ][ 2 ] = gradTable [ i ][ 2 ] ; + } + + initPermTable () ; +} + + diff --git a/src/sg/sgd.cxx b/src/sg/sgd.cxx new file mode 100644 index 0000000..46b4c5c --- /dev/null +++ b/src/sg/sgd.cxx @@ -0,0 +1,2064 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sgd.cxx 1944 2004-08-05 01:07:09Z puggles $ +*/ + + +#include "sg.h" + +sgdVec3 _sgdGravity = { 0.0f, 0.0f, -9.8f } ; + +void sgdVectorProductVec3 ( sgdVec3 dst, const sgdVec3 a, const sgdVec3 b ) +{ + dst[0] = a[1] * b[2] - a[2] * b[1] ; + dst[1] = a[2] * b[0] - a[0] * b[2] ; + dst[2] = a[0] * b[1] - a[1] * b[0] ; +} + +inline SGDfloat _sgdClampToUnity ( const SGDfloat x ) +{ + if ( x > SGD_ONE ) return SGD_ONE ; + if ( x < -SGD_ONE ) return -SGD_ONE ; + return x ; +} + +int sgdCompare3DSqdDist( const sgdVec3 v1, const sgdVec3 v2, const SGDfloat sqd_dist ) +{ + sgdVec3 tmp ; + + sgdSubVec3 ( tmp, v2, v1 ) ; + + SGDfloat sqdist = tmp[0] * tmp[0] + tmp[1] * tmp[1] + tmp[2] * tmp[2] ; + + if ( sqdist > sqd_dist ) return 1 ; + if ( sqdist < sqd_dist ) return -1 ; + return 0 ; +} + +void sgdMakeRotMat4( sgdMat4 mat, const SGDfloat angle, const sgdVec3 axis ) +{ + sgdVec3 ax ; + sgdNormalizeVec3 ( ax, axis ) ; + + SGDfloat temp_angle = angle * SGD_DEGREES_TO_RADIANS ; + SGDfloat s = sin ( temp_angle ) ; + SGDfloat c = cos ( temp_angle ) ; + SGDfloat t = SGD_ONE - c ; + + mat[0][0] = t * ax[0] * ax[0] + c ; + mat[0][1] = t * ax[0] * ax[1] + s * ax[2] ; + mat[0][2] = t * ax[0] * ax[2] - s * ax[1] ; + mat[0][3] = SGD_ZERO ; + + mat[1][0] = t * ax[1] * ax[0] - s * ax[2] ; + mat[1][1] = t * ax[1] * ax[1] + c ; + mat[1][2] = t * ax[1] * ax[2] + s * ax[0] ; + mat[1][3] = SGD_ZERO ; + + mat[2][0] = t * ax[2] * ax[0] + s * ax[1] ; + mat[2][1] = t * ax[2] * ax[1] - s * ax[0] ; + mat[2][2] = t * ax[2] * ax[2] + c ; + mat[2][3] = SGD_ZERO ; + + mat[3][0] = SGD_ZERO ; + mat[3][1] = SGD_ZERO ; + mat[3][2] = SGD_ZERO ; + mat[3][3] = SGD_ONE ; +} + + + + +void sgdMakePickMatrix( sgdMat4 mat, sgdFloat x, sgdFloat y, + sgdFloat width, sgdFloat height, sgdVec4 viewport ) +{ + sgdFloat sx = viewport[2] / width ; + sgdFloat sy = viewport[3] / height ; + sgdFloat tx = ( viewport[2] + SGD_TWO * (viewport[0] - x) ) / width ; + sgdFloat ty = ( viewport[3] + SGD_TWO * (viewport[1] - y) ) / height ; + + mat[0][0] = sx ; + mat[0][1] = SGD_ZERO ; + mat[0][2] = SGD_ZERO ; + mat[0][3] = SGD_ZERO ; + + mat[1][0] = SGD_ZERO ; + mat[1][1] = sy ; + mat[1][2] = SGD_ZERO ; + mat[1][3] = SGD_ZERO ; + + mat[2][0] = SGD_ZERO ; + mat[2][1] = SGD_ZERO ; + mat[2][2] = SGD_ONE ; + mat[2][3] = SGD_ZERO ; + + mat[3][0] = tx ; + mat[3][1] = ty ; + mat[3][2] = SGD_ZERO ; + mat[3][3] = SGD_ONE ; +} + + + +void sgdMakeLookAtMat4 ( sgdMat4 dst, const sgdVec3 eye, + const sgdVec3 center, + const sgdVec3 up ) +{ + // Caveats: + // 1) In order to compute the line of sight, the eye point must not be equal + // to the center point. + // 2) The up vector must not be parallel to the line of sight from the eye + // to the center point. + + /* Compute the direction vectors */ + sgdVec3 x,y,z; + + /* Y vector = center - eye */ + sgdSubVec3 ( y, center, eye ) ; + + /* Z vector = up */ + sgdCopyVec3 ( z, up ) ; + + /* X vector = Y cross Z */ + sgdVectorProductVec3 ( x, y, z ) ; + + /* Recompute Z = X cross Y */ + sgdVectorProductVec3 ( z, x, y ) ; + + /* Normalize everything */ + sgdNormaliseVec3 ( x ) ; + sgdNormaliseVec3 ( y ) ; + sgdNormaliseVec3 ( z ) ; + + /* Build the matrix */ + sgdSetVec4 ( dst[0], x[0], x[1], x[2], SGD_ZERO ) ; + sgdSetVec4 ( dst[1], y[0], y[1], y[2], SGD_ZERO ) ; + sgdSetVec4 ( dst[2], z[0], z[1], z[2], SGD_ZERO ) ; + sgdSetVec4 ( dst[3], eye[0], eye[1], eye[2], SGD_ONE ) ; +} + +// -dw- inconsistent linkage! + +sgdFloat sgdTriArea( sgdVec3 p0, sgdVec3 p1, sgdVec3 p2 ) +{ + /* + From comp.graph.algorithms FAQ + + 2A(P) = abs(N.(sum_{i=0}^{n-1}(v_i x v_{i+1}))) + This is an optimized version for a triangle + but easily extended for planar polygon's with more sides + by passing in the number of sides and the vv array + sgdTriArea( int nsides, float **vv ) + and changing the normal calculation and the for loop appropriately + sgdMakeNormal( norm, vv[0], vv[1], vv[2] ) + for( int i=0; i 0.99999 ) + deltaAngle = SGD_PI*0.5; + else + deltaAngle = (SGDfloat)asin((double)myNorm); + + // deltaAngle is in the range -SGD_PI*0.5 to +SGD_PI*0.5 here + // However, the correct result could also be + // deltaAngleS := pi - deltaAngle + // Please note that: + // cos(deltaAngleS)=cos(pi-deltaAngle)=-cos(deltaAngle) + // So, the question is whether + or - cos(deltaAngle) + // is sgdScalarProductVec3(first, second) + + if ( deltaAngle < 0 ) + deltaAngle = deltaAngle + 2*SGD_PI; // unnessecary? + + SProduct = sgdScalarProductVec3(first, second); + myCos = (SGDfloat) cos(deltaAngle); + + abs1 = SProduct - myCos; + abs2 = SProduct + myCos; + + if ( abs1 < 0 ) abs1 = -abs1 ; + if ( abs2 < 0 ) abs2 = -abs2 ; + + assert( (abs1 < 0.1) || (abs2 < 0.1) ) ; + + if ( abs2 < abs1 ) + { + // deltaAngleS is the correct result + + if ( deltaAngle <= SGD_PI ) + deltaAngle = SGD_PI - deltaAngle ; + else + deltaAngle = 3*SGD_PI - deltaAngle ; + } + + assert ( deltaAngle >= 0.0 ) ; + assert ( deltaAngle <= 2.0*SGD_PI ) ; + + return deltaAngle * SGD_RADIANS_TO_DEGREES ; +} + + +SGDfloat sgdAngleBetweenVec3 ( sgdVec3 v1, sgdVec3 v2, sgdVec3 normal ) +{ + // nornmal has to be normalized. + sgdVec3 nv1, nv2 ; + + sgdNormalizeVec3 ( nv1, v1 ) ; + sgdNormalizeVec3 ( nv2, v2 ) ; + return sgdAngleBetweenNormalizedVec3 ( nv1, nv2, normal ) ; +} + + +/*********************\ +* sgdBox routines * +\*********************/ + + +void sgdBox::extend ( const sgdVec3 v ) +{ + if ( isEmpty () ) + { + sgdCopyVec3 ( min, v ) ; + sgdCopyVec3 ( max, v ) ; + } + else + { + if ( v[0] < min[0] ) min[0] = v[0] ; + if ( v[1] < min[1] ) min[1] = v[1] ; + if ( v[2] < min[2] ) min[2] = v[2] ; + if ( v[0] > max[0] ) max[0] = v[0] ; + if ( v[1] > max[1] ) max[1] = v[1] ; + if ( v[2] > max[2] ) max[2] = v[2] ; + } +} + + +void sgdBox::extend ( const sgdBox *b ) +{ + if ( b -> isEmpty () ) + return ; + + if ( isEmpty () ) + { + sgdCopyVec3 ( min, b->getMin() ) ; + sgdCopyVec3 ( max, b->getMax() ) ; + } + else + { + extend ( b->getMin() ) ; + extend ( b->getMax() ) ; + } +} + + +void sgdBox::extend ( const sgdSphere *s ) +{ + if ( s -> isEmpty () ) + return ; + + /* + In essence, this extends around a box around the sphere - which + is still a perfect solution because both boxes are axially aligned. + */ + + sgdVec3 x ; + + sgdSetVec3 ( x, s->getCenter()[0]+s->getRadius(), + s->getCenter()[1]+s->getRadius(), + s->getCenter()[2]+s->getRadius() ) ; + extend ( x ) ; + + sgdSetVec3 ( x, s->getCenter()[0]-s->getRadius(), + s->getCenter()[1]-s->getRadius(), + s->getCenter()[2]-s->getRadius() ) ; + extend ( x ) ; +} + + +int sgdBox::intersects ( const sgdVec4 plane ) const +{ + /* + Save multiplies by not redoing Ax+By+Cz+D for each point. + */ + + SGDfloat Ax_min = plane[0] * min[0] ; + SGDfloat By_min = plane[1] * min[1] ; + SGDfloat Cz_min_plus_D = plane[2] * min[2] + plane[3] ; + + SGDfloat Ax_max = plane[0] * max[0] ; + SGDfloat By_max = plane[1] * max[1] ; + SGDfloat Cz_max_plus_D = plane[2] * max[2] + plane[3] ; + + /* + Count the number of vertices on the positive side of the plane. + */ + + int count = ( Ax_min + By_min + Cz_min_plus_D > SGD_ZERO ) + + ( Ax_min + By_min + Cz_max_plus_D > SGD_ZERO ) + + ( Ax_min + By_max + Cz_min_plus_D > SGD_ZERO ) + + ( Ax_min + By_max + Cz_max_plus_D > SGD_ZERO ) + + ( Ax_max + By_min + Cz_min_plus_D > SGD_ZERO ) + + ( Ax_max + By_min + Cz_max_plus_D > SGD_ZERO ) + + ( Ax_max + By_max + Cz_min_plus_D > SGD_ZERO ) + + ( Ax_max + By_max + Cz_max_plus_D > SGD_ZERO ) ; + + /* + The plane intersects the box unless all 8 are positive + or none of them are positive. + */ + + return count != 0 && count != 8 ; +} + + + +/**********************\ +* sgdSphere routines * +\**********************/ + +void sgdSphere::extend ( const sgdVec3 v ) +{ + if ( isEmpty () ) + { + sgdCopyVec3 ( center, v ) ; + radius = SGD_ZERO ; + return ; + } + + SGDfloat d = sgdDistanceVec3 ( center, v ) ; + + if ( d <= radius ) /* Point is already inside sphere */ + return ; + + SGDfloat new_radius = (radius + d) / SGD_TWO ; /* Grow radius */ + + SGDfloat ratio = (new_radius - radius) / d ; + + center[0] += (v[0]-center[0]) * ratio ; /* Move center */ + center[1] += (v[1]-center[1]) * ratio ; + center[2] += (v[2]-center[2]) * ratio ; + + radius = new_radius ; +} + + +void sgdSphere::extend ( const sgdBox *b ) +{ + if ( b -> isEmpty () ) + return ; + + if ( isEmpty() ) + { + sgdAddVec3 ( center, b->getMin(), b->getMax() ) ; + sgdScaleVec3 ( center, SGD_HALF ) ; + radius = sgdDistanceVec3 ( center, b->getMax() ) ; + return ; + } + + /* + I can't think of a faster way to get an + utterly minimal sphere. + + The tighter algorithm:- enclose each + of eight vertices of the box in turn - it + looks like being pretty costly. + [8 sqrt()'s] + + The looser algorithm:- enclose the box + with an empty sphere and then do a + sphere-extend-sphere. This algorithm + does well for close-to-cube boxes, but + makes very poor spheres for long, thin + boxes. + [2 sqrt()'s] + */ + +#ifdef DONT_REALLY_NEED_A_TIGHT_SPHERE_EXTEND_BOX + + /* LOOSER/FASTER sphere-around-sphere-around-box */ + sgdSphere s ; + s.empty () ; + s.enclose ( b ) ; /* Fast because s is empty */ + enclose ( s ) ; + +#else + + /* TIGHTER/EXPENSIVE sphere-around-eight-points */ + sgdVec3 x ; + extend ( b->getMin() ) ; + sgdSetVec3 ( x, b->getMin()[0],b->getMin()[1],b->getMax()[2] ) ; extend ( x ) ; + sgdSetVec3 ( x, b->getMin()[0],b->getMax()[1],b->getMin()[2] ) ; extend ( x ) ; + sgdSetVec3 ( x, b->getMin()[0],b->getMax()[1],b->getMax()[2] ) ; extend ( x ) ; + sgdSetVec3 ( x, b->getMax()[0],b->getMin()[1],b->getMin()[2] ) ; extend ( x ) ; + sgdSetVec3 ( x, b->getMax()[0],b->getMin()[1],b->getMax()[2] ) ; extend ( x ) ; + sgdSetVec3 ( x, b->getMax()[0],b->getMax()[1],b->getMin()[2] ) ; extend ( x ) ; + extend ( b->getMax() ) ; +#endif +} + + +void sgdSphere::extend ( const sgdSphere *s ) +{ + if ( s->isEmpty () ) + return ; + + if ( isEmpty () ) + { + sgdCopyVec3 ( center, s->getCenter() ) ; + radius = s->getRadius() ; + return ; + } + + /* + d == The distance between the sphere centers + */ + + SGDfloat d = sgdDistanceVec3 ( center, s->getCenter() ) ; + + if ( d + s->getRadius() <= radius ) /* New sphere is already inside this one */ + return ; + + if ( d + radius <= s->getRadius() ) /* New sphere completely contains this one */ + { + sgdCopyVec3 ( center, s->getCenter() ) ; + radius = s->getRadius() ; + return ; + } + + /* + Build a new sphere that completely contains the other two: + + The center point lies halfway along the line between + the furthest points on the edges of the two spheres. + Computing those two points is ugly - so we'll use similar + triangles + */ + + SGDfloat new_radius = (radius + d + s->getRadius() ) / SGD_TWO ; + + SGDfloat ratio = ( new_radius - radius ) / d ; + + center[0] += ( s->getCenter()[0] - center[0] ) * ratio ; + center[1] += ( s->getCenter()[1] - center[1] ) * ratio ; + center[2] += ( s->getCenter()[2] - center[2] ) * ratio ; + radius = new_radius ; +} + + +int sgdSphere::intersects ( const sgdBox *b ) const +{ + sgdVec3 closest ; + + if ( b->getMin()[0] > center[0] ) closest[0] = b->getMin()[0] ; else + if ( b->getMax()[0] < center[0] ) closest[0] = b->getMax()[0] ; else + closest[0] = center[0] ; + + if ( b->getMin()[1] > center[1] ) closest[1] = b->getMin()[1] ; else + if ( b->getMax()[1] < center[1] ) closest[1] = b->getMax()[1] ; else + closest[1] = center[1] ; + + if ( b->getMin()[2] > center[2] ) closest[2] = b->getMin()[2] ; else + if ( b->getMax()[2] < center[2] ) closest[2] = b->getMax()[2] ; else + closest[2] = center[2] ; + + return sgdCompare3DSqdDist ( closest, center, sgdSquare ( radius ) ) <= 0 ; +} + + +/************************\ +* sgdFrustum routines * +\************************/ + +void sgdFrustum::update () +{ + if ( fabs ( ffar - nnear ) < 0.1 ) + { + ulSetError ( UL_WARNING, "sgdFrustum: Can't support depth of view <0.1 units."); + return ; + } + + if ( hfov != SGD_ZERO && vfov != SGD_ZERO ) + { + if ( fabs ( hfov ) < 0.1 || fabs ( vfov ) < 0.1 ) + { + ulSetError ( UL_WARNING, ortho ? + "sgFrustum: Can't support width or height <0.1 units." : + "sgFrustum: Can't support fields of view narrower than 0.1 degrees." ) ; + return ; + } + + if ( ortho ) + { + right = SGD_HALF * hfov ; + top = SGD_HALF * vfov ; + } + else + { + right = nnear * tan ( hfov * SGD_DEGREES_TO_RADIANS / SGD_TWO ) ; + top = nnear * tan ( vfov * SGD_DEGREES_TO_RADIANS / SGD_TWO ) ; + } + + left = -right ; + bot = -top ; + } + + + /* Compute the projection matrix */ + + SGDfloat width = right - left ; + SGDfloat height = top - bot ; + SGDfloat depth = ffar - nnear ; + + if ( ortho ) + { + /* orthographic */ + + mat[0][0] = SGD_TWO / width ; + mat[0][1] = SGD_ZERO ; + mat[0][2] = SGD_ZERO ; + mat[0][3] = SGD_ZERO ; + + mat[1][0] = SGD_ZERO ; + mat[1][1] = SGD_TWO / height ; + mat[1][2] = SGD_ZERO ; + mat[1][3] = SGD_ZERO ; + + mat[2][0] = SGD_ZERO ; + mat[2][1] = SGD_ZERO ; + mat[2][2] = -SGD_TWO / depth ; + mat[2][3] = SGD_ZERO ; + + mat[3][0] = -( left + right ) / width ; + mat[3][1] = -( bot + top ) / height ; + mat[3][2] = -( nnear + ffar ) / depth ; + mat[3][3] = SGD_ONE ; + } + else + { + /* perspective */ + + mat[0][0] = SGD_TWO * nnear / width ; + mat[0][1] = SGD_ZERO ; + mat[0][2] = SGD_ZERO ; + mat[0][3] = SGD_ZERO ; + + mat[1][0] = SGD_ZERO ; + mat[1][1] = SGD_TWO * nnear / height ; + mat[1][2] = SGD_ZERO ; + mat[1][3] = SGD_ZERO ; + + mat[2][0] = ( right + left ) / width ; + mat[2][1] = ( top + bot ) / height ; + mat[2][2] = -( ffar + nnear ) / depth ; + mat[2][3] = -SGD_ONE ; + + mat[3][0] = SGD_ZERO ; + mat[3][1] = SGD_ZERO ; + mat[3][2] = -SGD_TWO * nnear * ffar / depth ; + mat[3][3] = SGD_ZERO ; + } + + + /* + * The clip planes are derived from the projection matrix. + * + * After projection (in clip coordinates), the clip planes are simply: + * + * left: ( 1, 0, 0, 1 ) + * right: ( -1, 0, 0, 1 ) + * bottom: ( 0, 1, 0, 1 ) + * top: ( 0, -1, 0, 1 ) + * near: ( 0, 0, 1, 1 ) + * far: ( 0, 0, -1, 1 ) + * + * These can easily be transformed *backwards* by + * multiplying by the transposed projection matrix, i.e: + * + * ( A ) ( A') + * ( B ) = mat^T ( B') + * ( C ) ( C') + * ( D ) ( D') + * + * where (A',B',C',D') represents a plane in clip coordinates, + * and (A,B,C,D) is the same plane expressed in eye coordinates. + */ + + sgdSetVec4( plane[ SG_LEFT_PLANE ], SGD_ONE, SGD_ZERO, SGD_ZERO, SGD_ONE ); + sgdSetVec4( plane[ SG_RIGHT_PLANE ], -SGD_ONE, SGD_ZERO, SGD_ZERO, SGD_ONE ); + sgdSetVec4( plane[ SG_BOT_PLANE ], SGD_ZERO, SGD_ONE, SGD_ZERO, SGD_ONE ); + sgdSetVec4( plane[ SG_TOP_PLANE ], SGD_ZERO, -SGD_ONE, SGD_ZERO, SGD_ONE ); + sgdSetVec4( plane[ SG_NEAR_PLANE ], SGD_ZERO, SGD_ZERO, SGD_ONE, SGD_ONE ); + sgdSetVec4( plane[ SG_FAR_PLANE ], SGD_ZERO, SGD_ZERO, -SGD_ONE, SGD_ONE ); + + for ( int i = 0 ; i < 6 ; i++ ) + { + sgdVec4 tmp ; + + for ( int j = 0 ; j < 4 ; j++ ) + tmp[j] = sgdScalarProductVec4 ( plane[i], mat[j] ) ; + + sgdScaleVec4 ( plane[i], tmp, SGD_ONE / sgdLengthVec3 ( tmp ) ) ; + } +} + + + +#define OC_LEFT_SHIFT 0 +#define OC_RIGHT_SHIFT 1 +#define OC_TOP_SHIFT 2 +#define OC_BOT_SHIFT 3 +#define OC_NEAR_SHIFT 4 +#define OC_FAR_SHIFT 5 + +#define OC_ALL_ON_SCREEN 0x3F +#define OC_OFF_TRF ((1<= -tmp[3] ) << OC_LEFT_SHIFT ) | + (( tmp[1] <= tmp[3] ) << OC_TOP_SHIFT ) | + (( tmp[1] >= -tmp[3] ) << OC_BOT_SHIFT ) | + (( tmp[2] <= tmp[3] ) << OC_FAR_SHIFT ) | + (( tmp[2] >= -tmp[3] ) << OC_NEAR_SHIFT ) ; +} + +int sgdFrustum::contains ( const sgdVec3 pt ) const +{ + return getOutcode ( pt ) == OC_ALL_ON_SCREEN ; +} + + +int sgdFrustum::contains ( const sgdSphere *s ) const +{ + + const SGDfloat *center = s->getCenter() ; + const SGDfloat radius = s->getRadius() ; + + /* + Lop off half the database (roughly) with a quick near-plane test - and + lop off a lot more with a quick far-plane test + */ + + if ( -center[2] + radius < nnear || -center[2] - radius > ffar ) + return SG_OUTSIDE ; + + /* + OK, so the sphere lies between near and far. + + Measure the distance of the center point from the four sides of the frustum, + if it's outside by more than the radius then it's history. + + It's tempting to do a quick test to see if the center point is + onscreen using sgdFrustumContainsPt - but that takes a matrix transform + which is 16 multiplies and 12 adds - versus this test which does the + whole task using only 12 multiplies and 8 adds. + */ + + /* + A few operations are saved by observing that certain values in the plane + equations are zero or one. These are specific to orthographic and perspective + projections respectively. + */ + + SGDfloat sp1, sp2, sp3, sp4 ; + + if ( ortho ) + { + /* + left: ( 1, 0, 0, x ) + right: ( -1, 0, 0, x ) + bottom: ( 0, 1, 0, x ) + top: ( 0, -1, 0, x ) + */ + sp1 = plane[ SG_LEFT_PLANE ][3] + center[0] ; + sp2 = plane[ SG_RIGHT_PLANE ][3] - center[0] ; + sp3 = plane[ SG_BOT_PLANE ][3] + center[1] ; + sp4 = plane[ SG_TOP_PLANE ][3] - center[1] ; + } + else + { + /* + left: ( x, 0, x, 0 ) + right: ( x, 0, x, 0 ) + bottom: ( 0, x, x, 0 ) + top: ( 0, x, x, 0 ) + */ + sp1 = plane[ SG_LEFT_PLANE ][0] * center[0] + plane[ SG_LEFT_PLANE ][2] * center[2] ; + sp2 = plane[ SG_RIGHT_PLANE ][0] * center[0] + plane[ SG_RIGHT_PLANE ][2] * center[2] ; + sp3 = plane[ SG_BOT_PLANE ][1] * center[1] + plane[ SG_BOT_PLANE ][2] * center[2] ; + sp4 = plane[ SG_TOP_PLANE ][1] * center[1] + plane[ SG_TOP_PLANE ][2] * center[2] ; + } + + /* + Note: in the general case, we would have to do: + + sp1 = sgdScalarProductVec3 ( left_plane, center ) + left_plane[3] ; + sp2 = sgdScalarProductVec3 ( right_plane, center ) + right_plane[3] ; + ... + sp6 = sgdScalarProductVec3 ( far_plane, center ) + far_plane[3] ; + */ + + + if ( -sp1 > radius || -sp2 > radius || -sp3 > radius || -sp4 > radius ) + return SG_OUTSIDE ; + + /* + If it's inside by more than the radius then it's *completely* inside + and we can save time elsewhere if we know that for sure. + */ + + if ( sp1 >= radius && sp2 >= radius && sp3 >= radius && sp4 >= radius + && -center[2] - radius >= nnear && -center[2] + radius <= ffar ) + return SG_INSIDE ; + + return SG_STRADDLE ; +} + + +int sgdFrustum::contains ( const sgdBox *b ) const +{ + sgdVec3 p[8] = { + { b->getMin()[0], b->getMin()[1], b->getMin()[2] }, + { b->getMax()[0], b->getMin()[1], b->getMin()[2] }, + { b->getMin()[0], b->getMax()[1], b->getMin()[2] }, + { b->getMax()[0], b->getMax()[1], b->getMin()[2] }, + { b->getMin()[0], b->getMin()[1], b->getMax()[2] }, + { b->getMax()[0], b->getMin()[1], b->getMax()[2] }, + { b->getMin()[0], b->getMax()[1], b->getMax()[2] }, + { b->getMax()[0], b->getMax()[1], b->getMax()[2] }, + } ; + + int all = -1 ; + int one = 0 ; + + for (int i = 0 ; i < 8 ; i++ ) + { + int tmp = ~ getOutcode ( p[i] ) ; + all &= tmp ; + one |= tmp ; + } + + return ( all ? SG_OUTSIDE : one ? SG_STRADDLE : SG_INSIDE ) ; +} + + + + +SGDfloat sgdDistSquaredToLineVec3 ( const sgdLine3 line, const sgdVec3 pnt ) +{ + sgdVec3 r ; sgdSubVec3 ( r, pnt, line.point_on_line ) ; + + return sgdScalarProductVec3 ( r, r ) - + sgdScalarProductVec3 ( r, line.direction_vector ) ; +} + + + +SGDfloat sgdDistSquaredToLineSegmentVec3 ( const sgdLineSegment3 line, + const sgdVec3 pnt ) +{ + sgdVec3 v ; sgdSubVec3 ( v, line.b, line.a ) ; + sgdVec3 r1 ; sgdSubVec3 ( r1, pnt, line.a ) ; + + SGDfloat r1_dot_v = sgdScalarProductVec3 ( r1, v ) ; + + if ( r1_dot_v <= 0 ) /* Off the "A" end */ + return sgdScalarProductVec3 ( r1, r1 ) ; + + sgdVec3 r2 ; sgdSubVec3 ( r2, pnt, line.b ) ; + + SGDfloat r2_dot_v = sgdScalarProductVec3 ( r2, v ) ; + + if ( r2_dot_v >= 0 ) /* Off the "B" end */ + return sgdScalarProductVec3 ( r2, r2 ) ; + + /* Closest point on line is on the line segment */ + + return sgdScalarProductVec3 ( r1, r1 ) - r1_dot_v * r1_dot_v / sgdScalarProductVec3 ( v, v ) ; +} + + + +void sgdMakeCoordMat4 ( sgdMat4 m, const SGDfloat x, const SGDfloat y, const SGDfloat z, const SGDfloat h, const SGDfloat p, const SGDfloat r ) +{ + SGDfloat ch, sh, cp, sp, cr, sr, srsp, crsp, srcp ; + + if ( h == SGD_ZERO ) + { + ch = SGD_ONE ; + sh = SGD_ZERO ; + } + else + { + sh = sgdSin( h ) ; + ch = sgdCos( h ) ; + } + + if ( p == SGD_ZERO ) + { + cp = SGD_ONE ; + sp = SGD_ZERO ; + } + else + { + sp = sgdSin( p ) ; + cp = sgdCos( p ) ; + } + + if ( r == SGD_ZERO ) + { + cr = SGD_ONE ; + sr = SGD_ZERO ; + srsp = SGD_ZERO ; + srcp = SGD_ZERO ; + crsp = sp ; + } + else + { + sr = sgdSin( r ) ; + cr = sgdCos( r ) ; + srsp = sr * sp ; + crsp = cr * sp ; + srcp = sr * cp ; + } + + m[0][0] = ch * cr - sh * srsp ; + m[1][0] = -sh * cp ; + m[2][0] = sr * ch + sh * crsp ; + m[3][0] = x ; + + m[0][1] = cr * sh + srsp * ch ; + m[1][1] = ch * cp ; + m[2][1] = sr * sh - crsp * ch ; + m[3][1] = y ; + + m[0][2] = -srcp ; + m[1][2] = sp ; + m[2][2] = cr * cp ; + m[3][2] = z ; + + m[0][3] = SGD_ZERO ; + m[1][3] = SGD_ZERO ; + m[2][3] = SGD_ZERO ; + m[3][3] = SGD_ONE ; +} + + +void sgdMakeTransMat4 ( sgdMat4 m, const sgdVec3 xyz ) +{ + m[0][1] = m[0][2] = m[0][3] = + m[1][0] = m[1][2] = m[1][3] = + m[2][0] = m[2][1] = m[2][3] = SGD_ZERO ; + m[0][0] = m[1][1] = m[2][2] = m[3][3] = SGD_ONE ; + sgdCopyVec3 ( m[3], xyz ) ; +} + + +void sgdMakeTransMat4 ( sgdMat4 m, const SGDfloat x, const SGDfloat y, const SGDfloat z ) +{ + m[0][1] = m[0][2] = m[0][3] = + m[1][0] = m[1][2] = m[1][3] = + m[2][0] = m[2][1] = m[2][3] = SGD_ZERO ; + m[0][0] = m[1][1] = m[2][2] = m[3][3] = SGD_ONE ; + sgdSetVec3 ( m[3], x, y, z ) ; +} + + +void sgdSetCoord ( sgdCoord *dst, const sgdMat4 src ) +{ + sgdCopyVec3 ( dst->xyz, src[3] ) ; + + sgdMat4 mat ; + + SGDfloat s = sgdLengthVec3 ( src[0] ) ; + + if ( s <= 0.00001 ) + { + ulSetError ( UL_WARNING, "sgdMat4ToCoord: ERROR - Bad Matrix." ) ; + sgdSetVec3 ( dst -> hpr, SGD_ZERO, SGD_ZERO, SGD_ZERO ) ; + return ; + } + + sgdScaleMat4 ( mat, src, SGD_ONE / s ) ; + + dst->hpr[1] = sgdASin ( _sgdClampToUnity ( mat[1][2] ) ) ; + + SGDfloat cp = sgdCos ( dst->hpr[1] ) ; + + /* If pointing nearly vertically up - then heading is ill-defined */ + + if ( cp > -0.00001 && cp < 0.00001 ) + { + SGDfloat cr = _sgdClampToUnity ( mat[0][1] ) ; + SGDfloat sr = _sgdClampToUnity (-mat[2][1] ) ; + + dst->hpr[0] = SGD_ZERO ; + dst->hpr[2] = sgdATan2 ( sr, cr ) ; + } + else + { + cp = SGD_ONE / cp ; + SGDfloat sr = _sgdClampToUnity ( -mat[0][2] * cp ) ; + SGDfloat cr = _sgdClampToUnity ( mat[2][2] * cp ) ; + SGDfloat sh = _sgdClampToUnity ( -mat[1][0] * cp ) ; + SGDfloat ch = _sgdClampToUnity ( mat[1][1] * cp ) ; + + if ( (sh == SGD_ZERO && ch == SGD_ZERO) || (sr == SGD_ZERO && cr == SGD_ZERO) ) + { + cr = _sgdClampToUnity ( mat[0][1] ) ; + sr = _sgdClampToUnity (-mat[2][1] ) ; + + dst->hpr[0] = SGD_ZERO ; + } + else + dst->hpr[0] = sgdATan2 ( sh, ch ) ; + + dst->hpr[2] = sgdATan2 ( sr, cr ) ; + } +} + + +void sgdMakeNormal(sgdVec2 dst, const sgdVec2 a, const sgdVec2 b ) +{ + sgdSubVec2 ( dst, b, a ) ; + SGDfloat tmp = dst [ 0 ] ; dst [ 0 ] = -dst [ 1 ] ; dst [ 1 ] = tmp ; + sgdNormaliseVec2 ( dst ) ; +} + + +void sgdMakeNormal(sgdVec3 dst, const sgdVec3 a, const sgdVec3 b, const sgdVec3 c ) +{ + sgdVec3 ab ; sgdSubVec3 ( ab, b, a ) ; + sgdVec3 ac ; sgdSubVec3 ( ac, c, a ) ; + sgdVectorProductVec3 ( dst, ab,ac ) ; sgdNormaliseVec3 ( dst ) ; +} + + +void sgdPreMultMat4( sgdMat4 dst, const sgdMat4 src ) +{ + sgdMat4 mat ; + sgdMultMat4 ( mat, dst, src ) ; + sgdCopyMat4 ( dst, mat ) ; +} + +void sgdPostMultMat4( sgdMat4 dst, const sgdMat4 src ) +{ + sgdMat4 mat ; + sgdMultMat4 ( mat, src, dst ) ; + sgdCopyMat4 ( dst, mat ) ; +} + +void sgdMultMat4( sgdMat4 dst, const sgdMat4 m1, const sgdMat4 m2 ) +{ + for ( int j = 0 ; j < 4 ; j++ ) + { + dst[0][j] = m2[0][0] * m1[0][j] + + m2[0][1] * m1[1][j] + + m2[0][2] * m1[2][j] + + m2[0][3] * m1[3][j] ; + + dst[1][j] = m2[1][0] * m1[0][j] + + m2[1][1] * m1[1][j] + + m2[1][2] * m1[2][j] + + m2[1][3] * m1[3][j] ; + + dst[2][j] = m2[2][0] * m1[0][j] + + m2[2][1] * m1[1][j] + + m2[2][2] * m1[2][j] + + m2[2][3] * m1[3][j] ; + + dst[3][j] = m2[3][0] * m1[0][j] + + m2[3][1] * m1[1][j] + + m2[3][2] * m1[2][j] + + m2[3][3] * m1[3][j] ; + } +} + + +void sgdTransposeNegateMat4 ( sgdMat4 dst, const sgdMat4 src ) +{ + /* Poor man's invert - can be used when matrix is a simple rotate-translate */ + + dst[0][0] = src[0][0] ; + dst[1][0] = src[0][1] ; + dst[2][0] = src[0][2] ; + dst[3][0] = - sgdScalarProductVec3 ( src[3], src[0] ) ; + + dst[0][1] = src[1][0] ; + dst[1][1] = src[1][1] ; + dst[2][1] = src[1][2] ; + dst[3][1] = - sgdScalarProductVec3 ( src[3], src[1] ) ; + + dst[0][2] = src[2][0] ; + dst[1][2] = src[2][1] ; + dst[2][2] = src[2][2] ; + dst[3][2] = - sgdScalarProductVec3 ( src[3], src[2] ) ; + + dst[0][3] = SGD_ZERO ; + dst[1][3] = SGD_ZERO ; + dst[2][3] = SGD_ZERO ; + dst[3][3] = SGD_ONE ; +} + + +void sgdTransposeNegateMat4 ( sgdMat4 dst ) +{ + sgdMat4 src ; + sgdCopyMat4 ( src, dst ) ; + sgdTransposeNegateMat4 ( dst, src ) ; +} + + + +void sgdInvertMat4 ( sgdMat4 dst, const sgdMat4 src ) +{ + sgdMat4 tmp ; + + sgdCopyMat4 ( tmp, src ) ; + sgdMakeIdentMat4 ( dst ) ; + + for ( int i = 0 ; i != 4 ; i++ ) + { + SGDfloat val = tmp[i][i] ; + int ind = i ; + int j ; + + for ( j = i + 1 ; j != 4 ; j++ ) + { + if ( fabs ( tmp[i][j] ) > fabs(val) ) + { + ind = j; + val = tmp[i][j] ; + } + } + + if ( ind != i ) + { /* swap columns */ + for ( j = 0 ; j != 4 ; j++ ) + { + SGDfloat t ; + t = dst[j][i]; dst[j][i] = dst[j][ind]; dst[j][ind] = t ; + t = tmp[j][i]; tmp[j][i] = tmp[j][ind]; tmp[j][ind] = t ; + } + } + + // if ( val == SG_ZERO) + if ( fabs(val) <= DBL_EPSILON ) + { + ulSetError ( UL_WARNING, "sg: ERROR - Singular matrix, no inverse!" ) ; + sgdMakeIdentMat4 ( dst ) ; /* Do *something* */ + return; + } + + SGDfloat ival = SGD_ONE / val ; + + for ( j = 0 ; j != 4 ; j++ ) + { + tmp[j][i] *= ival ; + dst[j][i] *= ival ; + } + + for (j = 0; j != 4; j++) + { + if ( j == i ) + continue ; + + val = tmp[i][j] ; + + for ( int k = 0 ; k != 4 ; k++ ) + { + tmp[k][j] -= tmp[k][i] * val ; + dst[k][j] -= dst[k][i] * val ; + } + } + } +} + + + +void sgdXformVec3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) +{ + SGDfloat t0 = src[ 0 ] ; + SGDfloat t1 = src[ 1 ] ; + SGDfloat t2 = src[ 2 ] ; + + dst[0] = ( t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] ) ; + + dst[1] = ( t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] ) ; + + dst[2] = ( t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] ) ; +} + + +void sgdXformPnt3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) +{ + SGDfloat t0 = src[ 0 ] ; + SGDfloat t1 = src[ 1 ] ; + SGDfloat t2 = src[ 2 ] ; + + dst[0] = ( t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] + + mat[ 3 ][ 0 ] ) ; + + dst[1] = ( t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] + + mat[ 3 ][ 1 ] ) ; + + dst[2] = ( t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] + + mat[ 3 ][ 2 ] ) ; +} + + +void sgdXformPnt4 ( sgdVec4 dst, const sgdVec4 src, const sgdMat4 mat ) +{ + SGDfloat t0 = src[ 0 ] ; + SGDfloat t1 = src[ 1 ] ; + SGDfloat t2 = src[ 2 ] ; + SGDfloat t3 = src[ 3 ] ; + + dst[0] = ( t0 * mat[ 0 ][ 0 ] + + t1 * mat[ 1 ][ 0 ] + + t2 * mat[ 2 ][ 0 ] + + t3 * mat[ 3 ][ 0 ] ) ; + + dst[1] = ( t0 * mat[ 0 ][ 1 ] + + t1 * mat[ 1 ][ 1 ] + + t2 * mat[ 2 ][ 1 ] + + t3 * mat[ 3 ][ 1 ] ) ; + + dst[2] = ( t0 * mat[ 0 ][ 2 ] + + t1 * mat[ 1 ][ 2 ] + + t2 * mat[ 2 ][ 2 ] + + t3 * mat[ 3 ][ 2 ] ) ; + + dst[3] = ( t0 * mat[ 0 ][ 3 ] + + t1 * mat[ 1 ][ 3 ] + + t2 * mat[ 2 ][ 3 ] + + t3 * mat[ 3 ][ 3 ] ) ; +} + + +void sgdFullXformPnt3 ( sgdVec3 dst, const sgdVec3 src, const sgdMat4 mat ) +{ + sgdVec4 tmp ; + + tmp [ 0 ] = src [ 0 ] ; + tmp [ 1 ] = src [ 1 ] ; + tmp [ 2 ] = src [ 2 ] ; + tmp [ 3 ] = SGD_ONE ; + + sgdXformPnt4 ( tmp, tmp, mat ) ; + sgdScaleVec3 ( dst, tmp, SGD_ONE / tmp [ 3 ] ) ; +} + +void sgdHPRfromVec3 ( sgdVec3 hpr, sgdVec3 src ) +{ + sgdVec3 tmp ; + sgdCopyVec3 ( tmp, src ) ; + sgdNormaliseVec3 ( tmp ) ; + hpr[0] = - sgdATan2 ( tmp [ 0 ], tmp [ 1 ] ) ; + hpr[1] = - sgdATan2 ( tmp [ 2 ], sgdSqrt ( sgdSquare ( tmp [ 0 ] ) + + sgdSquare ( tmp [ 1 ] ) ) ) ; + hpr[2] = SGD_ZERO ; +} + + + +/* + Quaternion routines are Copyright (C) 1999 + Kevin B. Thompson + Modified by Sylvan W. Clebsch + Largely rewritten by "Negative0" +*/ + + +void sgdQuatToAngleAxis ( SGDfloat *angle, + SGDfloat *x, SGDfloat *y, SGDfloat *z, + const sgdQuat src ) +{ + sgdVec3 axis ; + + sgdQuatToAngleAxis ( angle, axis, src ) ; + + *x = axis [ 0 ] ; + *y = axis [ 1 ] ; + *z = axis [ 2 ] ; +} + + +void sgdQuatToAngleAxis ( SGDfloat *angle, sgdVec3 axis, const sgdQuat src ) +{ + SGDfloat a = (SGDfloat) acos ( src[SGD_W] ) ; + SGDfloat s = (SGDfloat) sin ( a ) ; + + *angle = a * SGD_RADIANS_TO_DEGREES * SGD_TWO ; + + if ( s == SGD_ZERO ) + sgdSetVec3 ( axis, SGD_ZERO, SGD_ZERO, SGD_ONE ); + else + { + sgdSetVec3 ( axis, src[SGD_X], src[SGD_Y], src[SGD_Z] ) ; + sgdScaleVec3 ( axis, SGD_ONE / s ) ; + } +} + + +void sgdAngleAxisToQuat ( sgdQuat dst, + const SGDfloat angle, + const SGDfloat x, const SGDfloat y, const SGDfloat z ) +{ + sgdVec3 axis ; + sgdSetVec3 ( axis, x, y, z ) ; + sgdAngleAxisToQuat ( dst, angle, axis ) ; +} + + +void sgdAngleAxisToQuat ( sgdQuat dst, const SGDfloat angle, const sgdVec3 axis ) +{ + SGDfloat temp_angle = angle * SGD_DEGREES_TO_RADIANS / SGD_TWO ; + + sgdVec3 ax ; + sgdNormaliseVec3 ( ax, axis ) ; + + SGDfloat s = - (SGDfloat) sin ( temp_angle ) ; + + dst[SGD_W] = (SGDfloat) cos ( temp_angle ) ; + sgdScaleVec3 ( dst, ax, s ) ; +} + + +//from gamasutra.com +//by nb + +void sgdMatrixToQuat( sgdQuat quat, const sgdMat4 m ) +{ + SGDfloat tr, s, q[4] ; + int i, j, k ; + + int nxt[3] = {1, 2, 0}; + + tr = m[0][0] + m[1][1] + m[2][2]; + + // check the diagonal + if (tr > SGD_ZERO ) + { + s = (SGDfloat) sqrt (tr + SGD_ONE); + quat[SGD_W] = s / SGD_TWO; + s = SGD_HALF / s; + quat[SGD_X] = (m[1][2] - m[2][1]) * s; + quat[SGD_Y] = (m[2][0] - m[0][2]) * s; + quat[SGD_Z] = (m[0][1] - m[1][0]) * s; + } + else + { + // diagonal is negative + i = 0; + if (m[1][1] > m[0][0]) i = 1; + if (m[2][2] > m[i][i]) i = 2; + j = nxt[i]; + k = nxt[j]; + s = sqrt ((m[i][i] - (m[j][j] + m[k][k])) + SGD_ONE); + q[i] = s * SGD_HALF; + + if (s != SGD_ZERO) s = SGD_HALF / s; + + q[3] = (m[j][k] - m[k][j]) * s; + q[j] = (m[i][j] + m[j][i]) * s; + q[k] = (m[i][k] + m[k][i]) * s; + + quat[SGD_X] = q[0]; + quat[SGD_Y] = q[1]; + quat[SGD_Z] = q[2]; + quat[SGD_W] = q[3]; + } + + // seems to yield the inverse rotation, so: + quat[SG_W] = - quat[SG_W]; +} + + +void sgdMultQuat ( sgdQuat dst, const sgdQuat a, const sgdQuat b ) +{ + /* [ ww' - v.v', vxv' + wv' + v'w ] */ + + SGDfloat t[8]; + + t[0] = (a[SGD_W] + a[SGD_X]) * (b[SGD_W] + b[SGD_X]); + t[1] = (a[SGD_Z] - a[SGD_Y]) * (b[SGD_Y] - b[SGD_Z]); + t[2] = (a[SGD_X] - a[SGD_W]) * (b[SGD_Y] + b[SGD_Z]); + t[3] = (a[SGD_Y] + a[SGD_Z]) * (b[SGD_X] - b[SGD_W]); + t[4] = (a[SGD_X] + a[SGD_Z]) * (b[SGD_X] + b[SGD_Y]); + t[5] = (a[SGD_X] - a[SGD_Z]) * (b[SGD_X] - b[SGD_Y]); + t[6] = (a[SGD_W] + a[SGD_Y]) * (b[SGD_W] - b[SGD_Z]); + t[7] = (a[SGD_W] - a[SGD_Y]) * (b[SGD_W] + b[SGD_Z]); + + dst[SGD_W] = t[1] + ((-t[4] - t[5] + t[6] + t[7]) * SGD_HALF); + dst[SGD_X] = t[0] - (( t[4] + t[5] + t[6] + t[7]) * SGD_HALF); + dst[SGD_Y] = -t[2] + (( t[4] - t[5] + t[6] - t[7]) * SGD_HALF); + dst[SGD_Z] = -t[3] + (( t[4] - t[5] - t[6] + t[7]) * SGD_HALF); +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgdMultQuat2 ( sgdQuat dst, const sgdQuat a, const sgdQuat b ) +{ + SGDfloat A, B, C, D, E, F, G, H; + + A = (a[SGD_W] + a[SGD_X]) * (b[SGD_W] + b[SGD_X]) ; + B = (a[SGD_Z] - a[SGD_Y]) * (b[SGD_Y] - b[SGD_Z]) ; + C = (a[SGD_X] - a[SGD_W]) * (b[SGD_Y] + b[SGD_Z]) ; + D = (a[SGD_Y] + a[SGD_Z]) * (b[SGD_X] - b[SGD_W]) ; + E = (a[SGD_X] + a[SGD_Z]) * (b[SGD_X] + b[SGD_Y]) ; + F = (a[SGD_X] - a[SGD_Z]) * (b[SGD_X] - b[SGD_Y]) ; + G = (a[SGD_W] + a[SGD_Y]) * (b[SGD_W] - b[SGD_Z]) ; + H = (a[SGD_W] - a[SGD_Y]) * (b[SGD_W] + b[SGD_Z]) ; + + + dst[SGD_W] = B + (-E - F + G + H) / SGD_TWO ; + dst[SGD_X] = A - ( E + F + G + H) / SGD_TWO ; + dst[SGD_Y] = -C + ( E - F + G - H) / SGD_TWO ; + dst[SGD_Z] = -D + ( E - F - G + H) / SGD_TWO ; +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgdEulerToQuat(sgdQuat quat, const sgdVec3 hpr ) +{ + SGDfloat cr, cp, cy, sr, sp, sy, cpcy, spsy; + +// calculate trig identities + cr = (SGDfloat) cos(hpr[2]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + cp = (SGDfloat) cos(hpr[1]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + cy = (SGDfloat) cos(hpr[0]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + + sr = (SGDfloat) sin(hpr[2]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + sp = (SGDfloat) sin(hpr[1]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + sy = (SGDfloat) sin(hpr[0]*SGD_DEGREES_TO_RADIANS/SGD_TWO); + + cpcy = cp * cy; + spsy = sp * sy; + + quat[SGD_W] = cr * cpcy + sr * spsy; + quat[SGD_X] = sr * cpcy - cr * spsy; + quat[SGD_Y] = cr * sp * cy + sr * cp * sy; + quat[SGD_Z] = cr * cp * sy - sr * sp * cy; +} + +//from darwin3d.com +// jeffl@darwin3d.com + +void sgdQuatToEuler( sgdVec3 hpr, const sgdQuat quat ) +{ + SGDfloat matrix[3][3]; + SGDfloat cx,sx; + SGDfloat cy,sy; + SGDfloat cz,sz; + + // CONVERT QUATERNION TO MATRIX - I DON'T REALLY NEED ALL OF IT + + matrix[0][0] = SGD_ONE - (SGD_TWO * quat[SGD_Y] * quat[SGD_Y]) + - (SGD_TWO * quat[SGD_Z] * quat[SGD_Z]); +//matrix[0][1] = (SGD_TWO * quat->x * quat->y) - (SGD_TWO * quat->w * quat->z); +//matrix[0][2] = (SGD_TWO * quat->x * quat->z) + (SGD_TWO * quat->w * quat->y); + + matrix[1][0] = (SGD_TWO * quat[SGD_X] * quat[SGD_Y]) + + (SGD_TWO * quat[SGD_W] * quat[SGD_Z]); +//matrix[1][1] = SGD_ONE - (SGD_TWO * quat->x * quat->x) +// - (SGD_TWO * quat->z * quat->z); +//matrix[1][2] = (SGD_TWO * quat->y * quat->z) - (SGD_TWO * quat->w * quat->x); + + matrix[2][0] = (SGD_TWO * quat[SGD_X] * quat[SGD_Z]) - + (SGD_TWO * quat[SGD_W] * quat[SGD_Y]); + matrix[2][1] = (SGD_TWO * quat[SGD_Y] * quat[SGD_Z]) + + (SGD_TWO * quat[SGD_W] * quat[SGD_X]); + matrix[2][2] = SGD_ONE - (SGD_TWO * quat[SGD_X] * quat[SGD_X]) + - (SGD_TWO * quat[SGD_Y] * quat[SGD_Y]); + + sy = -matrix[2][0]; + cy = sgdSqrt(SGD_ONE - (sy * sy)); + + hpr[1] = sgdATan2( sy, cy ); + + // AVOID DIVIDE BY ZERO ERROR ONLY WHERE Y= +-90 or +-270 + // NOT CHECKING cy BECAUSE OF PRECISION ERRORS + if (sy != SGD_ONE && sy != -SGD_ONE) + { + cx = matrix[2][2] / cy; + sx = matrix[2][1] / cy; + hpr[0] = sgdATan2 ( sx, cx ); + + cz = matrix[0][0] / cy; + sz = matrix[1][0] / cy; + hpr[2] = sgdATan2 ( sz, cz ); + } + else + { + // SINCE Cos(Y) IS 0, I AM SCREWED. ADOPT THE STANDARD Z = 0 + // I THINK THERE IS A WAY TO FIX THIS BUT I AM NOT SURE. EULERS SUCK + // NEED SOME MORE OF THE MATRIX TERMS NOW + + matrix[1][1] = SGD_ONE - (SGD_TWO * quat[SGD_X] * quat[SGD_X]) + - (SGD_TWO * quat[SGD_Z] * quat[SGD_Z]); + matrix[1][2] = (SGD_TWO * quat[SGD_Y] * quat[SGD_Z]) - + (SGD_TWO * quat[SGD_W] * quat[SGD_X]); + + cx = matrix[1][1]; + sx = -matrix[1][2]; + hpr[0] = sgdATan2 ( sx, cx ); + + cz = SGD_ONE ; + sz = SGD_ZERO ; + hpr[2] = sgdATan2 ( sz, cz ); + } +} + + +void sgdQuatToMatrix ( sgdMat4 dst, const sgdQuat q ) +{ + SGDfloat two_xx = q[SGD_X] * (q[SGD_X] + q[SGD_X]) ; + SGDfloat two_xy = q[SGD_X] * (q[SGD_Y] + q[SGD_Y]) ; + SGDfloat two_xz = q[SGD_X] * (q[SGD_Z] + q[SGD_Z]) ; + + SGDfloat two_wx = q[SGD_W] * (q[SGD_X] + q[SGD_X]) ; + SGDfloat two_wy = q[SGD_W] * (q[SGD_Y] + q[SGD_Y]) ; + SGDfloat two_wz = q[SGD_W] * (q[SGD_Z] + q[SGD_Z]) ; + + SGDfloat two_yy = q[SGD_Y] * (q[SGD_Y] + q[SGD_Y]) ; + SGDfloat two_yz = q[SGD_Y] * (q[SGD_Z] + q[SGD_Z]) ; + + SGDfloat two_zz = q[SGD_Z] * (q[SGD_Z] + q[SGD_Z]) ; + + sgdSetVec4 ( dst[0], SGD_ONE-(two_yy+two_zz), two_xy-two_wz, two_xz+two_wy, SGD_ZERO ) ; + sgdSetVec4 ( dst[1], two_xy+two_wz, SGD_ONE-(two_xx+two_zz), two_yz-two_wx, SGD_ZERO ) ; + sgdSetVec4 ( dst[2], two_xz-two_wy, two_yz+two_wx, SGD_ONE-(two_xx+two_yy), SGD_ZERO ) ; + sgdSetVec4 ( dst[3], SGD_ZERO, SGD_ZERO, SGD_ZERO, SGD_ONE ) ; +} + + +//from gamasutra.com +//by nb@netcom.ca + +/************************************ + DEPRECATED - use sgdQuatToMatrix instead. +*************************************/ + +void sgdMakeRotMat42( sgdMat4 m, sgdQuat quat ){ + SGDfloat wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; + + // calculate coefficients + x2 = quat[SGD_X] + quat[SGD_X]; y2 = quat[SGD_Y] + quat[SGD_Y]; + z2 = quat[SGD_Z] + quat[SGD_Z]; + xx = quat[SGD_X] * x2; xy = quat[SGD_X] * y2; xz = quat[SGD_X] * z2; + yy = quat[SGD_Y] * y2; yz = quat[SGD_Y] * z2; zz = quat[SGD_Z] * z2; + wx = quat[SGD_W] * x2; wy = quat[SGD_W] * y2; wz = quat[SGD_W] * z2; + + m[0][0] = SGD_ONE- (yy + zz); m[0][1] = xy - wz; + m[0][2] = xz + wy; m[0][3] = SGD_ZERO ; + + m[1][0] = xy + wz; m[1][1] = SGD_ONE- (xx + zz); + m[1][2] = yz - wx; m[1][3] = SGD_ZERO ; + + m[2][0] = xz - wy; m[2][1] = yz + wx; + m[2][2] = SGD_ONE- (xx + yy); m[2][3] = SGD_ZERO ; + + m[3][0] = 0; m[3][1] = 0; + m[3][2] = 0; m[3][3] = 1; +} + +//from gamasutra.com +//by nb@netcom.ca + +void sgdSlerpQuat2( sgdQuat dst, const sgdQuat from, const sgdQuat to, const SGDfloat t ) +{ + SGDfloat to1[4]; + SGDfloat omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = from[SGD_X] * to[SGD_X] + + from[SGD_Y] * to[SGD_Y] + + from[SGD_Z] * to[SGD_Z] + + from[SGD_W] * to[SGD_W]; + + // adjust signs (if necessary) + + if ( cosom < SG_ZERO ) + { + cosom = -cosom; + to1[0] = - to[SGD_X]; + to1[1] = - to[SGD_Y]; + to1[2] = - to[SGD_Z]; + to1[3] = - to[SGD_W]; + } + else + { + to1[0] = to[SGD_X]; + to1[1] = to[SGD_Y]; + to1[2] = to[SGD_Z]; + to1[3] = to[SGD_W]; + } + + // calculate coefficients +#define DELTA SGD_ZERO + if ( (SGD_ONE- cosom) > DELTA ) { + // standard case (slerp) + omega = acos(cosom); + sinom = sin(omega); + scale0 = sin((SGD_ONE- t) * omega) / sinom; + scale1 = sin(t * omega) / sinom; + + } + else + { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = SGD_ONE- t; + scale1 = t; + } + + // calculate final values + dst[SGD_X] = scale0 * from[SGD_X] + scale1 * to1[0]; + dst[SGD_Y] = scale0 * from[SGD_Y] + scale1 * to1[1]; + dst[SGD_Z] = scale0 * from[SGD_Z] + scale1 * to1[2]; + dst[SGD_W] = scale0 * from[SGD_W] + scale1 * to1[3]; +} + +void sgdSlerpQuat( sgdQuat dst, const sgdQuat from, const sgdQuat to, const SGDfloat t ) +{ + SGDfloat co, scale0, scale1; + bool flip = false ; + + /* SWC - Interpolate between to quaternions */ + + co = sgdScalarProductVec4 ( from, to ) ; + + if ( co < SGD_ZERO ) + { + co = -co; + flip = true ; + } + + if ( co < SGD_ONE - (SGDfloat) 1e-6 ) + { + SGDfloat o = (SGDfloat) acos ( co ); + SGDfloat so = SGD_ONE / (SGDfloat) sin ( o ); + + scale0 = (SGDfloat) sin ( (SGD_ONE - t) * o ) * so; + scale1 = (SGDfloat) sin ( t * o ) * so; + } + else + { + scale0 = SGD_ONE - t; + scale1 = t; + } + + if ( flip ) + { + scale1 = -scale1 ; + } + + dst[SGD_X] = scale0 * from[SGD_X] + scale1 * to[SGD_X] ; + dst[SGD_Y] = scale0 * from[SGD_Y] + scale1 * to[SGD_Y] ; + dst[SGD_Z] = scale0 * from[SGD_Z] + scale1 * to[SGD_Z] ; + dst[SGD_W] = scale0 * from[SGD_W] + scale1 * to[SGD_W] ; +} + + + + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q r Q-1 -- this gives the components of a ROTATED vector in a STATIONARY + * coordinate system. We assume that Q is a unit quaternion. + */ +void sgdRotateVecQuat ( sgdVec3 vec, sgdQuat q ) +{ + sgdVec3 rot ; + sgdFloat qwqw = q[SG_W] * q[SG_W] ; + sgdFloat qwqx = q[SG_W] * q[SG_X] ; + sgdFloat qwqy = q[SG_W] * q[SG_Y] ; + sgdFloat qwqz = q[SG_W] * q[SG_Z] ; + sgdFloat qxqx = q[SG_X] * q[SG_X] ; + sgdFloat qxqy = q[SG_X] * q[SG_Y] ; + sgdFloat qxqz = q[SG_X] * q[SG_Z] ; + sgdFloat qyqy = q[SG_Y] * q[SG_Y] ; + sgdFloat qyqz = q[SG_Y] * q[SG_Z] ; + sgdFloat qzqz = q[SG_Z] * q[SG_Z] ; + rot[SG_X] = ( qwqw + qxqx - qyqy - qzqz ) * vec[SG_X] + 2.0f * ( qxqy - qwqz ) * vec[SG_Y] + 2.0f * ( qxqz + qwqy ) * vec[SG_Z] ; + rot[SG_Y] = ( qwqw - qxqx + qyqy - qzqz ) * vec[SG_Y] + 2.0f * ( qyqz - qwqx ) * vec[SG_Z] + 2.0f * ( qxqy + qwqz ) * vec[SG_X] ; + rot[SG_Z] = ( qwqw - qxqx - qyqy + qzqz ) * vec[SG_Z] + 2.0f * ( qxqz - qwqy ) * vec[SG_X] + 2.0f * ( qyqz + qwqx ) * vec[SG_Y] ; + sgdCopyVec3 ( vec, rot ) ; +} + +/* Function to rotate a vector through a given quaternion using the formula + * R = Q-1 r Q -- this gives the components of a STATIONARY vector in a ROTATED + * coordinate system. We assume that Q is a unit quaternion. + */ +void sgdRotateCoordQuat ( sgdVec3 vec, sgdQuat q ) +{ + sgdVec3 rot ; + sgdFloat qwqw = q[SG_W] * q[SG_W] ; + sgdFloat qwqx = q[SG_W] * q[SG_X] ; + sgdFloat qwqy = q[SG_W] * q[SG_Y] ; + sgdFloat qwqz = q[SG_W] * q[SG_Z] ; + sgdFloat qxqx = q[SG_X] * q[SG_X] ; + sgdFloat qxqy = q[SG_X] * q[SG_Y] ; + sgdFloat qxqz = q[SG_X] * q[SG_Z] ; + sgdFloat qyqy = q[SG_Y] * q[SG_Y] ; + sgdFloat qyqz = q[SG_Y] * q[SG_Z] ; + sgdFloat qzqz = q[SG_Z] * q[SG_Z] ; + rot[SG_X] = ( qwqw + qxqx - qyqy - qzqz ) * vec[SG_X] + 2.0f * ( qxqy + qwqz ) * vec[SG_Y] + 2.0f * ( qxqz - qwqy ) * vec[SG_Z] ; + rot[SG_Y] = ( qwqw - qxqx + qyqy - qzqz ) * vec[SG_Y] + 2.0f * ( qyqz + qwqx ) * vec[SG_Z] + 2.0f * ( qxqy - qwqz ) * vec[SG_X] ; + rot[SG_Z] = ( qwqw - qxqx - qyqy + qzqz ) * vec[SG_Z] + 2.0f * ( qxqz + qwqy ) * vec[SG_X] + 2.0f * ( qyqz - qwqx ) * vec[SG_Y] ; + sgdCopyVec3 ( vec, rot ) ; +} + + +sgdFloat sgdDistSquaredToLineLineSegment ( const sgdLineSegment3 seg, const sgdLine3 line ) +{ + /* Convert the line segment to a line. We will deal with the segment limits later. */ + sgdLine3 line2 ; + sgdLineSegment3ToLine3 ( &line2, seg ) ; + + /* Get the dot product of the two direction vectors */ + sgdFloat t1_dot_t2 = sgdScalarProductVec3 ( line.direction_vector, line2.direction_vector ) ; + + /* If the lines are parallel, distance is just the distance from a point to the other line */ + if ( fabs ( t1_dot_t2 ) >= 1.0 ) + return sgdDistSquaredToLineVec3 ( line, seg.a ) ; + + /* Get the parametric coordinates of the closest points on the two lines. The first line + * is parameterized by r = r1 + t1 u while the second is parameterized by r = r2 + t2 v. + * The square of the distance between them is [ ( r1 + t1 u ) - ( r2 + t2 v ) ] dot + * [ ( r1 + t1 u ) - ( r2 + t2 v ) ]. Differentiating this dot product with respect to + * u and v and setting the derivatives to zero gives a matrix equation: + * [ 1 -(t1 dot t2) ] [ u ] = [ ( r1 - r2 ) dot t1 ] + * [ -(t1 dot t2) 1 ] [ v ] [ -( r1 - r2 ) dot t2 ] + * We invert the matrix to get the equations below. + */ + sgdVec3 r1_minus_r2 ; + sgdSubVec3 ( r1_minus_r2, line.point_on_line, line2.point_on_line ) ; + + /* t1_t2_t2_minus_t1 = ( t1 dot t2 ) t2 - t1 + * t2_minus_t1_t2_t1 = t2 - ( t1 dot t2 ) t1 + */ + sgdVec3 t1_t2_t2_minus_t1, t2_minus_t1_t2_t1 ; + sgdAddScaledVec3 ( t1_t2_t2_minus_t1, line.direction_vector, line2.direction_vector, -t1_dot_t2 ) ; + sgdNegateVec3 ( t1_t2_t2_minus_t1 ) ; + sgdAddScaledVec3 ( t2_minus_t1_t2_t1, line2.direction_vector, line.direction_vector, -t1_dot_t2 ) ; + + sgdFloat u = sgdScalarProductVec3 ( r1_minus_r2, t1_t2_t2_minus_t1 ) / ( 1.0f - t1_dot_t2 * t1_dot_t2 ) ; + sgdFloat v = sgdScalarProductVec3 ( r1_minus_r2, t2_minus_t1_t2_t1 ) / ( 1.0f - t1_dot_t2 * t1_dot_t2 ) ; + + /* Since line 2 is a line segment, we limit "v" to between 0 and the distance between the points. */ + sgdFloat length = sgdDistanceVec3 ( seg.a, seg.b ) ; + if ( v < 0.0 ) v = 0.0 ; + if ( v > length ) v = length ; + + sgdVec3 point1, point2 ; + sgdAddScaledVec3 ( point1, line.point_on_line, line.direction_vector, u ) ; + sgdAddScaledVec3 ( point2, line2.point_on_line, line2.direction_vector, v ) ; + return sgdDistanceSquaredVec3 ( point1, point2 ) ; +} + + + +void sgdReflectInPlaneVec3 ( sgdVec3 dst, const sgdVec3 src, const sgdVec4 plane ) +{ + SGDfloat src_dot_norm = sgdScalarProductVec3 ( src, plane ) ; + + sgdVec3 tmp ; + + sgdScaleVec3 ( tmp, plane, SGD_TWO * src_dot_norm ) ; + sgdSubVec3 ( dst, src, tmp ) ; +} + + + +int sgdClassifyMat4 ( const sgdMat4 m ) +{ + const SGDfloat epsilon = 1e-6 ; + + int flags = 0 ; + + + SGDfloat sx, sy, sz ; + + if ( m[0][1] == SGD_ZERO && m[0][2] == SGD_ZERO && + m[1][0] == SGD_ZERO && m[1][2] == SGD_ZERO && + m[2][0] == SGD_ZERO && m[2][1] == SGD_ZERO ) + { + + int n = ( m[0][0] < 0 ) + ( m[1][1] < 0 ) + ( m[2][2] < 0 ) ; + + if ( n > 1 ) + flags |= SG_ROTATION ; + + if ( n % 2 != 0 ) + flags |= SG_MIRROR ; + + sx = m[0][0] * m[0][0] ; + sy = m[1][1] * m[1][1] ; + sz = m[2][2] * m[2][2] ; + + } + else + { + + flags |= SG_ROTATION ; + + if ( sgdAbs ( sgdScalarProductVec3 ( m[1], m[2] ) ) > epsilon || + sgdAbs ( sgdScalarProductVec3 ( m[2], m[0] ) ) > epsilon || + sgdAbs ( sgdScalarProductVec3 ( m[0], m[1] ) ) > epsilon ) + { + flags |= SG_NONORTHO ; + } + + sgdVec3 temp ; + sgdVectorProductVec3 ( temp, m[0], m[1] ) ; + SGDfloat det = sgdScalarProductVec3 ( temp, m[2] ) ; + + if ( det < 0 ) + flags |= SG_MIRROR ; + + sx = sgdScalarProductVec3 ( m[0], m[0] ) ; + sy = sgdScalarProductVec3 ( m[1], m[1] ) ; + sz = sgdScalarProductVec3 ( m[2], m[2] ) ; + + } + + + if ( sgdAbs ( sx - sy ) > epsilon || + sgdAbs ( sx - sz ) > epsilon ) + { + flags |= SG_NONORTHO ; + flags |= SG_GENERAL_SCALE ; // also set general scale bit, though it may be deleted in the future + } + else + { + if ( sgdAbs ( sx - SGD_ONE ) > epsilon ) + flags |= SG_SCALE ; + } + + + if ( m[3][0] != SGD_ZERO || m[3][1] != SGD_ZERO || m[3][2] != SGD_ZERO ) + { + flags |= SG_TRANSLATION ; + } + + + if ( m[0][3] != SGD_ZERO || m[1][3] != SGD_ZERO || m[2][3] != SGD_ZERO || + m[3][3] != SGD_ONE ) + { + flags |= SG_PROJECTION ; + } + + + return flags ; +} + + +SGDfloat sgdTriangleSolver_ASAtoArea ( SGDfloat angA, SGDfloat lenB, SGDfloat angC ) +{ + /* Get the third angle */ + + SGDfloat angB = SGD_180 - (angA + angC) ; + + /* Use Sine Rule to get length of a second side - then use SAStoArea. */ + + SGDfloat sinB = sgdSin ( angB ) ; + + if ( sinB == SGD_ZERO ) + return SGD_ZERO ; + + SGDfloat lenA = lenB * sgdSin(angA) / sinB ; + + return sgdTriangleSolver_SAStoArea ( lenA, angC, lenB ) ; +} + + +SGDfloat sgdTriangleSolver_SAStoArea ( SGDfloat lenA, SGDfloat angB, SGDfloat lenC ) +{ + return SGD_HALF * lenC * lenA * sgdSin ( angB ) ; +} + + +SGDfloat sgdTriangleSolver_SSStoArea ( SGDfloat lenA, SGDfloat lenB, SGDfloat lenC ) +{ + /* Heron's formula */ + + SGDfloat s = ( lenA + lenB + lenC ) / SGD_TWO ; + SGDfloat q = s * (s-lenA) * (s-lenB) * (s-lenC) ; + + /* Ikky illegal triangles generate zero areas. */ + + return ( q <= SGD_ZERO ) ? SGD_ZERO : sgdSqrt ( q ) ; +} + + +SGDfloat sgdTriangleSolver_ASStoArea ( SGDfloat angB, SGDfloat lenA, SGDfloat lenB, + int angA_is_obtuse ) +{ + SGDfloat lenC ; + + sgdTriangleSolver_ASStoSAA ( angB, lenA, lenB, angA_is_obtuse, + &lenC, NULL, NULL ) ; + + return sgdTriangleSolver_SAStoArea ( lenA, angB, lenC ) ; +} + +SGDfloat sgdTriangleSolver_SAAtoArea ( SGDfloat lenA, SGDfloat angB, SGDfloat angA ) +{ + SGDfloat lenC ; + + sgdTriangleSolver_SAAtoASS ( lenA, angB, angA, NULL, NULL, &lenC ) ; + + return sgdTriangleSolver_SAStoArea ( lenA, angB, lenC ) ; +} + +void sgdTriangleSolver_SSStoAAA ( SGDfloat lenA, SGDfloat lenB, SGDfloat lenC, + SGDfloat *angA, SGDfloat *angB, SGDfloat *angC ) +{ + SGDfloat aa, bb, cc ; + + int flag = ( lenA == SGD_ZERO ) | + (( lenB == SGD_ZERO )<<1) | + (( lenC == SGD_ZERO )<<2) ; + + /* Ikky zero-sized triangles generate zero/90 angles appropriately. */ + /* Ikky triangles with all lengths zero generate 60 degree angles. */ + /* Ikky impossible triangles generate all zero angles. */ + + switch ( flag ) + { + case 0 : /* no zero-lengthed sides */ + /* Cosine law */ + aa = sgdACos (( lenB*lenB + lenC*lenC - lenA*lenA )/(SGD_TWO*lenB*lenC)) ; + bb = sgdACos (( lenA*lenA + lenC*lenC - lenB*lenB )/(SGD_TWO*lenA*lenC)) ; + cc = sgdACos (( lenA*lenA + lenB*lenB - lenC*lenC )/(SGD_TWO*lenA*lenB)) ; + break ; + + case 1 : /* lenA is zero */ + aa = SGD_ZERO ; + bb = cc = SGD_90 ; + break ; + + case 2 : /* lenB is zero */ + bb = SGD_ZERO ; + aa = cc = SGD_90 ; + break ; + + case 4 : /* lenC is zero */ + cc = SGD_ZERO ; + aa = bb = SGD_90 ; + break ; + + case 3 : /* Two lengths are zero and the third isn't?!? */ + case 5 : + case 6 : + aa = bb = cc = SGD_ZERO ; + break ; + + default : /* All three sides are zero length */ + aa = bb = cc = SGD_60 ; + break ; + } + + if ( angA ) *angA = aa ; + if ( angB ) *angB = bb ; + if ( angC ) *angC = cc ; +} + +void sgdTriangleSolver_SAStoASA ( SGDfloat lenA, SGDfloat angB, SGDfloat lenC, + SGDfloat *angA, SGDfloat *lenB, SGDfloat *angC ) +{ + /* Get third side using Cosine Rule */ + + SGDfloat s = lenC * lenC + + lenA * lenA - SGD_TWO * lenC * lenA * sgdCos( angB ) ; + + SGDfloat lb = ( s <= SGD_ZERO ) ? SGD_ZERO : (SGDfloat) sqrt ( s ) ; + + if ( lenB ) *lenB = lb ; + + sgdTriangleSolver_SSStoAAA ( lenA, lb, lenC, angA, NULL, angC ) ; +} + + +void sgdTriangleSolver_ASAtoSAS ( SGDfloat angA, SGDfloat lenB, SGDfloat angC, + SGDfloat *lenA, SGDfloat *angB, SGDfloat *lenC ) +{ + /* Find the missing angle */ + + SGDfloat bb = SGD_180 - (angA + angC) ; + + if ( angB ) *angB = bb ; + + /* Use Sine Rule */ + + SGDfloat sinB = sgdSin ( bb ) ; + + if ( sinB == SGD_ZERO ) + { + if ( lenA ) *lenA = lenB / SGD_TWO ; /* One valid interpretation */ + if ( lenC ) *lenC = lenB / SGD_TWO ; + } + else + { + if ( lenA ) *lenA = lenB * sgdSin(angA) / sinB ; + if ( lenC ) *lenC = lenB * sgdSin(angC) / sinB ; + } +} + + +void sgdTriangleSolver_ASStoSAA ( SGDfloat angB, SGDfloat lenA, SGDfloat lenB, + int angA_is_obtuse, + SGDfloat *lenC, SGDfloat *angA, SGDfloat *angC ) +{ + /* Sine law */ + + SGDfloat aa = (lenB == SGD_ZERO ) ? SGD_ZERO : sgdASin (lenA * sgdSin(angB)/lenB) ; + + if ( angA_is_obtuse ) + aa = SGD_180 - aa ; + + if ( angA ) *angA = aa ; + + /* Find the missing angle */ + + SGDfloat cc = SGD_180 - (aa + angB) ; + + if ( angC ) *angC = cc ; + + /* Use SAStoASA to get the last length */ + + sgdTriangleSolver_SAStoASA ( lenA, cc, lenB, NULL, lenC, NULL ) ; +} + + +void sgdTriangleSolver_SAAtoASS ( SGDfloat lenA, SGDfloat angB, SGDfloat angA, + SGDfloat *angC, SGDfloat *lenB, SGDfloat *lenC ) +{ + /* Find the missing angle */ + + SGDfloat cc = SGD_180 - (angB + angA) ; + + if ( angC ) *angC = cc ; + + sgdTriangleSolver_ASAtoSAS ( cc, lenA, angB, lenC, NULL, lenB ) ; +} + + + diff --git a/src/sg/sgdIsect.cxx b/src/sg/sgdIsect.cxx new file mode 100755 index 0000000..d6339c4 --- /dev/null +++ b/src/sg/sgdIsect.cxx @@ -0,0 +1,354 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sgdIsect.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "sg.h" + +/* + Determine the origin and unit direction vector of a line + formed by the intersection of two planes. + Returned point will lie on axis that was used to + determine intersection. + Norman Vine - nhv@yahoo.com +*/ + +int sgdIsectPlanePlane ( sgdVec3 point, sgdVec3 dir, + sgdVec4 plane1, sgdVec4 plane2 ) +{ + /* Determine intersection line direction. */ + + sgdVectorProductVec3 ( dir, plane1, plane2 ) ; + + SGDfloat dnorm = sgdLengthVec3 ( dir ) ; + + /* If planes are parallel then fail */ + + if ( dnorm < DBL_EPSILON ) + { + sgdZeroVec3 ( point ) ; + sgdZeroVec3 ( dir ) ; + return FALSE ; + } + + /* Determine intersection point with the best suited coordinate plane. */ + + SGDfloat abs ; + SGDfloat maxabs = sgdAbs(dir[0]); + int index = 0; + + if ((abs = sgdAbs(dir[1])) > maxabs) { maxabs = abs ; index = 1; } + if ((abs = sgdAbs(dir[2])) > maxabs) { maxabs = abs ; index = 2; } + + switch ( index ) + { + case 0: + sgdSetVec3( point, + SGD_ZERO, + (plane1[2] * plane2[3] - plane2[2] * plane1[3]) / dir[0], + (plane2[1] * plane1[3] - plane1[1] * plane2[3]) / dir[0] ); + break; + case 1: + sgdSetVec3( point, + (plane2[2] * plane1[3] - plane1[2] * plane2[3]) / dir[1], + SGD_ZERO, + (plane1[0] * plane2[3] - plane2[0] * plane1[3]) / dir[1] ); + break; + case 2: + sgdSetVec3( point, + (plane1[1] * plane2[3] - plane2[1] * plane1[3]) / dir[2], + (plane2[0] * plane1[3] - plane1[0] * plane2[3]) / dir[2], + SGD_ZERO ); + break; + default: return FALSE ; /* Impossible */ + } + + /* Normalize the direction */ + + sgdScaleVec3( dir, SGD_ONE / dnorm ); + return TRUE; +} + + + +/* + Find the intersection of an infinite line with a plane + (the line being defined by a point and direction). + + Norman Vine -- nhv@yahoo.com (with hacks by Steve) +*/ + +int sgdIsectInfLinePlane( sgdVec3 dst, sgdVec3 l_org, sgdVec3 l_vec, sgdVec4 plane ) +{ + SGDfloat tmp = sgdScalarProductVec3 ( l_vec, plane ) ; + + /* Is line parallel to plane? */ + + if ( sgdAbs ( tmp ) < DBL_EPSILON ) + return FALSE ; + + sgdScaleVec3 ( dst, l_vec, -( sgdScalarProductVec3 ( l_org, plane ) + + plane[3] ) / tmp ) ; + sgdAddVec3 ( dst, l_org ) ; + + return TRUE ; +} + + + +/* + Given the origin and direction vector for two lines + find their intersection - or the point closest to + both lines if they don't intersect. + + Norman Vine -- nhv@yahoo.com +*/ + +int sgdIsectInfLineInfLine( sgdVec3 dst, + sgdVec3 l1_org, sgdVec3 l1_vec, + sgdVec3 l2_org, sgdVec3 l2_vec ) +{ + sgdVec3 vec_l1, vec_l2 ; + + sgdNormalizeVec3 ( vec_l1, l1_vec ) ; + sgdNormalizeVec3 ( vec_l2, l2_vec ) ; + + /* Connecting line 'C' is perpendicular to both */ + + sgdVec3 perp ; + sgdVectorProductVec3 ( perp, vec_l1, vec_l2 ) ; + + /* check for near-parallel lines */ + + SGDfloat dist = sgdScalarProductVec3 ( perp, perp ) ; + + if ( dist < DBL_EPSILON ) + { + /* degenerate: lines parallel - any point will do. */ + + sgdCopyVec3 ( dst, l2_org ) ; + return TRUE ; + } + + /* + Form a plane containing the line A and C, + and another containing B and C + */ + + sgdScaleVec3 ( perp, SGD_ONE / sgdSqrt( dist ) ) ; + + sgdVec4 pa ; + sgdVec4 pb ; + sgdVec3 tmp ; + + sgdVectorProductVec3 ( tmp, perp, vec_l1 ) ; + sgdNormalizeVec3 ( tmp ); + sgdMakePlane ( pa, tmp, l1_org ) ; + + sgdVectorProductVec3 ( tmp, perp, vec_l2 ) ; + sgdNormalizeVec3 ( tmp ); + sgdMakePlane ( pb, tmp, l2_org ) ; + + sgdVec3 tmp_org, tmp_vec ; + + if ( ! sgdIsectPlanePlane( tmp_org, tmp_vec, pa, pb ) ) + { + /* This *shouldn't* ever happen because we already + tested for parallel lines - but with roundoff + errors, it *might* in borderline cases so... + */ + + sgdCopyVec3 ( dst, l2_org ) ; + return FALSE ; + } + + if ( ! sgdIsectInfLinePlane ( dst, l2_org, vec_l2, pa ) ) + { + sgdCopyVec3 ( dst, l2_org ) ; + return FALSE ; + } + + return TRUE ; +} + + +/* + Intersect the line segment from v1->v2 with the 'plane'. + 'dst' is the intersection point and the return + result is in the range 0..1 if the intersection lies + between v1 and v2, >1 if beyond v2 and <0 if before v1. + FLT_MAX is returned if the vector does not intersect + the plane. + + Steve Baker +*/ + +SGDfloat sgdIsectLinesegPlane ( sgdVec3 dst, sgdVec3 v1, sgdVec3 v2, sgdVec4 plane ) +{ + sgdVec3 delta ; + + sgdSubVec3 ( delta, v2, v1 ) ; + + SGDfloat p = sgdScalarProductVec3 ( plane, delta ) ; + + if ( p == SGD_ZERO ) + { + dst [ 0 ] = dst [ 1 ] = dst [ 2 ] = FLT_MAX ; + return FLT_MAX ; + } + + float s = (float) (- ( sgdScalarProductVec3 ( plane, v1 ) + plane[3] ) / p) ; + + sgdScaleVec3 ( dst, delta, s ) ; + sgdAddVec3 ( dst, dst, v1 ) ; + + return s ; +} + + + +// return the sign of a value +static inline const int SGD_SIGN(const SGDfloat x) { + return x < 0 ? -1 : 1; +} + +// return the minimum of two values +static inline SGDfloat SGD_MIN2(const SGDfloat a, const SGDfloat b) { + return a < b ? a : b; +} + +// return the minimum of three values +static inline SGDfloat SGD_MIN3( const SGDfloat a, const SGDfloat b, const SGDfloat c) { + return (a < b ? SGD_MIN2 (a, c) : SGD_MIN2 (b, c)); +} + +// return the minimum and maximum of three values +static void SGD_MIN_MAX3 ( SGDfloat &min, SGDfloat &max, + const SGDfloat &a, const SGDfloat &b, + const SGDfloat &c) +{ + if( a > b ) { + if( a > c ) { + max = a; + min = SGD_MIN2( b, c ); + } else { + max = c; + min = SGD_MIN2( a, b ); + } + } else { + if( b > c ) { + max = b; + min = SGD_MIN2( a, c ); + } else { + max = c; + min = SGD_MIN2( a, b ); + } + } +} + +/* + * Given a point and a triangle lying on the same plane + * check to see if the point is inside the triangle + */ +bool sgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] ) +{ + sgdVec3 dif; + + int i; + for( i=0; i<3; i++ ) { + SGDfloat min, max; + SGD_MIN_MAX3 ( min, max, tri[0][i], tri[1][i], tri[2][i] ); + // punt if outside bouding cube + if( (point[i] < min) || (point[i] > max) ) + return false; + dif[i] = max - min; + } + + // drop the smallest dimension so we only have to work in 2d. + SGDfloat min_dim = SGD_MIN3 (dif[0], dif[1], dif[2]); + SGDfloat x1, y1, x2, y2, x3, y3, rx, ry; + if ( fabs(min_dim-dif[0]) <= DBL_EPSILON ) { + // x is the smallest dimension + x1 = point[1]; + y1 = point[2]; + x2 = tri[0][1]; + y2 = tri[0][2]; + x3 = tri[1][1]; + y3 = tri[1][2]; + rx = tri[2][1]; + ry = tri[2][2]; + } else if ( fabs(min_dim-dif[1]) <= DBL_EPSILON ) { + // y is the smallest dimension + x1 = point[0]; + y1 = point[2]; + x2 = tri[0][0]; + y2 = tri[0][2]; + x3 = tri[1][0]; + y3 = tri[1][2]; + rx = tri[2][0]; + ry = tri[2][2]; + } else if ( fabs(min_dim-dif[2]) <= DBL_EPSILON ) { + // z is the smallest dimension + x1 = point[0]; + y1 = point[1]; + x2 = tri[0][0]; + y2 = tri[0][1]; + x3 = tri[1][0]; + y3 = tri[1][1]; + rx = tri[2][0]; + ry = tri[2][1]; + } else { + // all dimensions are really small so lets call it close + // enough and return a successful match + return true; + } + + // check if intersection point is on the same side of p1 <-> p2 as p3 + SGDfloat tmp = (y2 - y3) / (x2 - x3); + int side1 = SGD_SIGN (tmp * (rx - x3) + y3 - ry); + int side2 = SGD_SIGN (tmp * (x1 - x3) + y3 - y1); + if ( side1 != side2 ) { + // printf("failed side 1 check\n"); + return false; + } + + // check if intersection point is on correct side of p2 <-> p3 as p1 + tmp = (y3 - ry) / (x3 - rx); + side1 = SGD_SIGN (tmp * (x2 - rx) + ry - y2); + side2 = SGD_SIGN (tmp * (x1 - rx) + ry - y1); + if ( side1 != side2 ) { + // printf("failed side 2 check\n"); + return false; + } + + // check if intersection point is on correct side of p1 <-> p3 as p2 + tmp = (y2 - ry) / (x2 - rx); + side1 = SGD_SIGN (tmp * (x3 - rx) + ry - y3); + side2 = SGD_SIGN (tmp * (x1 - rx) + ry - y1); + if ( side1 != side2 ) { + // printf("failed side 3 check\n"); + return false; + } + + return true; +} + diff --git a/src/sl/Makefile.am b/src/sl/Makefile.am new file mode 100644 index 0000000..97b3fb3 --- /dev/null +++ b/src/sl/Makefile.am @@ -0,0 +1,20 @@ +if BUILD_SL + +lib_LIBRARIES = libplibsl.a libplibsm.a + +include_HEADERS = sl.h slPortability.h sm.h + +libplibsl_a_SOURCES = \ + slDSP.cxx slSample.cxx slEnvelope.cxx \ + slPlayer.cxx slMODPlayer.cxx slSamplePlayer.cxx \ + slScheduler.cxx slMODdacio.cxx slMODfile.cxx \ + slMODinst.cxx slMODnote.cxx slMODPrivate.h slMODfile.h + +libplibsm_a_SOURCES = slPortability.h smMixer.cxx + +INCLUDES = -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = sl.dsp + diff --git a/src/sl/sl.dsp b/src/sl/sl.dsp new file mode 100644 index 0000000..9748bfd --- /dev/null +++ b/src/sl/sl.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="sl" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=sl - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sl.mak" CFG="sl - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sl - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "sl - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sl - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /Zi /O2 /I "..\util" /I ".." /D "NDEBUG" /D "_LIB" /D "NEEDNAMESPACESTD" /D "HAVE_CONFIG_H" /D "HAVE_WINDOWS_H" /D "WIN32" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy sl.h ..\..\sl.h copy slPortability.h ..\..\slPortability.h copy sm.h ..\..\sm.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "sl - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\util" /I ".." /D "_DEBUG" /D "_LIB" /D "NEEDNAMESPACESTD" /D "HAVE_CONFIG_H" /D "HAVE_WINDOWS_H" /D "WIN32" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\sl_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy sl.h ..\..\sl.h copy slPortability.h ..\..\slPortability.h copy sm.h ..\..\sm.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "sl - Win32 Release" +# Name "sl - Win32 Debug" +# Begin Source File + +SOURCE=.\sl.h +# End Source File +# Begin Source File + +SOURCE=.\slDSP.cxx +# End Source File +# Begin Source File + +SOURCE=.\slEnvelope.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODdacio.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODfile.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODfile.h +# End Source File +# Begin Source File + +SOURCE=.\slMODinst.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODnote.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODPlayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\slMODPrivate.h +# End Source File +# Begin Source File + +SOURCE=.\slPlayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\slPortability.h +# End Source File +# Begin Source File + +SOURCE=.\slSample.cxx +# End Source File +# Begin Source File + +SOURCE=.\slSamplePlayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\slScheduler.cxx +# End Source File +# Begin Source File + +SOURCE=.\sm.h +# End Source File +# Begin Source File + +SOURCE=.\smMixer.cxx +# End Source File +# End Target +# End Project diff --git a/src/sl/sl.h b/src/sl/sl.h new file mode 100644 index 0000000..65325eb --- /dev/null +++ b/src/sl/sl.h @@ -0,0 +1,731 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sl.h 1924 2004-04-06 13:32:26Z sjbaker $ +*/ + + +#ifndef __SL_H__ +#define __SL_H__ 1 + +#include +#include "slPortability.h" + +#ifdef SL_USING_OSS_AUDIO +#define SLDSP_DEFAULT_DEVICE "/dev/dsp" +#elif defined(UL_WIN32) +#define SLDSP_DEFAULT_DEVICE "dsp" +#elif defined(UL_BSD) +#define SLDSP_DEFAULT_DEVICE "/dev/audio" +#elif defined(UL_IRIX) +#define SLDSP_DEFAULT_DEVICE "dsp" // dummy ... +#elif defined(UL_AIX) +#define SLDSP_DEFAULT_DEVICE "dsp" // dummy ... +#elif defined(UL_SOLARIS) +#define SLDSP_DEFAULT_DEVICE "/dev/audio" +#elif defined(UL_HPUX) +#define SLDSP_DEFAULT_DEVICE "/dev/audio" +#elif defined(UL_MACINTOSH) || defined(UL_MAC_OSX) +#define SLDSP_DEFAULT_DEVICE "dsp" // dummy +#else +#error "Port me !" +#endif + +# define SL_TRUE 1 +# define SL_FALSE 0 + +typedef unsigned char Uchar ; +typedef unsigned short Ushort ; + +#define SL_DEFAULT_SAMPLING_RATE 11025 + +class slSample ; +class slPlayer ; +class slSamplePlayer ; +class slEnvelope ; +class slScheduler ; +class slDSP ; + +extern const char *__slPendingError ; + +class slDSP +{ +private: + + int stereo ; + int rate ; + int bps ; + + int error ; + int fd ; + +#if defined(SL_USING_OSS_AUDIO) + audio_buf_info buff_info ; +#elif defined(UL_BSD) + audio_info_t ainfo; // ioctl structure + audio_offset_t audio_offset; // offset in audiostream + long counter; // counter-written packets +#elif defined(UL_SOLARIS) + audio_info_t ainfo; + long counter; +#elif defined(UL_IRIX) + ALconfig config; // configuration stuff + ALport port; // .. we are here +#elif defined(UL_MACINTOSH) || defined(UL_MAC_OSX) + +// Size of the data chunks written with write(). +// This should be a multiple of 1024. +#define BUFFER_SIZE 8192 + +// Size of storage space for sound data. +// This should be evenly divisible by BUFFER_SIZE. +#define VIRTUAL_BUFFER_SIZE 131072 + + SndChannelPtr sndChannel ; // sound channel we are using + ExtSoundHeader extSndHeader ; // sound header for bufferCmd + SndCommand currentCmd ; // command we are sending to chnl + OSErr osErr ; + + char *buf ; // buffer containing sound data + char *rpos ; // read position for buffer + char *wpos ; // write position for buffer + char *ptr ; // ptr to data from write() + + float bytesPerSample ; // conversions + float bytesPerSecond ; + float secondsPerPacket ; + + float secLeft ; // seconds left in buffer + float secUsed ; // seconds used in buffer + UnsignedWide lastTime ; // used for timing in secondsUsed() + UnsignedWide currTime ; + +#endif + + +#if !defined(UL_WIN32) && !defined(UL_MACINTOSH) && !defined(UL_MAC_OSX) + int ioctl ( int cmd, int param = 0 ) + { + if ( error ) return param ; + + if ( ::ioctl ( fd, cmd, & param ) == -1 ) + { + perror ( "slDSP: ioctl" ) ; + error = SL_TRUE ; + } + + return param ; + } + +#elif defined(UL_WIN32) + +#define BUFFER_COUNT (3*8) +#define BUFFER_SIZE (1024*1) + + friend void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + HWAVEOUT hWaveOut; // device handle + WAVEFORMATEX Format; // open needs this + MMTIME mmt; // timing + WAVEHDR *wavehdr[ BUFFER_COUNT ]; // for round robin .. + int curr_header; // index of actual wavehdr + int counter; // counter-written packets + DWORD written; // counter-written samples + +#endif + + void open ( const char *device, int _rate, int _stereo, int _bps ) ; + void close () ; + void getBufferInfo () ; + void write ( void *buffer, size_t length ) ; + +protected: + + void setError () { error = SL_TRUE ; } + int getDriverBufferSize () ; + +public: + + slDSP ( int _rate = SL_DEFAULT_SAMPLING_RATE, + int _stereo = SL_FALSE, int _bps = 8 ) + { + open ( SLDSP_DEFAULT_DEVICE, _rate, _stereo, _bps ) ; + } + + slDSP ( const char *device, int _rate = SL_DEFAULT_SAMPLING_RATE, + int _stereo = SL_FALSE, int _bps = 8 ) + { + open ( device, _rate, _stereo, _bps ) ; + } + + ~slDSP () { close () ; } + + float secondsRemaining () ; + float secondsUsed () ; + + void play ( void *buffer, size_t length ) { write ( buffer, length ) ; } + + /* This was a typo - but people use it */ + int not_working() const { return error ; } + + int notWorking () const { return error ; } + + int getBps () const { return bps ; } + int getRate () const { return rate ; } + int getStereo() const { return stereo ; } + + void sync () ; + void stop () ; +} ; + + +class slSample +{ + int ref_count ; +protected: + + char *comment; + int rate ; + int bps ; + int stereo ; + + Uchar *buffer ; + int length ; + + void init () + { + ref_count = 0 ; + comment = NULL ; + buffer = NULL ; + length = 0 ; + rate = SL_DEFAULT_SAMPLING_RATE ; + bps = 8 ; + stereo = SL_FALSE ; + } + +public: + + slSample () { init () ; } + + slSample ( const Uchar *buff, int leng ) + { + init () ; + setBuffer ( buff, leng ) ; + } + + slSample ( const char *fname, const class slDSP *dsp = NULL ) + { + if ( dsp != NULL && dsp->notWorking () ) + dsp = NULL ; + + init () ; + loadFile ( fname ) ; + autoMatch ( dsp ) ; + } + + ~slSample () + { + if ( ref_count != 0 && __slPendingError == NULL ) + __slPendingError = + "slSample: FATAL ERROR - Application deleted a sample while it was playing." ; + + delete [] buffer ; + } + + void ref () { ref_count++ ; } + void unRef () { ref_count-- ; } + + int getPlayCount () const { return ref_count ; } + + char *getComment () const { return comment ; } + + void setComment ( const char *nc ) + { + delete [] comment ; + comment = ulStrDup ( nc ) ; + } + + Uchar *getBuffer () const { return buffer ; } + int getLength () const { return length ; } + + void autoMatch ( const slDSP *dsp ) ; + + void setBuffer ( const Uchar *buff, int leng ) + { + delete [] buffer ; + + buffer = new Uchar [ leng ] ; + + if ( buff != NULL ) + memcpy ( buffer, buff, leng ) ; + + length = leng ; + } + + /* These routines only set flags - use changeXXX () to convert a sound */ + + void setRate ( int r ) { rate = r ; } + void setBps ( int b ) { bps = b ; } + void setStereo ( int s ) { stereo = s ; } + + int getRate () const { return rate ; } + int getBps () const { return bps ; } + int getStereo () const { return stereo ; } + + float getDuration () const { return (float) getLength() / + (float) ( (getStereo()?2.0f:1.0f)* + (getBps()/8.0f)*getRate() ) ; } + + int loadFile ( const char *fname ) ; + + int loadRawFile ( const char *fname ) ; + int loadAUFile ( const char *fname ) ; + int loadWavFile ( const char *fname ) ; + + void changeRate ( int r ) ; + void changeBps ( int b ) ; + void changeStereo ( int s ) ; + void changeToUnsigned () ; + + void adjustVolume ( float vol ) ; + + void print ( FILE *fd ) const + { + if ( buffer == NULL ) + { + fprintf ( fd, "Empty sample buffer\n" ) ; + } + else + { + fprintf ( fd, "\"%s\"\n",(getComment() == NULL || + getComment()[0]=='\0') ? "Sample" : comment ) ; + fprintf ( fd, "%s, %d bits per sample.\n", + getStereo() ? "Stereo" : "Mono", getBps() ) ; + fprintf ( fd, "%gKHz sample rate.\n", (float) getRate() / 1000.0f ) ; + fprintf ( fd, "%d bytes of samples == %g seconds duration.\n", getLength(), getDuration() ) ; + } + } +} ; + + +enum slSampleStatus +{ + SL_SAMPLE_WAITING, /* Sound hasn't started playing yet */ + SL_SAMPLE_RUNNING, /* Sound has started playing */ + SL_SAMPLE_DONE , /* Sound is complete */ + SL_SAMPLE_PAUSED /* Sound hasn't started playing yet */ +} ; + + +enum slPreemptMode +{ + SL_SAMPLE_CONTINUE, /* Don't allow yourself to be preempted */ + SL_SAMPLE_ABORT , /* Abort playing the sound when preempted */ + SL_SAMPLE_RESTART , /* Restart the sound when load permits */ + SL_SAMPLE_MUTE , /* Continue silently until load permits */ + SL_SAMPLE_DELAY /* Pause until load permits */ +} ; + + +enum slReplayMode +{ + SL_SAMPLE_LOOP, /* Loop sound so that it plays forever */ + SL_SAMPLE_ONE_SHOT /* Play sound just once */ +} ; + +enum slEvent +{ + SL_EVENT_COMPLETE, /* Sound finished playing */ + SL_EVENT_LOOPED, /* Sound looped back to the start */ + SL_EVENT_PREEMPTED /* Sound was preempted */ +} ; + +typedef void (*slCallBack) ( slSample *, slEvent, int ) ; + +class slEnvelope +{ +protected: + + float *time ; + float *value ; + int nsteps ; + int ref_count ; + float previous_value ; + unsigned char prev_pitchenv ; + slReplayMode replay_mode ; + + int getStepDelta ( float *_time, float *delta ) const ; + +public: + + slEnvelope ( int _nsteps, slReplayMode _rm, float *_times, float *_values ) + { + ref_count = 0 ; + nsteps = _nsteps ; + time = new float [ nsteps ] ; + value = new float [ nsteps ] ; + memcpy ( time , _times , sizeof(float) * nsteps ) ; + memcpy ( value, _values, sizeof(float) * nsteps ) ; + prev_pitchenv = 0x80 ; + previous_value = 0.0f ; + replay_mode = _rm ; + } + + + slEnvelope ( int _nsteps = 1, slReplayMode _rm = SL_SAMPLE_ONE_SHOT ) + { + ref_count = 0 ; + nsteps = _nsteps ; + time = new float [ nsteps ] ; + value = new float [ nsteps ] ; + prev_pitchenv = 0x80 ; + previous_value = 0.0f ; + + for ( int i = 0 ; i < nsteps ; i++ ) + time [ i ] = value [ i ] = 0.0 ; + + replay_mode = _rm ; + } + + ~slEnvelope () + { + if ( ref_count != 0 && __slPendingError == NULL ) + __slPendingError = + "slEnvelope: FATAL ERROR - Application deleted an envelope while it was playing.\n" ; + + delete [] time ; + delete [] value ; + } + + void ref () { ref_count++ ; } + void unRef () { ref_count-- ; } + + int getPlayCount () const { return ref_count ; } + + void applyToLPFilter ( unsigned char *dst, + unsigned char *src, + int nframes, int start ) ; + + void setStep ( int n, float _time, float _value ) + { + if ( n >= 0 && n < nsteps ) + { + time [ n ] = _time ; + value [ n ] = _value ; + } + } + + float getStepValue ( int s ) const { return value [ s ] ; } + float getStepTime ( int s ) const { return time [ s ] ; } + + int getNumSteps () const { return nsteps ; } + + float getValue ( float _time ) const ; + + void applyToPitch ( Uchar *dst, slPlayer *src, int nframes, int start, int next_env ) ; + void applyToInvPitch ( Uchar *dst, slPlayer *src, int nframes, int start, int next_env ) ; + void applyToVolume ( Uchar *dst, Uchar *src, int nframes, int start ) ; + void applyToInvVolume ( Uchar *dst, Uchar *src, int nframes, int start ) ; +} ; + +#define SL_MAX_PRIORITY 16 +#define SL_MAX_SAMPLES 32 +#define SL_MAX_CALLBACKS (SL_MAX_SAMPLES * 2) +#define SL_MAX_ENVELOPES 32 +#define SL_MAX_MIXERINPUTS 16 + +enum slEnvelopeType +{ + SL_PITCH_ENVELOPE , SL_INVERSE_PITCH_ENVELOPE , + SL_VOLUME_ENVELOPE, SL_INVERSE_VOLUME_ENVELOPE, + SL_FILTER_ENVELOPE, SL_INVERSE_FILTER_ENVELOPE, + SL_PAN_ENVELOPE , SL_INVERSE_PAN_ENVELOPE , + SL_ECHO_ENVELOPE , SL_INVERSE_ECHO_ENVELOPE , + + SL_NULL_ENVELOPE +} ; + +struct slPendingCallBack +{ + slCallBack callback ; + slSample *sample ; + slEvent event ; + int magic ; +} ; + +class slPlayer +{ +protected: + + slEnvelope *env [ SL_MAX_ENVELOPES ] ; + slEnvelopeType env_type [ SL_MAX_ENVELOPES ] ; + int env_start_time [ SL_MAX_ENVELOPES ] ; + + slReplayMode replay_mode ; + slPreemptMode preempt_mode ; + slSampleStatus status ; + int priority ; + + slCallBack callback ; + int magic ; + + virtual void low_read ( int nframes, Uchar *dest ) = 0 ; + virtual void skip ( int nframes ) = 0 ; + +public: + + slPlayer ( slReplayMode rp_mode = SL_SAMPLE_ONE_SHOT, + int pri = 0, slPreemptMode pr_mode = SL_SAMPLE_DELAY, + int _magic = 0, slCallBack cb = NULL ) + { + magic = _magic ; + callback = cb ; + + for ( int i = 0 ; i < SL_MAX_ENVELOPES ; i++ ) + { + env [ i ] = NULL ; + env_type [ i ] = SL_NULL_ENVELOPE ; + } + + status = SL_SAMPLE_WAITING ; + replay_mode = rp_mode ; + preempt_mode = pr_mode ; + priority = pri ; + } + + virtual ~slPlayer () ; + + slPreemptMode getPreemptMode () const { return preempt_mode ; } + + int getPriority () const + { + return ( isRunning() && + preempt_mode == SL_SAMPLE_CONTINUE ) ? (SL_MAX_PRIORITY+1) : + priority ; + } + + void addEnvelope ( int i, slEnvelope *_env, slEnvelopeType _type ) ; + + virtual void pause () + { + if ( status != SL_SAMPLE_DONE ) + status = SL_SAMPLE_PAUSED ; + } + + virtual void resume () + { + if ( status == SL_SAMPLE_PAUSED ) + status = SL_SAMPLE_RUNNING ; + } + + virtual void reset () + { + status = SL_SAMPLE_WAITING ; + } + + virtual void start () + { + status = SL_SAMPLE_RUNNING ; + } + + virtual void stop () + { + status = SL_SAMPLE_DONE ; + } + + int getMagic () const { return magic ; } + int isWaiting () const { return status == SL_SAMPLE_WAITING ; } + int isPaused () const { return status == SL_SAMPLE_PAUSED ; } + int isRunning () const { return status == SL_SAMPLE_RUNNING ; } + int isDone () const { return status == SL_SAMPLE_DONE ; } + + virtual int preempt ( int delay ) ; + + virtual slSample *getSample () const = 0 ; + + void read ( int nframes, Uchar *dest, int next_env = 0 ) ; +} ; + + +class MODfile ; + +class slMODPlayer : public slPlayer +{ + MODfile *mf ; + + void low_read ( int nframes, Uchar *dest ) ; + void init ( const char *fname ) ; +public: + + slMODPlayer ( const char *fname, slReplayMode rp_mode = SL_SAMPLE_ONE_SHOT, + int pri = 0, slPreemptMode pr_mode = SL_SAMPLE_DELAY, + int _magic = 0, slCallBack cb = NULL ) : + slPlayer ( rp_mode, pri, pr_mode, _magic, cb ) + { + init ( fname ) ; + reset () ; + } + + ~slMODPlayer () ; + + int preempt ( int delay ) ; + virtual slSample *getSample () const { return NULL ; } + + void skip ( int nframes ) ; +} ; + + +class slSamplePlayer : public slPlayer +{ + int lengthRemaining ; /* Sample frames remaining until repeat */ + Uchar *bufferPos ; /* Sample frame to replay next */ + slSample *sample ; + + void low_read ( int nframes, Uchar *dest ) ; + +public: + + slSamplePlayer ( slSample *s, slReplayMode rp_mode = SL_SAMPLE_ONE_SHOT, + int pri = 0, slPreemptMode pr_mode = SL_SAMPLE_DELAY, + int _magic = 0, slCallBack cb = NULL ) : + slPlayer ( rp_mode, pri, pr_mode, _magic, cb ) + { + sample = s ; + + if ( sample ) sample -> ref () ; + + reset () ; + } + + ~slSamplePlayer () ; + + int preempt ( int delay ) ; + + void reset () + { + slPlayer::reset () ; + + lengthRemaining = sample->getLength () ; + bufferPos = sample->getBuffer () ; + } + + void start () + { + slPlayer::start () ; + + lengthRemaining = sample->getLength () ; + bufferPos = sample->getBuffer () ; + } + + void stop () + { + slPlayer::stop () ; + + lengthRemaining = 0 ; + bufferPos = NULL ; + } + + slSample *getSample () const { return sample ; } + + void skip ( int nframes ) ; +} ; + + +class slScheduler : public slDSP +{ + slPendingCallBack pending_callback [ SL_MAX_CALLBACKS ] ; + int num_pending_callbacks ; + + float safety_margin ; + + int mixer_buffer_size, mixer_gain ; + float seconds_per_buffer; + + Uchar *mixer_buffer ; + Uchar *mixer_inputs [ SL_MAX_MIXERINPUTS + 1 ] ; + + Uchar *mixer ; + int amount_left ; + + slPlayer *player [ SL_MAX_SAMPLES ] ; + slPlayer *music ; + + void init () ; + + void realUpdate ( int dump_first = SL_FALSE ) ; + + void initBuffers () ; + + int now ; + + static slScheduler *current ; + +public: + + slScheduler ( int _rate = SL_DEFAULT_SAMPLING_RATE ) : slDSP ( _rate, SL_FALSE, 8 ) { init () ; } + slScheduler ( const char *device, + int _rate = SL_DEFAULT_SAMPLING_RATE ) : slDSP ( device, _rate, SL_FALSE, 8 ) { init () ; } + ~slScheduler () ; + + static slScheduler *getCurrent () { return current ; } + + int getTimeNow () const { return now ; } + float getElapsedTime ( int then ) const { return (float)(now-then)/(float)getRate() ; } + + void setMaxConcurrent ( int mc ); + int getMaxConcurrent () const ; + + void flushCallBacks () ; + void addCallBack ( slCallBack c, slSample *s, slEvent e, int m ) ; + + void update () { realUpdate ( SL_FALSE ) ; } + void dumpUpdate () { realUpdate ( SL_TRUE ) ; } + + void resumeSample ( slSample *s = NULL, int magic = 0 ) ; + void resumeMusic ( int magic = 0 ) ; + void pauseSample ( slSample *s = NULL, int magic = 0 ) ; + void pauseMusic ( int magic = 0 ) ; + void stopSample ( slSample *s = NULL, int magic = 0 ) ; + void stopMusic ( int magic = 0 ) ; + + void addSampleEnvelope ( slSample *s = NULL, int magic = 0, + int slot = 1, slEnvelope *e = NULL, + slEnvelopeType t = SL_VOLUME_ENVELOPE ) ; + void addMusicEnvelope ( int magic = 0, + int slot = 1, slEnvelope *e = NULL, + slEnvelopeType t = SL_VOLUME_ENVELOPE ) ; + int loopSample ( slSample *s, int pri = 0, + slPreemptMode mode = SL_SAMPLE_MUTE, + int magic = 0, slCallBack cb = NULL ) ; + int loopMusic ( const char *fname, int pri = 0, + slPreemptMode mode = SL_SAMPLE_MUTE, + int magic = 0, slCallBack cb = NULL ) ; + int playSample ( slSample *s, int pri = 1, + slPreemptMode mode = SL_SAMPLE_ABORT, + int magic = 0, slCallBack cb = NULL ) ; + int playMusic ( const char *fname, int pri = 1, + slPreemptMode mode = SL_SAMPLE_ABORT, + int magic = 0, slCallBack cb = NULL ) ; + + void setSafetyMargin ( float seconds ) { safety_margin = seconds ; } +} ; + +#endif + diff --git a/src/sl/slDSP.cxx b/src/sl/slDSP.cxx new file mode 100644 index 0000000..933e20b --- /dev/null +++ b/src/sl/slDSP.cxx @@ -0,0 +1,1085 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slDSP.cxx 1775 2003-07-04 14:33:03Z puggles $ +*/ + + +#include "sl.h" +#include + +static int init_bytes ; + +#ifdef SL_USING_OSS_AUDIO + +/* ------------------------------------------------------------ */ +/* OSSAUDIO - Linux, FreeBSD, etc */ +/* ------------------------------------------------------------ */ + +void slDSP::open ( const char *device, int _rate, int _stereo, int _bps ) +{ + fd = ::open ( device, O_WRONLY | O_NONBLOCK ) ; + + if ( fd < 0 ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + + stereo = SL_FALSE ; + bps = 1 ; + rate = 8000 ; + init_bytes = 0 ; + } + else + { + error = SL_FALSE ; + + /* Set up a driver fragment size of 1024 (ie 2^10) */ + +errno = 0 ; + ioctl ( SNDCTL_DSP_SETFRAGMENT, 0x7FFF000A ) ; + + stereo = ioctl ( SOUND_PCM_WRITE_CHANNELS, _stereo ? 2 : 1 ) >= 2 ; + bps = ioctl ( SOUND_PCM_WRITE_BITS, _bps ) ; + rate = ioctl ( SOUND_PCM_WRITE_RATE, _rate ) ; + +if ( errno != 0 ) perror ( "slDSP: ioctl" ) ; + + getBufferInfo () ; + init_bytes = buff_info.bytes ; + } +} + + +void slDSP::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + getBufferInfo () ; + return buff_info.fragsize ; +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; + + if ( ::ioctl ( fd, SNDCTL_DSP_GETOSPACE, & buff_info ) < 0 ) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + if ( error || (int)length <= 0 ) + return ; + + size_t nwritten = ::write ( fd, (const char *) buffer, length ) ; + + if ( (int)nwritten < 0 ) + perror ( "slDSP: write" ) ; + else + if ( nwritten != length ) + perror ( "slDSP: short write" ) ; +} + + +float slDSP::secondsRemaining () +{ + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + int samples_left = buff_info.fragments * buff_info.fragsize ; + + if ( stereo ) samples_left /= 2 ; + if ( bps == 16 ) samples_left /= 2 ; + return (float) samples_left / (float) rate ; +} + + +float slDSP::secondsUsed () +{ + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + int samples_used = init_bytes - buff_info.bytes ; + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ + if ( !error) ::ioctl ( fd, SOUND_PCM_SYNC , 0 ) ; +} + +void slDSP::stop () +{ + if ( !error) ::ioctl ( fd, SOUND_PCM_RESET, 0 ) ; +} + +#endif + +#ifdef UL_WIN32 + +/* ------------------------------------------------------------ */ +/* win32 */ +/* ------------------------------------------------------------ */ + +static void wperror(MMRESULT num) +{ + char buffer[0xff]; // yes, this is hardcoded :-) + + waveOutGetErrorText( num, buffer, sizeof(buffer)-1); + + ulSetError ( UL_WARNING, "SlDSP: %s (%d)", buffer, num ); +} + + + +void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + slDSP *pDsp = (slDSP *)dwInstance; + switch( uMsg ) + { + case WOM_CLOSE: + break; + + case WOM_OPEN: + break; + + case WOM_DONE: + pDsp->counter--; + break; + } +} + + +void slDSP::open ( const char *device, int _rate, int _stereo, int _bps ) +{ + MMRESULT result; + int i; + + hWaveOut = NULL; + curr_header = 0; + counter = 0; + written = 0; + for ( i = 0; i < BUFFER_COUNT; i++ ) wavehdr[i] = NULL; + + Format.wFormatTag = WAVE_FORMAT_PCM; + Format.nChannels = _stereo ? 2 : 1; + Format.nSamplesPerSec = _rate; + Format.wBitsPerSample = _bps; + Format.nBlockAlign = 1; + Format.nAvgBytesPerSec = _rate * Format.nChannels; + Format.cbSize = 0; + + result = waveOutOpen( & hWaveOut, WAVE_MAPPER, & Format, 0, + 0L, WAVE_FORMAT_QUERY ); + + if ( result != MMSYSERR_NOERROR ) + { + wperror( result); + + error = SL_TRUE ; + stereo = SL_FALSE ; + bps = _bps ; + rate = _rate ; + init_bytes = 0 ; + + return; + } + +#if 0 + ulSetError ( UL_DEBUG, "Request: stereo=%d bps=%d rate=%d", + _stereo, _bps, _rate ); + ulSetError ( UL_DEBUG, "Result: channels=%d bps=%d rate=%d", + Format.nChannels, Format.wBitsPerSample, + Format.nSamplesPerSec ); +#endif + + // Now the hwaveouthandle "should" be valid + + if ( ( result = waveOutOpen( & hWaveOut, WAVE_MAPPER, + (WAVEFORMATEX *)& Format, (DWORD)waveOutProc, + (DWORD)this, CALLBACK_FUNCTION )) != MMSYSERR_NOERROR ) + { + wperror( result); + + error = SL_TRUE ; + stereo = SL_FALSE ; + bps = _bps ; + rate = _rate ; + init_bytes = 0 ; + return; + } + else + { + error = SL_FALSE ; + stereo = _stereo; + bps = _bps; + rate = _rate; + + /* hmm ?! */ + + init_bytes = BUFFER_SIZE; + + for ( i = 0; i < BUFFER_COUNT; i++ ) + { + Uchar *p = new Uchar[sizeof(WAVEHDR) + BUFFER_SIZE]; + + wavehdr[i] = (WAVEHDR*) p; + wavehdr[i]->lpData = (LPSTR) p + sizeof(WAVEHDR); + wavehdr[i]->dwBufferLength = (DWORD) BUFFER_SIZE; + wavehdr[i]->dwBytesRecorded = 0L; + wavehdr[i]->dwUser = 0; + wavehdr[i]->dwFlags = 0; + wavehdr[i]->dwLoops = 0; + wavehdr[i]->lpNext = 0; + wavehdr[i]->reserved = 0; + + result = waveOutPrepareHeader( hWaveOut, + wavehdr[i], sizeof(WAVEHDR) ); + + if ( result != MMSYSERR_NOERROR ) + { + wperror ( result ); + error = SL_TRUE; + return; + } + } + } +} + + +void slDSP::close () +{ + if ( hWaveOut != NULL ) + { + waveOutReset( hWaveOut ); + + for ( int i = 0; i < BUFFER_COUNT; i++ ) + { + waveOutUnprepareHeader( hWaveOut, wavehdr[i], sizeof(WAVEHDR) ); + + delete[] (Uchar*) wavehdr[i]; + } + + waveOutClose( hWaveOut ); + hWaveOut = NULL; + } +} + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + /* hmm ?! */ + + return BUFFER_SIZE; +} + +void slDSP::getBufferInfo () +{ + return ; +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + MMRESULT result; + + if ( error || (int)length <= 0 ) + return ; + +#if 0 + ulSetError ( UL_DEBUG, "written=%ld counter=%d curr_header=%d", + written, counter, curr_header ); +#endif + + memcpy(wavehdr[curr_header]->lpData, buffer, length); + wavehdr[curr_header]->dwBufferLength = (DWORD) length; + + result = waveOutWrite(hWaveOut, wavehdr[curr_header], sizeof(WAVEHDR)); + + if ( result != MMSYSERR_NOERROR ) + { + wperror ( result ); + error = SL_TRUE; + } + + counter ++; + written += (DWORD) BUFFER_SIZE; + + curr_header = ( curr_header + 1 ) % BUFFER_COUNT; +} + + +float slDSP::secondsRemaining () +{ + if ( error ) + return 0.0f ; + + return 10.0f ; +} + + +float slDSP::secondsUsed () +{ + DWORD samples_used; + MMRESULT result; + float samp_time; + + if ( error ) + return 0.0f ; + + mmt.wType = TIME_BYTES; + + result = waveOutGetPosition( hWaveOut, &mmt, sizeof( mmt )); + + if ( mmt.u.cb == 0 || counter == 0) + return (float)0.0; + + if ( counter < BUFFER_COUNT ) + samples_used = written - mmt.u.cb ; + else + samples_used = BUFFER_COUNT * BUFFER_SIZE ; + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + + samp_time = (float) samples_used / (float) rate ; + +#if 0 + ulSetError ( UL_DEBUG, "%0.2f written packets=%ld stereo=%d bps=%d rate=%d", + samp_time, counter, stereo, bps, rate ); +#endif + + return samp_time; +} + + +void slDSP::sync () +{ +} + +void slDSP::stop () +{ + if ( error ) + return ; + + waveOutReset( hWaveOut ); + written = 0 ; +} + +/* ------------------------------------------------------------ */ +/* NetBSD/OpenBSD 2.3 this should be very close to SUN Audio */ +/* ------------------------------------------------------------ */ + +#elif (defined(UL_BSD) && !defined(__FreeBSD__)) || defined(UL_SOLARIS) +void slDSP::open ( const char *device, int _rate, int _stereo, int _bps ) +{ + + counter = 0; + + fd = ::open ( device, O_RDWR | O_NONBLOCK ) ; + + if ( fd < 0 ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + } + else + { + + if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) == -1) + { + perror("slDSP: open - getinfo"); + stereo = SL_FALSE ; + bps = 8 ; + rate = 8000 ; + init_bytes = 0 ; + + return; + } + +#ifdef UL_SOLARIS + AUDIO_INITINFO(&ainfo); +#endif + + ainfo.play.sample_rate = _rate; + ainfo.play.precision = _bps; +#ifdef UL_SOLARIS + if ( ainfo.play.port == AUDIO_SPEAKER ) + ainfo.play.channels = 1; + else + ainfo.play.channels = _stereo ? 2 : 1; + + ainfo.play.encoding = AUDIO_ENCODING_ALAW; + +// if ( _bps < 16 ) +// { +// ainfo.play.encoding = AUDIO_ENCODING_LINEAR8; +// } +// else +// { +// ainfo.play.encoding = AUDIO_ENCODING_LINEAR; +// } +#else + ainfo.play.encoding = AUDIO_ENCODING_ULINEAR; +#endif + + if( :: ioctl(fd, AUDIO_SETINFO, &ainfo) == -1) + { + perror("slDSP: open - setinfo"); + stereo = SL_FALSE ; + bps = 8 ; + rate = 8000 ; + init_bytes = 0 ; + return; + } + + rate = _rate; + stereo = _stereo; + bps = _bps; + + error = SL_FALSE ; + + getBufferInfo (); + + // I could not change the size, + // so let's try this ... + + init_bytes = 1024 * 8; + } +} + + +void slDSP::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + getBufferInfo () ; + + // HW buffer is 0xffff on my box + +#ifdef UL_SOLARIS + return ainfo.play.buffer_size; +#else + return 1024 * 8; +#endif + +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; + + if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) < 0) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } + +#ifndef UL_SOLARIS + if( ::ioctl( fd, AUDIO_GETOOFFS, &audio_offset ) < 0) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } +#endif +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + if ( error || (int)length <= 0 ) + return ; + + size_t nwritten = ::write ( fd, (const char *) buffer, length ) ; + + if ( (int)nwritten < 0 ) + perror ( "slDSP: write" ) ; + else if ( nwritten != length ) + perror ( "slDSP: short write" ) ; + + counter ++; /* hmmm */ +} + + +float slDSP::secondsRemaining () +{ + return 10.0f ; +} + + +float slDSP::secondsUsed () +{ + /* + * original formula from Steve: + * ----------------------------- + * + * int samples_used = init_bytes - buff_info.bytes ; + * | | + * | +--- current available + * | space in bytes ! + * +---------------- available space + * when empty; + * + * sample_used contains the number of bytes which are + * "used" or in the DSP "pipeline". + */ + + + int samples_used; + + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + //This is wrong: this is the hw queue in the kernel ! + //samples_used = ainfo.play.buffer_size - audio_offset.offset ; + + // This is: all data written minus where we are now in the queue + + if ( counter == 0 ) + return 0.0; + +#ifdef UL_SOLARIS + samples_used = ( counter * init_bytes ) - ainfo.play.samples; +#else + samples_used = ( counter * init_bytes ) - audio_offset.samples; +#endif + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ +#ifdef UL_SOLARIS + if ( !error) ::ioctl ( fd, I_FLUSH, FLUSHRW ) ; +#else + if ( !error) ::ioctl ( fd, AUDIO_FLUSH , 0 ) ; +#endif +} + +void slDSP::stop () +{ + // nothing found yet +} + +/* ------------------------------------------------------------ */ +/* SGI IRIX audio */ +/* ------------------------------------------------------------ */ + +#elif defined(UL_IRIX) + +void slDSP::open ( const char *device, int _rate, int _stereo, int _bps ) +{ + if ( _bps != 8 ) + { + perror ( "slDSP: supports only 8bit audio for sgi" ) ; + error = SL_TRUE; + return; + } + + init_bytes = 1024 * 4 ; + + config = ALnewconfig(); + + ALsetchannels ( config, _stereo ? AL_STEREO : AL_MONO ); + ALsetwidth ( config, _bps == 8 ? AL_SAMPLE_8 : AL_SAMPLE_16 ); + ALsetqueuesize( config, init_bytes * 2 ); + + port = ALopenport( device, "w", config ); + + if ( port == NULL ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + } + else + { + long params[2] = {AL_OUTPUT_RATE, 0 }; + + params[1] = _rate; + + if ( ALsetparams(AL_DEFAULT_DEVICE, params, 2) != 0 ) + { + perror ( "slDSP: open - ALsetparams" ) ; + error = SL_TRUE ; + return; + } + + rate = _rate; + stereo = _stereo; + bps = _bps; + + error = SL_FALSE ; + + } +} + + +void slDSP::close () +{ + if ( port != NULL ) + { + ALcloseport ( port ); + ALfreeconfig( config ); + port = NULL; + } +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + return ALgetqueuesize( config ) / 2 ; +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + char *buf = (char *)buffer; + + if ( error || (int)length <= 0 ) + return ; + + for ( int i = 0; i < (int)length; i++ ) + buf[i] = buf[i] >> 1; + + ALwritesamps(port, (void *)buf, length ); +} + + +float slDSP::secondsRemaining () +{ + int samples_remain; + + if ( error ) + return 0.0f ; + + samples_remain = ALgetfillable(port) ; + + return (float) samples_remain / (float) rate ; +} + + +float slDSP::secondsUsed () +{ + int samples_used; + + if ( error ) + return 0.0f ; + + samples_used = ALgetfilled(port) * 2 ; + + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ + if ( error ) + return ; + + /* found this in the header file - but no description + * or example for the long parameter. + */ + + // ALflush(ALport, long); +} + +void slDSP::stop () +{ +} + + +#endif + + +#if defined(UL_MACINTOSH) || defined(UL_MAC_OSX) + + +// Print out debugging info when secondsUsed is called, +// track useful information, and used extended error checking. +//#define SL_MAC_DEBUG + +pascal void sndCallbackProc ( SndChannelPtr theChan, SndCommand *theCmd ); + void doError ( OSErr theError ); + +int bytesUsed; +bool playing; + +//SCStatus sndChanStatus; + +#ifdef SL_MAC_DEBUG + int underWrites; + int queued; + int writes; + int callBacks; + float roughTime; + int qLength; + int qHead; + int qTail; +#endif + +void doError( OSErr theError ) { + + const char* msg = 0 ; + + switch( theError ) { + + case 0: + msg = "No Error." ; + break; + case notEnoughHardwareErr: + msg = "Insufficient hardware available." ; + break; + case badChannel: + msg = "Channel is corrupt or unusable." ; + break; + case badFormat: + msg = "Resource is corrupt or unusable." ; + break; + case queueFull: + msg = "No room in the queue." ; + break; + case channelBusy: + msg = "Channel is busy." ; + break; + case siInvalidCompression: + msg = "Invalid compression type." ; + break; + case notEnoughBufferSpace: + msg = "Insufficient memory available." ; + break; + case buffersTooSmall: + msg = "Buffer is too small." ; + break; + case paramErr: + msg = "Invalid parameter specified." ; + break; + } + + if ( msg != 0 ) + ulSetError ( UL_WARNING, "OSErr : %s", msg ) ; + else + ulSetError ( UL_WARNING, "OSErr : Unknown Error : %d.", theError ); +} + +pascal void sndCallbackProc ( SndChannelPtr chan, SndCommand *cmd ) +{ + + //SndChannelStatus is a BIG waste of cpu time, but I don't know + //any other way to remedy this. I will leave it out for now. + //SndChannelStatus(chan, sizeof(SCStatus), &sndChanStatus); + //playing = sndChanStatus.scChannelBusy ? true : false; + + bytesUsed -= BUFFER_SIZE ; + +#ifdef SL_MAC_DEBUG + callBacks++; + queued--; + qLength = chan->qLength; + qTail = chan->qTail; + qHead = chan->qHead; +#endif +} + +void slDSP::open ( const char *device, int _rate, int _stereo, int _bps ) +{ + + // Check for valid ranges on inputs + if ( _rate > 65535 ) + { + ulSetError ( UL_WARNING, + "slDsp : Sample rate out of bounds! Setting to 44100hz."); + _rate = 44100; + } + + error = SL_FALSE; + stereo = _stereo; + rate = _rate; + bps = _bps; + osErr = noErr; + + bytesPerSample = (stereo ? 2:1) * (bps/8); + bytesPerSecond = bytesPerSample * rate; + secondsPerPacket = BUFFER_SIZE / bytesPerSecond; + secUsed = 0; + secLeft = 0; + playing = false; + + long initOptions = 0; // Channel init options + if ( stereo ) + initOptions += initStereo; + else + initOptions += initMono; + + // Extra init options available + //initOptions += initNoInterp; // No linear interpolation + //initOptions += initNoDrop; // No drop-sample conversion + //initOptions += initChanLeft; // Left stereo channel + //initOptions += initChanRight; // Right stereo channel + + // Define a call-back routine - invoked via callBackCmd + SndCallBackUPP callBackRoutine = NewSndCallBackUPP ( sndCallbackProc ); + + // Allocate a sound channel + sndChannel = new SndChannel; + sndChannel -> userInfo = 0; + sndChannel -> qLength = 128; // Queue as many as 128 packets at a time; + osErr = SndNewChannel( &sndChannel, sampledSynth, + initOptions, callBackRoutine ); + + if ( osErr != noErr ) + { + SndDisposeChannel( sndChannel, true ); + ulSetError ( UL_WARNING, + "slDSP::open() Problem creating sound channel" ); + doError (osErr); + error = SL_TRUE; + } + + // Format sound header structure + extSndHeader.numChannels = stereo ? 2 : 1; + extSndHeader.sampleRate = rate << 16; + extSndHeader.encode = extSH; + extSndHeader.sampleSize = bps; + + // Allocate the sound buffer + buf = new char [VIRTUAL_BUFFER_SIZE]; + if ( !buf ) + { + ulSetError ( UL_WARNING, + "slDSP::open() Not enough memory to allocate sound buffer." ); + SndDisposeChannel( sndChannel, true ); + error = SL_TRUE; + } + rpos = buf; + wpos = buf; +} + +void slDSP::close () +{ + SndDisposeChannel( sndChannel, true ); + delete [] buf; +} + +void slDSP::write ( void *buffer, size_t length ) +{ + + if ( error || (int)length <= 0 || length > VIRTUAL_BUFFER_SIZE ) + return; + + // Make sure data will fit into available space + if ( length >= VIRTUAL_BUFFER_SIZE - (rpos - buf) ) + wpos = buf; + + // Copy sound data into buffer + rpos = wpos; + ptr = (char*)buffer; + for ( int i = 0; i < length; i++ ) + *wpos++ = *ptr++; + + // Format the sound header + extSndHeader.samplePtr = rpos; + extSndHeader.numFrames = length / bytesPerSample; + + // Format the buffer command + currentCmd.cmd = bufferCmd; + currentCmd.param2 = (long)&extSndHeader; + + // Do the sound command + osErr = SndDoCommand (sndChannel, ¤tCmd, false); + +#ifdef SL_MAC_DEBUG + if ( osErr != noErr ) + { + ulSetError ( UL_WARNING, "Error slDsp::write - bufferCmd() : " ); + doError( osErr ); + error = SL_TRUE; + } +#endif + + // Issue a callBack command when the bufferCmd has finished + currentCmd.cmd = callBackCmd; + osErr = SndDoCommand (sndChannel, ¤tCmd, false); + +#ifdef SL_MAC_DEBUG + if ( osErr != noErr ) + { + ulSetError ( UL_WARNING, "Error slDsp::write - callBackCmd() : " ); + doError( osErr ); + error = SL_TRUE; + } +#endif + + // Add on time for this packet + secUsed += length / bytesPerSecond; + + // Add on bytes used + bytesUsed += BUFFER_SIZE; + + // Reset the timing for the buffer + if (!playing) + { + Microseconds(&lastTime); + playing = true; + } + +#ifdef SL_MAC_DEBUG + queued++; + writes++; +#endif + +} + +void slDSP::sync () +{ + if ( error ) return; + + // flushCmd will remove all queued commands in a channel, + // and the command in progress is not affected. + // This is not exactly what sync() is supposed to do! + currentCmd.cmd = flushCmd; + osErr = SndDoImmediate ( sndChannel, ¤tCmd ); + +#ifdef SL_MAC_DEBUG + if ( osErr != noErr ) + { + ulSetError ( UL_WARNING, "Error slDsp::sync() : " ); + doError( osErr ); + error = SL_TRUE; + } +#endif +} + +void slDSP::stop () +{ + if ( error ) return; + + currentCmd.cmd = quietCmd; + osErr = SndDoImmediate ( sndChannel, ¤tCmd ); + playing = false; + +#ifdef SL_MAC_DEBUG + if ( osErr != noErr ) + { + ulSetError ( UL_WARNING, "Error slDsp::stop() : " ); + doError( osErr ); + error = SL_TRUE; + } +#endif +} + +void slDSP::getBufferInfo () +{ + return; +} + +int slDSP::getDriverBufferSize () +{ + return BUFFER_SIZE; +} + +float slDSP::secondsRemaining () +{ + if ( error ) + return 0.0f; + + // sl doesn't use this, so I didn't write it! + return 10.0f; +} + +float slDSP::secondsUsed () +{ + if ( error || secUsed <= 0 ) + return secUsed = 0; + + Microseconds( &currTime ); + secUsed -= (currTime.lo - lastTime.lo) / 1000000.0; + Microseconds( &lastTime ); + + // This fixes inaccuracy with Microseconds + if ( secUsed > bytesUsed / bytesPerSecond + secondsPerPacket ) + secUsed -= secondsPerPacket; + +#ifdef SL_MAC_DEBUG + if (queued == 0) + underWrites++; + roughTime = bytesUsed / bytesPerSecond; + ulSetError ( UL_DEBUG, "%d\t%d\t%f\t%d\t%d\t%f\t%d\t%d\t%d\t%d", + bytesUsed, queued, secUsed, callBacks, + writes, roughTime, underWrites, qLength, qHead, qTail ); +#endif + + return secUsed; +} + +#endif + + diff --git a/src/sl/slEnvelope.cxx b/src/sl/slEnvelope.cxx new file mode 100644 index 0000000..b905603 --- /dev/null +++ b/src/sl/slEnvelope.cxx @@ -0,0 +1,219 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slEnvelope.cxx 1926 2004-04-06 14:26:03Z sjbaker $ +*/ + + +#include "sl.h" + +float slEnvelope::getValue ( float _time ) const +{ + float delta ; + int step = getStepDelta ( &_time, &delta ) ; + + return delta * (_time - time[step]) + value[step] ; +} + + + +int slEnvelope::getStepDelta ( float *_time, float *delta ) const +{ + float tt ; + + if ( replay_mode == SL_SAMPLE_LOOP ) + { + tt = (float) floor ( *_time / time [ nsteps-1 ] ) ; + *_time -= tt * time [ nsteps-1 ] ; + } + + tt = *_time ; + + if ( tt <= time[ 0 ] ) { *delta = 0.0f ; return 0 ; } + if ( tt >= time[nsteps-1] ) { *delta = 0.0f ; return nsteps-1 ; } + + for ( int i = 1 ; i <= nsteps-1 ; i++ ) + if ( tt <= time[i] ) + { + float t1 = time[i-1] ; float v1 = value[i-1] ; + float t2 = time[ i ] ; float v2 = value[ i ] ; + + if ( t1 == t2 ) + { + *delta = 0.0f ; + return i ; + } + + *delta = (v2-v1) / (t2-t1) ; + return i-1 ; + } + + *delta = 0.0f ; + return nsteps - 1 ; +} + + +void slEnvelope::applyToPitch ( Uchar *dst, slPlayer *src, + int nframes, int start, int next_env ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + unsigned char tmp [ 512 ] ; + float pos = 0 ; + float npos = 0 ; + unsigned char last = prev_pitchenv; + + while ( nframes-- ) + { + npos += _value ; + _value += delta ; + + int offset = (int) ( npos - pos ) ; + + if ( offset > 512 ) + offset = 512 ; + + if ( offset < 1 ) + *(dst++) = last ; + else + { + pos += offset ; + + src -> read ( offset, tmp, next_env ) ; + + *(dst++) = last = tmp [ offset-1 ] ; + prev_pitchenv = last; + } + } +} + + +void slEnvelope::applyToInvPitch ( Uchar *dst, slPlayer *src, + int nframes, int start, int next_env ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + unsigned char tmp [ 512 ] ; + float pos = 0 ; + float npos = 0 ; + unsigned char last = 0x80 ; + + while ( nframes-- ) + { + npos += 1.0f / _value ; + _value += delta ; + + int offset = (int) ( npos - pos ) ; + + if ( offset > 512 ) + offset = 512 ; + + if ( offset < 1 ) + *(dst++) = last ; + else + { + pos += offset ; + + src -> read ( offset, tmp, next_env ) ; + + *(dst++) = last = tmp [ offset-1 ] ; + } + } +} + +void slEnvelope::applyToVolume ( Uchar *dst, Uchar *src, + int nframes, int start ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + while ( nframes-- ) + { + register int res = (int)( (float)((int)*(src++)-0x80) * _value ) + 0x80 ; + + _value += delta ; + + *(dst++) = ( res > 255 ) ? 255 : ( res < 0 ) ? 0 : res ; + } +} + +void slEnvelope::applyToLPFilter ( Uchar *dst, Uchar *src, + int nframes, int start ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + register float p_v = previous_value; + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + while ( nframes-- ) + { + + register float c_v = (float) ((int)*(src++) - 0x80); + register float f=_value; + p_v = p_v*(1.0f - f)+ f*c_v; + register int res = (int)(p_v) + 0x80 ; + _value += delta ; + + *(dst++) = ( res > 255 ) ? 255 : ( res < 0 ) ? 0 : res ; + } + previous_value = p_v; +} + + +void slEnvelope::applyToInvVolume ( Uchar *dst, Uchar *src, + int nframes, int start ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + delta = - delta ; + _value = 1.0f - _value ; + + while ( nframes-- ) + { + register int res = (int)( (float)((int)*(src++)-0x80) * _value ) + 0x80 ; + + _value += delta ; + + *(dst++) = ( res > 255 ) ? 255 : ( res < 0 ) ? 0 : res ; + } +} + + diff --git a/src/sl/slMODPlayer.cxx b/src/sl/slMODPlayer.cxx new file mode 100644 index 0000000..5e8ebc2 --- /dev/null +++ b/src/sl/slMODPlayer.cxx @@ -0,0 +1,101 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODPlayer.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "sl.h" +#include "slMODPrivate.h" +#include "slMODfile.h" + +int slMODPlayer::preempt ( int delay ) +{ + return slPlayer::preempt ( delay ) ; +} + +void slMODPlayer::init ( const char *fname ) +{ + mf = new MODfile ( fname, slScheduler::getCurrent()->getRate (), SL_FALSE ) ; +} + +slMODPlayer::~slMODPlayer () +{ + delete mf ; +} + +void slMODPlayer::skip ( int /* nframes */ ) +{ +} + + +void slMODPlayer::low_read ( int nframes, Uchar *dst ) +{ + if ( isWaiting() ) start () ; + + int need_bytes = nframes ; + int all_done = 0 ; + + while ( need_bytes > 0 && !all_done ) + { + int new_bytes = dacioGetLen () ; + + /* Compute some more audio */ + + while ( new_bytes == 0 && !all_done ) + { + all_done = ! mf -> update () ; + new_bytes = dacioGetLen () ; + } + + /* How much did we get? */ + + if ( new_bytes > need_bytes ) /* oops! Too much */ + { + memcpy ( dst, dacioGetOutBuffer (), need_bytes ) ; + dacioSubtract ( need_bytes ) ; + dst += need_bytes ; + need_bytes = 0 ; + } + else + { + memcpy ( dst, dacioGetOutBuffer (), new_bytes ) ; + dacioEmpty () ; + dst += new_bytes ; + need_bytes -= new_bytes ; + } + } + + /* Pad with silence if not enough data */ + + if ( need_bytes > 0 ) + memset ( dst, 128, need_bytes ) ; + + if ( all_done ) + { + if ( replay_mode == SL_SAMPLE_ONE_SHOT ) + stop () ; + else + start () ; + } +} + + + diff --git a/src/sl/slMODPrivate.h b/src/sl/slMODPrivate.h new file mode 100644 index 0000000..143df19 --- /dev/null +++ b/src/sl/slMODPrivate.h @@ -0,0 +1,159 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODPrivate.h 1732 2002-11-30 00:41:50Z sjbaker $ +*/ + + +#include "ul.h" +#include +#include +#include +#include +#include + +#ifdef UL_MSVC +#include +#include +#else +#include +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +int *dacioGetBuffer () ; +void dacioInit( int speed, int stereo ); +void dacioFlush(void); +void dacioIncomingBufLen(int len); +void dacioGlobalVol(int v); +void dacioOut(void); +void dacioEmpty () ; +void dacioSubtract ( int n ) ; +int dacioGetLen () ; +unsigned char *dacioGetOutBuffer () ; + +#define MAX_OUTRATE 65536 +#define MIN_TEMPO 32 +#define DEF_OUTRATE 44100 +#define DEF_TEMPO 125 +#define DEF_SPEED 6 + + +struct InstHirevInfo +{ + unsigned int w; /* omega: 65536 = 2*pi */ + unsigned int wAcc; /* w accumlator */ + unsigned char *ptr; + unsigned char *end; + unsigned char *loopBeg; + int x_or; + int volL; + int volR; + int fadeout; + int lastL; + int lastR; +} ; + +void _MOD_instHirevInit(void); +void _MOD_instHirevSetFrameLen(int l); +void _MOD_instHirevEraseBuf(void); +void _MOD_instHirevFlushBuf(void); +void _MOD_instHirevLoop(InstHirevInfo *ihip); + +/* sample info */ + +struct SampleInfo +{ + unsigned char *beg; + unsigned char *end; + int x_or; /* 0x80 (or 0x8000?) */ + unsigned char *loopBeg; + int c4spd; + int vol; + int mag; /* magnification */ +} ; + +void _MOD_instOutRate(unsigned int _or); +void _MOD_instSelectCh(int ch); +void _MOD_instClearPFW(void); +void _MOD_instDoPerFrameWorks(int frame); +void _MOD_instLoop(void); +void _MOD_instInit(void); +void _MOD_instNote(int n, int delay); +void _MOD_instMono(int n); +void _MOD_instVol(int v, int delay); +void _MOD_instTuning(int c4spd); +void _MOD_instSample(SampleInfo *sip, int delay); +void _MOD_instVolSlide(void); +void _MOD_instSetVolSlideParams(int d, int mul, int div, int nthFrame, int fine); +void _MOD_instSetVolSlideFast(int onOff); +void _MOD_instPeriodSlideUp(void); +void _MOD_instPeriodSlideDown(void); +void _MOD_instSetPeriodSlideParams(int speed, int fine); +void _MOD_instSetPeriodAmigaLimit(int onOff); +void _MOD_instPortamento(void); +void _MOD_instSetPortamentoTo(int to); +void _MOD_instSetPortamentoSpeed(int speed); +void _MOD_instSetPortamentoDefaultVol(void); +void _MOD_instSetPortamentoGlissando(int onOff); +void _MOD_instArpeggio(void); +void _MOD_instSetArpeggioParams(int plus1, int plus2); +void _MOD_instRetrig(void); +void _MOD_instSetRetrigParam(int nthFrame); +void _MOD_instSampleOffset(int offset); +void _MOD_instVibrato(void); +void _MOD_instSetVibratoParams(int d, int depth); +void _MOD_instSetVibratoWave(int type, int noRetrig); +void _MOD_instTremolo(void); +void _MOD_instSetTremoloParams(int d, int depth); +void _MOD_instSetTremoloWave(int type, int noRetrig); +void _MOD_instNoteCut(int frame); +void _MOD_instTremor(void); +void _MOD_instSetTremorParams(int onTime, int offTime); +void _MOD_instNoteOff(int delay); +int _MOD_instIsNoteOff(void); +void _MOD_instPanPosition(int pos); +void _MOD_instEmptyCmd(void); + + +struct Note +{ + unsigned char note ; + unsigned char ins ; + unsigned char vol ; + unsigned char cmd ; + unsigned char info ; +} ; + +void _MOD_playNoteSetMono(int m); +void _MOD_playNoteSetMasterVol(int mv); +void _MOD_playNoteSetGlobalVol(int gv); +void _MOD_playNoteSetSample(SampleInfo *sip); +void _MOD_playNoteInit(void); +void _MOD_playNoteSetOutRate(int _or); +void _MOD_playNoteSetTempo(int n); +void _MOD_playNoteSetSpeed(int n); +void _MOD_playNoteSetNote(int ch, Note *np); +void _MOD_playNoteSetPatRepeat(int n); +void _MOD_playNote(void); + diff --git a/src/sl/slMODdacio.cxx b/src/sl/slMODdacio.cxx new file mode 100644 index 0000000..6361e9b --- /dev/null +++ b/src/sl/slMODdacio.cxx @@ -0,0 +1,118 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODdacio.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include "slMODPrivate.h" + +static int stereo ; +static int glob_vol = 0x40 * 0x40 ; /* default g.v = m.v = 64 */ + +#define VOL_MAX (64*64*128) /* vol max * g.v max * m.v max */ +#define VOL_MAX_LOG ( 6 + 6 + 7 ) /* 1 << VOL_MAX_LOG == VOL_MAX */ +#define LEV_MAX (128*VOL_MAX) +#define MAX_FRAMELEN (MAX_OUTRATE * 60 / (MIN_TEMPO * 24)) + +static inline unsigned char to8bit ( int x ) +{ + int tmpvar = ( x * glob_vol + LEV_MAX ) >> VOL_MAX_LOG ; + + return (tmpvar & ~255) ? ~tmpvar >> 16 : tmpvar ; +} + + +struct ucharBuffer +{ + unsigned char data [ MAX_FRAMELEN * 4 ] ; + unsigned char *ptr ; + unsigned char *end ; + int size ; + +public: + + ucharBuffer () + { + ptr = data ; + size = sizeof ( data ) ; + end = data + size ; + } + + void copyFrom ( int *inbufp, int n ) + { + if ( n == 0 ) + return ; + + if ( stereo ) + for (; n > 0; n--) + { + *ptr++ = to8bit ( *inbufp++ ) ; /* L */ + *ptr++ = to8bit ( *inbufp++ ) ; /* R */ + } + else + for (; n > 0; n--) + { + *ptr++ = to8bit ( *inbufp ) ; + inbufp += 2 ; + } + } + + int getLen () { return ptr - data ; } + + unsigned char *getBuffer () { return data ; } + + void empty () + { + ptr = data ; + } + + void subtract ( int n ) + { + /* Delete n bytes from the front of the buffer */ + + /* WARNING - NOT memcpy OK? */ + memmove ( data, data+n, ptr - data - n ) ; + ptr -= n ; + } +} ; + + +struct intBuffer +{ + int data [ MAX_FRAMELEN * 2 ] ; + int len ; +} ; + + +static intBuffer inbuf ; +static ucharBuffer outbuf ; + +int *dacioGetBuffer () +{ + return inbuf.data ; +} + +void dacioIncomingBufLen ( int len ) { inbuf.len = len ; } +void dacioGlobalVol(int v) { glob_vol = v; } +int dacioGetLen () { return outbuf.getLen () ; } +void dacioOut ( void ) { outbuf.copyFrom ( inbuf.data, inbuf.len ) ; } +void dacioEmpty () { outbuf.empty (); } +void dacioSubtract ( int n ) { outbuf.subtract(n) ; } +unsigned char *dacioGetOutBuffer () { return outbuf.getBuffer() ; } diff --git a/src/sl/slMODfile.cxx b/src/sl/slMODfile.cxx new file mode 100644 index 0000000..ce58444 --- /dev/null +++ b/src/sl/slMODfile.cxx @@ -0,0 +1,687 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODfile.cxx 1770 2003-06-02 23:49:44Z puggles $ +*/ + + +#include "slMODPrivate.h" +#include "slMODfile.h" + +#define NOTE_MAX (12*5-1) /* 5 octave */ +#define shift(x,n) ((n)>= 0 ? (x) << (n) : (x) >> -(n)) /* x * 2**n */ + +#define u16LittleEndian(x) (((unsigned char *)(x))[0] + \ + ((unsigned char *)(x))[1]*(unsigned short)256) +#define u16BigEndian(x) (((unsigned char *)(x))[0]*(unsigned short)256 + \ + ((unsigned char *)(x))[1]) + +static const char *transTab = "JFEGHLKRXODB.C" ; +static const char *transTabE = "SFESSSSSSQ..SSSS" ; +static const char transTabEX[] = { 0,0xF,0xF,1,3,2,0xB,4,8,0,0,0,0xC,0xD,0xE,0xF } ; +static int oct[12] = { 1712, 1616, 1524, 1440, 1356, 1280, + 1208, 1140, 1076, 1016, 960, 907 } ; +static int freq[16] = { 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757, + 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280 } ; +static unsigned char emptySample ; + +class SlmInfo ; + +static SlmInfo *top = NULL ; + +class SlmInfo +{ + char *data ; + SlmInfo *next ; +public: + + SlmInfo ( unsigned int size ) + { + data = new char [ size ] ; + next = top ; + top = this ; + } + + ~SlmInfo () + { + if ( this == top ) + top = NULL ; + + delete [] data ; + delete next ; + } + + void *get () { return (void *) data ; } +} ; + + +/* Allocate song-life memory */ + +static void *memSong ( unsigned size ) +{ + SlmInfo *sip = new SlmInfo ( size ) ; + return sip -> get () ; +} + +/* free all song-life memory */ + +static void memSongFree(void) +{ + delete top ; +} + + +/* LPF fc=fs/4 impulse response * Hamming window (N=19) */ +static int h2[][8] = /* h(n) = */ +{ + /* n = */ + /*-7 -5 -3 -1 1 3 5 7*/ + { -4, 15, -42, 158, 158, -42, 15, -4} +}; +/* LPF fc=fs/6 impulse response * Hamming window (N=29) */ +static int h3[][8] = +{ + /*-10 -7 -4 -1 2 5 8 11*/ + { -5, 16, -44, 209, 101, -31, 12, -3}, + /*-11 -8 -5 -2 1 4 7 10*/ + { -3, 12, -31, 101, 209, -44, 16, -5} +}; +/* LPF fc=fs/8 impulse response * Hamming window (N=35) */ +static int h4[][8] = +{ + /*-13 -9 -5 -1 3 7 11 15*/ + { -4, 13, -38, 229, 72, -22, 7, -2}, + /*-14 -10 -6 -2 2 6 10 14*/ + { -3, 13, -40, 158, 158, -40, 13, -3}, + /*-15 -11 -7 -3 1 5 9 13*/ + { -2, 7, -22, 72, 229, -38, 13, -4} +}; +static int (*hn[])[8] = { h2, h3, h4 }; + +static SampleInfo sis; + +static void convolute ( int mag, char *dp ) +{ + unsigned char *sp0; + int i,j; + + for (sp0 = sis.beg; sp0 < sis.end; sp0++) { + *dp++ = *sp0 ^ sis.x_or; + for (i = 0; i < mag-1; i++,dp++) { + int *hp = hn[mag-2][i]; + unsigned char *sp = sp0 - 3; + int sum = 0; + for (j = 8; j > 0; j--,sp++,hp++) { + if (sp < sis.beg) continue; + if (sp >= sis.end) { + if (sis.loopBeg) sp = sis.loopBeg; + else break; + } + sum += *hp * (signed char)(*sp ^ sis.x_or); + } + sum /= 256; + if (sum > 127) sum = 127; + else if (sum < -128) sum = -128; + *dp = sum; + } + } +} + + +static void perSampleWork(SampleInfo *sip, unsigned int c4req) +{ + static SampleInfo sid; + unsigned char *dp0; + unsigned int len; + int mag; + + mag = c4req / sip->c4spd; + + if (!mag) return; /* no need to oversampling */ + mag++; + if (mag > 4) mag = 4; /* currently max = 4x */ + sis = *sip; + len = sis.end - sis.beg; + if (len <= 2) return; /* too short sample */ + dp0 = (unsigned char *) memSong(len * mag); + sid.beg = dp0 ; + sid.end = dp0 + len*mag ; + convolute(mag, (char *)dp0); + sid.loopBeg = sis.loopBeg? (sid.beg + (sis.loopBeg - sis.beg)*mag) : (unsigned char *)NULL; + sid.x_or = 0; + sid.c4spd = sis.c4spd; + sid.vol = sis.vol; + sid.mag = sis.mag * mag; + *sip = sid; +} + +static void oversample ( int insNum, SampleInfo *sip, unsigned int c4req ) +{ + for ( ; insNum > 0 ; insNum--, sip++ ) + perSampleWork ( sip, c4req ) ; +} + +enum MagicType +{ + MAGIC_S3M, MAGIC_MOD ,MAGIC_MODX, MAGIC_MOD15, MAGIC_MTM +} ; + +/* stricter 15-sample mod checking */ + +static int isMod15(unsigned char *p0, int size) +{ + int ordNum, patNum ; + unsigned char *p ; + + if ( size < 20+30*15+130+1024 ) + return 0 ; /* check min length */ + + ordNum = p0[20+30*15] ; + + if (!ordNum || ordNum >= 128) + return 0 ; + + patNum = 0 ; + p = p0 + 20+30*15+2 ; /* pattern table */ + + for ( int i = 0 ; i < ordNum ; i++, p++ ) + if (patNum < *p) + patNum = *p ; + + if (patNum >= 64) + return 0 ; + + if (size < 20 + 30*15 + 130 + 1024 + 1024*patNum ) + return 0 ; + + /* sample length is not checked here */ + return 1 ; +} + + +struct MagicInfo +{ + const char *str ; + int off ; + MagicType type ; +} ; + + +MagicInfo magicInfo[] = +{ + {"SCRM", 0x2c , MAGIC_S3M } , + {"M.K.", 0x438 /*20+30*31+130=1080*/, MAGIC_MOD}, + {"M!K!", 0x438, MAGIC_MOD }, + {"FLT4", 0x438, MAGIC_MOD }, /* FLT8's pattern is different */ + {"#CHN", 0x438, MAGIC_MODX }, + {"##CH", 0x438, MAGIC_MODX }, /* dope.mod */ + {"MTM", 0 , MAGIC_MTM }, /* check after above patterns fail. */ + /* (A mod songname can be "MTMxxx") */ + {0} +} ; + + +#define isnum(x) ('0' <= (x) && (x) <= '9') + +static int magic(unsigned char *p, int size, MagicType *mtp, int *numChp) +{ + MagicInfo *mip; + int ch; + + for (mip = magicInfo; mip->str != 0; mip++) + { + const char *ss ; + char *sp ; + + if (mip->off + 16 >= size) + continue ; /* Magicinfo.str must < 16 */ + + sp = (char *)p + mip->off; + ch = 0; + + int got_it = FALSE ; + + for (ss = mip->str; *ss != 0; ss++,sp++) + { + if (*ss == '#') + { + if (isnum(*sp)) + { + ch = ch*10 + *sp - '0'; + continue ; + } + else + { + got_it = TRUE ; + break ; + } + } + + if (*sp != *ss) + { + got_it = TRUE ; + break ; + } + } + + if ( ! got_it ) + { + *mtp = mip->type ; + *numChp = ch ; + return 0 ; + } + } + + if ( isMod15 ( p, size ) ) + { + *mtp = MAGIC_MOD15 ; + return 0 ; + } + else + return 1 ; +} + +void MODfile::makeNoteTable(void) +{ + note = (short *) memSong ( (NOTE_MAX+1) * sizeof(short) ) ; + + for ( int i = 0 ; i <= NOTE_MAX ; i++ ) + note[i] = shift ( oct[i%12], -i/12 ) ; +} + + +int MODfile::roundToNote ( int p ) +{ + if ( p == 0 ) return 0 ; + if ( p >= note [ 0 ] ) return 0 ; + if ( p <= note [NOTE_MAX] ) return NOTE_MAX ; + + /* 32: =2**n, and 32*2 > NOTE_MAX */ + + int i, s ; + + for ( i = 0, s = 32 ; s > 0 ; s /= 2 ) + if ( i + s < NOTE_MAX && note[i+s] > p ) + i += s ; /* i < NOTE_MAX !! */ + + if ( note[i] - p > p - note[i+1] ) + i++ ; /* choose nearest */ + + return i ; +} + + +void MODfile::modToS3m(ModNote *mp, Note *np) +{ + int X = (*mp)[3] / 16 ; + int Y = (*mp)[3] % 16 ; + int XY = (*mp)[3] ; + + int n = roundToNote ( (*mp)[0]%16 * 256 + (*mp)[1] ) ; + + np->note = (n!=0) ?(n/12+2)*16 + n%12 : 255 ; + np->ins = ((*mp)[0] & 0xf0) | (*mp)[2]/16; + np->vol = 255 ; + np->cmd = 255 ; + np->info = 0 ; + + switch ( (*mp)[2] % 16 ) + { + case 0x0: if (X || Y) { np->cmd = 'J' - '@'; np->info = XY; } break; + case 0x1: + case 0x2: if (XY) { np->cmd = transTab[(*mp)[2]%16] - '@'; np->info = (XY >= 0xE0)? 0xdf : XY; } break; + case 0xA: if (!XY) break; + case 0xC: np->vol = XY>64? 64 : XY; np->cmd = 255; np->info = 0; break; + case 0xE: + switch (X) + { + case 0xA: if (Y) { np->cmd = 'D' - '@'; np->info = Y*16 + 15; } break ; + case 0xB: if (Y) { np->cmd = 'D' - '@'; np->info = 0xf0 + Y; } break ; + default: { np->cmd = transTabE[X] - '@'; np->info = transTabEX[X]*16 + Y; } break ; + } + break; + case 0xF: if (XY) { np->cmd = XY <= 32? 'A' - '@' : 'T' - '@'; np->info = XY? XY : 1; } break; + default : { np->cmd = transTab[(*mp)[2]%16] - '@'; np->info = XY ; } break ; + } +} + + +void MODfile::play_one ( int ppat ) +{ + ModNote *np = & pat [ chNum * ( 64 * ppat + play_row ) ] ; + + for ( int ch = 0 ; ch < chNum ; ch++, np++ ) + { + Note note ; + + modToS3m ( np, ¬e ) ; + _MOD_playNoteSetNote ( ch, ¬e ) ; + + switch ( note.cmd ) + { + case 'A' - '@': /* Set speed */ + _MOD_playNoteSetSpeed ( note.info ) ; + break; + + case 'B' - '@': + play_nextOrd = note.info | 0x100 ; + play_row = 64 ; + break ; + + case 'C' - '@': + play_row0 = note.info / 16 * 10 + note.info % 16 ; + play_row = 64 ; + break ; + + case 'S' - '@': + switch ( note.info / 16 ) + { + case 0xB: /* pattern loop */ + if ( note.info % 16 ) /* Jump to mark */ + { + if ( play_loopCnt < note.info % 16 ) + { + play_row = play_loopBeg - 1 ; + play_loopCnt++ ; + } + else + play_loopCnt = 0 ; + } + else /* Set mark */ + play_loopBeg = play_row ; + + break; + + case 0xE: /* Pattern delay */ + _MOD_playNoteSetPatRepeat ( note.info % 16 ) ; + break; + } + break; + + case 'T' - '@': /* Set tempo */ + _MOD_playNoteSetTempo ( note.info ) ; + break ; + } + } + + _MOD_playNote () ; +} + + + +void MODfile::tellChSettings( void ) +{ + for ( int i = 0; i < chNum; i++ ) + { + _MOD_instSelectCh ( i ) ; + + switch ( i % 4 ) + { + case 0 : + case 3 : _MOD_instPanPosition ( 3 * 64/15 ) ; break ; /* left */ + default : _MOD_instPanPosition (12 * 64/15 ) ; break ; /* right */ + } + } +} + + + +void MODfile::makeSampleInfo( int smp15 ) +{ + ModSample *msp ; + SampleInfo *p ; + unsigned char *sp ; + + p = sip = (SampleInfo *) memSong ( sizeof(SampleInfo) * insNum ) ; + sp = smp0p ; + msp = &smpInfop[0] ; + + for ( int i = 0; i < insNum; i++, p++, msp++) + { + unsigned int lOff = 0 ; + + p->beg = sp ; + + unsigned int len = u16BigEndian(*msp+22) * 2 ; /*len*/ + unsigned int lLen = u16BigEndian(*msp+28) ; /*loopLen*/ + + if (lLen > 1) + { + lLen *= smp15? 1:2; + lOff = u16BigEndian(*msp+26)*(smp15? 1:2); /*loopOffset*/ + p->loopBeg = sp + lOff; /*sp+loopOffset*/ + p->end = p->loopBeg + lLen; + } + else + { + p->loopBeg = NULL; + p->end = sp + len; /*sp+len*/ + } + + p->x_or = 0 ; + p->mag = 1 ; + p->c4spd = freq[(*msp)[24]%16] ; + p->vol = (*msp)[25] > 64 ? 64 : (*msp)[25] ; + + if (p->end > fileEnd) + { + if (p->beg >= fileEnd || p->loopBeg >= fileEnd) + { + ulSetError ( UL_WARNING, + "short file (assigned an empty sample for #%d)", i+1 ) ; + p->beg = &emptySample ; + p->end = &emptySample+1 ; + p->loopBeg = NULL ; + p->vol = 0 ; + } + else + { + ulSetError ( UL_WARNING, + "short file (sample #%d truncated)", i+1 ) ; + p->end = fileEnd ; + } + } + + sp += u16BigEndian(*msp+22) * 2; /*sp+len*/ + } +} + + +void MODfile::parseMod(unsigned char *pp0, int smp15) +{ + int i,n; + unsigned char *p; + + p = pp0; + p0 = songName = p ; + p += 20; + + smpInfop = (ModSample *) p ; + + if ( smp15 ) { p += 15*sizeof(ModSample); insNum = 15; } + else { p += 31*sizeof(ModSample); insNum = 31; } + + ordNum = *p ; p++ ; + rstOrd = *p ; p++ ; + + ord = p; + + for ( i = 0, n = 0 ; i < 128 ; i++ ) + if ( n < p[i] ) + n = p[i] ; /* find max pat */ + + patNum = n + 1 ; + p += 128 + ( smp15 ? 0 : 4 ) ; + pat = (ModNote *) p ; + smp0p = pat [ chNum * 64 * patNum ] ; +} + + +int MODfile::update () +{ + if ( broken ) + return FALSE ; + + if ( firsttime ) + { + play_ord = play_ord0 = 0 ; + memset ( repCounter, 0, ordNum ) ; + firsttime = FALSE ; + } + + play_one ( ord [ play_ord ] ) ; + + if ( ++play_row >= 64 ) + { + play_loopBeg = 0 ; + play_loopCnt = 0 ; + play_nextOrd = 0 ; + + play_row = play_row0 ; + play_row0 = 0 ; + + play_ord = (play_nextOrd==0) ? (play_ord+1) : (play_nextOrd & 0xff) ; + + if ( play_ord >= ordNum ) + { + play_ord0 = rstOrd ; + play_ord = play_ord0 ; + + if ( rstOrd > ordNum ) + { + firsttime = TRUE ; + return FALSE ; + } + } + } + + return TRUE ; /* Carry on playing! */ +} + + + +unsigned char *MODfile::read_whole_file ( const char *fname, int *len ) +{ + struct stat statbuf ; + + int l = 0 ; + + int fd = open ( fname, O_RDONLY ) ; + + if ( fd < 0 ) + { + perror ( "open" ) ; + ulSetError ( UL_WARNING, + "SL: Couldn't open MOD file '%s' for reading", fname ) ; + return NULL ; + } + + if ( fstat ( fd, &statbuf ) < 0 ) + { + perror ( "fstat" ) ; + return NULL ; + } + + l = statbuf.st_size ; + + unsigned char *p = new unsigned char [ l ] ; + read ( fd, (char *)p, l ) ; + close ( fd ) ; + + if ( len != NULL ) + *len = l ; + + return p ; +} + + + +MODfile::MODfile ( const char *fname, int speed, int stereo ) +{ + p0 = NULL ; songName = NULL ; ord = NULL ; + smpInfop = NULL ; smp0p = NULL ; pat = NULL ; + note = NULL ; fileEnd = NULL ; sip = NULL ; + repCounter = NULL ; + + play_nextOrd = play_loopBeg = play_loopCnt = play_row0 = + play_row = play_ord0 = play_ord = ordNum = insNum = + patNum = chNum = rstOrd = firsttime = broken = 0 ; + + MagicType mt ; + + int len = 0 ; + + buffer = read_whole_file ( fname, &len ) ; + + if ( buffer == NULL ) + { + broken = TRUE ; + return ; + } + + if ( magic ( buffer, len, &mt, &chNum ) ) + { + broken = TRUE ; + ulSetError ( UL_WARNING, "Unknown format" ) ; + return ; + } + + if ( mt == MAGIC_MOD || mt == MAGIC_MOD15 ) + chNum = 4 ; + else + if ( mt != MAGIC_MODX ) + { + ulSetError ( UL_WARNING, "Unknown format" ) ; + broken = TRUE ; + return ; + } + + _MOD_playNoteInit () ; + _MOD_playNoteSetOutRate ( speed ) ; + _MOD_playNoteSetMono ( !stereo ) ; + + fileEnd = & buffer [ len ] ; + repCounter = (unsigned char *) memSong ( 128 ) ; + parseMod ( buffer, mt == MAGIC_MOD15 ) ; + _MOD_instSetPeriodAmigaLimit ( 1 ) ; + tellChSettings () ; + + makeSampleInfo ( mt == MAGIC_MOD15 ) ; + + oversample ( insNum, sip, (unsigned int) speed ) ; + + _MOD_playNoteSetSample ( sip ) ; + _MOD_playNoteSetTempo ( 125 ) ; + _MOD_playNoteSetSpeed ( 6 ) ; + _MOD_playNoteSetMasterVol ( 0x30 ) ; + _MOD_playNoteSetGlobalVol ( 0x40 ) ; + makeNoteTable () ; +} + + +MODfile::~MODfile () +{ + if ( broken ) + return ; + + delete [] buffer ; + memSongFree () ; +} + diff --git a/src/sl/slMODfile.h b/src/sl/slMODfile.h new file mode 100644 index 0000000..6463598 --- /dev/null +++ b/src/sl/slMODfile.h @@ -0,0 +1,78 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODfile.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +typedef unsigned char ModSample [ 30 ] ; +typedef unsigned char ModNote [ 4 ] ; +struct SampleInfo ; +struct Note ; + +class MODfile +{ + unsigned char *buffer ; + unsigned char *p0 ; + unsigned char *songName ; + + int ordNum ; + int insNum ; + int patNum ; + int chNum ; + int rstOrd ; + + unsigned char *ord ; + ModSample *smpInfop ; + unsigned char *smp0p ; + ModNote *pat ; + short *note ; + unsigned char *fileEnd ; + unsigned char *repCounter; + SampleInfo *sip ; + + int firsttime ; + int broken ; + + int play_nextOrd ; + int play_loopBeg ; + int play_loopCnt ; + int play_row0 ; + int play_row ; + int play_ord0 ; + int play_ord ; + + void makeNoteTable ( void ) ; + void tellChSettings ( void ) ; + int roundToNote ( int p ) ; + void modToS3m ( ModNote *mp, Note *np ) ; + void makeSampleInfo ( int smp15 ) ; + void parseMod ( unsigned char *pp0, int smp15 ) ; + void play_one ( int ppat ) ; + unsigned char *read_whole_file ( const char *fname, int *len ) ; + +public: + + MODfile ( const char *fname, int speed = 44100, int stereo = 0 ) ; + ~MODfile () ; + + int update () ; +} ; + diff --git a/src/sl/slMODinst.cxx b/src/sl/slMODinst.cxx new file mode 100644 index 0000000..242f82d --- /dev/null +++ b/src/sl/slMODinst.cxx @@ -0,0 +1,1096 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODinst.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "slMODPrivate.h" + +/* note-period table */ + +#define NOTE_MAX (12*8-1) /* 8octave */ +static short *note ; + +#define shift(x,n) ((n) >= 0? (x) << (n) : (x) >> -(n)) /* x * 2**n */ + +static struct +{ + int *p0 ; + int *p ; + int len ; +} hirev_buf ; + +void _MOD_instHirevInit ( void ) +{ + hirev_buf.p0 = dacioGetBuffer () ; +} + +void _MOD_instHirevSetFrameLen ( int l ) +{ + dacioIncomingBufLen ( l ) ; + hirev_buf.len = l ; +} + +void _MOD_instHirevEraseBuf ( void ) +{ + memset ( hirev_buf.p0, 0, sizeof ( int ) * hirev_buf.len * 2 ) ; +} + +void _MOD_instHirevFlushBuf ( void ) +{ + dacioOut () ; +} + + +static void fadeout ( InstHirevInfo *ihip ) +{ + int lastL = ihip->lastL / 64 ; + int lastR = ihip->lastR / 64 ; + int f = ihip->fadeout ; + + if ( f > 63 ) + f = 63 ; + + if ( lastL || lastR ) + { + int *bufp = hirev_buf.p ; + int n = ( hirev_buf.p0 + hirev_buf.len - bufp ) / 2 ; + + if ( n > f ) n = f ; + + for ( ; n > 0 ; n--,f-- ) + { + *bufp++ += lastL * f ; + *bufp++ += lastR * f ; + } + } + + ihip->fadeout = f ; + + if ( !f ) + ihip->lastL = ihip->lastR = 0 ; +} + +/* vol zero optimization: just calculates phase progress within this frame */ + +static void vol0Opt ( InstHirevInfo *ihip ) +{ + if ( ihip->lastL || ihip->lastR ) + { + /* suddenly volume is turned to 0 -> click ( chi_mai.s3m ) */ + + ihip->fadeout = 256 ; + fadeout ( ihip ) ; + ihip->fadeout = 0 ; /* don't note off ( ambient_power.mod ) */ + } + + ihip->wAcc = ( unsigned int ) ( unsigned short ) ihip->wAcc + ( unsigned short )( ihip->w * hirev_buf.len ) ; + ihip->ptr += ( ihip->w >> 16 ) * hirev_buf.len + + ( ( unsigned short ) ihip->w * hirev_buf.len >> 16 ) + + ( ihip->wAcc >> 16 ) ; + + if ( ihip->ptr >= ihip->end ) + { + if ( ihip->loopBeg ) + ihip->ptr = ihip->loopBeg + ( ihip->ptr - ihip->end ) % ( ihip->end - ihip->loopBeg ) ; + else + ihip->ptr = NULL ; /* note off */ + } + + ihip->lastL = ihip->lastR = 0 ; +} + +static InstHirevInfo ihi ; /* cacheing */ + +static void hirevLoop0 ( unsigned int n ) +{ + int *bufp = hirev_buf.p ; + unsigned char *ihiPtr = ihi.ptr ; + unsigned int ihiWAcc = ihi.wAcc ; + + for ( ; n > 0 ; n-- ) + { + int d = ( signed char ) *ihiPtr ; + *bufp++ += d * ihi.volL ; + *bufp++ += d * ihi.volR ; + + ihiWAcc = ( unsigned short ) ihiWAcc + ihi.w ; /*ihiWAcc & 0xffff + w*/ + ihiPtr += ihiWAcc >> 16 ; + } + + hirev_buf.p = bufp ; + ihi.ptr = ihiPtr ; + ihi.wAcc = ihiWAcc ; +} + +static void hirevLoop80 ( unsigned int n ) +{ + int *bufp = hirev_buf.p ; + unsigned char *ihiPtr = ihi.ptr ; + unsigned int ihiWAcc = ihi.wAcc ; + + for ( ; n > 0 ; n-- ) + { + int d = ( signed char ) ( *ihiPtr ^ 0x80 ) ; + *bufp++ += d * ihi.volL ; + *bufp++ += d * ihi.volR ; + + ihiWAcc = ( unsigned short ) ihiWAcc + ihi.w ; /*ihiWAcc & 0xffff + w*/ + ihiPtr += ihiWAcc >> 16 ; + } + + hirev_buf.p = bufp ; + ihi.ptr = ihiPtr ; + ihi.wAcc = ihiWAcc ; +} + +#define hirevLoop(x) { if ( ihi.x_or ) hirevLoop80(x) ; else hirevLoop0(x) ; } + + +void _MOD_instHirevLoop ( InstHirevInfo *ihip ) +{ + unsigned int restF ; + unsigned int restS ; + int lastD ; + + if ( ihip->ptr == NULL ) + return ; /* note is off */ + + hirev_buf.p = hirev_buf.p0 ; + + if ( ihip->fadeout ) + { + fadeout ( ihip ) ; + + if ( !ihip->fadeout ) + ihip->ptr = NULL ; /* note off */ + + return ; + } + + if ( !ihip->volL && !ihip->volR ) { vol0Opt ( ihip ) ; return ; } + + ihi = *ihip ; /* load to cache */ + restF = hirev_buf.len ; + + do + { + int l8, l0 ; + + ihi.wAcc = ( unsigned short ) ihi.wAcc ; + + l8 = ( ihi.end - ihi.ptr ) << 8 ; + + if ( !l8 && !ihi.wAcc ) + { /* happens only on empty samples */ + + ihip->fadeout = 256 ; + fadeout ( ihip ) ; + + if ( !ihip->fadeout ) + ihip->ptr = NULL ; /* note off */ + + return ; + } + + if ( l8 <= 0 ) + { + ulSetError ( UL_WARNING, "bug: restF=%u",restF ) ; + ulSetError ( UL_WARNING, "end-ptr=%d w=%u",ihi.end-ihi.ptr,ihi.w ) ; + ulSetError ( UL_WARNING, "wAcc = %u",ihi.wAcc ) ; + } + + l0 = ihi.w - 1 - ihi.wAcc ; + l8 += l0 >> 8 ; + l0 &= 0xff ; + restS = ( ( l8 / ihi.w ) << 8 ) + ( ( ( l8 % ihi.w ) << 8 ) + l0 ) / ihi.w ; + + if ( restF < restS ) + { /* sample is longer than frame */ + + hirevLoop ( restF ) ; + lastD = ( signed char ) ( * ( ihi.ptr - ( ihi.wAcc >> 16 ) ) ^ ihi.x_or ) ; + + break ; + } + + /* restF >= restS */ + hirevLoop ( restS ) ; + lastD = ( signed char ) ( * ( ihi.ptr - ( ihi.wAcc >> 16 ) ) ^ ihi.x_or ) ; + + if ( ihi.ptr < ihi.end || ihi.end <= ihi.ptr - ( ihi.wAcc >> 16 ) ) + { + ulSetError ( UL_FATAL, "SL: Internal Error in _MOD_instHirevLoop." ) ; +/* + ulSetError ( UL_DEBUG, "bug: restS = %u restF=%u end-ptr = %d, ptr=%p", + restS, restF, ihi.end-ihi.ptr, ihi.ptr ) ; + ulSetError ( UL_DEBUG, "last ptr=%p", ( ihi.ptr - ( ihi.wAcc >> 16 ) ) ) ; +*/ + } + + restF -= restS ; + + if ( ihi.loopBeg ) + { + ihi.ptr = ihi.loopBeg + ( ihi.ptr - ihi.end ) % ( ihi.end - ihi.loopBeg ) ; + } + else + { + ihi.lastL = lastD * ihi.volL ; + ihi.lastR = lastD * ihi.volR ; + ihi.fadeout = 256 ; + + fadeout ( &ihi ) ; + + if ( !ihi.fadeout ) + ihi.ptr = NULL ; /* note off */ + + *ihip = ihi ; /* save cache */ + return ; + } + } while ( restF ) ; + + ihi.lastL = lastD * ihi.volL ; + ihi.lastR = lastD * ihi.volR ; + *ihip = ihi ; /* save cache */ +} + +static void makeNoteTable ( void ) +{ + static int oct4[] = + { + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907 + } ; + + note = new short [ NOTE_MAX + 1 ] ; + + for ( int i = 0 ; i <= NOTE_MAX ; i++ ) + note[i] = shift ( oct4[i%12], 4-i/12 ) ; +} + +/* period -> nearest note */ + +static int normalizePeriod ( int *pp ) +{ + int i ; + int s ; + int p = *pp ; + + if ( p >= note [ 0 ] ) { *pp = note [ 0 ] ; return 0 ; } + if ( p <= note [ NOTE_MAX ] ) { *pp = note [ NOTE_MAX ] ; return NOTE_MAX ; } + + for ( i = 0, s = 64 ; s > 0 ; s /= 2 ) /* 64: =2**n, and 64*2 > NOTE_MAX */ + if ( i + s < NOTE_MAX && note [ i + s ] > p ) /* i < NOTE_MAX !! */ + i += s ; + + if ( note [ i ] - p > p - note [ i + 1 ] ) + i++ ; /* choose nearest */ + + *pp = note [ i ] ; + + return i ; +} + + + +struct ModulateInfo +{ + int type ; /* sine,ramp,square,... */ + int noRetrig ; + int phase ; + int d ; /* phase accumlates this for each frame */ + int depth ; /* org * table[phase] * depth/MAX_IN_TABLE = cur */ +} ; + + +/* output rate */ + +static unsigned int mclk ; + +#define MCLK0 ((unsigned int)3579545*4) + +void _MOD_instOutRate ( unsigned int rate ) +{ + mclk = MCLK0 / rate * 65536 + MCLK0 % rate * 65536 / rate ; +} + +#define PFW_MAX 3 /* note delay = note delay + sample delay + vol delay */ + +struct InstInfo +{ + InstHirevInfo hirev ; /* for hirev loop */ + + struct + { + int cur ; /* current period */ + int org ; /* original period */ + int note ; /* note */ + int notePer ; /* note period */ + int mclk ; /* master clock in Hz (c4spd * 1712) */ + + struct + { + int speed ; /* added/sub'ed for each frame */ + int fine ; /* fine slide */ + } slide ; + + struct + { + int speed ; + int glissando ; + int nextNote ; /* (glissando) next note */ + } port ; /* portamento */ + + ModulateInfo mod ; + + struct + { + int baseNote ; + int plus[2] ; /* baseNote + plus[n] halfnote is played */ + } rpgo ; /* arpeggio */ + + struct + { + int n ; + int newNote ; + } delay ; + + } per ; /* period */ + + struct + { + int cur ; /* current volume */ + int org ; + int pan ; /* 0(left)..64(right) */ + + struct + { + int d ; /* added for each frame */ + int mul ; /* multiply for each frame */ + int div ; /* divide for each frame */ + int nthFrame ; /* slide every nth frame */ + int fine ; /* fine slide */ + int count ; /* frame counter */ + } slide ; + + ModulateInfo mod ; + + struct + { + int onOff ; + int count ; + int onTime ; + int offTime ; + } tremor ; + + struct + { + int n ; + int newest ; + } delay ; + + } vol ; + + struct + { + int cur ; /* current frame */ + int count ; /* decrement counter, 0 starts next frame */ + } frame ; + + struct + { + struct + { + int nthFrame ; /* retrig every nth frame */ + int count ; /* frame counter */ + } retrig ; + + SampleInfo *sip ; + SampleInfo *newSip ; + int c4spd ; + + struct + { + int n ; + SampleInfo *sip ; + } delay ; + + int cutFrame ; + } smp ; + + struct + { + void (*func[PFW_MAX])(void) ; + int n ; + } pfw ; /* per frame works */ + +} ; + +static InstInfo *instBank ; +static InstInfo *instp ; + + +/* select channel */ + +void _MOD_instSelectCh(int ch) +{ + instp = &instBank[ch]; +} + + +/* per frame work */ + +void _MOD_instClearPFW(void) +{ + instp->pfw.n = 0; +} + +void _MOD_instDoPerFrameWorks(int frame) +{ + instp->frame.cur = frame; + + for (int i = 0; i < instp->pfw.n; i++) + (*instp->pfw.func[i])(); +} + +static void addPerFrameWork(void (*f)(void)) +{ + if (instp->pfw.n >= PFW_MAX) + { + ulSetError ( UL_FATAL, "Too many PFWs"); + } + + instp->pfw.func[instp->pfw.n++] = f; +} + + +/* hirev loop */ + +void _MOD_instLoop(void) +{ + _MOD_instHirevLoop(&instp->hirev); +} + + +/* initialize */ + +void _MOD_instInit(void) +{ + static SampleInfo si0 ; + + _MOD_instHirevInit () ; + makeNoteTable () ; + + instBank = new InstInfo [ 32 ] ; + + si0.beg = si0.end = NULL ; + si0.c4spd = 8363 ; + si0.mag = 1 ; + + for ( int i = 0 ; i < 32 ; i++ ) + { + /* prepare for "note w/o inst and volume" */ + instBank[i].smp.sip = instBank[i].smp.newSip = &si0 ; + instBank[i].smp.c4spd = 8363; /* dope.mod ch14 begins with GF0 */ + instBank[i].hirev.end = instBank[i].hirev.ptr = NULL; + instBank[i].vol.slide.div = 1; + } +} + + +static void setW(void) +{ + instp->hirev.w = (mclk * instp->smp.sip->mag) / + (instp->per.cur < 16? 16 : instp->per.cur); +} + +#define noteToPeriod(n) ((int)note[(n)] * 8363 / instp->smp.c4spd) + +static void setPeriod(void) +{ + if (instp->smp.sip != instp->smp.newSip) + { + /* actual sample switching is carried out here */ + instp->smp.sip = instp->smp.newSip ; + instp->hirev.end = instp->smp.sip->end ; + instp->hirev.loopBeg = instp->smp.sip->loopBeg ; + instp->hirev.x_or = instp->smp.sip->x_or ; + } + + instp->per.note = instp->per.delay.newNote; + instp->per.cur = + instp->per.org = + instp->per.notePer = noteToPeriod(instp->per.note); + + instp->hirev.ptr = instp->smp.sip->beg; /* key-on */ + instp->hirev.wAcc = 0; + instp->hirev.fadeout = 0; + + if ( ! instp->per.mod.noRetrig ) instp->per.mod.phase = 0 ; + if ( ! instp->vol.mod.noRetrig ) instp->vol.mod.phase = 0 ; + + setW () ; +} + +static void setPeriodPFW(void) +{ + if (instp->per.delay.n == instp->frame.cur) + setPeriod() ; +} + + +void _MOD_instNote(int n, int delay) +{ + instp->per.delay.newNote = n; + + if (delay == 0) + setPeriod(); + else + { + instp->per.delay.n = delay; + addPerFrameWork(setPeriodPFW); + } +} + + +/* set volume */ + +static int mono; + +void _MOD_instMono(int n) +{ + mono = n; +} + +static void setHirevVol(void) +{ + if ( mono ) + { + instp->hirev.volL = instp->vol.cur ; + return ; + } + + /* currently linear, which makes front sounds rather weaker */ + + if (instp->vol.pan >= 0) + { + instp->hirev.volL = instp->vol.cur * (64 - instp->vol.pan)/64; + instp->hirev.volR = instp->vol.cur * instp->vol.pan/64; + } + else + { /* surround!! */ + instp->hirev.volL = instp->vol.cur / 2; + instp->hirev.volR = -instp->vol.cur / 2; + } +} + +static void setVol(void) +{ + instp->vol.cur = instp->vol.org = instp->vol.delay.newest; + setHirevVol(); +} + +static void setVolPFW(void) +{ + if (instp->vol.delay.n == instp->frame.cur) setVol(); +} + +void _MOD_instVol(int v, int delay) +{ + instp->vol.delay.newest = v > 64? 64: v; + + if (delay == 0) + setVol(); + else + { + instp->vol.delay.n = delay; + addPerFrameWork(setVolPFW); + } +} + + +/* tuning */ + +void _MOD_instTuning(int c4spd) +{ + instp->smp.c4spd = c4spd; +} + + +/* set sample */ + +static void setSample() +{ + /* actual sample switching is not done here.. */ + + instp->smp.newSip = instp->smp.delay.sip; + + /* set smp's default vol.. */ + + instp->vol.cur = instp->vol.org = instp->smp.newSip->vol; + + /* set smp's c4spd.. */ + + instp->smp.c4spd = instp->smp.newSip->c4spd; + setHirevVol(); +} + + +static void setSamplePFW(void) +{ + if (instp->smp.delay.n == instp->frame.cur) setSample(); +} + +void _MOD_instSample(SampleInfo *sip, int delay) +{ + instp->smp.delay.sip = sip; + + if (delay) + { + instp->smp.delay.n = delay; + addPerFrameWork(setSamplePFW); + } else setSample(); +} + + +/* volume slide */ + +inline int limitVol ( int v ) +{ + if ( v > 64 ) + return 64 ; + + if ( v < 0 ) + return 0 ; + + return v ; +} + +static int fastVolSlide; + +static void volSlidePFW(void) +{ + if (!fastVolSlide && !instp->frame.cur) return; /* skip frame 0 */ + if (--instp->vol.slide.count <= 0) { + instp->vol.slide.count = instp->vol.slide.nthFrame; + instp->vol.cur = + instp->vol.cur * instp->vol.slide.mul / instp->vol.slide.div + + instp->vol.slide.d; + + instp->vol.cur = limitVol ( instp->vol.cur ) ; + setHirevVol(); + } +} + +void _MOD_instVolSlide(void) +{ + if (instp->vol.slide.fine) { + instp->vol.cur = + instp->vol.cur * instp->vol.slide.mul / instp->vol.slide.div + + instp->vol.slide.d; + + instp->vol.cur = limitVol ( instp->vol.cur ) ; + setHirevVol(); + } else addPerFrameWork(volSlidePFW); +} + +void _MOD_instSetVolSlideParams(int d, int mul, int div, int nthFrame, int fine) +{ + instp->vol.slide.d = d; + instp->vol.slide.mul = mul; + instp->vol.slide.div = div; + instp->vol.slide.nthFrame = instp->vol.slide.count = nthFrame; + instp->vol.slide.fine = fine; +} + +void _MOD_instSetVolSlideFast(int onOff) +{ + fastVolSlide = onOff; +} + + +/* period slide */ + +#define noteOff() { instp->hirev.fadeout = 256; } /* eventually note-off */ + +static int amigaLimit; + +static void limitPeriod(void) +{ +#define p instp->per.cur + if (amigaLimit) { + if ((p) > note[3*12]) (p) = note[3*12]; + else if ((p) < note[5*12+11]) (p) = note[5*12+11]; + } else { + if ((p) > 32000) (p) = 32000; + else if ((p) < 0) { (p) = 0; noteOff(); /*panic.s3m needs this*/ } + } +#undef p +} + +static void periodSlideUpPFW(void) +{ + if (!instp->frame.cur) return; /* skip frame 0 */ + instp->per.cur -= instp->per.slide.speed; /* ignore per.org */ + limitPeriod(/*instp->per.cur*/); + instp->per.org = instp->per.cur; + setW(); +} + +static void periodSlideDownPFW(void) +{ + if (!instp->frame.cur) return; /* skip frame 0 */ + instp->per.cur += instp->per.slide.speed; + limitPeriod(/*instp->per.cur*/); + instp->per.org = instp->per.cur; + setW(); +} + +void _MOD_instPeriodSlideUp(void) +{ + if (instp->per.slide.fine) { + instp->per.cur -= instp->per.slide.speed; + limitPeriod(/*instp->per.cur*/); + instp->per.org = instp->per.cur; + setW(); + } else addPerFrameWork(periodSlideUpPFW); +} + +void _MOD_instPeriodSlideDown(void) +{ + if (instp->per.slide.fine) { + instp->per.cur += instp->per.slide.speed; + limitPeriod(/*instp->per.cur*/); + instp->per.org = instp->per.cur; + setW(); + } else addPerFrameWork(periodSlideDownPFW); +} + +void _MOD_instSetPeriodSlideParams(int speed, int fine) +{ + instp->per.slide.speed = speed; + instp->per.slide.fine = fine; +} + +void _MOD_instSetPeriodAmigaLimit(int onOff) +{ + amigaLimit = onOff; +} + + +/* portamento */ + +static void portamentoPFW(void) +{ + if (!instp->frame.cur) return; /* which S3M slides at frame 0? */ + if (instp->per.org > instp->per.notePer) { /* port up now */ + instp->per.org -= instp->per.port.speed; + if (instp->per.org < instp->per.notePer) + instp->per.cur = instp->per.org = instp->per.notePer; + else { + instp->per.cur = instp->per.org; + if (instp->per.port.glissando) normalizePeriod(&instp->per.cur); + } + } else { /* port down now */ + instp->per.org += instp->per.port.speed; + if (instp->per.org > instp->per.notePer) + instp->per.cur = instp->per.org = instp->per.notePer; + else { + instp->per.cur = instp->per.org; + if (instp->per.port.glissando) normalizePeriod(&instp->per.cur); + } + } + setW(); +} + +void _MOD_instPortamento(void) +{ + addPerFrameWork(portamentoPFW); +} + +void _MOD_instSetPortamentoTo(int to) +{ + instp->per.note = to; + instp->per.notePer = noteToPeriod(to); +} + +void _MOD_instSetPortamentoSpeed(int speed) +{ + instp->per.port.speed = speed; +} + +void _MOD_instSetPortamentoDefaultVol(void) +{ + instp->vol.cur = instp->vol.org = instp->smp.sip->vol; + setHirevVol(); +} + +void _MOD_instSetPortamentoGlissando(int onOff) +{ + instp->per.port.glissando = onOff; +} + + +/* arpeggio */ + +static void arpeggioPFW(void) +{ + if (instp->frame.cur % 3) { + instp->per.cur = note[instp->per.note + + instp->per.rpgo.plus[instp->frame.cur%3 - 1]]; + } else instp->per.cur = instp->per.notePer; + setW(); +} + +void _MOD_instArpeggio(void) +{ + addPerFrameWork(arpeggioPFW); +} + +void _MOD_instSetArpeggioParams(int plus1, int plus2) +{ + instp->per.rpgo.plus[0] = plus1; + instp->per.rpgo.plus[1] = plus2; +} + + +/* retrig */ + +static void retrigPFW(void) +{ + if (--instp->smp.retrig.count <= 0) { + instp->smp.retrig.count = instp->smp.retrig.nthFrame; + instp->hirev.ptr = instp->smp.sip->beg; + setW(); + } +} + +void _MOD_instRetrig(void) +{ + addPerFrameWork(retrigPFW); +} + +void _MOD_instSetRetrigParam(int nthFrame) +{ + instp->smp.retrig.nthFrame = nthFrame; + instp->smp.retrig.count = 0; +} + + +/* sample offset */ + +void _MOD_instSampleOffset(int offset) +{ + instp->hirev.ptr = instp->smp.sip->beg + offset * instp->smp.sip->mag; + if (instp->hirev.ptr >= instp->hirev.end) { + if (instp->hirev.loopBeg) { + instp->hirev.ptr = + instp->hirev.loopBeg + + (instp->hirev.ptr - instp->hirev.end) % + (instp->hirev.end - instp->hirev.loopBeg); + } else { noteOff(); } + } +} + + +/* modulation */ + +static unsigned char sine[] = { + 000, 25, 50, 74, 98, 120, 142, 162, + 180, 197, 212, 225, 236, 244, 250, 254, + 255 +}; /* sin(x), 0 <= x < pi/4, max = 255 */ + + +static int wave(ModulateInfo *mip) +{ + int i; + + switch (mip->type) { + case 1: /* ramp up (period: down) */ + i = (255 * 2 * mip->phase)/63 - 255; + break; + case 2: /* square */ + i = (mip->phase < 32)? 255 : 0; /* yes, not 255/-255 */ + break; + default: /* sine */ + if (mip->phase < 16) i = sine[mip->phase]; + else if (mip->phase < 32) i = sine[32 - mip->phase]; + else if (mip->phase < 48) i = -sine[mip->phase - 32]; + else i = -sine[64 - mip->phase]; + } + return mip->depth * i / 255; +} + +static void vibratoPFW(void) +{ + if (!instp->frame.cur) return; /* skip frame 0 */ + instp->per.mod.phase += instp->per.mod.d; + instp->per.mod.phase %= 64; + /* per.org: no change.. */ + instp->per.cur = instp->per.org + wave(&instp->per.mod); + limitPeriod(/*instp->per.cur*/); + setW(); +} + +void _MOD_instVibrato(void) +{ + addPerFrameWork(vibratoPFW); +} + +/* depth = period */ +void _MOD_instSetVibratoParams(int d, int depth) +{ + if (d) instp->per.mod.d = d; + instp->per.mod.depth = depth; + /*if (!instp->per.mod.noRetrig) instp->per.mod.phase = 0;*/ +} + +void _MOD_instSetVibratoWave(int type, int noRetrig) +{ + if (type == 3) type = rand() % 3; + instp->per.mod.type = type; + instp->per.mod.noRetrig = noRetrig; +} + + +static void tremoloPFW(void) +{ + if (!instp->frame.cur) return; /* skip frame 0 */ + instp->vol.mod.phase += instp->vol.mod.d; + instp->vol.mod.phase %= 64; + instp->vol.cur = instp->vol.org + wave(&instp->vol.mod); + instp->vol.cur = limitVol ( instp->vol.cur ) ; + setHirevVol(); +} + +void _MOD_instTremolo(void) +{ + addPerFrameWork(tremoloPFW); +} + +void _MOD_instSetTremoloParams(int d, int depth) +{ + if (d) instp->vol.mod.d = d; + instp->vol.mod.depth = depth; + /*if (!instp->vol.mod.noRetrig) instp->vol.mod.phase = 0;*/ +} + +void _MOD_instSetTremoloWave(int type, int noRetrig) +{ + if (type == 3) type = rand() % 3; + instp->vol.mod.type = type; + instp->vol.mod.noRetrig = noRetrig; +} + + +/* note cut */ + +static void noteCutPFW(void) +{ + /* said to be vol := 0, but ST3 seems to key-off */ + if (instp->smp.cutFrame == instp->frame.cur) noteOff(); +} + +void _MOD_instNoteCut(int frame) +{ + if (frame) { + instp->smp.cutFrame = frame; + addPerFrameWork(noteCutPFW); + } else noteOff(); +} + + +/* tremor */ + +static void tremorPFW(void) +{ + if (--instp->vol.tremor.count <= 0) { + if (instp->vol.tremor.onOff) { + instp->vol.cur = 0; + setHirevVol(); + instp->vol.tremor.onOff = 0; + instp->vol.tremor.count = instp->vol.tremor.offTime; + } else { + instp->vol.cur = instp->vol.org; + setHirevVol(); + instp->vol.tremor.onOff = 1; + instp->vol.tremor.count = instp->vol.tremor.onTime; + } + } +} + +void _MOD_instTremor(void) +{ + addPerFrameWork(tremorPFW); +} + +void _MOD_instSetTremorParams(int onTime, int offTime) +{ + instp->vol.tremor.onTime = onTime; + instp->vol.tremor.offTime = offTime; + instp->vol.tremor.count = 0; + instp->vol.tremor.onOff = 0; +} + + +/* note off */ + +static void noteOffPFW(void) +{ + if (instp->per.delay.n == instp->frame.cur) noteOff(); +} + +void _MOD_instNoteOff(int delay) +{ + if (delay) { + instp->per.delay.n = delay; + addPerFrameWork(noteOffPFW); + } else { noteOff(); } +} + +int _MOD_instIsNoteOff(void) +{ + return instp->hirev.ptr == NULL; +} + + +/* pan position */ + +/* pos = 0(left)..64(right), -1(surround) */ + +void _MOD_instPanPosition(int pos) +{ + instp->vol.pan = pos; + setHirevVol(); +} + + +/* empty command */ + +void _MOD_instEmptyCmd(void) +{ + instp->per.cur = instp->per.org; + setW(); + /* empty command has some special meanings... + (when after glissando portamento or vibrato) */ +} + diff --git a/src/sl/slMODnote.cxx b/src/sl/slMODnote.cxx new file mode 100644 index 0000000..fe97662 --- /dev/null +++ b/src/sl/slMODnote.cxx @@ -0,0 +1,469 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slMODnote.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "slMODPrivate.h" + +static SampleInfo *smp ; +static unsigned char chToPlay[32]; + +static int masterVol = 0x30 * 4/3; +static int globalVol = 0x40; +static int mono; +static int outRate = DEF_OUTRATE ; +static int tempo = DEF_TEMPO ; /* BPM = rows/minute/4 */ +static int speed = DEF_SPEED ; /* frames/row */ +static int frameLen = DEF_OUTRATE * 60 / ( DEF_TEMPO * 24 ) ; +static int patRepeat = 0 ; + + + +static void commonWork ( Note *np ) +{ + _MOD_instClearPFW(); + + if (np->ins) + _MOD_instSample(&smp[np->ins - 1], 0); + + if (np->note != 255) + if (np->note == 254) + _MOD_instNoteOff(0); + else + _MOD_instNote((np->note/16) * 12 + np->note%16, 0); + + if (np->vol != 255) + _MOD_instVol(np->vol, 0); +} + + +static void noEffect ( Note *np ) +{ + if ( np->cmd == 255 ) + _MOD_instEmptyCmd () ; + + commonWork ( np ) ; +} + + +static void unknownEffect ( Note *np ) +{ + ulSetError ( UL_WARNING, "Unknown effect: %c%02X", np->cmd + '@', np->info ) ; + commonWork ( np ) ; +} + +#define X (np->info/16) +#define Y (np->info%16) + +/* Dxy, Kxy, Lxy common work */ + +static void dklCommonWork ( Note *np ) +{ + if ( np->info ) + { + if ( Y == 0 ) /* Dx0=up */ + _MOD_instSetVolSlideParams ( X, 1, 1, 1, 0 ) ; + else + if ( X > 0 && Y == 0xf ) /* DxF=fine up */ + _MOD_instSetVolSlideParams ( X, 1, 1, 1, 1 ) ; + else + if ( X == 0xf ) /* DFy=fine down */ + _MOD_instSetVolSlideParams ( -Y, 1, 1, 1, 1 ) ; + else /* D0y=down, but also D46 or something is here */ + _MOD_instSetVolSlideParams ( -Y, 1, 1, 1, 0 ) ; + } + + _MOD_instVolSlide () ; +} + +/* Dxy = volume slide */ + +static void dCmd ( Note *np ) +{ + commonWork ( np ) ; + dklCommonWork ( np ) ; +} + +/* Exx, Fxx common work */ + +static void efCommonWork ( Note *np ) +{ + if (np->info) + { + switch (X) + { + case 0xf: /* [EF]Fx = fine */ + _MOD_instSetPeriodSlideParams((np->info%16)*4, 1); + break; + + case 0xe: /* [EF]Ex = extra fine */ + /* ST3 plays [EF]E0 and [EF]F0 differently */ + /* so it may be incompatible.. */ + _MOD_instSetPeriodSlideParams(np->info%16, 1); + break; + + default: + _MOD_instSetPeriodSlideParams(np->info*4, 0); + break; + } + } +} + +/* Exx = slide down */ + +static void eCmd(Note *np) +{ + commonWork(np); + efCommonWork(np); + _MOD_instPeriodSlideDown(); +} + +/* Fxx = slide up */ + +static void fCmd(Note *np) +{ + commonWork(np); + efCommonWork(np); + _MOD_instPeriodSlideUp(); +} + +/* Gxx = portamento */ + +static void gCmd(Note *np) +{ + _MOD_instClearPFW(); + + if (np->ins) _MOD_instSetPortamentoDefaultVol(); /* only set default volume */ + if (np->vol != 255) _MOD_instVol(np->vol, 0); + if (np->note < 254 ) _MOD_instSetPortamentoTo((np->note/16)*12 + np->note%16); + if (np->info) _MOD_instSetPortamentoSpeed(np->info*4); + + _MOD_instPortamento(); +} + +/* Hxy = vibrato */ + +static void hCmd(Note *np) +{ + commonWork(np); + if (np->info) _MOD_instSetVibratoParams(X, Y*8); + _MOD_instVibrato(); +} + +/* Ixy = tremor */ + +static void iCmd(Note *np) +{ + commonWork(np); + if (np->info) _MOD_instSetTremorParams(X+1, Y+1); + _MOD_instTremor(); +} + +/* Jxy = arpeggio */ + +static void jCmd(Note *np) +{ + commonWork(np); + if (np->info) _MOD_instSetArpeggioParams(X, Y); + _MOD_instArpeggio(); +} + +/* Kxy = H00 and Dxy */ + +static void kCmd(Note *np) +{ + commonWork(np); + _MOD_instVibrato(); /* H00 */ + dklCommonWork(np); +} + +/* Lxy = G00 and Dxy */ + +static void lCmd(Note *np) +{ + commonWork(np); + _MOD_instPortamento(); /* G00 */ + dklCommonWork(np); +} + +/* Oxx = sample offset */ + +static void oCmd(Note *np) +{ + commonWork(np); + _MOD_instSampleOffset(np->info * 0x100); +} + +/* Qxy = retrig + volumeslide */ + +static void qCmd(Note *np) +{ + static int add[16]={0,-1,-2,-4,-8,-16,0,0,0,1,2,4,8,16,0,0}; + static int mul[16]={1,1,1,1,1,1,2,1,1,1,1,1,1,1,3,2}; + static int div[16]={1,1,1,1,1,1,3,2,1,1,1,1,1,1,2,1}; + + commonWork(np); + + if (np->info) + { + _MOD_instSetVolSlideParams(add[X], mul[X], div[X], Y, 0); + _MOD_instSetRetrigParam(Y); + } + + _MOD_instVolSlide(); + _MOD_instRetrig(); +} + +/* Rxy = tremolo */ + +static void rCmd(Note *np) +{ + commonWork(np); + if (np->info) _MOD_instSetTremoloParams(X, Y*2); + _MOD_instTremolo(); +} + +/* Uxy = fine vibrato */ + +static void uCmd(Note *np) +{ + commonWork(np); + if (np->info) _MOD_instSetVibratoParams(X, Y*2); + _MOD_instVibrato(); +} + +/* Sxy = misc */ + +static void sCmd(Note *np) +{ + if (X == 0xd) /* notedelay */ + { + _MOD_instClearPFW(); + + if (np->ins) + _MOD_instSample(&smp[np->ins - 1], Y); + + if (np->note != 255) + if (np->note == 254) + _MOD_instNoteOff(Y); + else + _MOD_instNote((np->note/16) * 12 + np->note%16, Y); + + if (np->vol != 255) + _MOD_instVol(np->vol, Y); + } + else + { + commonWork ( np ) ; + + switch ( X ) + { + case 1: /* set glissando control */ + _MOD_instSetPortamentoGlissando(Y); + break; + case 2: /* set finetune */ + /* ...but not tested yet. which tune use this? */ + ulSetError ( UL_DEBUG, "Got it! Set Finetune"); + { + static int freq[16] = + { + 8363,8413,8463,8529,8581,8651,8723,8757, + 7895,7941,7985,8046,8107,8169,8232,8280 + }; + _MOD_instTuning(freq[Y]); + /* the tuning effects from next key-on */ + } + break; + case 3: /* set vibrato waveform */ + _MOD_instSetVibratoWave(Y%4, Y/4); + break; + case 4: /* set tremolo waveform */ + _MOD_instSetTremoloWave(Y%4, Y/4); + break; + case 8: /* set pan position */ + _MOD_instPanPosition(Y*64/15); + break; + case 0xc: /* notecut */ + _MOD_instNoteCut(Y); + break; + case 0xb: /* pattern loop */ + case 0xe: /* pattern delay */ + break; + default: + ulSetError ( UL_WARNING, "%c%02X not supported.", np->cmd+'@', np->info); + } + } +} + +/* Vxx = set global volume */ + + +static void setGlobalVol(void) +{ + dacioGlobalVol ( masterVol * globalVol ) ; +} + + +void _MOD_playNoteSetMono(int m) +{ + mono = m; + _MOD_instMono(m); +} + +void _MOD_playNoteSetMasterVol(int mv) +{ + masterVol = mono? mv : mv * 4 / 3; + setGlobalVol(); +} + +void _MOD_playNoteSetGlobalVol(int gv) +{ + globalVol = gv; + setGlobalVol(); +} + +static void vCmd(Note *np) +{ + commonWork(np); + _MOD_playNoteSetGlobalVol(np->info); +} + +/* Xxx = DMP style pan position */ + +static void xCmd(Note *np) +{ + commonWork(np); + + if (np->info <= 0x80) + _MOD_instPanPosition(np->info * 64 / 0x80); + else + if (np->info == 0xa4) + _MOD_instPanPosition(-1); /* surround */ + else + _MOD_instPanPosition(32); /* unknown -> center */ +} + + +static void (*cmdTbl[])( Note *np ) = +{ + /*@*/ unknownEffect, /*A*/ noEffect , /*B*/ noEffect, /*C*/noEffect, + /*D*/ dCmd , /*E*/ eCmd , /*F*/ fCmd , /*G*/gCmd, + /*H*/ hCmd , /*I*/ iCmd , /*J*/ jCmd , /*K*/kCmd, + /*L*/ lCmd , /*M*/ unknownEffect, /*N*/ unknownEffect, /*O*/oCmd, + /*P*/ unknownEffect, /*Q*/ qCmd , /*R*/ rCmd , /*S*/sCmd, + /*T*/ noEffect , /*U*/ uCmd , /*V*/ vCmd , /*W*/unknownEffect, + /*X*/ xCmd , /*Y*/ unknownEffect, /*Z*/ unknownEffect +} ; + + +void _MOD_playNoteSetSample ( SampleInfo *sip ) +{ + smp = sip ; +} + + +void _MOD_playNoteInit ( void ) +{ + _MOD_instInit () ; +} + +static void setFrameLen ( void ) +{ + frameLen = outRate * 60 / (tempo * 24) ; + _MOD_instHirevSetFrameLen ( frameLen ) ; +} + + +void _MOD_playNoteSetOutRate ( int _or ) +{ + if ( _or > MAX_OUTRATE ) + { + ulSetError ( UL_FATAL, "Too high output sample rate." ) ; + } + + _MOD_instOutRate ( _or ) ; + outRate = _or ; + setFrameLen () ; +} + + +void _MOD_playNoteSetTempo ( int n ) +{ + if ( n < MIN_TEMPO ) + { + ulSetError ( UL_WARNING, "Illegal tempo (%d) ignored.", n ) ; + return ; + } + + tempo = n ; + setFrameLen () ; +} + + +void _MOD_playNoteSetSpeed ( int n ) +{ + speed = n ; +} + + +void _MOD_playNoteSetNote ( int ch, Note *np ) +{ + chToPlay [ ch ] = 1 ; + _MOD_instSelectCh ( ch ) ; + + if ( np->cmd == 255 ) + noEffect ( np ) ; + else + (*cmdTbl[np->cmd]) ( np ) ; +} + + +void _MOD_playNoteSetPatRepeat ( int n ) +{ + patRepeat = n ; +} + +void _MOD_playNote ( void ) +{ + for ( int r = 0 ; r <= patRepeat ; r++ ) + for ( int f = 0 ; f < speed ; f++ ) + { + _MOD_instHirevEraseBuf () ; + + for ( int ch = 0 ; ch < 32 ; ch++ ) + if ( chToPlay [ ch ] ) + { + _MOD_instSelectCh ( ch ) ; + _MOD_instDoPerFrameWorks ( f ) ; + _MOD_instLoop () ; + } + + _MOD_instHirevFlushBuf () ; + } + + patRepeat = 0 ; + + for ( int ch = 0 ; ch < 32 ; ch++ ) + chToPlay [ ch ] = 0 ; +} + + diff --git a/src/sl/slPlayer.cxx b/src/sl/slPlayer.cxx new file mode 100644 index 0000000..4327c90 --- /dev/null +++ b/src/sl/slPlayer.cxx @@ -0,0 +1,139 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slPlayer.cxx 1924 2004-04-06 13:32:26Z sjbaker $ +*/ + + +#include "sl.h" + +void slPlayer::addEnvelope ( int i, slEnvelope *_env, slEnvelopeType _type ) +{ + if ( i < 0 || i >= SL_MAX_ENVELOPES ) return ; + + if ( env [ i ] != NULL ) + env [ i ] -> unRef () ; + + env [ i ] = _env ; + + if ( _env != NULL ) + env [ i ] -> ref () ; + + env_type [ i ] = _type ; + env_start_time [ i ] = slScheduler::getCurrent() -> getTimeNow () ; +} + +int slPlayer::preempt ( int delay ) +{ + switch ( preempt_mode ) + { + case SL_SAMPLE_CONTINUE: if ( isRunning() ) + return SL_FALSE ; + /* FALLTHROUGH! */ + case SL_SAMPLE_DELAY : break ; + case SL_SAMPLE_MUTE : skip ( delay ) ; break ; + case SL_SAMPLE_ABORT : stop () ; break ; + case SL_SAMPLE_RESTART : reset () ; break ; + default : break ; + } + + return SL_TRUE ; +} + +slPlayer::~slPlayer () +{ +} + + +void slPlayer::read ( int nframes, Uchar *dst, int next_env ) +{ + /* + WARNING: + + CO-RECURSIVE! + */ + + /* Find the next envelope */ + + while ( next_env < SL_MAX_ENVELOPES && env [ next_env ] == NULL ) + next_env++ ; + + /* + If there are no fancy envelopes to process then return + the raw data. + */ + + if ( next_env >= SL_MAX_ENVELOPES ) /* No fancy envelopes left */ + { + low_read ( nframes, dst ) ; + return ; + } + + /* + Envelope processing required... + + Process the next envelope using data read recursively through + the remaining envelopes. + */ + + switch ( env_type [ next_env ] ) + { + /* For Volume envelopes, SRC and DST can be the same buffer */ + + case SL_INVERSE_VOLUME_ENVELOPE: + read ( nframes, dst, next_env+1 ) ; + env[ next_env ]->applyToInvVolume ( dst,dst,nframes,env_start_time[ next_env ] ) ; + break ; + + case SL_VOLUME_ENVELOPE : + read ( nframes, dst, next_env+1 ) ; + env[ next_env ]->applyToVolume ( dst,dst,nframes,env_start_time[ next_env ] ) ; + break ; + + case SL_INVERSE_PITCH_ENVELOPE : + env[ next_env ]->applyToInvPitch ( dst,this,nframes,env_start_time[ next_env ], next_env+1 ) ; + break ; + + case SL_PITCH_ENVELOPE : + env[ next_env ]->applyToPitch ( dst,this,nframes,env_start_time[ next_env ], next_env+1 ) ; + break ; + + case SL_INVERSE_FILTER_ENVELOPE: + case SL_FILTER_ENVELOPE : + read ( nframes, dst, next_env+1 ) ; + env [ next_env ] -> applyToLPFilter ( dst, dst, nframes, + env_start_time [ next_env ] ) ; + break ; + + case SL_INVERSE_PAN_ENVELOPE : + case SL_PAN_ENVELOPE : + read ( nframes, dst, next_env+1 ) ; + break ; + + case SL_INVERSE_ECHO_ENVELOPE : + case SL_ECHO_ENVELOPE : + read ( nframes, dst, next_env+1 ) ; + break ; + + default : + break ; + } +} + diff --git a/src/sl/slPortability.h b/src/sl/slPortability.h new file mode 100644 index 0000000..a5ba64b --- /dev/null +++ b/src/sl/slPortability.h @@ -0,0 +1,95 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slPortability.h 2013 2005-01-16 06:03:31Z sjbaker $ +*/ + + +#ifndef __SLPORTABILITY_H__ +#define __SLPORTABILITY_H__ 1 + +/* ------------------------------------------------------------- */ +/* OS specific includes and defines ... */ +/* ------------------------------------------------------------- */ + +#include "ul.h" +#include +#include + +#ifdef UL_MACINTOSH + #include + #include + #ifdef __MWERKS__ + #include + #endif +#endif + +#ifdef UL_MAC_OSX + #include +#endif + +#include +#include +#include +#include +#include + +#if (defined(UL_LINUX) || defined(UL_BSD)) && !defined(__NetBSD__) +#define SL_USING_OSS_AUDIO 1 +#endif + +#ifdef SL_USING_OSS_AUDIO +# if defined(UL_LINUX) +# include +# include +# elif defined(__FreeBSD__) +# include +# else + /* + Tom thinks this file may be under some + unixen - but that isn't where the OSS manuals say it + should be. + + If you ever find out the truth, please email me: + Steve Baker + */ +# include +# endif +#endif + +#ifdef UL_BSD +#ifndef __FreeBSD__ +# include +#endif +#endif + +/* Tom */ + +#ifdef UL_IRIX +# include +#endif + +#ifdef UL_SOLARIS +# include +# include +#endif + +#endif + diff --git a/src/sl/slSample.cxx b/src/sl/slSample.cxx new file mode 100644 index 0000000..4396781 --- /dev/null +++ b/src/sl/slSample.cxx @@ -0,0 +1,527 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slSample.cxx 1853 2004-02-08 18:41:15Z stromberg $ +*/ + + +#include "sl.h" +#include + +void slSample::autoMatch ( const slDSP *dsp ) +{ + if ( dsp == NULL || dsp->notWorking () ) return ; + + changeRate ( dsp->getRate () ) ; + changeBps ( dsp->getBps () ) ; + changeStereo ( dsp->getStereo () ) ; +} + +void slSample::adjustVolume ( float vol ) +{ + for ( int i = 0 ; i < length ; i++ ) + { + int s = (int)(((float) buffer[i] - (float) 0x80) * vol) + 0x80 ; + + buffer [ i ] = ( s > 255 ) ? 255 : + ( s < 0 ) ? 0 : s ; + } +} + + +void slSample::changeRate ( int r ) +{ + if ( r == rate ) return ; + + int length2 = (int) ( (float) length * ( (float) r / (float) rate ) ) ; + Uchar *buffer2 = new Uchar [ length2 ] ; + + int samps = length / (getBps() / 8) ; + int samps2 = length2 / (getBps() / 8) ; + + float step = (float) length / (float) length2 ; + + for ( int i = 0 ; i < samps2 ; i++ ) + { + float pos = (float) i * step ; + + int p1 = (int) floor ( pos ) ; + int p2 = (int) ceil ( pos ) ; + + if ( stereo ) + { + if ( ( p1 & 1 ) != ( i & 1 ) ) { pos++ ; p1++ ; p2++ ; } + p2++ ; + } + + float ratio = pos - (float) p1 ; + + float b1 = (getBps()==8) ? + (float) buffer [(p1<0)?0:(p1>=samps)?samps-1:p1] : + (float) ((Ushort*)buffer)[(p1<0)?0:(p1>=samps)?samps-1:p1] ; + float b2 = (getBps()==8) ? + (float) buffer [(p2<0)?0:(p2>=samps)?samps-1:p2] : + (float) ((Ushort*)buffer)[(p2<0)?0:(p2>=samps)?samps-1:p2] ; + + float res = b1 * (1.0f-ratio) + b2 * ratio ; + + if ( getBps () == 8 ) + buffer2 [ i ] = (Uchar) ( (res < 0) ? 0 : (res > 255) ? 255 : res ) ; + else + ((Ushort *) buffer2 ) [ i ] = + (Ushort) ( (res < 0) ? 0 : (res > 65535) ? 65535 : res ) ; + } + + rate = r ; + length = length2 ; + delete [] buffer ; + buffer = buffer2 ; +} + + +void slSample::changeToUnsigned () +{ + if ( getBps() == 16 ) + { + int length2 = length / 2 ; + Ushort *buffer2 = (Ushort *) buffer ; + + for ( int i = 0 ; i < length2 ; i++ ) + buffer2 [ i ] = buffer2 [ i ] + 32768 ; + } + else + { + for ( int i = 0 ; i < length ; i++ ) + buffer [ i ] = (buffer [ i ]>0x80) ? (buffer[i]-0x80) : + (0xFF-buffer[i]) ; + } +} + + + +void slSample::changeBps ( int b ) +{ + if ( b == getBps () ) return ; + + if ( b == 8 && getBps() == 16 ) + { + length /= 2 ; + Uchar *buffer2 = new Uchar [ length ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i ] = ((Ushort *)buffer) [ i ] >> 8 ; + + delete [] buffer ; + buffer = buffer2 ; + setBps ( b ) ; + } + else + if ( b == 16 && getBps() == 8 ) + { + Ushort *buffer2 = new Ushort [ length ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i ] = buffer [ i ] << 8 ; + + delete [] buffer ; + buffer = (Uchar *) buffer2 ; + length *= 2 ; + setBps ( b ) ; + } +} + +void slSample::changeStereo ( int s ) +{ + if ( s == getStereo () ) + return ; + + if ( s && ! getStereo () ) + { + if ( getBps () == 8 ) + { + Uchar *buffer2 = new Uchar [ length * 2 ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i*2 ] = buffer2 [ i*2+1 ] = buffer [ i ] ; + + delete [] buffer ; + buffer = buffer2 ; + length *= 2 ; + setStereo ( SL_TRUE ) ; + } + else + { + Ushort *buffer2 = new Ushort [ length ] ; + + for ( int i = 0 ; i < length / 2 ; i++ ) + buffer2 [ i*2 ] = buffer2 [ i*2+1 ] = ((Ushort *) buffer) [ i ] ; + + delete [] buffer ; + buffer = (Uchar *)buffer2 ; + length *= 2 ; + setStereo ( SL_TRUE ) ; + } + } + else + { + if ( getBps () == 8 ) + { + Uchar *buffer2 = new Uchar [ length / 2 ] ; + + for ( int i = 0 ; i < (length-1)/2 ; i++ ) + buffer2 [ i ] = ((int)buffer [ i*2 ] + (int)buffer [ i*2 + 1 ] ) / 2 ; + + delete [] buffer ; + buffer = buffer2 ; + length /= 2 ; + setStereo ( SL_FALSE ) ; + } + else + { + Ushort *buffer2 = new Ushort [ length / 4 ] ; + + for ( int i = 0 ; i < (length-3) / 4 ; i++ ) + buffer2 [ i ] = ((int)((Ushort *)buffer) [ i*2 ] + + (int)((Ushort *)buffer) [ i*2 + 1 ] ) / 2 ; + + delete [] buffer ; + buffer = (Uchar *)buffer2 ; + length /= 4 ; + setStereo ( SL_FALSE ) ; + } + } +} + + +static void swap_Ushort ( Ushort *i ) +{ + *i = ((*i << 8) & 0xFF00) + + ((*i >> 8) & 0x00FF) ; +} + +static void swap_int ( int *i ) +{ + *i = ((*i << 24) & 0xFF000000) + + ((*i << 8) & 0x00FF0000) + + ((*i >> 8) & 0x0000FF00) + + ((*i >> 24) & 0x000000FF) ; +} + + +int slSample::loadFile ( const char *fname ) +{ + if ( ulStrEqual ( & fname [ strlen ( fname ) - 4 ], ".wav" ) ) + return loadWavFile ( fname ) ; + + if ( ulStrEqual ( & fname [ strlen ( fname ) - 3 ], ".au" ) ) + return loadAUFile ( fname ) ; + + if ( ulStrEqual ( & fname [ strlen ( fname ) - 3 ], ".ub" ) ) + return loadRawFile ( fname ) ; + + ulSetError ( UL_WARNING, + "slSample:loadFile: Unknown file type for '%s'.", fname ) ; + return SL_FALSE ; +} + + +int slSample::loadWavFile ( const char *fname ) +{ + int found_header = SL_FALSE ; + int needs_swabbing = SL_FALSE ; + + delete [] buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + ulSetError ( UL_WARNING, + "slSample: loadWavFile: Cannot open '%s' for reading.", fname ) ; + return SL_FALSE ; + } + + char magic [ 8 ] ; + + if ( fread ( magic, 4, 1, fd ) == 0 || + magic[0] != 'R' || magic[1] != 'I' || + magic[2] != 'F' || magic[3] != 'F' ) + { + ulSetError ( UL_WARNING, "slWavSample: File '%s' has wrong magic number", fname ) ; + ulSetError ( UL_WARNING, " - it probably isn't in '.wav' format." ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + int leng1 ; + + if ( fread ( & leng1, sizeof(int), 1, fd ) == 0 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has premature EOF in header", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + fread ( magic, 4, 1, fd ) ; + + if ( magic[0] != 'W' || magic[1] != 'A' || + magic[2] != 'V' || magic[3] != 'E' ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has no WAVE tag.", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + while ( ! feof ( fd ) ) + { + fread ( magic, 4, 1, fd ) ; + + if ( magic[0] == 'f' && magic[1] == 'm' && + magic[2] == 't' && magic[3] == ' ' ) + { + found_header = SL_TRUE ; + + if ( fread ( & leng1, sizeof(int), 1, fd ) == 0 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has premature EOF in header", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( leng1 > 65536 ) + { + needs_swabbing = SL_TRUE ; + swap_int ( & leng1 ) ; + } + + Ushort header [ 8 ] ; + + if ( leng1 != sizeof ( header ) ) + ulSetError ( UL_WARNING, + "slSample: File '%s' has unexpectedly long (%d byte) header", + fname, leng1 ) ; + + fread ( & header, sizeof(header), 1, fd ) ; + + for ( int junk = sizeof(header) ; junk < leng1 ; junk++ ) + getc ( fd ) ; + + if ( needs_swabbing ) + { + swap_Ushort ( & header[0] ) ; + swap_Ushort ( & header[1] ) ; + swap_int ( (int *) & header[2] ) ; + swap_int ( (int *) & header[4] ) ; + swap_Ushort ( & header[6] ) ; + swap_Ushort ( & header[7] ) ; + } + + if ( header [ 0 ] != 0x0001 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' is not WAVE_FORMAT_PCM!", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + setStereo ( header[1] > 1 ) ; + setRate ( *((int *) (& header[2])) ) ; + setBps ( header[7] ) ; + } + else + if ( magic[0] == 'd' && magic[1] == 'a' && + magic[2] == 't' && magic[3] == 'a' ) + { + if ( ! found_header ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has no data section", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( fread ( & length, sizeof(int), 1, fd ) == 0 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has premature EOF in data", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( needs_swabbing ) + swap_int ( & length ) ; + + buffer = new Uchar [ length ] ; + + fread ( buffer, 1, length, fd ) ; + + if ( getBps () == 16 ) + { + changeToUnsigned () ; + } + + fclose ( fd ) ; + return SL_TRUE ; + } + } + + ulSetError ( UL_WARNING, "slSample: Premature EOF in '%s'.", fname ) ; + + fclose ( fd ) ; + return SL_FALSE ; +} + +int slSample::loadAUFile ( const char *fname ) +{ + delete [] buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + ulSetError ( UL_WARNING, + "slSample: loadAUFile: Cannot open '%s' for reading.", + fname ) ; + return SL_FALSE ; + } + + char magic [ 4 ] ; + + if ( fread ( magic, 4, 1, fd ) == 0 || + magic[0] != '.' || magic[1] != 's' || + magic[2] != 'n' || magic[3] != 'd' ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has wrong magic number", fname ) ; + ulSetError ( UL_WARNING, " - it probably isn't in '.au' format." ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + int hdr_length ; + int dat_length ; + int nbytes ; + int irate ; + int nchans ; + + if ( fread ( & hdr_length, sizeof(int), 1, fd ) == 0 || + fread ( & dat_length, sizeof(int), 1, fd ) == 0 || + fread ( & nbytes , sizeof(int), 1, fd ) == 0 || + fread ( & irate , sizeof(int), 1, fd ) == 0 || + fread ( & nchans , sizeof(int), 1, fd ) == 0 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has premature EOF in header", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( hdr_length > 65536 ) + { + swap_int ( & hdr_length ) ; + swap_int ( & dat_length ) ; + swap_int ( & nbytes ) ; + swap_int ( & irate ) ; + swap_int ( & nchans ) ; + } + + bps = nbytes * 8 ; + stereo = (nchans>1) ; + rate = irate ; + + if ( nbytes > 2 || nbytes <= 0 || hdr_length > 512 || hdr_length < 24 || + irate > 65526 || irate <= 1000 || nchans < 1 || nchans > 2 ) + { + ulSetError ( UL_WARNING, "slSample: File '%s' has a very strange header", fname ) ; + ulSetError ( UL_WARNING, " Header Length = %d", hdr_length ) ; + ulSetError ( UL_WARNING, " Data Length = %d", dat_length ) ; + ulSetError ( UL_WARNING, " Bytes/sample = %d", nbytes ) ; + ulSetError ( UL_WARNING, " Sampling Rate = %dHz",irate ) ; + ulSetError ( UL_WARNING, " Num Channels = %d", nchans ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( hdr_length > 24 ) + { + delete [] comment ; + comment = new char [ hdr_length - 24 + 1 ] ; + + fread ( comment, 1, hdr_length - 24, fd ) ; + } + + if ( dat_length > 0 ) + { + buffer = new Uchar [ dat_length ] ; + length = fread ( buffer, 1, dat_length, fd ) ; + + if ( length != dat_length ) + ulSetError ( UL_WARNING, "slAUSample: File '%s' has premature EOF in data.", fname ) ; + changeToUnsigned () ; + } + + fclose ( fd ) ; + return SL_TRUE ; +} + + +int slSample::loadRawFile ( const char *fname ) +{ + delete [] buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + ulSetError ( UL_WARNING, + "slSample: loadRawFile: Cannot open '%s' for reading.", + fname ) ; + return SL_FALSE ; + } + + struct stat stat_buf ; + + if ( fstat ( fileno ( fd ), & stat_buf ) != 0 ) + { + ulSetError ( UL_WARNING, + "slSample: loadRawFile: Cannot get status for '%s'.", + fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + length = stat_buf . st_size ; + + if ( length > 0 ) + { + buffer = new Uchar [ length ] ; + length = fread ( buffer, 1, length, fd ) ; + } + + bps = 8 ; + stereo = SL_FALSE ; + rate = 8000 ; /* Guess */ + + fclose ( fd ) ; + return SL_TRUE ; +} + + diff --git a/src/sl/slSamplePlayer.cxx b/src/sl/slSamplePlayer.cxx new file mode 100644 index 0000000..7e4da1c --- /dev/null +++ b/src/sl/slSamplePlayer.cxx @@ -0,0 +1,120 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slSamplePlayer.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "sl.h" + +int slSamplePlayer::preempt ( int delay ) +{ + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_PREEMPTED, magic ) ; + + return slPlayer::preempt ( delay ) ; +} + +slSamplePlayer::~slSamplePlayer () +{ + if ( sample ) + sample -> unRef () ; + + /* unRef() attached envelopes */ + for ( int i = 0 ; i < SL_MAX_ENVELOPES ; i++ ) + { + if ( env [ i ] != NULL ) + env [ i ] -> unRef(); + } + + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_COMPLETE, magic ) ; +} + +void slSamplePlayer::skip ( int nframes ) +{ + if ( nframes < lengthRemaining ) + { + lengthRemaining -= nframes ; + bufferPos += nframes ; + } + else + if ( replay_mode == SL_SAMPLE_LOOP ) + { + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_LOOPED, magic ) ; + + nframes -= lengthRemaining ; + + while ( nframes >= sample->getLength () ) + nframes -= sample->getLength () ; + + lengthRemaining = sample->getLength() - nframes ; + bufferPos = & ( sample->getBuffer() [ nframes ] ) ; + } + else + stop () ; +} + + +void slSamplePlayer::low_read ( int nframes, Uchar *dst ) +{ + if ( isWaiting() ) start () ; + + if ( bufferPos == NULL ) /* Run out of sample & no repeats */ + { + memset ( dst, 0x80, nframes ) ; + return ; + } + + while ( SL_TRUE ) + { + /* + If we can satisfy this request in one read (with data left in + the sample buffer ready for next time around) - then we are done... + */ + + if ( nframes < lengthRemaining ) + { + memcpy ( dst, bufferPos, nframes ) ; + bufferPos += nframes ; + lengthRemaining -= nframes ; + return ; + } + + memcpy ( dst, bufferPos, lengthRemaining ) ; + bufferPos += lengthRemaining ; + dst += lengthRemaining ; + nframes -= lengthRemaining ; + lengthRemaining = 0 ; + + if ( replay_mode == SL_SAMPLE_ONE_SHOT ) + { + stop () ; + memset ( dst, 0x80, nframes ) ; + return ; + } + else + { + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_LOOPED, magic ) ; + start () ; + } + } +} + + + diff --git a/src/sl/slScheduler.cxx b/src/sl/slScheduler.cxx new file mode 100644 index 0000000..9d066cf --- /dev/null +++ b/src/sl/slScheduler.cxx @@ -0,0 +1,545 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: slScheduler.cxx 1764 2003-05-18 13:45:04Z puggles $ +*/ + + +#include "sl.h" + +const char *__slPendingError = NULL ; + +slScheduler *slScheduler::current = NULL ; + +void slScheduler::init () +{ + int mi; + mixer = NULL ; + + mixer_buffer = NULL ; + /* Note there is a required null element on the end of array */ + for ( mi=0; mi <= SL_MAX_MIXERINPUTS; mi++ ) + mixer_inputs[mi] = NULL ; + + current = this ; + mixer_gain = 0x100 ; /* When we have to make sound quieter */ + + if ( notWorking () ) + { + ulSetError ( UL_WARNING, "slScheduler: soundcard init failed." ) ; + setError () ; + return ; + } + + if ( getBps() != 8 ) + { + ulSetError ( UL_WARNING, "slScheduler: Needs a sound card that supports 8 bits per sample." ) ; + setError () ; + return ; + } + + if ( getStereo() ) + { + ulSetError ( UL_WARNING, "slScheduler: Needs a sound card that supports monophonic replay." ) ; + setError () ; + return ; + } + + music = NULL ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + player [ i ] = NULL ; + + amount_left = 0 ; + now = 0 ; + num_pending_callbacks = 0 ; + safety_margin = 1.0 ; + + initBuffers () ; +} + +void slScheduler::initBuffers () +{ + if ( notWorking () ) + return ; + + delete [] mixer_buffer ; + setMaxConcurrent ( 0 ); + + mixer_buffer_size = getDriverBufferSize () ; + int div = 1; + if ( getStereo() ) div *= 2 ; + if ( getBps() == 16 ) div *= 2 ; + seconds_per_buffer = (float)mixer_buffer_size / ( div * getRate() ) ; + + mixer_buffer = new Uchar [ mixer_buffer_size ] ; + memset ( mixer_buffer, 0x80, mixer_buffer_size ) ; + setMaxConcurrent ( 3 ); +} + +void slScheduler::setMaxConcurrent ( int mc ) +{ + int mi; + for ( mi = 0; mi < SL_MAX_MIXERINPUTS; mi ++ ) + if ( mi < mc ) + { if ( mixer_inputs[mi] == NULL ) + mixer_inputs[mi] = new Uchar [ mixer_buffer_size ] ; + } else + { if ( mixer_inputs[mi] != NULL ) + delete [] mixer_inputs[mi] ; + mixer_inputs[mi] = NULL; + } +} + +int slScheduler::getMaxConcurrent ( void ) const +{ + int mi, mc = 0; + for ( mi = 0; mi < SL_MAX_MIXERINPUTS; mi ++ ) + if ( NULL != mixer_inputs[mi] ) + mc ++; + return mc; +} + +slScheduler::~slScheduler () +{ + delete [] mixer_buffer ; + setMaxConcurrent(0); + if ( current == this ) + current = NULL ; +} + +void slScheduler::realUpdate ( int dump_first ) +{ + if ( notWorking () ) return ; + + if ( __slPendingError != NULL ) + { + ulSetError ( UL_FATAL, "%s", __slPendingError ) ; + } + + int i ; + + while ( ( secondsUsed() <= safety_margin ) && ( secondsRemaining() >= seconds_per_buffer ) ) + { + slPlayer *psp [ SL_MAX_MIXERINPUTS ] ; + int pri [ SL_MAX_MIXERINPUTS ] ; + int inputsused = 0 ; + int lowest = 0 ; /* makes it safe to reference before assignment below */ + + /* Decide who we're going to use */ + + for ( i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + { + /* Ignore non-existent channels */ + + if ( player [ i ] == NULL ) + continue ; + + /* Clean up dead sample players */ + + if ( player [ i ] -> isDone () ) + { + if ( player [ i ] == music ) + music = NULL ; + + delete player [ i ] ; + player [ i ] = NULL ; + continue ; + } + + /* Skip paused players, but leave them alone */ + + if ( player [ i ] -> isPaused () ) + continue ; + + /* This one is viable, make sure we have room */ + + int mypri = player[i]->getPriority() ; + + if ( ( inputsused > 0 ) && + ( mixer_inputs[inputsused] == NULL ) && + ( mypri > pri[lowest] ) + ) + { /* Not enough room; get rid of someone */ + if ( player[lowest] != NULL ) + player[lowest] -> preempt ( mixer_buffer_size ) ; + + psp[lowest] = player[i] ; + pri[lowest] = mypri ; + int j; + for ( j = 0; j < inputsused; j++ ) + if ( pri[lowest] < pri[j] ) + lowest = j; + } + else if ( mixer_inputs[inputsused] != NULL ) + { /* Ok, we've got room, presumably, for another item */ + psp[inputsused] = player[i]; + pri[inputsused] = mypri; + if ( ( ( inputsused ++ ) == 0 ) || + ( pri[lowest] > mypri ) ) + lowest = inputsused; + } + else + { /* Oops, something very wrong. Shouldn't get here in normal use. + One obvious reason is if user specified zero channels to mix! */ + player[i] -> preempt ( mixer_buffer_size ) ; + } + } + + /* We've now chosen our sources. Previously we'd go back through + and figure out who to report as pre-empted. But we already did + that as part of getting our list of sources, so that disappeared. */ + + /* Now, we actually get the sound samples into the mixer_buffer. + All the versions here are fastpaths except the two last ones. + Just read the comments on the last one to understand it. */ + + + /* We put a minus sign on the number of inputs if we're attenuating */ + switch ( mixer_gain != 0x100 ? -inputsused : inputsused ) + { + + case 0: + /* No sound whatsoever, the attenuation is irrelevant */ + memset ( mixer_buffer, 0x80, mixer_buffer_size ) ; + amount_left = 0 ; + if ( mixer_gain < 0x100 ) mixer_gain ++; + break ; + + case 1: + /* One sample, so overflow is impossible. No legacy attenuation */ + psp[0] -> read ( mixer_buffer_size, mixer_buffer ) ; + break ; + + case 2: + { register int l = mixer_buffer_size ; + register Uchar *d = mixer_buffer, *a, *b ; + register int t, x = l / 100 ; + psp[0] -> read ( l, a = mixer_inputs [ 0 ] ) ; + psp[1] -> read ( l, b = mixer_inputs [ 1 ] ) ; + while ( l-- ) + { t = (*a++) - 0x80 + (*b++) ; + *d++ = ( t >= 0x100 ? x--, 0xff : + ( t <= 0x000 ? x--, 0x00 : t ) ); + } + if ( x < 0 ) mixer_gain -- ; + } break; + + case 3: + { register int l = mixer_buffer_size ; + register Uchar *d = mixer_buffer, *a, *b, *c ; + register int t, x = l / 100 ; + psp[0] -> read ( l, a = mixer_inputs [ 0 ] ) ; + psp[1] -> read ( l, b = mixer_inputs [ 1 ] ) ; + psp[2] -> read ( l, c = mixer_inputs [ 2 ] ) ; + while ( l-- ) *d++ = ( t = (*a++) - 0x100 + + (*b++) + + (*c++) ), + t >= 0x100 ? x--, 0xff : + t <= 0x000 ? x--, 0x00 : t; + if ( x < 0 ) mixer_gain -- ; + } break; + + default: + { /* How much data to get, and where to put it */ + int l = mixer_buffer_size ; + register Uchar *d = mixer_buffer ; + /* We compute one value in here, in the loop below */ + register int t ; + /* We need to determine the maximum range of values */ + int tmax = 0x80, tmin = 0x80; + /* A pointer to the element about to be added to "t" */ + register Uchar **p ; + /* Sound cards use offset binary, so do zero adjustment */ + int z = ( (int) 0x8000 / mixer_gain ) - ( inputsused * 0x80 ) ; + /* An array of pointers to keep in step with "d" above */ + register Uchar * s [ SL_MAX_MIXERINPUTS + 1 ] ; + /* First thing is to read individual channels into buffers */ + for ( i = 0; i < inputsused; i++ ) + psp[i] -> read ( l, s [ i ] = mixer_inputs [ i ] ); + /* Put a null on the end of "s" so we don't have to count */ + s [ inputsused ] = NULL; + /* This while loop is executed for each audio sample */ + while ( l-- ) + { /* Accumulation to zero, and point to first buffer item */ + t = z; + p = s; + /* Iterate through all buffers to do accumulation */ + while ( /* Add this buffer element, advance within buffer */ + t += *((*p)++), + /* Also advance to the next channel buffer */ + *(++p) + /* Make the while loop quit when we find NULL */ + ) { }; + /* Apply gain correction if needed */ + if ( mixer_gain != 0x100 ) + t = ( t * mixer_gain ) / 0x100 ; + /* Store accumulation, but force onscale */ + *(d++) = t >= 0x100 ? 0xff : + t <= 0x000 ? 0x00 : t ; + /* Figure out the maximum signal range */ + if ( tmax < t ) tmax = t ; + else + if ( tmin > t ) tmin = t ; + } + /* Consider changing the attenuation */ + if ( ( ( tmin < 0x00 ) || ( tmax > 0xFF ) ) + && ( mixer_gain > 1 ) ) + mixer_gain -- ; + else + if ( ( tmin * ( mixer_gain + 1 ) > 0x80 + 0x10 * mixer_gain ) + && ( tmax * ( mixer_gain + 1 ) < 0x80 + 0xE0 * mixer_gain ) + && ( mixer_gain < 0x100 ) ) + mixer_gain ++ ; + // printf ( "Gain=%03x, Range=%04x,%04x\n", mixer_gain, tmin, tmax ); + } break; + } + + /* We've created the next buffer of sound data ... now what ? */ + + if ( dump_first ) + { + stop () ; + dump_first = SL_FALSE ; + } + + play ( mixer_buffer, mixer_buffer_size ) ; + + now += mixer_buffer_size ; + } + + flushCallBacks () ; +} + +void slScheduler::addCallBack ( slCallBack c, slSample *s, slEvent e, int m ) +{ + if ( notWorking () ) return ; + + if ( num_pending_callbacks >= SL_MAX_CALLBACKS ) + { + ulSetError ( UL_WARNING, "slScheduler: Too many pending callback events!" ) ; + return ; + } + + slPendingCallBack *p = & ( pending_callback [ num_pending_callbacks++ ] ) ; + + p -> callback = c ; + p -> sample = s ; + p -> event = e ; + p -> magic = m ; +} + +void slScheduler::flushCallBacks () +{ + if ( notWorking () ) return ; + + /* + Execute all the callbacks that we accumulated + in this iteration. + + This is done at the end of 'update' to reduce the risk + of nasty side-effects caused by 'unusual' activities + in the application's callback function. + */ + + while ( num_pending_callbacks > 0 ) + { + slPendingCallBack *p = & ( pending_callback [ --num_pending_callbacks ] ) ; + + if ( p -> callback ) + (*(p->callback))( p->sample, p->event, p->magic ) ; + } +} + +void slScheduler::addSampleEnvelope ( slSample *s, int magic, + int slot, slEnvelope *e, + slEnvelopeType t) +{ + if ( notWorking () ) return ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] != NULL && player[i] != music && + ( s == NULL || player [ i ] -> getSample () == s ) && + ( magic == 0 || player [ i ] -> getMagic () == magic ) ) + player [ i ] -> addEnvelope ( slot, e, t ) ; +} + +void slScheduler::resumeSample ( slSample *s, int magic) +{ + if ( notWorking () ) return ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] != NULL && player[i] != music && + ( s == NULL || player [ i ] -> getSample () == s ) && + ( magic == 0 || player [ i ] -> getMagic () == magic ) ) + player [ i ] -> resume () ; +} + +void slScheduler::pauseSample ( slSample *s, int magic) +{ + if ( notWorking () ) return ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] != NULL && player[i] != music && + ( s == NULL || player [ i ] -> getSample () == s ) && + ( magic == 0 || player [ i ] -> getMagic () == magic ) ) + player [ i ] -> pause () ; +} + +void slScheduler::stopSample ( slSample *s, int magic ) +{ + if ( notWorking () ) return ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] != NULL && player[i] != music && + ( s == NULL || player [ i ] -> getSample () == s ) && + ( magic == 0 || player [ i ] -> getMagic () == magic ) ) + player [ i ] -> stop () ; +} + +int slScheduler::loopSample ( slSample *s, int pri, + slPreemptMode mode, + int magic, slCallBack cb ) +{ + if ( notWorking () ) return -1 ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] == NULL ) + { + player [ i ] = new slSamplePlayer ( s, SL_SAMPLE_LOOP, pri, mode, magic, cb ) ; + return i ; + } + + return -1 ; +} + +int slScheduler::playSample ( slSample *s, int pri, + slPreemptMode mode, + int magic, slCallBack cb) +{ + if ( notWorking () ) return SL_FALSE ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] == NULL ) + { + player [ i ] = new slSamplePlayer ( s, SL_SAMPLE_ONE_SHOT, pri, mode, magic, cb ) ; + return SL_TRUE ; + } + + return SL_FALSE ; +} + +void slScheduler::addMusicEnvelope ( int magic, + int slot, slEnvelope *e, + slEnvelopeType t) +{ + if ( notWorking () ) return ; + + if ( music != NULL && + ( magic == 0 || music -> getMagic () == magic ) ) + music -> addEnvelope ( slot, e, t ) ; +} + +void slScheduler::resumeMusic ( int magic) +{ + if ( notWorking () ) return ; + + if ( music != NULL && + ( magic == 0 || music -> getMagic () == magic ) ) + music -> resume () ; +} + +void slScheduler::pauseMusic ( int magic) +{ + if ( notWorking () ) return ; + + if ( music != NULL && + ( magic == 0 || music -> getMagic () == magic ) ) + music -> pause () ; +} + +void slScheduler::stopMusic ( int magic ) +{ + if ( notWorking () ) return ; + + if ( music != NULL && + ( magic == 0 || music -> getMagic () == magic ) ) + { + music -> stop () ; + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] == music ) + player [ i ] = NULL ; + + delete music ; + music = NULL ; + } +} + +int slScheduler::loopMusic ( const char *fname, int pri, + slPreemptMode mode, + int magic, slCallBack cb ) +{ + if ( notWorking () ) return -1 ; + + if ( music != NULL ) + { + ulSetError ( UL_WARNING, "slScheduler: Can't play two music tracks at once." ) ; + return -1 ; + } + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] == NULL ) + { + music = new slMODPlayer ( fname, SL_SAMPLE_LOOP, + pri, mode, magic, cb ) ; + player [ i ] = music ; + return i ; + } + + return -1 ; +} + +int slScheduler::playMusic ( const char *fname, int pri, + slPreemptMode mode, + int magic, slCallBack cb ) +{ + if ( notWorking () ) return SL_FALSE ; + + if ( music != NULL ) + { + ulSetError ( UL_WARNING, "slScheduler: Can't play two music tracks at once." ) ; + return SL_FALSE ; + } + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( player [ i ] == NULL ) + { + music = new slMODPlayer ( fname, SL_SAMPLE_ONE_SHOT, + pri, mode, magic, cb ) ; + player [ i ] = music ; + + return SL_TRUE ; + } + + return SL_FALSE ; +} + + diff --git a/src/sl/sm.h b/src/sl/sm.h new file mode 100644 index 0000000..236d60c --- /dev/null +++ b/src/sl/sm.h @@ -0,0 +1,111 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: sm.h 1732 2002-11-30 00:41:50Z sjbaker $ +*/ + + +#ifndef __SM_H__ +#define __SM_H__ 1 + +#include "slPortability.h" + +#ifdef SL_USING_OSS_AUDIO +#define SMMIXER_DEFAULT_DEVICE "/dev/mixer" +#elif defined(UL_WIN32) +#define SMMIXER_DEFAULT_DEVICE "mixer" +#else +#endif + + +# define SM_TRUE 1 +# define SM_FALSE 0 + +typedef unsigned char Uchar ; +typedef unsigned short Ushort ; + + +class smMixer +{ +private: + + int devices ; + int error ; + int fd ; + +#ifdef SL_USING_OSS_AUDIO + + int ioctl ( int cmd, int param = 0 ) + { + if ( error ) return param ; + + if ( ::ioctl ( fd, cmd, & param ) == -1 ) + { + perror ( "smMixer: ioctl" ) ; + error = SM_TRUE ; + } + + return param ; + } +#endif + void open ( const char *device ) ; + void close () ; + +public: + + /* Tom */ + + smMixer (); + smMixer ( const char *device ); + ~smMixer (); + + int notWorking () const ; + int not_working () const { return notWorking () ; } /* Originally a typo! */ + + /* Volume controls are in integer percentages */ + + int getVolume ( int channel ); + void setVolume ( int channel, int volume ); + + void getVolume ( int channel, int *left, int *right ); + void setVolume ( int channel, int left, int right ); + + void setTreble ( int treble ); + void setBass ( int bass ); + + void setMasterVolume ( int volume ); + void setSynthVolume ( int volume ); + void setPCMVolume ( int volume ); + void setSpeakerVolume( int volume ); + void setLineVolume ( int volume ); + void setMicVolume ( int volume ); + void setCDVolume ( int volume ); + + void setMasterVolume ( int left, int right ); + void setSynthVolume ( int left, int right ); + void setPCMVolume ( int left, int right ); + void setSpeakerVolume( int left, int right ); + void setLineVolume ( int left, int right ); + void setMicVolume ( int left, int right ); + void setCDVolume ( int left, int right ); +} ; + +#endif + diff --git a/src/sl/smMixer.cxx b/src/sl/smMixer.cxx new file mode 100644 index 0000000..9442a3f --- /dev/null +++ b/src/sl/smMixer.cxx @@ -0,0 +1,279 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: smMixer.cxx 1732 2002-11-30 00:41:50Z sjbaker $ +*/ + +#include "sm.h" + +#ifdef SL_USING_OSS_AUDIO +/* ------------------------------------------------------------ */ +/* OSSAUDIO - Linux, FreeBSD */ +/* ------------------------------------------------------------ */ + + +void smMixer::open ( const char *device ) +{ + fd = ::open ( device, O_WRONLY ) ; + + if ( fd < 0 ) + { + perror ( "smMixer: open" ) ; + error = SM_TRUE ; + } + else + error = SM_FALSE ; + + devices = ioctl ( SOUND_MIXER_READ_DEVMASK ) ; +} + +void smMixer::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +smMixer::smMixer () +{ + open ( SMMIXER_DEFAULT_DEVICE ) ; +} + +smMixer::smMixer ( const char *device ) +{ + open ( device ) ; +} + +smMixer::~smMixer () +{ + close () ; +} + +int smMixer::notWorking () const +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int channel ) +{ + return ioctl ( MIXER_READ ( channel ) ) & 0xFF ; +} + + +void smMixer::setVolume ( int channel, int volume ) +{ + ioctl ( MIXER_WRITE ( channel ), (( volume & 255 ) << 8 ) | + ( volume & 255 ) ) ; +} + +void smMixer::getVolume ( int channel, int *left, int *right ) +{ + int vv = ioctl ( MIXER_READ ( channel ) ) ; + + if ( left ) *left = vv & 0xFF ; + if ( right ) *right = (vv>>8) & 0xFF ; +} + +void smMixer::setVolume ( int channel, int left, int right ) +{ + ioctl ( MIXER_WRITE ( channel ), (( right & 255 ) << 8 ) | + ( left & 255 ) ) ; +} + +void smMixer::setTreble ( int treble ) +{ + setVolume ( SOUND_MIXER_TREBLE , treble ) ; +} + +void smMixer::setBass ( int bass ) +{ + setVolume ( SOUND_MIXER_TREBLE , bass ) ; +} + +void smMixer::setMasterVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_VOLUME , volume ) ; +} + +void smMixer::setSynthVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_SYNTH , volume ) ; +} + +void smMixer::setPCMVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_PCM , volume ) ; +} + +void smMixer::setSpeakerVolume( int volume ) +{ + setVolume ( SOUND_MIXER_SPEAKER, volume ) ; +} + +void smMixer::setLineVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_LINE , volume ) ; +} + +void smMixer::setMicVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_MIC , volume ) ; +} + +void smMixer::setCDVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_CD , volume ) ; +} + +void smMixer::setMasterVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_VOLUME , left, right ) ; +} + +void smMixer::setSynthVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_SYNTH , left, right ) ; +} + +void smMixer::setPCMVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_PCM , left, right ) ; +} + +void smMixer::setSpeakerVolume( int left, int right ) +{ + setVolume ( SOUND_MIXER_SPEAKER, left, right ) ; +} + +void smMixer::setLineVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_LINE , left, right ) ; +} + +void smMixer::setMicVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_MIC , left, right ) ; +} + +void smMixer::setCDVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_CD , left, right ) ; +} + +#elif defined(UL_BSD) + +/* ------------------------------------------------------------ */ +/* NetBSD or OpenBSD 2.3 */ +/* ------------------------------------------------------------ */ + +void smMixer::open ( const char *device ) +{ +} + +void smMixer::close (){} + +smMixer::smMixer () {} +smMixer::smMixer ( const char * ) {} +smMixer::~smMixer () {} + +int smMixer::notWorking () const +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int ) { return 50 ; } +void smMixer::getVolume ( int, int *left, int *right ) +{ + if ( left ) *left = 50 ; + if ( right ) *right = 50 ; +} + +void smMixer::setVolume ( int , int ) {} +void smMixer::setVolume ( int , int , int ){} +void smMixer::setTreble ( int ) {} +void smMixer::setBass ( int ) {} +void smMixer::setMasterVolume ( int ) {} +void smMixer::setSynthVolume ( int ) {} +void smMixer::setPCMVolume ( int ) {} +void smMixer::setSpeakerVolume( int ) {} +void smMixer::setLineVolume ( int ) {} +void smMixer::setMicVolume ( int ) {} +void smMixer::setCDVolume ( int ) {} +void smMixer::setMasterVolume ( int, int ) {} +void smMixer::setSynthVolume ( int, int ) {} +void smMixer::setPCMVolume ( int, int ) {} +void smMixer::setSpeakerVolume( int, int ) {} +void smMixer::setLineVolume ( int, int ) {} +void smMixer::setMicVolume ( int, int ) {} +void smMixer::setCDVolume ( int, int ) {} + + +#else +/* ------------------------------------------------------------ */ +/* win32 */ +/* ------------------------------------------------------------ */ + +void smMixer::open ( const char * ) {} + +void smMixer::close (){} + +smMixer::smMixer () {} +smMixer::smMixer ( const char * ) {} +smMixer::~smMixer () {} + +int smMixer::notWorking () const +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int ) { return 50 ; } +void smMixer::getVolume ( int, int *left, int *right ) +{ + if ( left ) *left = 50 ; + if ( right ) *right = 50 ; +} + +void smMixer::setVolume ( int, int ) {} +void smMixer::setVolume ( int, int, int ){} +void smMixer::setTreble ( int ) {} +void smMixer::setBass ( int ) {} +void smMixer::setMasterVolume ( int ) {} +void smMixer::setSynthVolume ( int ) {} +void smMixer::setPCMVolume ( int ) {} +void smMixer::setSpeakerVolume( int ) {} +void smMixer::setLineVolume ( int ) {} +void smMixer::setMicVolume ( int ) {} +void smMixer::setCDVolume ( int ) {} +void smMixer::setMasterVolume ( int, int ) {} +void smMixer::setSynthVolume ( int, int ) {} +void smMixer::setPCMVolume ( int, int ) {} +void smMixer::setSpeakerVolume( int, int ) {} +void smMixer::setLineVolume ( int, int ) {} +void smMixer::setMicVolume ( int, int ) {} +void smMixer::setCDVolume ( int, int ) {} + + +#endif diff --git a/src/ssg/Makefile.am b/src/ssg/Makefile.am new file mode 100644 index 0000000..bdfeac8 --- /dev/null +++ b/src/ssg/Makefile.am @@ -0,0 +1,37 @@ +if BUILD_SSG + +lib_LIBRARIES = libplibssg.a + +include_HEADERS = ssg.h ssgconf.h ssgMSFSPalette.h ssgKeyFlier.h pcx.h + +libplibssg_a_SOURCES = ssg.cxx ssgAnimation.cxx ssgBase.cxx \ + ssgBaseTransform.cxx ssgBranch.cxx ssgContext.cxx ssgCutout.cxx \ + ssgDList.cxx ssgEntity.cxx ssgIsect.cxx ssgLeaf.cxx ssgList.cxx \ + ssgLoadDOF.cxx ssgLoadAC.cxx \ + ssgLocal.h ssgOptimiser.cxx ssgRoot.cxx ssgSaveAC.cxx \ + ssgSelector.cxx ssgSimpleState.cxx ssgState.cxx ssgStateTables.cxx \ + ssgStats.cxx ssgTween.cxx ssgTweenController.cxx \ + ssgTexTrans.cxx ssgTexture.cxx ssgTransform.cxx \ + ssgStateSelector.cxx ssgRangeSelector.cxx ssgLoadTexture.cxx \ + ssgLoadBMP.cxx ssgLoadSGI.cxx ssgLoadPNG.cxx ssgLoadTGA.cxx \ + ssgLoadPCX.cxx ssgInvisible.cxx ssgLoadSSG.cxx ssgVTable.cxx \ + ssgSimpleList.cxx ssgVtxTable.cxx ssgIO.cxx ssgLoad3ds.cxx \ + ssgSaveASE.cxx ssgLoadASE.cxx ssgParser.cxx ssgParser.h \ + ssgLoadDXF.cxx ssgSaveDXF.cxx ssgLoadTRI.cxx ssgSaveTRI.cxx \ + ssgLoadOBJ.cxx ssgSaveOBJ.cxx ssgVtxArray.cxx ssgLoadMD2.cxx \ + ssgLoadMDL.cxx ssgLoadX.cxx ssgSaveX.cxx ssgLoaderWriterStuff.cxx \ + ssgLoadFLT.cxx ssgLoadM.cxx ssgSaveM.cxx ssgLoadStrip.cxx \ + ssgLoadOFF.cxx ssgSaveOFF.cxx ssgSaveQHI.cxx ssgLoadVRML1.cxx \ + ssgSaveVRML1.cxx ssgLoaderWriterStuff.h ssgMSFSPalette.h ssg3ds.h \ + ssgLoadMDL.h ssgSave3ds.cxx ssgAxisTransform.cxx ssgLoadATG.cxx \ + ssgSaveFLT.cxx ssgSaveATG.cxx ssgLoadIV.cxx ssgLoad.cxx ssgLoadVRML.h \ + ssgLoadMDL_BGLTexture.cxx ssgLoadXPlaneObj.cxx ssgLoadASC.cxx ssgSaveASC.cxx \ + ssgSaveIV.cxx ssgAnimTransform.cxx\ + ssgVertSplitter.h ssgVertSplitter.cxx ssgStatistics.cxx + +INCLUDES = -I$(top_srcdir)/src/sg -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = ssg.dsp + diff --git a/src/ssg/pcx.h b/src/ssg/pcx.h new file mode 100644 index 0000000..66b4cc5 --- /dev/null +++ b/src/ssg/pcx.h @@ -0,0 +1,86 @@ +// Loading of the PCX texture file format +// +// This file is used for both PLIB and for BoB. +// +// Written by Wolfram Kuss in december 2003 +// +// This file is published as public domain + + +struct pcxHeaderType + { + char manufacturer; // always 10 + char version; // usually 5 + char encoding; // 1 = RLE + char bitsPerPixel; // number of bits to represent a pixel + short x, y; // upper left coordinates + short xmax, ymax; + short hDPI, vDPI; // DPI + char egaPalette[48]; // obsolete + char reserved; // unused, always 0 + char nColorPlanes; // number of color planes. not implemented yet? + short bytesPerLine; // bytes per scan line. not implemented yet? todo kludge + short paletteInfo; // colored or grey scale. not implemented yet? + short hScreenSize, cScreenSize; + char padding[54]; // reserved, always 0 + }; + + +#define READ_BYTE (buffer[0]); buffer++; +#define READ_SHORT ((((unsigned long)buffer[0])<<8) + buffer[1]); buffer += 2; +#define READ_LONG ((((unsigned long)buffer[0])<<24) + (((unsigned long)buffer[1])<<16) + (((unsigned long)buffer[2])<<8) + buffer[3]); buffer += 4; + + +// It is bad style to have a function inside a header file, but this way there is only one file +// I need to "synchronise" between PLIB and BoB. + +static int ReadPCXBody(UByte*& buffer, pcxHeaderType *ppcxHeader, UByte * pBody) +// returns TRUE on success. +// reads from buffer (which will normally just hold the file contents, but in memory, +// writes to pBody, which must have been allocated +{ + unsigned short x,y; + + if ((ppcxHeader->bitsPerPixel != 8) // unsupported pcx + || (ppcxHeader->encoding>1) // invalid PCX + // || (pcxHeader.nColorPlanes != 1) // unsupported pcx + || (ppcxHeader->manufacturer != 10)) // invalid pcx + return (FALSE); + + unsigned short width = ppcxHeader->xmax-ppcxHeader->x+1; + + x=0; y=0; // upper left coordinates + for(;;) + { + UByte color = READ_BYTE; + if ((color &0xC0) != 0xC0) // uncompressed? + { + *pBody++ = color; + x++; + if (x >= width) + { x=0; y++; // next line + if(y > ppcxHeader->ymax-ppcxHeader->y) + break; + } + } + else + { + UByte counter, length = color & 0x3F; // number of times next byte repeated + color = READ_BYTE; + for (counter=0; counter= width) + { x=0; y++; // next line + counter=length; + if(y > ppcxHeader->ymax-ppcxHeader->y) + break; // breaks for (counter=0; counte... + } + } + if(y > ppcxHeader->ymax-ppcxHeader->y) + break; // breaks for(;;) + } + } + return TRUE; +} diff --git a/src/ssg/ssg.cxx b/src/ssg/ssg.cxx new file mode 100644 index 0000000..fec2fce --- /dev/null +++ b/src/ssg/ssg.cxx @@ -0,0 +1,523 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssg.cxx 2171 2012-08-14 17:52:40Z wolfram_kuss $ +*/ + + +#define _UL_GENERATE_CODE_ +#include "ssgLocal.h" + +#if defined(UL_MACINTOSH) +# include +#elif defined(UL_MAC_OSX) +# include +#elif defined(UL_WIN32) + /* Nothing */ +#else +# include +#endif + +static bool glIsValidContext ( void ) +{ +#if defined(CONSOLE) + return true ; +#elif defined(UL_WIN32) + return ( wglGetCurrentContext () != NULL ) ; +#elif defined(UL_MACINTOSH) + return ( aglGetCurrentContext () != NULL ) ; +#elif defined(UL_MAC_OSX) + return ( CGLGetCurrentContext () != NULL ) ; +#else + return ( glXGetCurrentContext () != NULL ) ; +#endif +} + +sgMat4 _ssgOpenGLAxisSwapMatrix = +{ + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 1.0f } +} ; + +sgVec3 _ssgVertex000 = { 0.0f, 0.0f, 0.0f } ; +sgVec4 _ssgColourWhite = { 1.0f, 1.0f, 1.0f, 1.0f } ; +sgVec3 _ssgNormalUp = { 0.0f, 0.0f, 1.0f } ; +sgVec2 _ssgTexCoord00 = { 0.0f, 0.0f } ; +short _ssgIndex0 = 0; +bool _ssgBackFaceCollisions = false ; + +void ssgSetBackFaceCollisions ( bool b ) { _ssgBackFaceCollisions = b ; } + +static ssgLight _ssgLights [ 8 ] ; +static int _ssgFrameCounter = 0 ; + +int ssgGetFrameCounter () { return _ssgFrameCounter ; } +void ssgSetFrameCounter ( int fc ) { _ssgFrameCounter = fc ; } + +const char *ssgGetVersion () +{ +#ifdef VERSION + return VERSION ; +#else + return "Unknown" ; +#endif +} + +void ssgDeRefDelete ( ssgBase *s ) +{ + if ( s == NULL ) return ; + + s -> deRef () ; + + if ( s -> getRef () <= 0 ) + delete s ; +} + +void ssgDelete ( ssgBranch *br ) +{ + if ( br == NULL ) + return ; + + br -> removeAllKids () ; + delete br ; +} + +ssgLight *ssgGetLight ( int i ) +{ + return &_ssgLights [ i ] ; +} + +void ssgInit () +{ +#ifndef NO_GFX_DISPLAY + if ( ! glIsValidContext () ) + { + ulSetError ( UL_FATAL, "ssgInit called without a valid OpenGL context."); + } +#endif + + ssgTexturePath ( "." ) ; + ssgModelPath ( "." ) ; + +#ifndef NO_GFX_DISPLAY + _ssgLights [ 0 ] . setID ( 0 ) ; + _ssgLights [ 0 ] . on () ; + + for ( int i = 1 ; i < 8 ; i++ ) + { + _ssgLights [ i ] . setID ( i ) ; + _ssgLights [ i ] . off () ; + } +#endif + + + new ssgContext ; /* Sets the current context with defaults */ + + ssgAddModelFormat ( ".ssg", ssgLoadSSG , ssgSaveSSG ) ; + + ssgAddModelFormat ( ".3ds", ssgLoad3ds , ssgSave3ds ) ; + ssgAddModelFormat ( ".ac" , ssgLoadAC3D , ssgSaveAC ) ; + ssgAddModelFormat ( ".ase", ssgLoadASE , ssgSaveASE ) ; + ssgAddModelFormat ( ".dof", ssgLoadDOF , NULL ) ; + ssgAddModelFormat ( ".dxf", ssgLoadDXF , ssgSaveDXF ) ; + ssgAddModelFormat ( ".obj", ssgLoadOBJ , ssgSaveOBJ ) ; + ssgAddModelFormat ( ".tri", ssgLoadTRI , ssgSaveTRI ) ; + ssgAddModelFormat ( ".md2", ssgLoadMD2 , NULL ) ; + ssgAddModelFormat ( ".x" , ssgLoadX , ssgSaveX ) ; + ssgAddModelFormat ( ".flt", ssgLoadFLT , ssgSaveFLT ) ; + ssgAddModelFormat ( ".strip", ssgLoadStrip, NULL ) ; + ssgAddModelFormat ( ".m" , ssgLoadM , ssgSaveM ) ; + ssgAddModelFormat ( ".off" , ssgLoadOFF , ssgSaveOFF ) ; + ssgAddModelFormat ( ".atg" , ssgLoadATG , ssgSaveATG ) ; + ssgAddModelFormat ( ".qhi" , NULL , ssgSaveQHI ) ; + ssgAddModelFormat ( ".wrl", ssgLoadVRML1, ssgSaveVRML1 ) ; + ssgAddModelFormat ( ".iv", ssgLoadIV , ssgSaveIV ) ; + ssgAddModelFormat ( ".asc", ssgLoadASC , ssgSaveASC ) ; +//ssgAddModelFormat ( ".pov", NULL , ssgSavePOV ) ; + + +#ifdef SSG_LOAD_MDL_SUPPORTED + ssgAddModelFormat ( ".mdl", ssgLoadMDL , NULL ) ; +#endif + +#ifdef SSG_LOAD_TGA_SUPPORTED + ssgAddTextureFormat ( ".tga" , ssgLoadTGA ) ; +#endif + +#ifdef SSG_LOAD_BMP_SUPPORTED + ssgAddTextureFormat ( ".bmp" , ssgLoadBMP ) ; +#endif + +#ifdef SSG_LOAD_PNG_SUPPORTED + ssgAddTextureFormat ( ".png" , ssgLoadPNG ) ; +#endif + +#ifdef SSG_LOAD_PCX_SUPPORTED + ssgAddTextureFormat ( ".pcx" , ssgLoadPCX ) ; +#endif + +#ifdef SSG_LOAD_SGI_SUPPORTED + ssgAddTextureFormat ( ".rgb" , ssgLoadSGI ) ; + ssgAddTextureFormat ( ".rgba" , ssgLoadSGI ) ; + ssgAddTextureFormat ( ".int" , ssgLoadSGI ) ; + ssgAddTextureFormat ( ".inta" , ssgLoadSGI ) ; + ssgAddTextureFormat ( ".bw" , ssgLoadSGI ) ; +#endif + +#if defined(SSG_LOAD_MDL_SUPPORTED) || defined(SSG_LOAD_MDL_BGL_TEXTURE_SUPPORTED) + ssgAddTextureFormat ( ".0af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".1af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".2af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".3af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".4af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".5af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".6af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".7af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".8af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".9af" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".aaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".baf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".caf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".daf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".eaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".faf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".gaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".haf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".iaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".jaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".kaf" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".pat" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".r8" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".naz" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".ktx" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".oav" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".sky" , ssgLoadMDLTexture ) ; + ssgAddTextureFormat ( ".ngt" , ssgLoadMDLTexture ) ; +#endif + +#ifdef SSG_LOAD_XPL_SUPPORTED + ssgAddModelFormat ( ".xpl", ssgLoadXPlaneOBJ , NULL ) ; +#endif +} + + + +void ssgCullAndPick ( ssgBranch *r, sgVec2 botleft, sgVec2 topright ) +{ + if ( _ssgCurrentContext == NULL ) + { + ulSetError ( UL_FATAL, "ssg: No Current Context: Did you forgot to call ssgInit()?" ) ; + } + + ssgForceBasicState () ; + + GLint vp [ 4 ] ; + sgVec4 viewport ; + sgMat4 mat ; + + float w = (topright[0] - botleft[0]) ; + float h = (topright[1] - botleft[1]) ; + + float x = (botleft[0] + topright[0]) / 2.0f ; + float y = (botleft[1] + topright[1]) / 2.0f ; + + glGetIntegerv ( GL_VIEWPORT, vp ) ; + + sgSetVec4 ( viewport, (float)vp[0], (float)vp[1], + (float)vp[2], (float)vp[3] ) ; + sgMakePickMatrix ( mat, x, y, w, h, viewport ) ; + + glMatrixMode ( GL_PROJECTION ) ; + glLoadIdentity () ; + glMultMatrixf ( (float *) mat ) ; + + _ssgCurrentContext->pushProjectionMatrix () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glLoadIdentity () ; + + int i ; + + for ( i = 0 ; i < 8 ; i++ ) + if ( _ssgLights [ i ] . isHeadlight () ) + _ssgLights [ i ] . setup () ; + + _ssgCurrentContext->loadModelviewMatrix () ; + + for ( i = 0 ; i < 8 ; i++ ) + if ( ! _ssgLights [ i ] . isHeadlight () ) + _ssgLights [ i ] . setup () ; + + _ssgCurrentContext->cull(r) ; + _ssgDrawDList () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glLoadIdentity () ; +} + + +void ssgCullAndDraw ( ssgBranch *r ) +{ + if ( _ssgCurrentContext == NULL ) + { + ulSetError ( UL_FATAL, "ssg: No Current Context: Did you forgot to call ssgInit()?" ) ; + } + + _ssgStartOfFrameInit () ; + + ssgForceBasicState () ; + + glMatrixMode ( GL_PROJECTION ) ; + _ssgCurrentContext->loadProjectionMatrix () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glLoadIdentity () ; + + int i ; + + for ( i = 0 ; i < 8 ; i++ ) + if ( _ssgLights [ i ] . isHeadlight () ) + _ssgLights [ i ] . setup () ; + + _ssgCurrentContext->loadModelviewMatrix () ; + _ssgCurrentContext->applyClipPlanes () ; + + for ( i = 0 ; i < 8 ; i++ ) + if ( ! _ssgLights [ i ] . isHeadlight () ) + _ssgLights [ i ] . setup () ; + + _ssgCurrentContext->cull(r) ; + _ssgDrawDList () ; + + _ssgCurrentContext->removeClipPlanes () ; + + glMatrixMode ( GL_MODELVIEW ) ; + glLoadIdentity () ; + + _ssgEndOfFrameCleanup () ; + _ssgFrameCounter++ ; +} + + +const char *ssgAxisTransform::getTypeName (void) { return "ssgAxisTransform" ; } +const char *ssgBase ::getTypeName (void) { return "ssgBase" ; } +const char *ssgTexture ::getTypeName (void) { return "ssgTexture" ; } +const char *ssgState ::getTypeName (void) { return "ssgState" ; } +const char *ssgSimpleState ::getTypeName (void) { return "ssgSimpleState" ; } +const char *ssgStateSelector::getTypeName (void) { return "ssgStateSelector" ; } +const char *ssgEntity ::getTypeName (void) { return "ssgEntity" ; } +const char *ssgLeaf ::getTypeName (void) { return "ssgLeaf" ; } +const char *ssgVTable ::getTypeName (void) { return "ssgVTable" ; } +const char *ssgVtxTable ::getTypeName (void) { return "ssgVtxTable" ; } +const char *ssgVtxArray ::getTypeName (void) { return "ssgVtxArray" ; } +const char *ssgBranch ::getTypeName (void) { return "ssgBranch" ; } +const char *ssgSelector ::getTypeName (void) { return "ssgSelector" ; } +const char *ssgRangeSelector::getTypeName (void) { return "ssgRangeSelector" ; } +const char *ssgTimedSelector::getTypeName (void) { return "ssgTimedSelector" ; } +const char *ssgBaseTransform::getTypeName (void) { return "ssgBaseTransform" ; } +const char *ssgTransform ::getTypeName (void) { return "ssgTransform" ; } +const char *ssgTexTrans ::getTypeName (void) { return "ssgTexTrans" ; } +const char *ssgCutout ::getTypeName (void) { return "ssgCutout" ; } +const char *ssgRoot ::getTypeName (void) { return "ssgRoot" ; } + +const char *ssgSimpleList ::getTypeName (void) { return "ssgSimpleList" ; } +const char *ssgColourArray ::getTypeName (void) { return "ssgColourArray" ; } +const char *ssgIndexArray ::getTypeName (void) { return "ssgIndexArray" ; } +const char *ssgTransformArray::getTypeName (void) { return "ssgTransformArray" ; } +const char *ssgTexCoordArray::getTypeName (void) { return "ssgTexCoordArray" ; } +const char *ssgVertexArray ::getTypeName (void) { return "ssgVertexArray" ; } +const char *ssgNormalArray ::getTypeName (void) { return "ssgNormalArray" ; } +const char *ssgInterleavedArray::getTypeName (void) { return "ssgInterleavedArray"; } + + + +static ssgBase *createBase () { return new ssgBase ; } + +//static ssgBase *createEntity () { return new ssgEntity ; } +//static ssgBase *createLeaf () { return new ssgLeaf ; } +static ssgBase *createVTable () { return new ssgVTable ; } +static ssgBase *createVtxTable () { return new ssgVtxTable ; } +static ssgBase *createVtxArray () { return new ssgVtxArray ; } +static ssgBase *createTween () { return new ssgTween ; } +static ssgBase *createBranch () { return new ssgBranch ; } +//static ssgBase *createBaseTransform () { return new ssgBaseTransform ; } +static ssgBase *createTransform () { return new ssgTransform ; } +static ssgBase *createTexTrans () { return new ssgTexTrans ; } +static ssgBase *createAnimTransform() { return new ssgAnimTransform ; } +static ssgBase *createAxisTransform() { return new ssgAxisTransform ; } +static ssgBase *createSelector () { return new ssgSelector ; } +static ssgBase *createRangeSelector () { return new ssgRangeSelector ; } +static ssgBase *createTimedSelector () { return new ssgTimedSelector ; } +static ssgBase *createTweenController () { return new ssgTweenController ; } +static ssgBase *createRoot () { return new ssgRoot ; } +static ssgBase *createCutout () { return new ssgCutout ; } +static ssgBase *createInvisible () { return new ssgInvisible ; } + +//static ssgBase *createState () { return new ssgState ; } +static ssgBase *createSimpleState () { return new ssgSimpleState ; } +static ssgBase *createStateSelector () { return new ssgStateSelector ; } + +static ssgBase *createSimpleList () { return new ssgSimpleList ; } +static ssgBase *createVertexArray () { return new ssgVertexArray ; } +static ssgBase *createNormalArray () { return new ssgNormalArray ; } +static ssgBase *createTexCoordArray () { return new ssgTexCoordArray ; } +static ssgBase *createColourArray () { return new ssgColourArray ; } +static ssgBase *createIndexArray () { return new ssgIndexArray ; } +static ssgBase *createTransformArray () { return new ssgTransformArray ; } +static ssgBase *createInterleavedArray () { return new ssgInterleavedArray ; } + +static ssgBase *createTexture () { return new ssgTexture ; } + + +static struct { + + int type ; + ssgBase * ( *func ) () ; + +} table[256] = { + + { ssgTypeBase () , createBase }, + +//{ ssgTypeEntity () , createEntity }, +//{ ssgTypeLeaf () , createLeaf }, + { ssgTypeVTable () , createVTable }, + { ssgTypeVtxTable () , createVtxTable }, + { ssgTypeVtxArray () , createVtxArray }, + { ssgTypeTween () , createTween }, + { ssgTypeBranch () , createBranch }, +//{ ssgTypeBaseTransform () , createBaseTransform }, + { ssgTypeTransform () , createTransform }, + { ssgTypeAnimTransform () , createAnimTransform }, + { ssgTypeTexTrans () , createTexTrans }, + { ssgTypeAxisTransform () , createAxisTransform }, + { ssgTypeSelector () , createSelector }, + { ssgTypeRangeSelector () , createRangeSelector }, + { ssgTypeTimedSelector () , createTimedSelector }, + { ssgTypeTweenController () , createTweenController }, + { ssgTypeRoot () , createRoot }, + { ssgTypeCutout () , createCutout }, + { ssgTypeInvisible () , createInvisible }, + +//{ ssgTypeState () , createState }, + { ssgTypeSimpleState () , createSimpleState }, + { ssgTypeStateSelector () , createStateSelector }, + + { ssgTypeSimpleList () , createSimpleList }, + { ssgTypeVertexArray () , createVertexArray }, + { ssgTypeNormalArray () , createNormalArray }, + { ssgTypeTexCoordArray () , createTexCoordArray }, + { ssgTypeColourArray () , createColourArray }, + { ssgTypeIndexArray () , createIndexArray }, + { ssgTypeTransformArray () , createTransformArray }, + { ssgTypeInterleavedArray () , createInterleavedArray }, + + { ssgTypeTexture () , createTexture }, + + { 0, NULL } + +}; + +void ssgRegisterType ( int type, ssgBase * ( *func ) () ) +{ + if ( type == 0 || func == NULL ) + { + ulSetError ( UL_WARNING, "ssgRegisterType: Bad arguments (type %#x, func %p).", + type, func ) ; + return ; + } + + int i ; + for ( i = 0 ; table[i].type != 0 && table[i].type != type ; i++ ) + ; + + if ( table[i].type == type && table[i].func != func ) + ulSetError ( UL_WARNING, "ssgRegisterType: Type %#x redefined differently.", type ) ; + + table[i].type = type ; + table[i].func = func ; +} + +ssgBase *ssgCreateOfType ( int type ) +{ + // XXX linear search + int i ; + + for ( i = 0 ; table[i].type != 0 && table[i].type != type ; i++ ) + ; + + if ( table[i].type == 0 ) + { + ulSetError ( UL_WARNING, "ssgCreateOfType: Unrecognized type %#x.", type ) ; + return NULL ; + } + + ssgBase *obj = (*table[i].func) () ; + + if ( obj == NULL ) + ulSetError ( UL_WARNING, "ssgCreateOfType: Got null object for type %#x.", type ) ; + else if ( obj -> getType () != type ) + ulSetError ( UL_WARNING, + "ssgCreateOfType: Created object has wrong type %#x (%s), expected %#x.", + obj -> getType (), obj -> getTypeName (), type ) ; + + return obj ; +} + +static bool ssgSearchExtensionString(char *extString, char *extName) +{ + // Returns GL_TRUE if the *extName string appears in the *extString string, + // surrounded by white spaces, or GL_FALSE otherwise. + + char *p, *end; + int n, extNameLen; + + if ( ( extString == NULL ) || ( extName == NULL ) ) + return false; + + extNameLen = strlen ( extName ); + + p = extString; + end = p + strlen ( p ); + + while ( p < end ) + { + n = strcspn ( p, " " ); + if ( ( extNameLen == n ) && ( strncmp ( extName, p, n ) == 0 ) ) + return GL_TRUE; + + p += (n + 1); + } + + return GL_FALSE; +} + +bool ssgIsExtensionSupported(char *extName) +{ + // Returns GL_TRUE if the OpenGL Extension whose name is *extName + // is supported by the system, or GL_FALSE otherwise. + + // The *extName string must follow the OpenGL extensions naming scheme + // (ie: "GL_type_extension", like GL_EXT_convolution) + + return ssgSearchExtensionString ( (char *)glGetString(GL_EXTENSIONS), extName ); +} + diff --git a/src/ssg/ssg.dsp b/src/ssg/ssg.dsp new file mode 100644 index 0000000..8643d3c --- /dev/null +++ b/src/ssg/ssg.dsp @@ -0,0 +1,466 @@ +# Microsoft Developer Studio Project File - Name="ssg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=ssg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ssg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ssg.mak" CFG="ssg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ssg - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "ssg - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ssg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\sg" /I "..\util" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "PROXY_TEXTURES_ARE_NOT_BROKEN" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy ssg.h ..\..\ssg.h copy ssgconf.h ..\..\ssgconf.h copy ssgMSFSPalette.h ..\..\ssgMSFSPalette.h copy ssgKeyFlier.h ..\..\ssgKeyFlier.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "ssg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\sg" /I "..\util" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "PROXY_TEXTURES_ARE_NOT_BROKEN" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\ssg_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy ssg.h ..\..\ssg.h copy ssgconf.h ..\..\ssgconf.h copy ssgMSFSPalette.h ..\..\ssgMSFSPalette.h copy ssgKeyFlier.h ..\..\ssgKeyFlier.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "ssg - Win32 Release" +# Name "ssg - Win32 Debug" +# Begin Source File + +SOURCE=.\ssg.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssg.h +# End Source File +# Begin Source File + +SOURCE=.\ssg3ds.h +# End Source File +# Begin Source File + +SOURCE=.\ssgAnimation.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgAnimTransform.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgAxisTransform.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgBase.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgBaseTransform.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgBranch.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgconf.h +# End Source File +# Begin Source File + +SOURCE=.\ssgContext.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgCutout.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgDList.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgEntity.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgInvisible.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgIO.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgIsect.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgKeyFlier.h +# End Source File +# Begin Source File + +SOURCE=.\ssgLeaf.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgList.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoad.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoad3ds.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadAC.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadASC.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadASE.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadATG.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadBMP.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadDOF.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadDXF.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoaderWriterStuff.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoaderWriterStuff.h +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadFLT.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadIV.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadM.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadMD2.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadMDL.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadMDL.h +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadMDL_BGLTexture.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadOBJ.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadOFF.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadPCX.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadPNG.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadSGI.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadSSG.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadStrip.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadTexture.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadTGA.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadTRI.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadVRML.h +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadVRML1.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadX.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLoadXPlaneObj.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgLocal.h +# End Source File +# Begin Source File + +SOURCE=.\ssgMSFSPalette.h +# End Source File +# Begin Source File + +SOURCE=.\ssgOptimiser.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgParser.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgParser.h +# End Source File +# Begin Source File + +SOURCE=.\ssgRangeSelector.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgRoot.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSave3ds.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveAC.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveASC.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveASE.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveATG.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveDXF.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveFLT.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveIV.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveM.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveOBJ.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveOFF.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveQHI.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveTRI.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveVRML1.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSaveX.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSelector.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSimpleList.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgSimpleState.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgState.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgStateSelector.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgStateTables.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgStatistics.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgStats.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgTexTrans.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgTexture.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgTransform.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgTween.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgTweenController.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgVertSplitter.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgVertSplitter.h +# End Source File +# Begin Source File + +SOURCE=.\ssgVTable.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgVtxArray.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgVtxTable.cxx +# End Source File +# End Target +# End Project diff --git a/src/ssg/ssg.h b/src/ssg/ssg.h new file mode 100644 index 0000000..77d089b --- /dev/null +++ b/src/ssg/ssg.h @@ -0,0 +1,2883 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssg.h 2173 2012-08-24 13:18:22Z wolfram_kuss $ +*/ + + +#ifndef _INCLUDED_SSG_H_ +#define _INCLUDED_SSG_H_ + +#include +#include "sg.h" + +#ifdef UL_MAC_OSX +# include +#else +# include +#endif + +#include "ssgconf.h" + +#ifndef _SSG_PUBLIC +#define _SSG_PUBLIC protected +#endif + +extern int sgebug ; + +enum ssgCullResult +{ + SSG_OUTSIDE = SG_OUTSIDE, + SSG_INSIDE = SG_INSIDE, + SSG_STRADDLE = SG_STRADDLE +} ; + +#define SSG_MAXPATH 50 + +#define SSGTRAV_CULL 1 +#define SSGTRAV_ISECT 2 +#define SSGTRAV_HOT 4 +#define SSGTRAV_LOS 8 + +class ssgList ; +class ssgKidList ; +class ssgBase ; +class ssgEntity ; +class ssgLeaf ; +class ssgVTable ; +class ssgVtxTable ; +class ssgVtxArray ; +class ssgTween ; +class ssgBranch ; +class ssgInvisible ; +class ssgBaseTransform ; +class ssgTransform ; +class ssgTexTrans ; +class ssgCutout ; +class ssgSelector ; +class ssgRangeSelector ; +class ssgTimedSelector ; +class ssgTweenController ; +class ssgRoot ; + +void ssgDeRefDelete ( ssgBase *br ) ; + +/* + Think twice before assigning new type bits, it is rather complicated. + Perhaps some kind of automatic assignment would be good? + */ + +#define _SSG_BACKWARDS_REFERENCE 0x0000000 /* For SSG format files */ + +#define _SSG_TYPE_BASE 0x00000001 + +/* ssgEntities */ +#define _SSG_TYPE_ENTITY 0x00000002 +#define _SSG_TYPE_LEAF 0x00000020 +#define _SSG_TYPE_VTABLE 0x00000080 +#define _SSG_TYPE_VTXTABLE 0x00000100 +#define _SSG_TYPE_VTXARRAY 0x00000200 +#define _SSG_TYPE_BRANCH 0x00000040 +#define _SSG_TYPE_BASETRANSFORM 0x00000080 +#define _SSG_TYPE_TRANSFORM 0x00001000 +#define _SSG_TYPE_TEXTRANS 0x00002000 +#define _SSG_TYPE_AXISTRANSFORM 0x00004000 +#define _SSG_TYPE_ANIMTRANSFORM 0x00008000 +#define _SSG_TYPE_SELECTOR 0x00000100 +#define _SSG_TYPE_RANGESELECTOR 0x00001000 +#define _SSG_TYPE_TIMEDSELECTOR 0x00002000 +#define _SSG_TYPE_TWEEN 0x00008000 +#define _SSG_TYPE_TWEENCONTROLLER 0x00010000 +#define _SSG_TYPE_ROOT 0x00000200 +#define _SSG_TYPE_CUTOUT 0x00000400 +#define _SSG_TYPE_INVISIBLE 0x00000800 + +/* ssgStates */ +#define _SSG_TYPE_STATE 0x00000004 +#define _SSG_TYPE_SIMPLESTATE 0x00000020 +#define _SSG_TYPE_STATESELECTOR 0x00000040 + +/* ssgSimpleLists */ +#define _SSG_TYPE_SIMPLELIST 0x00000008 +#define _SSG_TYPE_VERTEXARRAY 0x00000020 +#define _SSG_TYPE_NORMALARRAY 0x00000040 +#define _SSG_TYPE_TEXCOORDARRAY 0x00000080 +#define _SSG_TYPE_COLOURARRAY 0x00000100 +#define _SSG_TYPE_INDEXARRAY 0x00000200 +#define _SSG_TYPE_TRANSFORMARRAY 0x00000400 +#define _SSG_TYPE_INTERLEAVEDARRAY 0x00000800 + +/* ssgTextures */ +#define _SSG_TYPE_TEXTURE 0x00000010 + +#define SSG_FILE_VERSION 0x01 +#define SSG_FILE_MAGIC_NUMBER (('S'<<24)+('S'<<16)+('G'<<8)+SSG_FILE_VERSION) + + +inline int ssgTypeBase () { return _SSG_TYPE_BASE ; } + +inline int ssgTypeEntity () { return _SSG_TYPE_ENTITY | ssgTypeBase () ; } +inline int ssgTypeLeaf () { return _SSG_TYPE_LEAF | ssgTypeEntity () ; } +inline int ssgTypeVTable () { return _SSG_TYPE_VTABLE | ssgTypeLeaf () ; } +inline int ssgTypeVtxTable () { return _SSG_TYPE_VTXTABLE | ssgTypeLeaf () ; } +inline int ssgTypeVtxArray () { return _SSG_TYPE_VTXARRAY | ssgTypeVtxTable() ; } +inline int ssgTypeTween () { return _SSG_TYPE_TWEEN | ssgTypeVtxTable() ; } +inline int ssgTypeBranch () { return _SSG_TYPE_BRANCH | ssgTypeEntity () ; } +inline int ssgTypeTweenController(){ return _SSG_TYPE_TWEENCONTROLLER | ssgTypeBranch() ; } +inline int ssgTypeBaseTransform() { return _SSG_TYPE_BASETRANSFORM | ssgTypeBranch () ; } +inline int ssgTypeTransform () { return _SSG_TYPE_TRANSFORM | ssgTypeBaseTransform () ; } +inline int ssgTypeAnimTransform() { return _SSG_TYPE_ANIMTRANSFORM | ssgTypeTransform () ; } +inline int ssgTypeTexTrans () { return _SSG_TYPE_TEXTRANS | ssgTypeBaseTransform () ; } +inline int ssgTypeAxisTransform() { return _SSG_TYPE_AXISTRANSFORM | ssgTypeTransform () ; } +inline int ssgTypeSelector () { return _SSG_TYPE_SELECTOR | ssgTypeBranch () ; } +inline int ssgTypeRangeSelector() { return _SSG_TYPE_RANGESELECTOR | ssgTypeSelector () ; } +inline int ssgTypeTimedSelector() { return _SSG_TYPE_TIMEDSELECTOR | ssgTypeSelector () ; } +inline int ssgTypeRoot () { return _SSG_TYPE_ROOT | ssgTypeBranch () ; } +inline int ssgTypeCutout () { return _SSG_TYPE_CUTOUT | ssgTypeBranch () ; } +inline int ssgTypeInvisible () { return _SSG_TYPE_INVISIBLE | ssgTypeBranch () ; } + +inline int ssgTypeState () { return _SSG_TYPE_STATE | ssgTypeBase () ; } +inline int ssgTypeSimpleState () { return _SSG_TYPE_SIMPLESTATE | ssgTypeState () ; } +inline int ssgTypeStateSelector() { return _SSG_TYPE_STATESELECTOR | ssgTypeSimpleState () ; } + +inline int ssgTypeSimpleList () { return _SSG_TYPE_SIMPLELIST | ssgTypeBase () ; } +inline int ssgTypeVertexArray () { return _SSG_TYPE_VERTEXARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeNormalArray () { return _SSG_TYPE_NORMALARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeTexCoordArray() { return _SSG_TYPE_TEXCOORDARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeColourArray () { return _SSG_TYPE_COLOURARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeIndexArray () { return _SSG_TYPE_INDEXARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeTransformArray() { return _SSG_TYPE_TRANSFORMARRAY | ssgTypeSimpleList () ; } +inline int ssgTypeInterleavedArray() { return _SSG_TYPE_INTERLEAVEDARRAY | ssgTypeSimpleList () ; } + +inline int ssgTypeTexture () { return _SSG_TYPE_TEXTURE | ssgTypeBase () ; } + +/* + It's critical that these numbers don't change without + some pretty pressing need because significant change to + ssgSimpleState.cxx and ssgStateTables.cxx would be needed. +*/ + +#define SSG_GL_TEXTURE_EN 0 +#define SSG_GL_CULL_FACE_EN 1 +#define SSG_GL_COLOR_MATERIAL_EN 2 +#define SSG_GL_BLEND_EN 3 +#define SSG_GL_ALPHA_TEST_EN 4 +#define SSG_GL_LIGHTING_EN 5 + +#define SSG_GL_TEXTURE 6 +#define SSG_GL_COLOR_MATERIAL 7 +#define SSG_GL_DIFFUSE 8 +#define SSG_GL_AMBIENT 9 +#define SSG_GL_SPECULAR 10 +#define SSG_GL_EMISSION 11 +#define SSG_GL_SHININESS 12 +#define SSG_GL_ALPHA_TEST 13 +#define SSG_GL_SHADE_MODEL 14 + +#define SSG_CLONE_RECURSIVE 1 +#define SSG_CLONE_GEOMETRY 2 +#define SSG_CLONE_USERDATA 4 +#define SSG_CLONE_STATE 8 +#define SSG_CLONE_STATE_RECURSIVE 16 +#define SSG_CLONE_TEXTURE 32 + +int ssgGetFrameCounter () ; +void ssgSetFrameCounter ( int fc ) ; + +// Returns GL_TRUE if the OpenGL Extension whose name is *extName +// is supported by the system, or GL_FALSE otherwise. +bool ssgIsExtensionSupported(char *extName); + +class ssgList +{ +protected: + unsigned int total ; /* The total number of entities in the list */ + unsigned int limit ; /* The current limit on number of entities */ + unsigned int next ; /* The next entity when we are doing getNext ops */ + + ssgEntity **entity_list ; /* The list. */ + + void sizeChk (void) ; + +public: + + ssgList ( int init_max = 1 ) ; + virtual ~ssgList (void) ; + + ssgEntity *getEntity ( unsigned int n ) + { + next = n + 1 ; + return ( n >= total ) ? (ssgEntity *) NULL : entity_list [ n ] ; + } + + virtual void addEntity ( ssgEntity *entity ) ; + virtual void removeEntity ( unsigned int n ) ; + + void removeAllEntities () ; + + void removeEntity ( ssgEntity *entity ) + { + removeEntity ( searchForEntity ( entity ) ) ; + } + + virtual void replaceEntity ( unsigned int n, ssgEntity *new_entity ) ; + + void replaceEntity ( ssgEntity *old_entity, ssgEntity *new_entity ) + { + replaceEntity ( searchForEntity ( old_entity ), new_entity ) ; + } + + int getNumEntities (void) { return total ; } + ssgEntity *getNextEntity (void) { return getEntity ( next ) ; } + int searchForEntity ( ssgEntity *entity ) ; +} ; + + +class ssgKidList : public ssgList +{ +public: + + ssgKidList ( int init_max = 1 ) ; + virtual ~ssgKidList (void) ; + + void addEntity ( ssgEntity *entity ) ; + void removeEntity ( unsigned int n ) ; + + void removeEntity ( ssgEntity *entity ) + { + removeEntity ( searchForEntity ( entity ) ) ; + } + + void replaceEntity ( unsigned int n, ssgEntity *new_entity ) ; + + void replaceEntity ( ssgEntity *old_entity, ssgEntity *new_entity ) + { + replaceEntity ( searchForEntity ( old_entity ), new_entity ) ; + } +} ; + + +class ssgBase +{ + int refc ; /* The number of references to this node */ + int unique ; /* A unique number for this node */ + +protected : + + int type ; + int spare ; /* This spare field is used in a bunch of short-term hacks */ + + char *name ; + ssgBase *user_data ; + virtual void copy_from ( ssgBase *src, int clone_flags ) ; + +public: + void *operator new ( size_t size ) ; + void operator delete ( void *ptr ) ; + + void deadBeefCheck () { assert ( type != (int) 0xDeadBeef ) ; } + + virtual void zeroSpareRecursive (); + virtual void zeroSpare () ; + virtual void incSpare () ; + virtual void setSpare ( int ss ) ; + virtual int getSpare () ; + + int getUnique () { return unique ; } + + ssgBase (void) ; + virtual ~ssgBase (void) ; + + void ref () { refc++ ; } + void deRef () { assert ( refc > 0 ) ; refc-- ; } + int getRef() { return refc ; } + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + + /* Type checking mechanism */ + + virtual const char *getTypeName(void) ; + + int getType (void) { return type ; } + int isA ( int ty ) { return getType() == ty ; } + int isAKindOf ( int ty ) { return ( getType() & ty ) == ty ; } + + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + + ssgBase *getUserData () { return user_data ; } + + void setUserData ( ssgBase *s ) + { + ssgDeRefDelete ( user_data ) ; + + user_data = s ; + if ( s != NULL ) + s -> ref () ; + } + + void setName ( const char *nm ) ; + char *getName () { return name ; } + const char *getPrintableName () { return (name == NULL) ? "NoName" : name ; } +} ; + + + +class ssgSimpleList : public ssgBase +{ +protected: + + unsigned int total ; /* The total number of things in the list */ + unsigned int limit ; /* The current limit on number of things */ + unsigned int size_of ; /* The size of each thing */ + char *list ; /* The list. */ + bool own_mem ; /* Do we own the list memory ? */ + + void sizeChk ( unsigned int n ) + { + if ( (total+n) > limit ) + { + if ( ! own_mem ) + ulSetError( UL_FATAL, "ssgSimpleList: Cannot resize array." ); + + /* how big should we make the list? */ + limit += limit ; /* double it */ + if ( limit <= 0 ) + limit = 3; + if ( (total+n) > limit ) + limit = (total+n) ; + + /* re-allocate the list */ + char *nlist = new char [ limit * size_of ] ; + memmove ( nlist, list, total * size_of ) ; + delete [] list ; + list = nlist ; + } + } + + virtual void copy_from ( ssgSimpleList *src, int clone_flags ) ; + +_SSG_PUBLIC: + + ssgSimpleList () + { + type = ssgTypeSimpleList () ; + limit = 0 ; + size_of = 0 ; + total = 0 ; + list = NULL ; + own_mem = true ; + } + +public: + + ssgSimpleList ( int sz, int init = 3, char* things = 0 ) + { + type = ssgTypeSimpleList () ; + limit = init ; + size_of = sz ; + if ( things ) + { + total = init ; + list = things ; + own_mem = false ; + } + else + { + total = 0 ; + list = new char [ limit * size_of ] ; + own_mem = true ; + } + } + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + + virtual ~ssgSimpleList (void) + { + if ( own_mem ) + delete [] list ; + list = NULL ; + } ; + + char *raw_get ( unsigned int n ) + { + return ( n >= total ) ? ((char *) 0) : & list [ n * size_of ] ; + } + + void raw_add ( char *thing ) + { + sizeChk ( 1 ) ; + memcpy ( & list [ size_of * total++ ], thing, size_of ) ; + } ; + + void raw_add ( char *things, unsigned int n ) + { + sizeChk ( n ) ; + memcpy ( & list [ size_of * total ], things, size_of * n ) ; + total += n ; + } + + void raw_set ( char *thing, unsigned int n ) + { + memcpy ( & list [ size_of * n ], thing, size_of ) ; + } ; + + void removeLast () + { + if ( total > 0 ) + total-- ; + } + + void removeAll () + { + if ( own_mem ) + delete [] list ; + list = NULL ; + limit = total = 0 ; + } + + int compare(ssgSimpleList *other, int print_result=TRUE); + int getSizeOf (void) { return size_of ; } + int getNum (void) { return total ; } + + void rawSetNum ( unsigned int n ) /* Better know what you're doing!! */ + { + total = n ; + } + + void setNum ( unsigned int n ) + { + if ( total < n ) + { + sizeChk ( n ) ; + memset ( & list [ size_of * total ], 0, size_of * (n-total) ) ; + total = n ; + } + } + + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgVertexArray : public ssgSimpleList +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgVertexArray ( int init = 3, sgVec3* things = 0 ) + : ssgSimpleList ( sizeof(sgVec3), init, (char*)things ) + { + type = ssgTypeVertexArray () ; + } + float *get ( unsigned int n ) { return (float *) raw_get ( n ) ; } + void add ( sgVec3 thing ) { raw_add ( (char *) thing ) ; } ; + void set ( sgVec3 thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + void set ( float x, float y, float z, unsigned int n ) + { sgVec3 tmp = { x, y, z } ; raw_set ( (char *) tmp, n ) ; } ; + void add ( float x, float y, float z ) + { sgVec3 tmp = { x, y, z } ; raw_add ( (char *) tmp ) ; } ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgNormalArray : public ssgSimpleList +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgNormalArray ( int init = 3, sgVec3* things = 0 ) + : ssgSimpleList ( sizeof(sgVec3), init, (char*)things ) + { + type = ssgTypeNormalArray () ; + } + float *get ( unsigned int n ) { return (float *) raw_get ( n ) ; } + void add ( sgVec3 thing ) { raw_add ( (char *) thing ) ; } ; + void set ( sgVec3 thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + void set ( float x, float y, float z, unsigned int n ) + { sgVec3 tmp = { x, y, z } ; raw_set ( (char *) tmp, n ) ; } ; + void add ( float x, float y, float z ) + { sgVec3 tmp = { x, y, z } ; raw_add ( (char *) tmp ) ; } ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgTexCoordArray : public ssgSimpleList +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgTexCoordArray ( int init = 3, sgVec2* things = 0 ) + : ssgSimpleList ( sizeof(sgVec2), init, (char*)things ) + { + type = ssgTypeTexCoordArray () ; + } + float *get ( unsigned int n ) { return (float *) raw_get ( n ) ; } + void add ( sgVec2 thing ) { raw_add ( (char *) thing ) ; } ; + void set ( sgVec2 thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + void set ( float u, float v, unsigned int n ) + { sgVec2 tmp = { u, v } ; raw_set ( (char *) tmp, n ) ; } ; + void add ( float u, float v ) + { sgVec2 tmp = { u, v } ; raw_add ( (char *) tmp ) ; } ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgColourArray : public ssgSimpleList +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgColourArray ( int init = 3, sgVec4* things = 0 ) + : ssgSimpleList ( sizeof(sgVec4), init, (char*)things ) + { + type = ssgTypeColourArray () ; + } + float *get ( unsigned int n ) { return (float *) raw_get ( n ) ; } + void add ( sgVec4 thing ) { raw_add ( (char *) thing ) ; } ; + void set ( sgVec4 thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + void set ( float r, float g, float b, float a, unsigned int n ) + { sgVec4 tmp = { r,g,b,a } ; raw_set ( (char *) tmp, n ) ; } ; + void add ( float r, float g, float b, float a ) + { sgVec4 tmp = { r,g,b,a } ; raw_add ( (char *) tmp ) ; } ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgIndexArray : public ssgSimpleList +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgIndexArray ( int init = 3, short* things = 0 ) + : ssgSimpleList ( sizeof(short), init, (char*)things ) + { + type = ssgTypeIndexArray () ; + } + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + short *get ( unsigned int n ) { return (short *) raw_get ( n ) ; } + void add ( short thing ) { raw_add ( (char *) &thing ) ; } ; + void set ( short thing, unsigned int n ) { raw_set ( (char *) &thing, n ) ; } ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgTransformArray : public ssgSimpleList +{ +public: + int selection ; /* used to remember last transform selected */ + + ssgTransformArray ( int init = 3, sgMat4* things = 0 ) + : ssgSimpleList ( sizeof(sgMat4), init, (char*)things ) + { + type = ssgTypeTransformArray () ; + selection = 0 ; + } + sgMat4 *get ( unsigned int n ) { return (sgMat4 *) raw_get ( n ) ; } + void add ( sgMat4 thing ) { raw_add ( (char *) thing ) ; } ; + virtual const char *getTypeName(void) ; +} ; + + +class ssgTexture ; +class ssgTextureArray : private ssgSimpleList +{ +public: + + ssgTextureArray ( int init = 3 ) + : ssgSimpleList ( sizeof(ssgTexture*), init ) + { + //type = ssgTypeTextureArray () ; + } + + int getNum (void) { return total ; } + ssgTexture* get ( unsigned int n ) + { return *( (ssgTexture**) raw_get ( n ) ) ; } + void add ( ssgTexture* thing ) ; + void removeAll () ; + ssgTexture* findByFilename ( const char* fname ) ; +} ; + + +class ssgSimpleState ; +class ssgSimpleStateArray : private ssgSimpleList +{ + void collect_recursive ( ssgEntity *e ); + +public: + + ssgSimpleStateArray ( int init = 3 ) + : ssgSimpleList ( sizeof(ssgSimpleState*), init ) + { + //type = ssgTypeSimpleStateArray () ; + } + + int getNum (void) { return total ; } + ssgSimpleState* get ( unsigned int n ) + { + assert(n getHandle () ; + return 0 ; + } + + virtual char *getTextureFilename(void) + { + if ( texture != NULL ) + return texture -> getFilename () ; + return NULL ; + } + + virtual ssgTexture* getTexture (void) + { + return texture ; + } + + virtual void setTexture ( ssgTexture *tex ) + { + ssgDeRefDelete ( texture ) ; + texture = tex ; + if ( texture != NULL ) + { + care_about ( SSG_GL_TEXTURE ) ; + texture -> ref () ; + } + else + { + dont_care_about ( SSG_GL_TEXTURE ) ; + } + } + + virtual void setTexture ( const char *fname, + int _wrapu = TRUE, int _wrapv = TRUE, int _mipmap = TRUE ) + { + mipmap = _mipmap ; + setTexture ( new ssgTexture ( fname, _wrapu, _wrapv, mipmap ) ) ; + } + + /* + WARNING - THIS FORM OF setTexture IS DEPRECATED + BECAUSE IT PREVENTS ssgSave FROM SAVING THE + TEXTURE FILENAME and it LEAKS texture handles! + */ + + virtual void setTexture ( GLuint tex ) + { + if ( !texture ) + setTexture ( new ssgTexture ) ; + + texture -> setHandle ( tex ) ; + texture -> setFilename ( NULL ) ; + } + + virtual void setTextureFilename ( const char *fname ) + { + if ( !texture ) + setTexture ( new ssgTexture ) ; + + texture -> setFilename ( fname ) ; + } + + virtual GLenum getColourMaterial () + { + return (GLenum) colour_material_mode ; + } + + virtual void setColourMaterial ( GLenum which ) + { + colour_material_mode = (int) which ; + care_about ( SSG_GL_COLOR_MATERIAL ) ; + } + + virtual void setMaterial ( GLenum which, float r, float g, + float b, float a = 1.0f ) + { + sgVec4 rgba ; + sgSetVec4 ( rgba, r, g, b, a ) ; + setMaterial ( which, rgba ) ; + } + + virtual void setMaterial ( GLenum which, sgVec4 rgba ) + { + switch ( which ) + { + case GL_EMISSION : sgCopyVec4 ( emission_colour, rgba ) ; + care_about ( SSG_GL_EMISSION ) ; + break ; + case GL_SPECULAR : sgCopyVec4 ( specular_colour, rgba ) ; + care_about ( SSG_GL_SPECULAR ) ; + break ; + case GL_AMBIENT : sgCopyVec4 ( ambient_colour , rgba ) ; + care_about ( SSG_GL_AMBIENT ) ; + break ; + case GL_DIFFUSE : sgCopyVec4 ( diffuse_colour , rgba ) ; + care_about ( SSG_GL_DIFFUSE ) ; + break ; + case GL_AMBIENT_AND_DIFFUSE : // in compliance with glMaterial + sgCopyVec4 ( ambient_colour , rgba ) ; + sgCopyVec4 ( diffuse_colour , rgba ) ; + care_about ( SSG_GL_AMBIENT | SSG_GL_DIFFUSE ) ; + break ; + default : break ; + } + } + + virtual float *getMaterial ( GLenum which ) + { + switch ( which ) + { + case GL_EMISSION : return emission_colour ; + case GL_SPECULAR : return specular_colour ; + case GL_AMBIENT : return ambient_colour ; + case GL_DIFFUSE : return diffuse_colour ; + default: break ; + } + + return NULL ; + } + + virtual float getShininess (void) + { + return shininess ; + } + + virtual void setShininess ( float sh ) + { + care_about ( SSG_GL_SHININESS ) ; + shininess = sh ; + } + + virtual void setShadeModel ( GLenum model ) + { + care_about ( SSG_GL_SHADE_MODEL ) ; + shade_model = model ; + } + + virtual void setAlphaClamp ( float clamp ) + { + care_about ( SSG_GL_ALPHA_TEST ) ; + alpha_clamp = clamp ; + } + + /*int getWrapU(); + int getWrapV();*/ + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + +class ssgStateSelector : public ssgSimpleState +{ + int nstates ; + int selection ; +_SSG_PUBLIC: + ssgSimpleState **statelist ; + +protected: + virtual void copy_from ( ssgStateSelector *src, int clone_flags ) ; + +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgStateSelector () ; + ssgStateSelector ( int ns ) ; + + virtual ~ssgStateSelector (void) ; + virtual const char *getTypeName(void) ; + + int getNumSteps ( void ) { return nstates ; } + void selectStep ( unsigned int s ) ; + unsigned int getSelectStep (void) ; + ssgSimpleState *getCurrentStep (void) ; + void setStep ( int i, ssgSimpleState *step ) ; + ssgSimpleState *getStep ( int i ) ; + + virtual int isTranslucent (void) ; + virtual void setTranslucent (void) ; + virtual void setOpaque (void) ; + void force (void) ; + void apply (void) ; + + void care_about ( int mode ) ; + void dont_care_about ( int mode ) ; + + int isEnabled ( GLenum mode ) ; + void disable ( GLenum mode ) ; + void enable ( GLenum mode ) ; + + char *getTextureFilename(void) ; + void setTextureFilename(char *fname) ; + + void setTexture ( char *fname, + int _wrapu = TRUE, + int _wrapv = TRUE, + int _mipmap = TRUE ) ; + GLuint getTextureHandle (void) ; + void setTexture ( ssgTexture *tex ) ; + + /* + WARNING - THIS FORM OF setTexture IS DEPRECATED + BECAUSE IT PREVENTS ssgSave FROM SAVING THE + TEXTURE FILENAME and LEAKS texture handles! + */ + + void setTexture ( GLuint tex ) ; + + void setColourMaterial(GLenum which); + void setMaterial ( GLenum which, float r, float g, + float b, float a = 1.0f ) ; + + void setMaterial ( GLenum which, sgVec4 rgba ) ; + float *getMaterial ( GLenum which ) ; + float getShininess (void) ; + void setShininess ( float sh ) ; + void setShadeModel ( GLenum model ) ; + void setAlphaClamp ( float clamp ) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + +struct ssgEntityBinding +{ + ssgEntity **entity ; + char *nameOrPath ; +} ; + + +class ssgEntity : public ssgBase +{ + ssgList parents ; + + int traversal_mask ; + ssgTravCallback preTravCB ; + ssgTravCallback postTravCB ; + +protected: + sgSphere bsphere ; + int bsphere_is_invalid ; + + void emptyBSphere () { bsphere.empty () ; } + void visualiseBSphere () ; + void extendBSphere ( sgSphere *s ) { bsphere.extend ( s ) ; } + void extendBSphere ( sgBox *b ) { bsphere.extend ( b ) ; } + void extendBSphere ( sgVec3 v ) { bsphere.extend ( v ) ; } + + virtual ssgCullResult cull_test ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual ssgCullResult isect_test ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual ssgCullResult hot_test ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual ssgCullResult los_test ( sgVec3 s, sgMat4 m, int test_needed ) ; + + virtual void copy_from ( ssgEntity *src, int clone_flags ) ; +public: + + ssgEntity (void) ; + virtual ~ssgEntity (void) ; + + int getTraversalMask () { return traversal_mask ;} + void setTraversalMask ( int t ) { traversal_mask = t ; } + void setTraversalMaskBits ( int t ) { traversal_mask |= t ; } + void clrTraversalMaskBits ( int t ) { traversal_mask &= ~t ; } + + ssgTravCallback getTravCallback ( int cb_type ) + { + return ( cb_type == SSG_CALLBACK_PRETRAV ) ? preTravCB : postTravCB ; + } + + void setTravCallback ( int cb_type, ssgTravCallback cb ) + { + if ( cb_type == SSG_CALLBACK_PRETRAV ) + preTravCB = cb ; + else + postTravCB = cb ; + } + + int preTravTests ( int *test_needed, int which ) ; + void postTravTests ( int which ) ; + + virtual void getNetTransform ( sgMat4 xform ) ; + virtual void getLastNetTransform ( sgMat4 xform ) ; + + /* for backward compatibility */ + virtual ssgCallback getCallback ( int cb_type ) ; + virtual void setCallback ( int cb_type, ssgCallback cb ) ; + + virtual ssgEntity* getByName ( char *nm ) ; + virtual ssgEntity* getByPath ( char *path ) ; + int bindEntities ( ssgEntityBinding *bind ) ; + + virtual void recalcBSphere (void) = 0 ; + int isDirtyBSphere (void) { return bsphere_is_invalid ; } + void dirtyBSphere () ; + + sgSphere *getBSphere () + { + if ( isDirtyBSphere () ) + recalcBSphere () ; + + return & bsphere ; + } + + virtual int getNumKids (void) { return 0 ; } + int getNumParents () { return parents.getNumEntities () ; } + ssgBranch *getParent ( int p ) { return (ssgBranch *) parents.getEntity ( p ) ; } + ssgBranch *getNextParent () { return (ssgBranch *) parents.getNextEntity () ; } + void addParent ( ssgEntity *entity ) { parents.addEntity ( entity ) ; } + void removeParent ( ssgEntity *entity ) { parents.removeEntity ( entity ) ; } + + virtual const char *getTypeName(void) ; + + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) = 0 ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) = 0 ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) = 0 ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) = 0 ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual void getStats ( int *num_branches, int *num_leaves, int *num_tris, int *num_vertices ) = 0 ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + + +class ssgLeaf : public ssgEntity +{ + int cull_face ; + ssgState *state ; + +protected: + GLenum gltype ; + + ssgCallback preDrawCB ; + ssgCallback postDrawCB ; + +#ifdef _SSG_USE_DLIST + GLuint dlist ; +#endif + + virtual void draw_geometry () = 0 ; + + int preDraw () ; + + virtual void copy_from ( ssgLeaf *src, int clone_flags ) ; +public: + ssgLeaf (void) ; + virtual ~ssgLeaf (void) ; + + virtual void drawHighlight ( sgVec4 colour ) = 0 ; + virtual void drawHighlight ( sgVec4 colour, int i ) = 0 ; + virtual void pick ( int baseName ) = 0 ; + +#ifdef _SSG_USE_DLIST + void makeDList () ; + void deleteDList () ; + GLuint getDListIndex () { return dlist ; } +#endif + + int getExternalPropertyIndex () + { return state ? state->getExternalPropertyIndex() : 0 ; } + + int isTranslucent () { return state ? state->isTranslucent() : FALSE ; } + int hasState () { return state != NULL ; } + + ssgState *getState () { return state ; } + void setState ( ssgState *st ); //~~ T.G. Body extended & moved into CXX file + + virtual ssgCallback getCallback ( int cb_type ) + { + return ( cb_type == SSG_CALLBACK_PREDRAW ) ? preDrawCB : postDrawCB ; + } + + virtual void setCallback ( int cb_type, ssgCallback cb ) + { + if ( cb_type == SSG_CALLBACK_PREDRAW ) + preDrawCB = cb ; + else + postDrawCB = cb ; + } + + virtual void setPrimitiveType ( GLenum ty ) { gltype = ty ; } + virtual GLenum getPrimitiveType () { return gltype ; } + + /* For backwards compatibility only -- DEPRECATED */ + GLenum getGLtype () { return getPrimitiveType() ; } + + virtual int getNumVertices () { return 0 ; } + virtual int getNumNormals () { return 0 ; } + virtual int getNumColours () { return 0 ; } + virtual int getNumTexCoords () { return 0 ; } + + virtual float *getVertex ( int i ) = 0 ; + virtual float *getNormal ( int i ) = 0 ; + virtual float *getColour ( int i ) = 0 ; + virtual float *getTexCoord ( int i ) = 0 ; + virtual int getNumTriangles () = 0 ; + virtual void getTriangle ( int n, short *v1, short *v2, short *v3 ) = 0 ; + virtual int getNumLines () = 0 ; + virtual void getLine ( int n, short *v1, short *v2 ) = 0 ; + + virtual void transform ( const sgMat4 m ) = 0 ; + + void setCullFace ( int cf ) { cull_face = cf ; } + int getCullFace () { return cull_face ; } + + virtual void recalcBSphere () = 0 ; + virtual const char *getTypeName(void) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual void getStats ( int *num_branches, int *num_leaves, int *num_tris, int *num_vertices ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void isect_triangles ( sgSphere *s, sgMat4 m, int test_needed ) = 0 ; + virtual void hot_triangles ( sgVec3 s, sgMat4 m, int test_needed ) = 0 ; + virtual void los_triangles ( sgVec3 s, sgMat4 m, int test_needed ) = 0 ; + virtual void draw () = 0 ; +} ; + +extern sgVec3 _ssgVertex000 ; +extern sgVec4 _ssgColourWhite ; +extern sgVec3 _ssgNormalUp ; +extern sgVec2 _ssgTexCoord00 ; +extern short _ssgIndex0 ; +extern float _ssgGlobTime ; // used by ssgAnimTransform. Has to be set by the appliation! + + +class ssgVTable : public ssgLeaf +{ +protected: + sgBox bbox ; + int indexed ; + + sgVec3 *vertices ; int num_vertices ; unsigned short *v_index ; + sgVec3 *normals ; int num_normals ; unsigned short *n_index ; + sgVec2 *texcoords ; int num_texcoords ; unsigned short *t_index ; + sgVec4 *colours ; int num_colours ; unsigned short *c_index ; + + virtual void draw_geometry () ; + virtual void copy_from ( ssgVTable *src, int clone_flags ) ; + +_SSG_PUBLIC: + int isIndexed () { return indexed ; } + +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgVTable () ; + ssgVTable ( GLenum ty, + int nv, unsigned short *vi, sgVec3 *vl, + int nn, unsigned short *ni, sgVec3 *nl, + int nt, unsigned short *ti, sgVec2 *tl, + int nc, unsigned short *ci, sgVec4 *cl ) ; + + ssgVTable ( GLenum ty, + int nv, sgVec3 *vl, + int nn, sgVec3 *nl, + int nt, sgVec2 *tl, + int nc, sgVec4 *cl ) ; + + virtual void drawHighlight ( sgVec4 colour ) ; + virtual void drawHighlight ( sgVec4 colour, int i ) ; + virtual void pick ( int baseName ) ; + virtual void transform ( const sgMat4 m ) ; + + int getNumVertices () { return num_vertices ; } + int getNumNormals () { return num_normals ; } + int getNumColours () { return num_colours ; } + int getNumTexCoords () { return num_texcoords ; } + int getNumTriangles () ; + void getTriangle ( int n, short *v1, short *v2, short *v3 ) ; + int getNumLines () ; + void getLine ( int n, short *v1, short *v2 ) ; + + void getColourList ( void **list, unsigned short **idx ) + { + *list = colours ; + *idx = c_index ; + } + + void getTexCoordList ( void **list, unsigned short **idx ) + { + *list = texcoords ; + *idx = t_index ; + } + + void getNormalList ( void **list, unsigned short **idx ) + { + *list = normals ; + *idx = n_index ; + } + + void getVertexList ( void **list, unsigned short **idx ) + { + *list = vertices ; + *idx = v_index ; + } + + float *getVertex (int i){ if(i>=num_vertices)i=num_vertices-1; + return (num_vertices<=0) ? _ssgVertex000 : + ((indexed)?vertices [v_index[i]]:vertices [i]);} + float *getColour (int i){ if(i>=num_colours)i=num_colours-1; + return (num_colours<=0) ? _ssgColourWhite : + ((indexed)?colours [c_index[i]]:colours [i]);} + float *getNormal (int i){ if(i>=num_normals)i=num_normals-1; + return (num_normals<=0) ? _ssgNormalUp : + ((indexed)?normals [n_index[i]]:normals [i]);} + float *getTexCoord(int i){ if(i>=num_texcoords)i=num_texcoords-1; + return (num_texcoords<=0) ? _ssgTexCoord00 : + ((indexed)?texcoords[t_index[i]]:texcoords[i]);} + + + virtual ~ssgVTable (void) ; + + virtual const char *getTypeName(void) ; + virtual void recalcBSphere () ; + virtual void draw () ; + + virtual void isect_triangles ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot_triangles ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los_triangles ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + +class ssgVtxTable : public ssgLeaf +{ +protected: + sgBox bbox ; + + virtual void draw_geometry () ; + virtual void copy_from ( ssgVtxTable *src, int clone_flags ) ; + +_SSG_PUBLIC: + + ssgVertexArray *vertices ; + ssgNormalArray *normals ; + ssgTexCoordArray *texcoords ; + ssgColourArray *colours ; + +public: + int compare(ssgVtxTable *other, int print_result=TRUE); + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgVtxTable () ; + + ssgVtxTable ( GLenum ty, ssgVertexArray *vl, + ssgNormalArray *nl, + ssgTexCoordArray *tl, + ssgColourArray *cl ) ; + + void textureMe(float uP, float vP) + { texcoords = new ssgTexCoordArray; + for(int i=vertices->getNum()-1; i>=0; i--) + { sgVec2 v; + v[0] = uP; v[1] = vP; + texcoords->add(v); + } + } + virtual void drawHighlight ( sgVec4 colour ) ; + virtual void drawHighlight ( sgVec4 colour, int i ) ; + virtual void pick ( int baseName ) ; + virtual void transform ( const sgMat4 m ) ; + + ssgVertexArray *getVertices () { return vertices ; } + ssgNormalArray *getNormals () { return normals ; } + ssgTexCoordArray *getTexCoords () { return texcoords ; } + ssgColourArray *getColours () { return colours ; } + + virtual void setVertices ( ssgVertexArray *vl ) ; + virtual void setNormals ( ssgNormalArray *nl ) ; + virtual void setTexCoords ( ssgTexCoordArray *tl ) ; + virtual void setColours ( ssgColourArray *cl ) ; + + int getNumVertices () { return vertices -> getNum () ; } + int getNumNormals () { return normals -> getNum () ; } + int getNumColours () { return colours -> getNum () ; } + int getNumTexCoords () { return texcoords -> getNum () ; } + + /* Don't call this unless you know what you are doing!! */ + void rawSetNumVertices ( unsigned int n ) { vertices -> rawSetNum ( n ) ; } + + int getNumTriangles () ; + void getTriangle ( int n, short *v1, short *v2, short *v3 ) ; + int getNumLines () ; + void getLine ( int n, short *v1, short *v2 ) ; + + void getVertexList ( void **list ) { *list = vertices -> get ( 0 ) ; } + void getNormalList ( void **list ) { *list = normals -> get ( 0 ) ; } + void getTexCoordList ( void **list ) { *list = texcoords -> get ( 0 ) ; } + void getColourList ( void **list ) { *list = colours -> get ( 0 ) ; } + + float *getVertex (int i){ int nv=getNumVertices(); if(i>=nv)i=nv-1; + return (nv<=0) ? _ssgVertex000:vertices->get(i);} + float *getNormal (int i){ int nn=getNumNormals(); if(i>=nn)i=nn-1; + return (nn<=0) ? _ssgNormalUp:normals->get(i);} + float *getTexCoord(int i){ int nc=getNumTexCoords(); if(i>=nc)i=nc-1; + return (nc<=0) ? _ssgTexCoord00:texcoords->get(i);} + float *getColour (int i){ int nc=getNumColours(); if(i>=nc)i=nc-1; + return (nc<=0) ? _ssgColourWhite:colours->get(i);} + + ssgVtxArray *getAs_ssgVtxArray (); + + virtual ~ssgVtxTable (void) ; + + virtual const char *getTypeName(void) ; + virtual void recalcBSphere () ; + virtual void draw () ; + + virtual void isect_triangles ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot_triangles ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los_triangles ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2); + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + +} ; + + +class ssgTween : public ssgVtxTable +{ + virtual void copy_from ( ssgTween *src, int clone_flags ) ; + void init () ; +protected: +_SSG_PUBLIC: + + int curr_bank ; + + ssgVertexArray *render_vertices ; + ssgNormalArray *render_normals ; + ssgTexCoordArray *render_texcoords ; + ssgColourArray *render_colours ; + + ulList *banked_vertices ; + ulList *banked_normals ; + ulList *banked_texcoords ; + ulList *banked_colours ; + +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + + ssgTween () ; + ssgTween ( GLenum ty ) ; + + virtual void setVertices ( ssgVertexArray *vl ) ; + virtual void setNormals ( ssgNormalArray *nl ) ; + virtual void setTexCoords ( ssgTexCoordArray *tl ) ; + virtual void setColours ( ssgColourArray *cl ) ; + + virtual void recalcBSphere () ; + virtual void draw () ; + virtual void transform ( const sgMat4 m ) ; + + virtual const char *getTypeName(void) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2); + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + + int getNumBanks () { return banked_vertices -> getNumEntities () ; } + + int newBank ( ssgVertexArray *vl, ssgNormalArray *nl, + ssgTexCoordArray *tl, ssgColourArray *cl ) ; + int newBank ( int newVertices , int newNormals, + int newTexCoords, int newColours ) ; + void setBank ( int bank ) ; + + virtual ~ssgTween (void) ; +} ; + + +class ssgVtxArray : public ssgVtxTable +{ +protected: + ssgIndexArray *indices; + + virtual void draw_geometry () ; + virtual void copy_from ( ssgVtxArray *src, int clone_flags ) ; + +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgVtxArray () ; + + ssgVtxArray ( GLenum ty, ssgVertexArray *vl, + ssgNormalArray *nl, + ssgTexCoordArray *tl, + ssgColourArray *cl, + ssgIndexArray *il ) ; + + virtual void drawHighlight ( sgVec4 colour ) ; + virtual void drawHighlight ( sgVec4 colour, int i ) ; + virtual void pick ( int baseName ) ; + + void setIndices ( ssgIndexArray *il ) ; + void addIndex ( short i) { indices->add(i); } + + int getNumIndices () { return indices -> getNum () ; } + + int getNumTriangles () ; + void getTriangle ( int n, short *v1, short *v2, short *v3 ) ; + + int getNumLines () ; + void getLine ( int n, short *v1, short *v2 ) ; + + void getIndexList ( void **list ) { *list = indices -> get ( 0 ) ; } + + short *getIndex (int i){ int ni=getNumIndices();if(i>=ni)i=ni-1; + return (ni<=0) ? &_ssgIndex0 : indices->get(i);} + + void removeUnusedVertices(); + virtual ~ssgVtxArray (void) ; + + virtual const char *getTypeName(void) ; + + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + +// class ssgVtxInterleavedArray + + +class ssgBranch : public ssgEntity +{ + ssgKidList kids ; + +protected: + virtual void copy_from ( ssgBranch *src, int clone_flags ) ; + +public: + virtual void zeroSpareRecursive (); + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgBranch (void) ; + virtual ~ssgBranch (void) ; + + virtual int getNumKids (void) { return kids.getNumEntities() ; } + ssgEntity *getKid ( int n ) { return kids.getEntity ( n ) ; } + ssgEntity *getNextKid (void) { return kids.getNextEntity () ; } + int searchForKid ( ssgEntity *entity ) + { return kids.searchForEntity(entity); } + + void addKid ( ssgEntity *entity ) ; + void removeKid ( int n ) ; + void removeKid ( ssgEntity *entity ) ; + void removeAllKids (void) ; + void replaceKid ( int n, ssgEntity *new_entity ) ; + void replaceKid ( ssgEntity *old_entity, ssgEntity *new_entity ) ; + + void mergeHNodes(); + + virtual ssgEntity *getByName ( char *match ) ; + virtual ssgEntity *getByPath ( char *path ) ; + + virtual const char *getTypeName(void) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual void getStats ( int *num_branches, int *num_leaves, int *num_tris, int *num_vertices ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void recalcBSphere () ; +} ; + + +#define SSGTWEEN_STOP_AT_END 0 +#define SSGTWEEN_REPEAT 1 + +class ssgTweenController : public ssgBranch +{ + float curr_bank ; + int mode ; /* STOP_AT_END or REPEAT */ + +protected: + + virtual void copy_from ( ssgTweenController *src, int clone_flags ) ; + +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgTweenController (void) ; + virtual ~ssgTweenController (void) ; + + void setMode ( int _mode ) { mode = _mode ; } + int getMode () { return mode ; } + + void selectBank ( float f ) { curr_bank = f ; } + float getCurrBank () { return curr_bank ; } + + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual const char *getTypeName(void) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + + +class ssgInvisible : public ssgBranch +{ +protected: + + virtual void copy_from ( ssgInvisible *src, int clone_flags ) ; + +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgInvisible (void) ; + virtual ~ssgInvisible (void) ; + + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + + +class ssgSelector : public ssgBranch +{ + unsigned char* selection ; + +protected: + int max_kids ; + + virtual void copy_from ( ssgSelector *src, int clone_flags ) ; + +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgSelector ( int max_kids = 32 ) ; + virtual ~ssgSelector (void) ; + + void selectStep ( unsigned int s ) + { + memset ( selection, 0, max_kids ) ; + selection [s] = 1 ; + } + + void select ( unsigned int s ) + { + for ( int i=0; i= 0) ? getKid ( k ) : NULL ; + } + + int getMaxKids (void) const { return max_kids ; } + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; +} ; + + +class ssgRangeSelector : public ssgSelector +{ + int additive ; + float rng_list [ 33 ] ; + +protected: + virtual void copy_from ( ssgRangeSelector *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgRangeSelector (void) ; + virtual ~ssgRangeSelector (void) ; + + void setRanges ( float *ranges, unsigned int nranges ) + { + for ( unsigned int i = 0 ; i < 33 ; i++ ) + if ( i < nranges ) + rng_list [ i ] = ranges [ i ] ; + else + rng_list [ i ] = SG_MAX ; + } + + void setRange ( unsigned int which, float range ) + { + if ( which < 33 ) + rng_list [ which ] = range ; + } + + float *getRanges () + { + return rng_list ; + } + + float getRange ( unsigned int which ) + { + return ( which < 33 ) ? rng_list[which] : SG_MAX ; + } + + void setAdditive ( int add ) { additive = add ; } + int isAdditive () { return additive ; } + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; +} ; + + +enum ssgAnimEnum +{ + SSG_ANIM_START, + SSG_ANIM_STOP, + SSG_ANIM_PAUSE, + SSG_ANIM_RESUME +} ; + +enum ssgAnimDirection +{ + SSG_ANIM_SWING, + SSG_ANIM_ONESHOT, + SSG_ANIM_SHUTTLE +} ; + +enum ssgAnimTimeMode +{ + SSG_ANIM_FRAME, + SSG_ANIM_CLOCK +}; + + +class ssgTimedSelector : public ssgSelector +{ +_SSG_PUBLIC: + ssgAnimEnum running ; + ssgAnimDirection mode ; + + double start_time ; + double pause_time ; + double loop_time ; + float *times ; + int curr ; + int start ; + int end ; + +protected: + ssgAnimTimeMode time_mode ; + static ulClock ck ; + + void compute_loop_time () + { + loop_time = 0 ; + + for ( int k = start ; k <= end ; k++ ) + loop_time += times [ k ] ; + } + + virtual void copy_from ( ssgTimedSelector *src, int clone_flags ) ; + + double get_time() const + { + if (time_mode == SSG_ANIM_FRAME) + return (double) ssgGetFrameCounter() ; + else + return ck.update(), ck.getAbsTime(); + } +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgTimedSelector ( int max_kids = 32 ) ; + virtual ~ssgTimedSelector (void) ; + + virtual const char *getTypeName(void) ; + + int getStep () ; + + ssgAnimTimeMode getTimeMode() const { return time_mode; } + + float getDuration ( int i = 0 ) { return times [ i ] ; } + + void setDuration ( float ti, int i = -1, ssgAnimTimeMode m = SSG_ANIM_FRAME ) + { + time_mode = m; + if ( i >= 0 && i < max_kids ) + times [ i ] = ti ; + else + for ( int j = 0 ; j < max_kids ; j++ ) + times [ j ] = ti ; + + compute_loop_time () ; + } + + void control ( ssgAnimEnum m ) + { + compute_loop_time () ; + + if ( m == SSG_ANIM_PAUSE ) + { + pause_time = get_time() ; + curr = getStep () ; + } + else + if ( m == SSG_ANIM_RESUME ) + { + start_time += get_time () - pause_time ; + + if ( running != SSG_ANIM_STOP ) + m = SSG_ANIM_START ; + } + else + if ( m == SSG_ANIM_START ) + { + start_time = get_time () ; + curr = getStep () ; + } + + running = m ; + } + + ssgAnimEnum getControl () { return running ; } + + void setMode ( ssgAnimDirection m ) { mode = m ; } + ssgAnimDirection getMode () { return mode ; } + + void setLimits ( int st, int en ) + { + curr = st ; + start = st ; + end = en ; + compute_loop_time () ; + } + + void getLimits ( int *st, int *en ) + { + if ( st != NULL ) *st = start ; + if ( en != NULL ) *en = end ; + } + + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; +} ; + + +class ssgBaseTransform : public ssgBranch +{ +protected: + + sgMat4 transform ; + sgMat4 last_transform ; + int last_updated ; + int first_time ; + virtual void copy_from ( ssgBaseTransform *src, int clone_flags ) ; +public: + + ssgBaseTransform (void) ; + virtual ~ssgBaseTransform (void) ; + + void firsttime () + { + if ( first_time ) + { + first_time = FALSE ; + updateTransform () ; + } + } + + void updateTransform () + { + sgCopyMat4 ( last_transform, transform ) ; + last_updated = ssgGetFrameCounter () ; + } + + void getLastTransform ( sgMat4 xform ) + { + /* + If the transform was not updated this - or last frame + then we need to equate the two transforms. + */ + + if ( last_updated < ssgGetFrameCounter () - 1 ) + updateTransform () ; + + sgCopyMat4 ( xform, last_transform ) ; + } + + void getTransform ( sgMat4 xform ) + { + sgCopyMat4 ( xform, transform ) ; + } + + virtual void setTransform ( sgVec3 xyz ) = 0 ; + virtual void setTransform ( sgCoord *xform ) = 0 ; + virtual void setTransform ( sgCoord *xform, float sx, float sy, float sz )=0; + virtual void setTransform ( sgMat4 xform ) = 0 ; + + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + + virtual const char *getTypeName(void) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; +} ; + + +class ssgTransform : public ssgBaseTransform +{ +protected: + virtual void copy_from ( ssgTransform *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgTransform (void) ; + ssgTransform ( sgCoord *c ) ; + virtual ~ssgTransform (void) ; + + virtual void setTransform ( sgVec3 xyz ) ; + virtual void setTransform ( sgCoord *xform ) ; + virtual void setTransform ( sgCoord *xform, float sx, float sy, float sz ) ; + virtual void setTransform ( sgMat4 xform ) ; + + virtual void getNetTransform ( sgMat4 xform ) ; + virtual void getLastNetTransform ( sgMat4 xform ) ; + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void recalcBSphere () ; +} ; + +class ssgAnimTransform : public ssgTransform +{ + float curr_bank ; + int mode ; /* SSGTWEEN_STOP_AT_END or SSGTWEEN_REPEAT */ + class ssgTransformArray transformations; + +protected: + + virtual void copy_from ( ssgAnimTransform *src, int clone_flags ) ; + +public: + + void setNum ( unsigned int n ) { transformations.setNum( n ); } + void setATransform ( sgMat4 thing, unsigned int n ) { transformations.raw_set ( (char *) thing, n ) ; } ; + + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgAnimTransform (void) ; + virtual ~ssgAnimTransform (void) ; + + void setMode ( int _mode ) { mode = _mode ; } + int getMode () { return mode ; } + + void selectBank ( float f ) { curr_bank = f ; } + float getCurrBank () { return curr_bank ; } + + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual const char *getTypeName(void) ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + +class ssgAxisTransform : public ssgTransform +{ + sgVec3 rotation_axis ; + sgVec3 rotation_center ; + float limit_low ; + float limit_high ; +protected: + virtual void copy_from ( ssgAxisTransform *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgAxisTransform (void) ; + ssgAxisTransform ( sgVec3 axis, sgVec3 center ) ; + virtual ~ssgAxisTransform (void) ; + + virtual void setAxis ( sgVec3 axis ) ; + virtual float *getAxis () ; + virtual void setCenter ( sgVec3 center ) ; + virtual float *getCenter () ; + virtual void setRotation ( float rot ) ; + + virtual void setRotationLimits ( float low, float high ) ; + virtual void setLinearRotation ( float rot ) ; + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +}; + + +class ssgTexTrans : public ssgBaseTransform +{ +protected: + virtual void copy_from ( ssgTexTrans *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgTexTrans (void) ; + ssgTexTrans ( sgCoord *c ) ; + virtual ~ssgTexTrans (void) ; + + virtual void setTransform ( sgVec3 xyz ) ; + virtual void setTransform ( sgCoord *xform ) ; + virtual void setTransform ( sgCoord *xform, float sx, float sy, float sz ) ; + virtual void setTransform ( sgMat4 xform ) ; + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; +} ; + + +class ssgCutout : public ssgBranch +{ + int point_rotate ; +protected: + virtual void copy_from ( ssgCutout *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgCutout (int pntrot=FALSE) ; + virtual ~ssgCutout (void) ; + + int isPointRotate (void) { return point_rotate ; } + + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; + virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ; + virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ; + virtual void los ( sgVec3 s, sgMat4 m, int test_needed ) ; +} ; + + +class ssgRoot : public ssgBranch +{ +protected: + virtual void copy_from ( ssgRoot *src, int clone_flags ) ; +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgRoot (void) ; + virtual ~ssgRoot (void) ; + virtual const char *getTypeName(void) ; + virtual int load ( FILE *fd ) ; + virtual int save ( FILE *fd ) ; +} ; + + +class ssgLight +{ + int id ; + int is_headlight ; + int is_turned_on ; + + sgVec4 ambient ; + sgVec4 diffuse ; + sgVec4 specular ; + + sgVec4 position ; + + sgVec3 direction ; + float exponent ; + float cutoff ; + sgVec3 atten ; + +public: + + ssgLight () + { + id = 0 ; + is_turned_on = FALSE ; + is_headlight = FALSE ; + sgSetVec4 ( position, 0.0f, 0.0f, 1.0f, 0.0f ) ; + sgSetVec4 ( ambient , 0.2f, 0.2f, 0.2f, 1.0f ) ; + sgSetVec4 ( diffuse , 1.0f, 1.0f, 1.0f, 1.0f ) ; + sgSetVec4 ( specular, 1.0f, 1.0f, 1.0f, 1.0f ) ; + sgSetVec3 ( direction, 0.0f, 0.0f, -1.0f ) ; + exponent = 1.0f; + cutoff = 90.0f; + sgSetVec3 ( atten, 1.0f, 0.0f, 0.0f ) ; + } + + void setID ( int i ) { id = i ; } + int isOn () { return is_turned_on ; } + void on () { is_turned_on = TRUE ; } + void off () { is_turned_on = FALSE ; } + + void setPosition ( const sgVec3 pos ) { sgCopyVec3 ( position, pos ) ; } + void getPosition ( sgVec3 pos ) { sgCopyVec3 ( pos, position ) ; } + void setPosition ( float x, float y, float z ) { sgSetVec3 ( position, x, y, z ) ; } + + void setColour ( GLenum which, const sgVec4 col ) + { + switch ( which ) + { + case GL_AMBIENT : sgCopyVec4 ( ambient , col ) ; break ; + case GL_DIFFUSE : sgCopyVec4 ( diffuse , col ) ; break ; + case GL_SPECULAR : sgCopyVec4 ( specular, col ) ; break ; + default : break ; + } + } + + void getColour ( GLenum which, sgVec4 col ) + { + switch ( which ) + { + case GL_AMBIENT : sgCopyVec4 ( col , ambient ) ; break ; + case GL_DIFFUSE : sgCopyVec4 ( col , diffuse ) ; break ; + case GL_SPECULAR : sgCopyVec4 ( col, specular ) ; break ; + default : break ; + } + } + + void setColour ( GLenum which, float r, float g, float b ) + { + sgVec4 colour = { r, g, b, 1.0f }; + setColour(which, colour); + } + + void setHeadlight ( int head ) { is_headlight = head ; } + int isHeadlight () { return is_headlight ; } + + void setSpotlight ( int spot ) { position[3] = ( spot ? 1.0f : 0.0f ) ; } + int isSpotlight () { return position[3] != 0.0f; } + + void setSpotDirection ( const sgVec3 dir ) { sgCopyVec3 ( direction, dir ) ; } + void getSpotDirection ( sgVec3 dir ) { sgCopyVec3 ( dir, direction ) ; } + void setSpotDirection ( float x, float y, float z ) { sgSetVec3 ( direction, x, y, z ) ; } + + void setSpotDiffusion ( float exp, float cut = 90.0f ) + { + exponent = exp; + cutoff = cut; + } + + void getSpotDiffusion ( float *exp, float *cut = 0 ) + { + if (exp != 0) *exp = exponent; + if (cut != 0) *cut = cutoff; + } + + void setSpotAttenuation ( float constant, float linear, float quadratic ) + { + sgSetVec3( atten, constant, linear, quadratic ); + } + + void getSpotAttenuation ( float *constant, float *linear, float *quadratic ) + { + if (constant != 0) *constant = atten[0]; + if (linear != 0) *linear = atten[1]; + if (quadratic != 0) *quadratic = atten[2]; + } + + void setup () + { + if ( is_turned_on ) + { + glEnable ( (GLenum)(GL_LIGHT0+id) ) ; + glLightfv ( (GLenum)(GL_LIGHT0+id), GL_AMBIENT , ambient ) ; + glLightfv ( (GLenum)(GL_LIGHT0+id), GL_DIFFUSE , diffuse ) ; + glLightfv ( (GLenum)(GL_LIGHT0+id), GL_SPECULAR, specular ) ; + glLightfv ( (GLenum)(GL_LIGHT0+id), GL_POSITION, position ) ; + if ( isSpotlight() ) { + glLightfv ( (GLenum)(GL_LIGHT0+id), GL_SPOT_DIRECTION, direction ) ; + glLightf ( (GLenum)(GL_LIGHT0+id), GL_SPOT_EXPONENT, exponent ) ; + glLightf ( (GLenum)(GL_LIGHT0+id), GL_SPOT_CUTOFF, cutoff ) ; + glLightf ( (GLenum)(GL_LIGHT0+id), GL_CONSTANT_ATTENUATION, atten[0] ); + glLightf ( (GLenum)(GL_LIGHT0+id), GL_LINEAR_ATTENUATION, atten[1] ); + glLightf ( (GLenum)(GL_LIGHT0+id), GL_QUADRATIC_ATTENUATION, atten[2] ); + } + } + else + glDisable ( (GLenum)(GL_LIGHT0+id) ) ; + } + +} ; + +class ssgHit +{ +_SSG_PUBLIC: + + int num_entries ; + ssgEntity *path [ SSG_MAXPATH ] ; + +public: + ssgLeaf *leaf ; + int triangle ; + sgVec4 plane ; + sgMat4 matrix ; + + ssgHit () + { + leaf = NULL ; + init () ; + } ; + + void init () { num_entries = 0 ; } + + void addPath ( ssgEntity *e ) + { + if ( num_entries < SSG_MAXPATH ) + path [ num_entries++ ] = e ; + } + + int getNumPathEntries () { return num_entries ; } + + ssgEntity *getPathEntry ( int i ) + { + return ( i >= 0 && i < num_entries ) ? path[i] : (ssgEntity *) NULL ; + } +} ; + +class ssgContext +{ + protected: + + int enabledClipPlanes ; + sgVec4 clipPlane [ 6 ] ; + ssgSimpleState *currentState ; + ssgSimpleState *basicState ; + sgFrustum *frustum ; + + sgMat4 cameraMatrix ; + int cullFace ; + int ovTexture ; + int ovCullface ; + ssgState *ovState ; + +public: + + + ssgContext () ; + ~ssgContext () ; + + void forceBasicState () ; + + void applyClipPlanes () ; + void removeClipPlanes () ; + + void makeCurrent () ; + int isCurrent () ; + + void clrClipPlane ( int i ) + { + if ( i >= 0 && i < 6 ) + enabledClipPlanes &= ~(1<= 0 && i < 6 ) + { + sgCopyVec4 ( clipPlane [ i ], plane ) ; + enabledClipPlanes |= (1<=6)) ? NULL : clipPlane [i] ; + } + + void overrideState ( ssgState *s ) ; + void overrideTexture ( int on_off ) ; + void overrideCullface ( int on_off ) ; + + void setCullface ( int on_off ) + { + if ( cullFace == on_off || ovCullface ) + return ; + + cullFace = on_off ; + + if ( cullFace ) glEnable ( GL_CULL_FACE ) ; + else glDisable ( GL_CULL_FACE ) ; + } + + ssgState *overriddenState () { return ovState ; } + int stateOverridden () { return ovState != NULL ; } + int textureOverridden () { return ovTexture ; } + int cullfaceOverridden () { return ovCullface ; } + int cullfaceIsEnabled () { return cullFace ; } + + sgFrustum *getFrustum () { return frustum ; } + + // make a perspective projection + void setFrustum ( float l, float r, float b, float t, float n, float f ) ; + + // make an orthographic projection + void setOrtho ( float l, float r, float b, float t, float n, float f ) ; + + void getNearFar ( float *n, float *f ) ; + void getFOV ( float *w, float *h ) ; + void getOrtho ( float *w, float *h ) ; + + void setNearFar ( float n, float f ) ; + void setOrtho ( float w, float h ) ; // make orthographic + void setFOV ( float w, float h ) ; // make perspective + + int isOrtho () { return frustum -> isOrtho () ; } // is orthographic + + ssgSimpleState *getState () { return currentState ; } + void cull ( ssgBranch *r ) ; + + void getCameraPosition ( sgVec3 pos ) ; + void setCamera ( sgMat4 mat ) ; + void setCamera ( const sgCoord *coord ) ; + void setCameraLookAt ( const sgVec3 eye, const sgVec3 center, const sgVec3 up ) ; + void setCameraLookAt ( const sgVec3 eye, const sgVec3 center ) ; + + void loadProjectionMatrix () ; + void loadModelviewMatrix () ; + + void getProjectionMatrix ( sgMat4 dst ) ; + void getModelviewMatrix ( sgMat4 dst ) ; + + void pushProjectionMatrix () ; + void pushProjectionMatrix ( sgFrustum *f ) ; + void loadModelviewMatrix ( sgMat4 mat ) ; + +} ; + + + +/* Compatibility inlines for pre-ssgContext applications */ + +extern ssgContext *_ssgCurrentContext ; + +inline ssgContext *ssgGetCurrentContext () { return _ssgCurrentContext ; } + +inline void ssgGetCameraPosition ( sgVec3 pos ) +{ + _ssgCurrentContext -> getCameraPosition ( pos ) ; +} + +inline void ssgOverrideState ( ssgState *s ) +{ + _ssgCurrentContext->overrideState ( s ) ; +} + +inline void ssgOverrideTexture ( int on_off ) +{ + _ssgCurrentContext->overrideTexture ( on_off ) ; +} + +inline void ssgOverrideCullface ( int on_off ) +{ + _ssgCurrentContext->overrideCullface ( on_off ) ; +} + +inline void ssgGetNearFar ( float *n, float *f ) +{ + _ssgCurrentContext->getNearFar ( n, f ) ; +} + +inline void ssgGetFOV ( float *w, float *h ) +{ + _ssgCurrentContext->getFOV ( w, h ) ; +} + +inline void ssgGetOrtho ( float *w, float *h ) +{ + _ssgCurrentContext->getOrtho ( w, h ) ; +} + +inline void ssgSetFrustum ( float l, float r, float b, float t, float n, float f ) +{ + _ssgCurrentContext->setFrustum ( l, r, b, t, n, f ) ; +} + +inline void ssgSetOrtho ( float l, float r, float b, float t, float n, float f ) +{ + _ssgCurrentContext->setOrtho ( l, r, b, t, n, f ) ; +} + +inline void ssgSetFOV ( float w, float h ) +{ + _ssgCurrentContext->setFOV ( w, h ) ; +} + +inline void ssgSetOrtho ( float w, float h ) +{ + _ssgCurrentContext->setOrtho ( w, h ) ; +} + +inline void ssgSetNearFar ( float n, float f ) +{ + _ssgCurrentContext->setNearFar ( n, f ) ; +} + +inline void ssgSetCamera ( sgMat4 mat ) +{ + _ssgCurrentContext-> setCamera ( mat ) ; +} + +inline void ssgSetCamera ( sgCoord *coord ) +{ + _ssgCurrentContext-> setCamera ( coord ) ; +} + +inline void ssgSetCameraLookAt ( const sgVec3 eye, const sgVec3 center, const sgVec3 up ) +{ + _ssgCurrentContext-> setCameraLookAt ( eye, center, up ) ; +} + +inline void ssgSetCameraLookAt ( const sgVec3 eye, const sgVec3 center ) +{ + _ssgCurrentContext-> setCameraLookAt ( eye, center ) ; +} + +inline void ssgLoadProjectionMatrix () +{ + _ssgCurrentContext->loadProjectionMatrix () ; +} + +inline void ssgLoadProjectionMatrix ( sgFrustum *f ) +{ + glLoadIdentity () ; + _ssgCurrentContext->pushProjectionMatrix ( f ) ; +} + +inline void ssgGetProjectionMatrix ( sgMat4 dst ) +{ + _ssgCurrentContext->getProjectionMatrix ( dst ) ; +} + +inline void ssgGetModelviewMatrix ( sgMat4 dst ) +{ + _ssgCurrentContext->getModelviewMatrix ( dst ) ; +} + +inline void ssgLoadModelviewMatrix () +{ + _ssgCurrentContext->loadModelviewMatrix () ; +} + +inline void ssgLoadModelviewMatrix ( sgMat4 mat ) +{ + _ssgCurrentContext->loadModelviewMatrix ( mat ) ; +} + +inline void ssgForceBasicState () +{ + _ssgCurrentContext -> forceBasicState () ; +} + +inline sgFrustum *ssgGetFrustum () +{ + return _ssgCurrentContext -> getFrustum() ; +} + +void ssgInit () ; + +void ssgCullAndDraw ( ssgBranch *root ) ; +void ssgCullAndPick ( ssgBranch *root, sgVec2 botleft, sgVec2 topright ) ; +int ssgIsect ( ssgBranch *root, sgSphere*s,sgMat4 m, ssgHit **results ) ; +int ssgHOT ( ssgBranch *root, sgVec3 s, sgMat4 m, ssgHit **results ) ; +int ssgLOS ( ssgBranch *root, sgVec3 s, sgMat4 m, ssgHit **results ) ; + +/* Load/Save functions */ + +enum { + SSG_MD2_STAND, + SSG_MD2_RUN, + SSG_MD2_ATTACK, + SSG_MD2_PAIN_1, + SSG_MD2_PAIN_2, + SSG_MD2_PAIN_3, + SSG_MD2_JUMP, + SSG_MD2_FLIPOFF, + SSG_MD2_SALUTE, + SSG_MD2_TAUNT, + SSG_MD2_WAVE, + SSG_MD2_POINT, + SSG_MD2_CROUCH_STAND, + SSG_MD2_CROUCH_WALK, + SSG_MD2_CROUCH_PAIN, + SSG_MD2_CROUCH_DEATH, + SSG_MD2_DEATH_1, + SSG_MD2_DEATH_2, + SSG_MD2_DEATH_3, + SSG_MD2_ALL, + SSG_MD2_POSE +}; + +class ssgLoaderOptions +{ + //NOTES: we could add more later + //...model scale factor... + //...create normals or read them from the file?... + //...cutoff angle for smooth shading where there isn`t one in the file format... + //...which direction is front-facing in formats that don`t get it right... + //...etc... + +protected: + + + /* for backward compatibility */ + ssgState *(*create_state_cb)( char *) ; + ssgBranch *(*create_branch_cb)(char *) ; + + char* model_dir ; + char* texture_dir ; + + char* make_path ( char* path, const char* dir, const char* fname ) const ; + +public: + ssgSimpleStateArray shared_states ; + ssgTextureArray shared_textures ; + + ssgLoaderOptions () + { + model_dir = 0 ; + texture_dir = 0 ; + + /* for backward compatibility */ + create_state_cb = 0 ; + create_branch_cb = 0 ; + } + + virtual ~ssgLoaderOptions() + { + if (model_dir) + { + delete [] model_dir; + model_dir = 0; + } + + if (texture_dir) + { + delete [] texture_dir; + texture_dir = 0; + } + } + + const char* getModelDir ( void ) const { return model_dir ; } + const char* getTextureDir ( void ) const { return texture_dir ; } + void setModelDir ( const char *s ) ; + void setTextureDir ( const char *s ) ; + + // + // the idea is that you derive a class from ssgLoaderOptions and + // override these methods to customize how the loaders work + // + virtual void makeModelPath ( char* path, const char *fname ) const ; + virtual void makeTexturePath ( char* path, const char *fname ) const ; + + virtual ssgLeaf* createLeaf ( ssgLeaf* leaf, const char* parent_name ) ; + virtual ssgTexture* createTexture ( char* tfname, + int wrapu = TRUE, int wrapv = TRUE, + int mipmap = TRUE ) ; + virtual ssgTransform* createTransform ( ssgTransform* tr, + ssgTransformArray* ta ) const ; + virtual ssgSelector* createSelector ( ssgSelector* s ) const ; + virtual ssgBranch* createBranch ( char* text ) const + { + if ( create_branch_cb != NULL ) + return (*create_branch_cb)(text) ; + return NULL ; + } + virtual ssgState* createState ( char* tfname ) const + { + if ( create_state_cb != NULL ) + return (*create_state_cb)(tfname) ; + return NULL ; + } + virtual ssgSimpleState* createSimpleState ( char* tfname ) const + { + ssgState *st = createState ( tfname ) ; + if ( st != NULL ) + { + if ( st -> isAKindOf ( ssgTypeSimpleState() ) ) + { + return (ssgSimpleState*) st ; + } + else + { + ulSetError(UL_WARNING, "createState() did not return simple state"); + } + } + return NULL ; + } + + virtual void endLoad () + { + /* default behavior is to reset sharing after loading a model */ + shared_textures.removeAll () ; + shared_states.removeAll () ; + } + + // + // *** DEPRECATED INTERFACE *** + // for backward compatibility + // derive and use virtual methods to override + // + void setCreateBranchCallback ( ssgBranch *(*cb)(char *) ) + { + create_branch_cb = cb ; + } + void setCreateStateCallback ( ssgState *(*cb)(char *) ) + { + create_state_cb = cb ; + } +} ; + + +int ssgSave ( const char *fname, ssgEntity *ent ) ; +int ssgSaveAC ( const char *fname, ssgEntity *ent ) ; +int ssgSaveASE ( const char *fname, ssgEntity *ent ) ; +int ssgSaveSSG ( const char *fname, ssgEntity *ent ) ; +int ssgSaveDXF ( const char *fname, ssgEntity *ent ) ; +int ssgSaveTRI ( const char *fname, ssgEntity *ent ) ; +int ssgSaveOBJ ( const char *fname, ssgEntity *ent ) ; +int ssgSaveX ( const char *fname, ssgEntity *ent ) ; +int ssgSaveM ( const char *fname, ssgEntity *ent ) ; +int ssgSave3ds ( const char *fname, ssgEntity *ent ) ; +int ssgSaveFLT ( const char *fname, ssgEntity *ent ) ; +int ssgSaveOFF ( const char *fname, ssgEntity *ent ) ; +int ssgSaveQHI ( const char *fname, ssgEntity *ent ) ; +int ssgSaveATG ( const char *fname, ssgEntity *ent ) ; +int ssgSaveVRML1( const char *fname, ssgEntity *ent ) ; +int ssgSaveASC ( const char *fname, ssgEntity *ent ) ; +int ssgSaveIV ( const char *fname, ssgEntity *ent ) ; +int ssgSavePOV ( const char *fname, ssgEntity *ent ) ; + + +ssgEntity *ssgLoad ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoad3ds ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadAC3D ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadASC ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadSSG ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadASE ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadDOF ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadDXF ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadTRI ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadOBJ ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadMD2 ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadMDL ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadX ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadFLT ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadM ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadStrip( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadOFF ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadATG ( const char *fname, const ssgLoaderOptions *options = NULL ) ; +ssgEntity *ssgLoadVRML1( const char *fname, const ssgLoaderOptions* options = NULL ) ; +ssgEntity *ssgLoadIV ( const char *fname, const ssgLoaderOptions* options = NULL ) ; +ssgEntity *ssgLoadXPlaneOBJ ( const char *fname, const ssgLoaderOptions *options = NULL ) ; + + +typedef ssgEntity *ssgLoadFunc ( const char *, const ssgLoaderOptions * ) ; +typedef int ssgSaveFunc ( const char *, ssgEntity * ) ; + +void ssgAddModelFormat ( const char* extension, + ssgLoadFunc *loadfunc , ssgSaveFunc *savefunc ) ; + +/* current loader options */ + +extern ssgLoaderOptions* _ssgCurrentOptions ; + +inline ssgLoaderOptions* ssgGetCurrentOptions () +{ + return _ssgCurrentOptions ; +} + +inline void ssgSetCurrentOptions ( ssgLoaderOptions* options ) +{ + if ( options != NULL ) + _ssgCurrentOptions = options ; +} + +/* For backwards compatibility */ + +inline ssgEntity *ssgLoad ( char *fname, ssgBranch *(*cb)(char *)) +{ + _ssgCurrentOptions -> setCreateBranchCallback ( cb ) ; + return ssgLoad ( fname ) ; +} + +inline void ssgSetAppStateCallback ( ssgState *(*cb)(char *) ) +{ + _ssgCurrentOptions -> setCreateStateCallback ( cb ) ; +} + +inline void ssgModelPath ( const char *path ) +{ + _ssgCurrentOptions -> setModelDir ( path ) ; +} + +inline void ssgTexturePath ( const char *path ) +{ + _ssgCurrentOptions -> setTextureDir ( path ) ; +} + +ssgEntity *ssgLoadAC ( const char *fname, const ssgLoaderOptions *options = NULL ) ; + + +void ssgGetValuesFromLastATGFile(double *x, double *y, double *z, double *r); + + bool ssgConvertTexture( char * fname_output, char * fname_input ) ; + + +class ssgStatistics +{ + int vertex_count ; + int leaf_count ; + +public: + + void reset () ; + + void bumpVertexCount ( int i ) { vertex_count += i ; } + void bumpLeafCount ( int i ) { leaf_count += i ; } + + int getVertexCount () { return vertex_count ; } + int getLeafCount () { return leaf_count ; } + + ssgStatistics () + { + reset () ; + } +} ; + +ssgStatistics *ssgGetLatestStatistics () ; + +/* scene walkers */ + +void ssgFlatten ( ssgEntity *ent ) ; +void ssgStripify ( ssgEntity *ent ) ; +void ssgArrayTool ( ssgEntity *ent, float* vtol = 0, bool make_normals = false ) ; +void ssgTransTool ( ssgEntity *ent, const sgMat4 trans ) ; + +ssgLight *ssgGetLight ( int i ) ; + +char *ssgShowStats () ; +void ssgDelete ( ssgBranch *br ) ; +const char *ssgGetVersion () ; + +void ssgSetLoadOFFTranslucent ( int i ); + +void ssgRegisterType ( int type, ssgBase * ( *create_func ) () ) ; +ssgBase *ssgCreateOfType ( int type ) ; + +#define SSG_BACKFACE_COLLISIONS_SUPPORTED 1 +void ssgSetBackFaceCollisions ( bool b ) ; + +#if (_MSC_VER >= 1700) +// WK +#define SSG_SAVE_TEXT_FILE "wt" +#else +#define SSG_SAVE_TEXT_FILE "wa" +#endif +#endif + diff --git a/src/ssg/ssg3ds.h b/src/ssg/ssg3ds.h new file mode 100644 index 0000000..3247af5 --- /dev/null +++ b/src/ssg/ssg3ds.h @@ -0,0 +1,79 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssg3ds.h 2108 2006-12-11 20:42:05Z fayjf $ +*/ + +/******************************************************* + ** ssg3ds.h + ** + * Common data for ssgLoad3ds.cxx and ssgSave3ds.cxx + *******************************************************/ + +// 3ds chunk identifiers +static const int CHUNK_VERSION = 0x0002; +static const int CHUNK_RGB1 = 0x0010; // 3 floats of RGB +static const int CHUNK_RGB2 = 0x0011; // 3 bytes of RGB +static const int CHUNK_RGB3 = 0x0012; // 3 bytes of RGB (gamma) +static const int CHUNK_AMOUNT = 0x0030; +static const int CHUNK_MAIN = 0x4D4D; +static const int CHUNK_OBJMESH = 0x3D3D; +static const int CHUNK_ONEUNIT = 0x0100; +static const int CHUNK_BKGCOLOR = 0x1200; +static const int CHUNK_AMBCOLOR = 0x2100; +static const int CHUNK_OBJBLOCK = 0x4000; +static const int CHUNK_TRIMESH = 0x4100; +static const int CHUNK_VERTLIST = 0x4110; +static const int CHUNK_FACELIST = 0x4120; +static const int CHUNK_FACEMAT = 0x4130; +static const int CHUNK_MAPLIST = 0x4140; +static const int CHUNK_SMOOLIST = 0x4150; +static const int CHUNK_TRMATRIX = 0x4160; +static const int CHUNK_LIGHT = 0x4600; +static const int CHUNK_SPOTLIGHT = 0x4610; +static const int CHUNK_CAMERA = 0x4700; +static const int CHUNK_MATERIAL = 0xAFFF; +static const int CHUNK_MATNAME = 0xA000; +static const int CHUNK_AMBIENT = 0xA010; +static const int CHUNK_DIFFUSE = 0xA020; +static const int CHUNK_SPECULAR = 0xA030; +static const int CHUNK_SHININESS = 0xA040; +static const int CHUNK_SHINE_STRENGTH = 0xA041; +static const int CHUNK_TRANSPARENCY = 0xA050; +static const int CHUNK_TRANSP_FALLOFF = 0xA052; +static const int CHUNK_DOUBLESIDED = 0xA081; +static const int CHUNK_TEXTURE = 0xA200; +static const int CHUNK_BUMPMAP = 0xA230; +static const int CHUNK_MAPFILENAME = 0xA300; +static const int CHUNK_MAPOPTIONS = 0xA351; +static const int CHUNK_MAP_VSCALE = 0xA354; +static const int CHUNK_MAP_USCALE = 0xA356; +static const int CHUNK_MAP_UOFFST = 0xA358; +static const int CHUNK_MAP_VOFFST = 0xA35A; +static const int CHUNK_KEYFRAMER = 0xB000; +static const int CHUNK_FRAMES = 0xB008; +static const int CHUNK_KEYFRAME_MESH = 0xB002; +static const int CHUNK_FRAME_OBJNAME = 0xB010; +static const int CHUNK_FRAME_DUMMYNAME = 0xB011; +static const int CHUNK_FRAME_PIVOT = 0xB013; +static const int CHUNK_FRAME_POSITION = 0xB020; +static const int CHUNK_FRAME_ROTATION = 0xB021; +static const int CHUNK_FRAME_SCALE = 0xB022; +static const int CHUNK_FRAME_HIERARCHY = 0xB030; diff --git a/src/ssg/ssgAnimTransform.cxx b/src/ssg/ssgAnimTransform.cxx new file mode 100644 index 0000000..bcb0709 --- /dev/null +++ b/src/ssg/ssgAnimTransform.cxx @@ -0,0 +1,174 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998-2004 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgAnimTransform.cxx,v +*/ + +// Written by Wolfram Kuss in Oct 2004 + +#include "ssgLocal.h" + +float _ssgGlobTime = 0.0f; + +const char *ssgAnimTransform::getTypeName (void) { return "ssgAnimTransform" ; } + +void ssgAnimTransform::copy_from ( ssgAnimTransform *src, int clone_flags ) +{ + selectBank ( src->getCurrBank () ) ; + mode = src->mode; + //ARGH!!!!!!!!!!!!!!!!!!!! transformations.copy_from(static_cast(&src->transformations), clone_flags); + ssgBranch::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgAnimTransform::clone ( int clone_flags ) +{ + ssgAnimTransform *b = new ssgAnimTransform ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + + +ssgAnimTransform::ssgAnimTransform (void) +{ + type = ssgTypeAnimTransform () ; + curr_bank = 0.0f ; + mode = SSGTWEEN_REPEAT; +} + + +ssgAnimTransform::~ssgAnimTransform (void) +{ + removeAllKids () ; +} +// ********* done till here ************* + +void ssgAnimTransform::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + // calculate the transformation + +// comment is partly obsolete: +// You need to have a object with animation loaded into memory for the following function to make sense. +// Animation can be discreet (in steps) or "smooth" (fluid). +// If you loaded an animation made of several meshes, it will use a selector and therefore always be "discreet" +// If you loaded some transformation matrices like some rotations to rotate the flap of a plane, +// the define DISCREET_STEPS will determine whether the animation is smooth (new code, WK) or +// in steps ("old" code, Dave McClurg). +// +// The anim_frame is for discreet animation and an index determining which matrix / which selector will be used. +// For the smooth animation, the parameter curr_time_ms is used that should be the current time, +// Typically in MilliSeconds. However, you can speed up or slow down time. +// You could even stop changing curr_time_ms to freeze the animation. +// +// typically, you will call ssgSetAnimTime directly before you call ssgCullAndDraw +//void ssgSetAnimTime( ssgEntity *e, int anim_frame, long curr_time_ms ) + + + int num = transformations. getNum () ; + if ( num > 0 ) + { +#ifdef DISCREET_STEPS + int frame = curr_bank ; + if ( frame >= num ) + frame = num-1 ; + transformations. selection = frame ; + setTransform ( *( transformations. get ( transformations. selection ) ) ) ; +#else + /* fluid motion */ + ///// copied from ssgTween.cxx and changed var names + + //if(global_mode) + curr_bank = _ssgGlobTime; + + if ( curr_bank < 0.0f ) curr_bank = 0.0f ; + + int state1 = (int) floor ( curr_bank ) ; + int state2 = state1 + 1 ; + float tween = curr_bank - (float) state1 ; + + if ( mode == SSGTWEEN_REPEAT ) + { + state1 %= num ; + state2 %= num ; + } + else + { + if ( state1 >= num ) state1 = num - 1 ; + if ( state2 >= num ) state2 = num - 1 ; + } + + if ( state1 == state2 ) tween = 0.0f; + ///// + + sgMat4 XForm, *pmFrame1; + pmFrame1 = transformations. get ( state1 ); + sgMat4 *pmFrame2 = transformations. get ( state2 ); + for(int i=0;i<4;i++) + for(int j=0; j<4; j++) + XForm[i][j] = tween * ((*pmFrame1)[i][j]) + + (1.0f-tween) * ((*pmFrame2)[i][j]); + setTransform ( XForm ); +#endif + } + + ssgTransform::cull ( f, m, test_needed ) ; +} + + +// ********* below: done ************* + +int ssgAnimTransform::load ( FILE *fd ) +{ + _ssgReadFloat ( fd, & curr_bank ) ; + int int_mode; + _ssgReadInt ( fd, &int_mode ); + mode = int_mode; + transformations.load( fd ); + + return ssgBranch::load ( fd ) ; +} + + +int ssgAnimTransform::save ( FILE *fd ) +{ + _ssgWriteFloat ( fd, curr_bank ) ; + int int_mode = mode; + _ssgWriteInt ( fd, int_mode ); + transformations.save( fd ); + + return ssgBranch::save ( fd ) ; +} + + + +void ssgAnimTransform::print ( FILE *fd, char *indent, int how_much ) +{ + if ( how_much == 0 ) + return ; + + fprintf ( fd, "%sCurrent Bank = %f\n", indent, curr_bank ); + fprintf ( fd, "%sMode = %d\n", indent, (int)mode ); + if ( how_much > 1 ) + transformations.print( fd, indent, how_much ); + + ssgBranch::print ( fd, indent, how_much ) ; +} + + diff --git a/src/ssg/ssgAnimation.cxx b/src/ssg/ssgAnimation.cxx new file mode 100644 index 0000000..2d3b928 --- /dev/null +++ b/src/ssg/ssgAnimation.cxx @@ -0,0 +1,205 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgAnimation.cxx 1725 2002-11-08 18:35:07Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +ulClock ssgTimedSelector::ck; + +void ssgTimedSelector::copy_from ( ssgTimedSelector *src, int clone_flags ) +{ + ssgSelector::copy_from ( src, clone_flags ) ; + + running = src -> running ; + mode = src -> mode ; + time_mode = src -> time_mode ; + + start_time = src -> start_time ; + pause_time = src -> pause_time ; + loop_time = src -> loop_time ; + + delete [] times ; + times = new float [ max_kids ] ; + for ( int i = 0 ; i < max_kids ; i++ ) + times [ i ] = src -> times [ i ] ; + + curr = src -> curr ; + start = src -> start ; + end = src -> end ; +} + + +ssgBase *ssgTimedSelector::clone ( int clone_flags ) +{ + ssgTimedSelector *b = new ssgTimedSelector ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgTimedSelector::ssgTimedSelector ( int max_kids ) : ssgSelector ( max_kids ) +{ + type = ssgTypeTimedSelector () ; + select ( 1 ) ; + running = SSG_ANIM_STOP ; + mode = SSG_ANIM_SHUTTLE ; + start_time = pause_time = 0.0 ; + loop_time = 1.0 ; + + times = new float [ max_kids ] ; + for ( int i = 0 ; i < max_kids ; i++ ) + times [ i ] = 1.0f ; + + curr = start = end = 0 ; + + time_mode = SSG_ANIM_FRAME ; + ck.reset() ; +} + + +void ssgTimedSelector::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + compute_loop_time () ; + selectStep ( getStep () ) ; + ssgSelector::cull ( f, m, test_needed ) ; +} + + +void ssgTimedSelector::hot ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + selectStep ( getStep () ) ; + ssgSelector::hot ( sp, m, test_needed ) ; +} + +void ssgTimedSelector::los ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + selectStep ( getStep () ) ; + ssgSelector::los ( sp, m, test_needed ) ; +} + +void ssgTimedSelector::isect ( sgSphere *sp, sgMat4 m, int test_needed ) +{ + selectStep ( getStep () ) ; + ssgSelector::isect ( sp, m, test_needed ) ; +} + + +int ssgTimedSelector::getStep () +{ + double t = get_time () ; + + if ( running == SSG_ANIM_STOP || running == SSG_ANIM_PAUSE ) + return curr ; + + /* SSG_ANIM_START */ + + t -= start_time ; /* t is time since start of run */ + + if ( mode == SSG_ANIM_ONESHOT ) + { + if ( t >= loop_time ) + { + running = SSG_ANIM_STOP ; + return end ; + } + } + else + if ( mode == SSG_ANIM_SHUTTLE ) + { + /* Compute time since start of this loop */ + + t = t - floor ( t / loop_time ) * loop_time ; + } + else + if ( mode == SSG_ANIM_SWING ) + { + /* Compute time since start of this swing loop */ + + t = t - floor ( t / (2.0 * loop_time) ) * (2.0 * loop_time) ; + + /* Are we on the reverse part of the loop? */ + if ( t >= loop_time ) + t = 2.0 * loop_time - t ; + } + + int k ; + + for ( k = start ; t > 0.0 && k <= end ; k++ ) + t -= (double) times [ k ] ; + +//DaveM: i removed this line because, in shuttle mode, start plays twice + k-- ; + + if ( k < start ) k = start ; + if ( k > end ) k = end ; + + curr = k ; + return curr ; +} + + + +ssgTimedSelector::~ssgTimedSelector (void) +{ + delete [] times ; +} + + + +int ssgTimedSelector::load ( FILE *fd ) +{ + _ssgReadInt ( fd, (int *) & running ) ; + _ssgReadInt ( fd, (int *) & mode ) ; +// _ssgReadDouble( fd, & start_time ) ; +// _ssgReadDouble( fd, & pause_time ) ; +// _ssgReadDouble( fd, & loop_time ) ; + _ssgReadInt ( fd, & max_kids ) ; + delete [] times ; + times = new float [ max_kids ] ; + _ssgReadFloat ( fd, max_kids, times ) ; + _ssgReadInt ( fd, & curr ) ; + _ssgReadInt ( fd, & start ) ; + _ssgReadInt ( fd, & end ) ; + _ssgReadInt ( fd, (int *) & time_mode ) ; + + return ssgSelector::load(fd) ; +} + +int ssgTimedSelector::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, (int) running ) ; + _ssgWriteInt ( fd, (int) mode ) ; +// _ssgWriteFloat ( fd, start_time ) ; +// _ssgWriteFloat ( fd, pause_time ) ; +// _ssgWriteFloat ( fd, loop_time ) ; + _ssgWriteInt ( fd, max_kids ) ; + _ssgWriteFloat ( fd, max_kids, times ) ; + _ssgWriteInt ( fd, curr ) ; + _ssgWriteInt ( fd, start ) ; + _ssgWriteInt ( fd, end ) ; + _ssgWriteInt ( fd, (int) time_mode ) ; + + return ssgSelector::save(fd) ; +} + + diff --git a/src/ssg/ssgAxisTransform.cxx b/src/ssg/ssgAxisTransform.cxx new file mode 100644 index 0000000..c1c95bf --- /dev/null +++ b/src/ssg/ssgAxisTransform.cxx @@ -0,0 +1,128 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgAxisTransform.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgAxisTransform::copy_from ( ssgAxisTransform *src, int clone_flags ) +{ + ssgBaseTransform::copy_from ( src, clone_flags ) ; + + sgCopyVec3 ( rotation_axis , src->rotation_axis ) ; + sgCopyVec3 ( rotation_center, src->rotation_center ) ; + limit_low = src->limit_low ; + limit_high = src->limit_high ; +} + +ssgBase *ssgAxisTransform::clone ( int clone_flags ) +{ + ssgAxisTransform *b = new ssgAxisTransform ; + b -> copy_from ( this, clone_flags ) ; + return b; +} + +ssgAxisTransform::ssgAxisTransform (void) +{ + type = ssgTypeAxisTransform () ; + sgZeroVec3 ( rotation_axis ) ; + limit_low = limit_high = 0.f; +} + +ssgAxisTransform::ssgAxisTransform ( sgVec3 axis, sgVec3 center ) +{ + type = ssgTypeAxisTransform () ; + setAxis ( axis ); + setCenter ( center ); + limit_low = limit_high = 0.f; +} + +ssgAxisTransform::~ssgAxisTransform (void) +{ +} + +void ssgAxisTransform::setAxis ( sgVec3 axis ) +{ + sgCopyVec3 ( rotation_axis, axis ) ; +} + +float *ssgAxisTransform::getAxis (void) +{ + return rotation_axis; +} + +void ssgAxisTransform::setCenter ( sgVec3 center ) +{ + sgCopyVec3 ( rotation_center, center ) ; +} + +float *ssgAxisTransform::getCenter (void) +{ + return rotation_center; +} + +void ssgAxisTransform::setRotation ( float rot ) +{ + sgMat4 rot_mat ; + sgMat4 trans1, trans2; + sgVec3 neg_center; + + sgNegateVec3 ( neg_center, rotation_center ) ; + + sgMakeTransMat4 ( trans1 , neg_center ) ; + sgMakeTransMat4 ( trans2 , rotation_center ) ; + sgMakeRotMat4 ( rot_mat, rot, rotation_axis ) ; + sgPreMultMat4 ( rot_mat, trans1 ) ; + sgPostMultMat4 ( rot_mat, trans2 ) ; + + setTransform ( rot_mat ) ; +} + +void ssgAxisTransform::setRotationLimits ( float low, float high ) +{ + limit_low = low ; + limit_high = high ; +} + +void ssgAxisTransform::setLinearRotation ( float rot ) +{ + assert( rot >= 0.0f && rot <= 1.0f ); + setRotation( limit_low + (limit_high - limit_low) * rot ) ; +} + +int ssgAxisTransform::load ( FILE *fd ) +{ + _ssgReadFloat ( fd, 3, rotation_axis ) ; + _ssgReadFloat ( fd, 3, rotation_center ) ; + _ssgReadFloat ( fd, & limit_low ) ; + _ssgReadFloat ( fd, & limit_high ) ; + return ssgTransform::load(fd) ; +} + +int ssgAxisTransform::save ( FILE *fd ) +{ + _ssgWriteFloat ( fd, 3, rotation_axis ) ; + _ssgWriteFloat ( fd, 3, rotation_center ) ; + _ssgWriteFloat ( fd, limit_low ) ; + _ssgWriteFloat ( fd, limit_high ) ; + return ssgTransform::save(fd) ; +} diff --git a/src/ssg/ssgBase.cxx b/src/ssg/ssgBase.cxx new file mode 100644 index 0000000..aac54cc --- /dev/null +++ b/src/ssg/ssgBase.cxx @@ -0,0 +1,138 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgBase.cxx 1673 2002-09-15 01:29:12Z ude $ +*/ + + +#include "ssgLocal.h" + +static int next_unique_id = 1 ; + +void *ssgBase::operator new ( size_t sz ) +{ + return malloc ( sz ) ; +} + +void ssgBase::operator delete ( void *ptr ) +{ + free ( ptr ) ; +} + +void ssgBase::copy_from ( ssgBase *src, int clone_flags ) +{ + if ( this == src ) + return ; /* Avoid corrupting userdata when copying to self */ + + // type = src -> getType () ; - cannot change the virtual function table... + spare = src -> getSpare () ; + // refc = 0 ; - okay? + + if ( clone_flags & SSG_CLONE_USERDATA ) + setUserData ( src -> getUserData () ) ; + else + setUserData ( NULL ) ; + + setName ( src -> getName () ) ; +} + + + +ssgBase *ssgBase::clone ( int /* clone_flags */ ) +{ + ulSetError ( UL_FATAL, "SSG: Can't clone abstract SSG class objects" ) ; + return NULL ; +} + +ssgBase:: ssgBase (void) +{ + spare = refc = 0 ; + type = ssgTypeBase () ; + unique = next_unique_id++ ; + user_data = NULL ; + name = NULL ; +} + +ssgBase::~ssgBase (void) +{ + ssgDeRefDelete ( user_data ) ; + + deadBeefCheck () ; + assert ( refc == 0 ) ; + + delete [] name ; + + /* + Set the type of deleted nodes to 0xDeadBeef so we'll + stand a chance of detecting re-use of deleted nodes. + */ + + type = (int) 0xDeadBeef ; +} + + +void ssgBase::setName ( const char *nm ) +{ + delete [] name ; + + if ( nm == NULL ) + name = NULL ; + else + name = ulStrDup ( nm ) ; +} + +void ssgBase::zeroSpareRecursive (){ zeroSpare () ; } +void ssgBase::zeroSpare () { spare = 0 ; } +void ssgBase::incSpare () { spare++ ; } +void ssgBase::setSpare ( int ss ) { spare = ss ; } +int ssgBase::getSpare () { return spare ; } + + +void ssgBase::print ( FILE *fd, char *indent, int how_much ) +{ + if ( how_much > 2 ) + { + fprintf ( fd, "%s%s: Ref Count=%d\n", indent, getTypeName(), getRef () ) ; + fprintf ( fd, "%s Name = \"%s\"\n", indent, getPrintableName() ) ; + } + else + fprintf ( fd, "%s%s: Name=%s\n", indent, getTypeName(), getPrintableName()); + + if ( how_much > 1 ) + fprintf ( fd, "%s Userdata = %p\n", indent, getUserData() ) ; + + deadBeefCheck () ; +} + +int ssgBase::load ( FILE *fd ) +{ + delete [] name ; + name = NULL ; + _ssgReadString ( fd, &name ) ; + return ! _ssgReadError () ; +} + +int ssgBase::save ( FILE *fd ) +{ + _ssgWriteString ( fd, name ) ; + return ! _ssgWriteError () ; +} + + diff --git a/src/ssg/ssgBaseTransform.cxx b/src/ssg/ssgBaseTransform.cxx new file mode 100644 index 0000000..d170ac9 --- /dev/null +++ b/src/ssg/ssgBaseTransform.cxx @@ -0,0 +1,79 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgBaseTransform.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgBaseTransform::copy_from ( ssgBaseTransform *src, int clone_flags ) +{ + ssgBranch::copy_from ( src, clone_flags ) ; + + src -> getTransform ( transform ) ; + src -> getLastTransform ( last_transform ) ; + last_updated = src -> last_updated ; + first_time = src -> first_time ; +} + + + +ssgBaseTransform::ssgBaseTransform (void) +{ + type = ssgTypeBaseTransform () ; + last_updated = -9999999 ; + first_time = TRUE ; + sgMakeIdentMat4 ( transform ) ; + sgMakeIdentMat4 ( last_transform ) ; +} + +ssgBaseTransform::~ssgBaseTransform (void) +{ +} + + +int ssgBaseTransform::load ( FILE *fd ) +{ + _ssgReadMat4 ( fd, transform ) ; + updateTransform () ; + first_time = TRUE ; + return ssgBranch::load(fd) ; +} + +int ssgBaseTransform::save ( FILE *fd ) +{ + _ssgWriteMat4 ( fd, transform ) ; + return ssgBranch::save(fd) ; +} + + +void ssgBaseTransform::print ( FILE *fd, char *indent, int how_much ) +{ + ssgBranch::print ( fd, indent, how_much ) ; + + if ( how_much >= 2 ) + for ( int row = 0 ; row < 4 ; row++ ) + fprintf ( fd, "%s Transform[%d]= %f,%f,%f,%f\n", indent, row, + transform[row][0], transform[row][1], + transform[row][2], transform[row][3] ) ; +} + + diff --git a/src/ssg/ssgBranch.cxx b/src/ssg/ssgBranch.cxx new file mode 100644 index 0000000..db3708b --- /dev/null +++ b/src/ssg/ssgBranch.cxx @@ -0,0 +1,631 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgBranch.cxx 1860 2004-02-16 20:51:12Z stromberg $ +*/ + + +#include "ssgLocal.h" + +void ssgBranch::copy_from ( ssgBranch *src, int clone_flags ) +{ + ssgEntity::copy_from ( src, clone_flags ) ; + + for ( int i = 0 ; i < src -> getNumKids () ; i++ ) + { + ssgEntity *k = src -> getKid ( i ) ; + + if ( k != NULL && ( clone_flags & SSG_CLONE_RECURSIVE ) ) + addKid ( (ssgEntity *)( k -> clone ( clone_flags )) ) ; + else + addKid ( k ) ; + } +} + +ssgBase *ssgBranch::clone ( int clone_flags ) +{ + ssgBranch *b = new ssgBranch ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + + +ssgBranch::ssgBranch (void) +{ + type = ssgTypeBranch () ; +} + + +ssgBranch::~ssgBranch (void) +{ + removeAllKids () ; +} + + +void ssgBranch::zeroSpareRecursive () +{ + zeroSpare () ; + + for ( ssgEntity *k = getKid ( 0 ) ; k != NULL ; k = getNextKid () ) + k -> zeroSpareRecursive () ; +} + + +void ssgBranch::recalcBSphere (void) +{ + emptyBSphere () ; + + for ( ssgEntity *k = getKid ( 0 ) ; k != NULL ; k = getNextKid () ) + extendBSphere ( k -> getBSphere () ) ; + + bsphere_is_invalid = FALSE ; +} + + +void ssgBranch::addKid ( ssgEntity *entity ) +{ + kids. addEntity ( entity ) ; + entity -> addParent ( this ) ; + dirtyBSphere () ; +} + + +void ssgBranch::removeKid ( int n ) +{ + ssgEntity *k = kids.getEntity ( n ) ; + + if ( k != NULL ) { + k -> removeParent ( this ) ; + kids.removeEntity ( n ) ; + dirtyBSphere () ; + } +} + + +void ssgBranch::removeKid ( ssgEntity *entity ) +{ + removeKid( searchForKid( entity ) ); +} + + +void ssgBranch::removeAllKids (void) +{ + for ( int k = getNumKids() - 1; k >= 0; --k ) { + removeKid ( k ) ; + } +} + + +void ssgBranch::replaceKid ( int n, ssgEntity *new_entity ) +{ + if ( n >= 0 && n < getNumKids () ) + { + getKid ( n ) -> removeParent ( this ) ; + kids.replaceEntity ( n, new_entity ) ; + new_entity -> addParent ( this ) ; + dirtyBSphere () ; + } +} + +void ssgBranch::replaceKid ( ssgEntity *old_entity, ssgEntity *new_entity ) +{ + replaceKid ( searchForKid( old_entity ), new_entity ) ; +} + + +void ssgBranch::print ( FILE *fd, char *indent, int how_much ) +{ + ssgEntity::print ( fd, indent, how_much ) ; + fprintf ( fd, "%s Num Kids=%d\n", indent, getNumKids() ) ; + + if ( getNumParents() != getRef() ) + ulSetError ( UL_WARNING, "Ref count doesn't tally with parent count" ) ; + + if ( how_much > 1 ) + { if ( bsphere.isEmpty() ) + fprintf ( fd, "%s BSphere is Empty.\n", indent ) ; + else + fprintf ( fd, "%s BSphere R=%g, C=(%g,%g,%g)\n", indent, + bsphere.getRadius(), bsphere.getCenter()[0], bsphere.getCenter()[1], bsphere.getCenter()[2] ) ; + } + + char in [ 100 ] ; + sprintf ( in, "%s ", indent ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> print ( fd, in, how_much ) ; +} + + +void ssgBranch::getStats ( int *num_branches, int *num_leaves, int *num_tris, int *num_verts ) +{ + int nb, nl, nt, nv ; + + *num_branches = 1 ; /* this! */ + *num_leaves = 0 ; + *num_tris = 0 ; + *num_verts = 0 ; + + for ( int i = 0 ; i < getNumKids () ; i++ ) + { + ssgEntity *e = getKid ( i ) ; + + e -> getStats ( & nb, & nl, & nt, & nv ) ; + *num_branches += nb ; + *num_leaves += nl ; + *num_tris += nt ; + *num_verts += nv ; + } +} + + +ssgEntity *ssgBranch::getByName ( char *match ) +{ + if ( getName() != NULL && strcmp ( getName(), match ) == 0 ) + return this ; + + /* Otherwise check the kids for a match */ + + for ( ssgEntity* k = getKid ( 0 ) ; k != NULL ; k = getNextKid() ) + { + ssgEntity *e = k -> getByName ( match ) ; + + if ( e != NULL ) + return e ; + } + + return NULL ; +} + + +ssgEntity *ssgBranch::getByPath ( char *path ) +{ + /* Ignore leading '/' */ + + if ( *path == '/' ) + ++path ; + + char *n = getName () ; + + /* + If this node has no name then pass the request down the tree + */ + + if ( n == NULL ) + { + for ( ssgEntity* k = getKid ( 0 ) ; k != NULL ; k = getNextKid () ) + { + ssgEntity *e = k -> getByPath ( path ) ; + + if ( e != NULL ) + return e ; + } + + return NULL ; + } + + /* + If this node does have a name - but it doesn't match the + next part of the path then punt. + */ + + unsigned int l = strlen ( n ) ; + + if ( strlen ( path ) < l || strncmp ( n, path, l ) != 0 ) + return NULL ; + + /* + If the first part of the path is this ssgBranch, we + may have a winner. + */ + + char c = path [ l ] ; + + /* If we reached the end of the path - we win! */ + + if ( c == '\0' ) + return this ; + + if ( c == '/' ) + { + /* If the path continues, try to follow the path to the kids */ + + for ( ssgEntity* k = getKid ( 0 ) ; k != NULL ; k = getNextKid () ) + { + ssgEntity *e = k -> getByPath ( path + l ) ; + + if ( e != NULL ) + return e ; + } + } + + return NULL ; +} + + + +void ssgBranch::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> cull ( f, m, cull_result != SSG_INSIDE ) ; + + postTravTests ( SSGTRAV_CULL ) ; +} + + + +void ssgBranch::hot ( sgVec3 s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_HOT ) ) + return ; + + int hot_result = hot_test ( s, m, test_needed ) ; + + if ( hot_result == SSG_OUTSIDE ) + return ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> hot ( s, m, hot_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_HOT ) ; +} + + + +void ssgBranch::los ( sgVec3 s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_LOS ) ) + return ; + + int los_result = los_test ( s, m, test_needed ) ; + + if ( los_result == SSG_OUTSIDE ) + return ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> los ( s, m, los_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_LOS) ; +} + + +void ssgBranch::isect ( sgSphere *s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_ISECT ) ) + return ; + + int isect_result = isect_test ( s, m, test_needed ) ; + + if ( isect_result == SSG_OUTSIDE ) + return ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> isect ( s, m, isect_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_ISECT ) ; +} + + +int ssgBranch::load ( FILE *fd ) +{ + int nkids ; + + _ssgReadInt ( fd, & nkids ) ; + + if ( ! ssgEntity::load ( fd ) ) + return FALSE ; + + for ( int i = 0 ; i < nkids ; i++ ) + { + ssgEntity *kid ; + + if ( ! _ssgLoadObject ( fd, (ssgBase **) &kid, ssgTypeEntity () ) ) + return FALSE ; + + addKid ( kid ) ; + } + + return TRUE ; +} + + +int ssgBranch::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, getNumKids() ) ; + + if ( ! ssgEntity::save ( fd ) ) + return FALSE ; + + for ( int i = 0 ; i < getNumKids() ; i++ ) + { + if ( ! _ssgSaveObject ( fd, getKid ( i ) ) ) + return FALSE ; + } + + return TRUE ; +} + +// ----------------------- code by W.K for merging hierarchy nodes ------------------------------ + + +// #define VERBOSE +int maxTriangles = 100; // Thats a nice value for handling MDL files for BoB. + // You might want to assign another value, possibly -1 before calling mergeHNodes +int maxVertices = 195; // see maxTriangles + + + +static int noOfMergedNodes; // number of merged nodes + +void AddLeafToTriangles(ssgVtxArray *pSrc , ssgVtxArray *pDest) +// pDest must be of type GL_TRIANGLES +// So, this function adds the contents of a leaf to a GL_TRIANGLES node. +{ + // Add vertices and keep track of old to new indexes + if(pSrc->getNumTriangles()==0) return; // wk 14.7.2002 + int * aiOld2NewIndex = new int[pSrc->getNumVertices()]; + int iSrc; // iDest; + for(iSrc = 0; iSrc < pSrc->getNumVertices(); iSrc++) + { float * pfSrc = pSrc->getVertex(iSrc); + int bFound = FALSE; + /* test + for(iDest = 0; iDest < pDest->getNumVertices(); iDest++) + { float * pfDest = pDest->getVertex(iDest); +#define MYABS(x) ((x>0)?(x):(-(x))) + if (MYABS(pfSrc[0]-pfDest[0])+MYABS(pfSrc[1]-pfDest[1])+ + MYABS(pfSrc[2]-pfDest[2]) < 0.0001) + { bFound = TRUE; + aiOld2NewIndex[iSrc] = iDest; + } + }*/ + if(!bFound) + { aiOld2NewIndex[iSrc] = pDest->getNumVertices(); + pDest->vertices->add(pfSrc); + float * f = pSrc->getNormal(iSrc); + if (f) + pDest->normals->add(f); + else + { + pDest->normals->add(_ssgNormalUp); + } + f = pSrc->getTexCoord(iSrc); + if (f) + pDest->texcoords->add(f); + else + { + pDest->texcoords->add(_ssgTexCoord00); + } + // assert(pSrc->colours->getNum()==0); + } + + } + // Add faces + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(0)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(1)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(2)]); + if(pSrc->getGLtype()==GL_TRIANGLE_FAN) + { + for(iSrc=1;iSrcgetNumTriangles();iSrc++) + { // add 0, i+1, i+2 + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(0)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(iSrc+1)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(iSrc+2)]); + + } + } + else + { assert(pSrc->getGLtype()==GL_TRIANGLES); + for(iSrc=1;iSrcgetNumTriangles();iSrc++) + { // add 0, i+1, i+2 + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(iSrc*3)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(iSrc*3+1)]); + pDest->addIndex(aiOld2NewIndex[*pSrc->getIndex(iSrc*3+2)]); + + } + } + + // State + pDest->setState(pSrc ->getState()); + delete aiOld2NewIndex ; +} + + + +void recursiveMergeHNodes ( ssgEntity *root, int delta ) +// This is the recursive part of "mergeHNodes". +// Walks the scene graph and looks for nodes to merge. +// Uses AddLeafToTriangles for the actual merging +{ + if ( root == NULL ) + return ; + + if ( root -> isAKindOf ( ssgTypeBranch() ) ) + { // see whether we can merge some of the kids + ssgBranch *b = (ssgBranch *) root ; + + int i1, // loop var. + oldnk=b -> getNumKids (), oldi1=-1 ; // for debug purpose only + i1 = 0 ; + while(i1 < b -> getNumKids () ) + { + // check that each loop increases i1 or decreases b -> getNumKids () + if(!((i1>oldi1) || (oldnk>b ->getNumKids ()))) + break; + assert((i1>oldi1) || (oldnk>b ->getNumKids ())); + oldi1=i1; + oldnk=b ->getNumKids (); + + ssgEntity * pThis = b -> getKid ( i1 ) ; + if ( pThis -> isAKindOf ( ssgTypeBranch() ) ) + { recursiveMergeHNodes ( pThis, delta ); // recursively go deeper into the scene graph + i1++; + } + else if(( pThis -> isAKindOf ( ssgTypeLeaf() ) ) && (i1+delta < b -> getNumKids ())) + { ssgEntity * pOther = b -> getKid ( i1+delta ); + if( pOther -> isAKindOf ( ssgTypeLeaf() ) ) + { ssgLeaf *l1 = (ssgLeaf *)pThis, *l2 = (ssgLeaf *)pOther; + // now: l1 = kid at i1, l2 = kid at i1+delta + if ( (l1->getState() == l2->getState()) && + ((maxTriangles < 0) || (l1->getNumTriangles()+l2->getNumTriangles()getNumVertices()+l2->getNumVertices()getPrintableName(), l2->getPrintableName())) + ) + { + if (((l1->getGLtype() == GL_TRIANGLE_FAN) || (l1->getGLtype() == GL_TRIANGLES)) && + ((l2->getGLtype() == GL_TRIANGLE_FAN) || (l2->getGLtype() == GL_TRIANGLES))) + { // For using MDL files, these are all the types that we need ... + + if (l1->isA(ssgTypeVtxTable())) + { l1 = ((ssgVtxTable *)l1)->getAs_ssgVtxArray(); // Kludge: mem leak + b->replaceKid ( i1, l1 ) ; + } + if (l2->isA(ssgTypeVtxTable())) + { l2 = ((ssgVtxTable *)l2)->getAs_ssgVtxArray(); // Kludge: mem leak + b->replaceKid ( i1+delta, l2 ) ; + } + assert(l1 -> isAKindOf ( ssgTypeVtxArray() )); + assert(l2 -> isAKindOf ( ssgTypeVtxArray() )); + if((l1->getNumTriangles()+l2->getNumTriangles())<1300) // ??? wk kludge + { +#ifdef VERBOSE + printf("I do sthg :-) %d\n", noOfMergedNodes++); +#else + noOfMergedNodes++; +#endif + if(l1->getGLtype() == GL_TRIANGLE_FAN) // convert to GL_TRIANGLES + { ssgVtxArray * pNewArray = new ssgVtxArray(GL_TRIANGLES, + new ssgVertexArray(), + new ssgNormalArray(), new ssgTexCoordArray(), + new ssgColourArray(), new ssgIndexArray()); + AddLeafToTriangles((ssgVtxArray *)l1, pNewArray); + pNewArray->setName(l1->getPrintableName()); + assert(l1==b->getKid(i1)); + b->removeKid(i1); + b->addKid(pNewArray); + pNewArray->dirtyBSphere (); + l1 = pNewArray; + } + // l1 is of type GL_TRIANGLES now. + { //ssgEntity *t= b->getKid(i1+delta); + //assert(l2==t); + } + AddLeafToTriangles((ssgVtxArray *)l2, (ssgVtxArray *)l1); + l1->dirtyBSphere (); + l2->dirtyBSphere (); // unnessesary ? + b->removeKid(l2); // kid(i1+delta) changed because of remove!?! i1+delta); + } + } + else + { i1++; + printf("wrong types: %ld, %ld, num Trias: %ld, %ld\n", + (long)l1->getGLtype(), (long)l1->getGLtype(), + (long)l1->getNumTriangles(), (long)l2->getNumTriangles()); + } + } + else + i1++; + } + else + i1++; + } + else + i1++; + } + + + +/* + int i1=0; + for ( i = 0 ; i < b -> getNumKids () ; i++ ) + { ssgEntity * pThis = b -> getKid ( i1 ) ; + if ( pThis -> isAKindOf ( ssgTypeBranch() ) ) + { recursiveMergeHNodes ( pThis ); + i1++; + } + else + { assert( pThis -> isAKindOf ( ssgTypeLeaf() ) ); + ssgLeaf *l1 = (ssgLeaf *)pThis; + assert(l1 -> isAKindOf ( ssgTypeVtxArray() )); + static int noOfMergedNodes=0; + printf("I could do sthg :-) %d\n", noOfMergedNodes++); + assert(l1->getGLtype() == GL_TRIANGLE_FAN); // convert to GL_TRIANGLES + ssgVtxArray * pNewArray = new ssgVtxArray(GL_TRIANGLES, + new ssgVertexArray(), new ssgNormalArray(), + new ssgTexCoordArray(), new ssgColourArray(), + new ssgIndexArray()); + AddLeafToTriangles((ssgVtxArray *)l1, pNewArray); + b->removeKid(i1); + pNewArray->dirtyBSphere (); + b->addKid(pNewArray); + + pNewArray->setName("meins!"); + } + + } + */ + } +} + + +void ssgBranch::mergeHNodes() +// merges hierarchy nodes if (not iff :-/): +// a) Both have the same state +// b) the same name +// c) the result would not have more than maxTriangles triangles (if maxTriangles is negative, this is not checked). +// d) the result would not have more than maxVertices vertices (if maxVertices is negative, this is not checked). +// e) both are of type GL_TRIANGLE_FAN or GL_TRIANGLES +{ + + +int deltas[] = { 1 ,2 ,1 ,3 ,2 ,1 ,2 ,4 ,2 ,1 ,5 ,1 ,2 ,6 ,2 ,1 ,2 ,7 ,2 ,1 ,2 ,1 ,8 ,1 ,2 ,1 ,2 ,9 ,2 ,1 ,2 ,1, + 10 ,1 ,2 ,1 ,2 ,11 ,1 ,12 ,1 ,2 ,13 ,1 ,2 ,14 ,2 ,1 ,1 ,15 ,1 ,16 ,1 ,17 ,2 ,1 ,18 ,1 ,22 ,2 ,25 , + 2 ,1 ,30 ,2 ,1 ,2 ,1 ,13 ,2 ,1 ,2 ,1 }; + noOfMergedNodes = 0; + for(unsigned i=0; i < sizeof(deltas)/sizeof(int); i++) + { + recursiveMergeHNodes ( this, deltas[i] ); +#ifdef VERBOSE + printf("############################################### ^%d\n". deltas[i]); +#endif + } + printf("%d nodes were merged!\n", noOfMergedNodes); +} + diff --git a/src/ssg/ssgContext.cxx b/src/ssg/ssgContext.cxx new file mode 100644 index 0000000..4d05bea --- /dev/null +++ b/src/ssg/ssgContext.cxx @@ -0,0 +1,292 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgContext.cxx 1736 2002-12-02 15:04:06Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +ssgContext *_ssgCurrentContext = NULL ; + +ssgContext::~ssgContext () +{ + if ( isCurrent() ) + _ssgCurrentContext = NULL ; + + //~~ T.G. was ordinary delete before - must be derefed + ssgDeRefDelete ( currentState ); + ssgDeRefDelete ( basicState ); + + delete frustum ; +} + + +ssgContext::ssgContext () +{ + makeCurrent () ; + currentState = NULL ; + basicState = NULL ; + //orthographic = FALSE ; + cullFace = TRUE ; + ovTexture = FALSE ; + ovCullface = FALSE ; + ovState = NULL ; + sgCopyMat4 ( cameraMatrix, _ssgOpenGLAxisSwapMatrix ) ; + + frustum = new sgFrustum ; + frustum -> setNearFar ( 1.0, 10000.0 ) ; + frustum -> setFOV ( 60.0, 45.0 ) ; + + currentState = new ssgSimpleState ( 1 ) ; + currentState -> ref(); //~~ T.G. + + basicState = new ssgSimpleState ( 0 ) ; + basicState -> ref(); //~~ T.G. + + currentState -> force () ; + + /* The order of the two following lines is essential. + setTexture(NULL) currently sets the TEXTURE bit in + dont_care... not the desired effect here. /PL */ + + basicState->setTexture ( (ssgTexture*) NULL ) ; + basicState->dont_care = 0 ; + basicState->colour_material_mode = GL_AMBIENT_AND_DIFFUSE ; + sgSetVec4(basicState->specular_colour,1.0f,1.0f,1.0f,1.0f); + sgSetVec4(basicState->emission_colour,0.0f,0.0f,0.0f,1.0f); + sgSetVec4(basicState->ambient_colour, 1.0f,1.0f,1.0f,1.0f); + sgSetVec4(basicState->diffuse_colour, 1.0f,1.0f,1.0f,1.0f); + basicState->shade_model = GL_SMOOTH ; + basicState->shininess = 0.0f ; + basicState->alpha_clamp = 0.01f ; + + for ( int i = 0 ; i < 6 ; i++ ) + sgSetVec4 ( clipPlane[i], 0, 0, 1, 0 ) ; + + enabledClipPlanes = 0 ; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + + +int ssgContext::isCurrent () +{ + return _ssgCurrentContext == this ; +} + +void ssgContext::forceBasicState () +{ + if ( ! ovCullface ) + glEnable ( GL_CULL_FACE ) ; + + cullFace = TRUE ; + + if ( ovState != NULL ) + ovState -> force () ; + else + basicState -> force () ; +} + + +void ssgContext::makeCurrent () +{ + _ssgCurrentContext = this ; +} + +void ssgContext::getCameraPosition ( sgVec3 pos ) +{ + sgCopyVec3 ( pos, cameraMatrix [ 3 ] ) ; +} + +void ssgContext::overrideState ( ssgState *s ) +{ + ovState = s ; +} + +void ssgContext::overrideTexture ( int on_off ) +{ + ovTexture = on_off ; +} + +void ssgContext::overrideCullface ( int on_off ) +{ + ovCullface = on_off ; +} + + +void ssgContext::setFrustum ( float l, float r, float b, float t, float n, float f ) +{ + frustum -> setFrustum ( l, r, b, t, n, f ) ; +} + +void ssgContext::setOrtho ( float l, float r, float b, float t, float n, float f ) +{ + frustum -> setOrtho ( l, r, b, t, n, f ) ; +} + +void ssgContext::getNearFar ( float *n, float *f ) +{ + frustum -> getNearFar ( n, f ) ; +} + +void ssgContext::getFOV ( float *w, float *h ) +{ + frustum -> getFOV ( w, h ) ; +} + +void ssgContext::setNearFar ( float n, float f ) +{ + frustum -> setNearFar ( n, f ) ; +} + +void ssgContext::getOrtho ( float *w, float *h ) +{ + frustum -> getOrtho ( w, h ) ; +} + +void ssgContext::setOrtho ( float w, float h ) +{ + frustum -> setOrtho ( w, h ) ; +} + +void ssgContext::setFOV ( float w, float h ) +{ + frustum -> setFOV ( w, h ) ; +} + + +void ssgContext::setCamera ( sgMat4 mat ) +{ + sgMat4 viewmat ; + + sgTransposeNegateMat4 ( viewmat, mat ) ; + + sgCopyMat4 ( cameraMatrix, _ssgOpenGLAxisSwapMatrix ) ; + sgPreMultMat4 ( cameraMatrix, viewmat ) ; +} + +void ssgContext::setCamera ( const sgCoord *coord ) +{ + sgMat4 viewmat, mat ; + + sgMakeCoordMat4 ( mat, coord ) ; + sgTransposeNegateMat4 ( viewmat, mat ) ; + + sgCopyMat4 ( cameraMatrix, _ssgOpenGLAxisSwapMatrix ) ; + sgPreMultMat4 ( cameraMatrix, viewmat ) ; +} + +void ssgContext::setCameraLookAt ( const sgVec3 eye, const sgVec3 center, const sgVec3 up ) +{ + sgMat4 mat ; + sgMakeLookAtMat4 ( mat, eye, center, up ) ; + + setCamera ( mat ) ; +} + +void ssgContext::setCameraLookAt ( const sgVec3 eye, const sgVec3 center ) +{ + sgVec3 up ; + sgSetVec3 ( up, 0.0f, 0.0f, 1.0f ) ; + + sgMat4 mat ; + sgMakeLookAtMat4 ( mat, eye, center, up ) ; + + setCamera ( mat ) ; +} + +void ssgContext::loadProjectionMatrix() +{ + glLoadIdentity () ; + pushProjectionMatrix () ; +} + +void ssgContext::pushProjectionMatrix () +{ + pushProjectionMatrix ( frustum ) ; +} + +void ssgContext::pushProjectionMatrix ( sgFrustum *f ) +{ + if ( f -> isOrtho() ) + glOrtho ( f -> getLeft() , f -> getRight(), + f -> getBot () , f -> getTop (), + f -> getNear() , f -> getFar () ) ; + else + glFrustum ( f -> getLeft() , f -> getRight(), + f -> getBot () , f -> getTop (), + f -> getNear() , f -> getFar () ) ; +} + +void ssgContext::getProjectionMatrix ( sgMat4 dst ) +{ + frustum -> getMat4 ( dst ) ; +} + +void ssgContext::getModelviewMatrix ( sgMat4 dst ) +{ + sgCopyMat4 ( dst, cameraMatrix ) ; +} + +void ssgContext::loadModelviewMatrix () +{ + glLoadMatrixf ( (float *) cameraMatrix ) ; +} + +void ssgContext::loadModelviewMatrix ( sgMat4 mat ) +{ + glLoadMatrixf ( (float *) mat ) ; +} + +void ssgContext::cull ( ssgBranch *r ) +{ + r -> cull ( frustum, cameraMatrix, TRUE ) ; +} + + +void ssgContext::removeClipPlanes () +{ + for ( int i = 0 ; i < 6 ; i++ ) + glDisable ( (GLenum)( GL_CLIP_PLANE0 + i) ) ; +} + +void ssgContext::applyClipPlanes () +{ + if ( enabledClipPlanes == 0 ) + return ; + + for ( int i = 0 ; i < 6 ; i++ ) + { + if ( enabledClipPlanes & (1< isPointRotate () ; +} + +ssgBase *ssgCutout::clone ( int clone_flags ) +{ + ssgCutout *b = new ssgCutout ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgCutout::ssgCutout (int pntrot) +{ + point_rotate = pntrot ; + type = ssgTypeCutout () ; +} + +ssgCutout::~ssgCutout () +{ +} + + +void ssgCutout::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + sgMat4 tmp ; + + if ( point_rotate ) + { + /* + Trash the current viewpoint transform and replace it + with one that only contains the OpenGL axis swap + kludge along with the current translation. + + This prevents the object from rotating relative to the + screen so that the modelled X/Z axis are always parallel + to the screen X/Y axes. + */ + + sgCopyMat4 ( tmp, _ssgOpenGLAxisSwapMatrix ) ; + sgCopyVec3 ( tmp[3], m[3] ) ; + } + else + { + sgCopyMat4 ( tmp, m ) ; + + /* + Figure out where the Z axis of the model ends up - that + isn't changed by the billboarding process. + + Next, figure out where the X axis should go - which + will be at right angles to the Z axis - and at right + angles to the view vector. A cross product achieves + that. + + Finally, figure out where the Y axis should go - which + will be at right angles to the new X and Z axes. A second + cross product sorts that out. + + Notice that the SSG coordinate system's Z axis is really + GL's Y axis. + */ + + sgVec3 x_axis ; + sgVec3 y_axis ; + sgVec3 z_axis ; + + sgSetVec3 ( y_axis, 0.0f, 0.0f, -1.0f ) ; + sgCopyVec3 ( z_axis, tmp[ 2 ] ) ; + + sgVectorProductVec3 ( x_axis, y_axis, z_axis ) ; + sgVectorProductVec3 ( y_axis, z_axis, x_axis ) ; + + sgNormaliseVec3 ( x_axis ) ; + sgNormaliseVec3 ( y_axis ) ; /* Optional if your cutout is flat */ + + /* + Now we know where we want the three axes to end up, + change the matrix to make it so. + */ + + sgCopyVec3 ( tmp[0], x_axis ) ; + sgCopyVec3 ( tmp[1], y_axis ) ; + } + + _ssgPushMatrix ( tmp ) ; + + glPushMatrix () ; + glLoadMatrixf ( (float *) tmp ) ; + ssgBranch::cull ( f, tmp, test_needed ) ; + glPopMatrix () ; + + _ssgPopMatrix () ; + + postTravTests ( SSGTRAV_CULL ) ; +} + + +void ssgCutout::hot ( sgVec3 s, sgMat4 m, int test_needed ) +{ + ssgBranch::hot ( s, m, test_needed ) ; +} + +void ssgCutout::los ( sgVec3 s, sgMat4 m, int test_needed ) +{ + ssgBranch::los ( s, m, test_needed ) ; +} + + +void ssgCutout::isect ( sgSphere *s, sgMat4 m, int test_needed ) +{ + ssgBranch::isect ( s, m, test_needed ) ; +} + + + +int ssgCutout::load ( FILE *fd ) +{ + _ssgReadInt ( fd, & point_rotate ) ; + + return ssgBranch::load(fd) ; +} + +int ssgCutout::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, point_rotate ) ; + + return ssgBranch::save(fd) ; +} + + + diff --git a/src/ssg/ssgDList.cxx b/src/ssg/ssgDList.cxx new file mode 100644 index 0000000..501c1de --- /dev/null +++ b/src/ssg/ssgDList.cxx @@ -0,0 +1,273 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgDList.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include "ssgLocal.h" + + +enum _ssgDListType +{ + SSG_DLIST_LEAF, + SSG_DLIST_SET_TWEEN_STATE, + SSG_DLIST_LOAD_MATRIX, + SSG_DLIST_PUSH_MATRIX, + SSG_DLIST_POP_MATRIX, + SSG_DLIST_LOAD_TEX_MATRIX, + SSG_DLIST_UNLOAD_TEX_MATRIX, + SSG_DLIST_NOTHING +} ; + +class _ssgDList +{ +public: + _ssgDListType type ; + sgMat4 mat ; + ssgLeaf *leaf ; + float tween_state ; + int tween_mode ; + + _ssgDList () { setEmpty () ; } + + void setTweenState ( float _state, int _mode ) + { + type = SSG_DLIST_SET_TWEEN_STATE ; + tween_state = _state ; + tween_mode = _mode ; + } + + void setPopMatrix () + { + type = SSG_DLIST_POP_MATRIX ; + } + + void setLoadTexMatrix ( sgMat4 m ) + { + sgCopyMat4 ( mat, m ) ; + type = SSG_DLIST_LOAD_TEX_MATRIX ; + } + + void setUnloadTexMatrix () + { + type = SSG_DLIST_UNLOAD_TEX_MATRIX ; + } + + void setLoadMatrix ( sgMat4 m ) + { + sgCopyMat4 ( mat, m ) ; + type = SSG_DLIST_LOAD_MATRIX ; + } + + void setPushMatrix ( sgMat4 m ) + { + sgCopyMat4 ( mat, m ) ; + type = SSG_DLIST_PUSH_MATRIX ; + } + + void setDrawLeaf ( ssgLeaf *l ) + { + leaf = l ; + type = SSG_DLIST_LEAF ; + } + + void setEmpty () + { + type = SSG_DLIST_NOTHING ; + } + + void draw () + { + switch ( type ) + { + case SSG_DLIST_LEAF : + leaf -> draw () ; + break ; + + case SSG_DLIST_SET_TWEEN_STATE : + _ssgSetRealCurrentTweenSettings ( tween_state, tween_mode ) ; + break ; + + case SSG_DLIST_POP_MATRIX : + glPopMatrix () ; + break ; + + case SSG_DLIST_LOAD_TEX_MATRIX : + glMatrixMode ( GL_TEXTURE ) ; + glLoadMatrixf ( (float *) mat ) ; + glMatrixMode ( GL_MODELVIEW ) ; + break ; + + case SSG_DLIST_UNLOAD_TEX_MATRIX : + glMatrixMode ( GL_TEXTURE ) ; + glLoadIdentity () ; + glMatrixMode ( GL_MODELVIEW ) ; + break ; + + case SSG_DLIST_LOAD_MATRIX : + glLoadMatrixf ( (float *) mat ) ; + break ; + + case SSG_DLIST_PUSH_MATRIX : + glPushMatrix () ; + glLoadMatrixf ( (float *) mat ) ; + break ; + + default: break ; + } + + setEmpty () ; + } +} ; + + +#define MAX_DLIST 8192 +//4096 +//2048 + +static int next_dlist = 0 ; +static _ssgDList dlist [ MAX_DLIST ] ; + +void _ssgDrawDList () +{ + for ( int i = 0 ; i < next_dlist ; i++ ) + dlist [ i ] . draw () ; + + next_dlist = 0 ; +} + +void _ssgPushMatrix ( sgMat4 m ) +{ + /* + There is no point in having a pop/push sequence, + so optimise it to a simple load. + */ + + if ( next_dlist > 0 && + dlist [ next_dlist - 1 ] . type == SSG_DLIST_POP_MATRIX ) + { + next_dlist-- ; + _ssgLoadMatrix ( m ) ; + } + else + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setPushMatrix ( m ) ; +} + + +void _ssgSetTweenState ( float state, int mode ) +{ + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setTweenState ( state, mode ) ; +} + + +void _ssgPopMatrix () +{ + /* + There is no point in having a push/pop sequence, + so optimise it to a single load. + */ + + if ( next_dlist > 0 && + dlist [ next_dlist - 1 ] . type == SSG_DLIST_PUSH_MATRIX ) + next_dlist-- ; + else + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setPopMatrix () ; +} + + +void _ssgLoadTexMatrix ( sgMat4 m ) +{ + /* + There is no point in having a unload/load or a load/load sequence, + so optimise it to a single load. + */ + + while ( next_dlist > 0 && + ( dlist [ next_dlist - 1 ] . type == SSG_DLIST_LOAD_TEX_MATRIX || + dlist [ next_dlist - 1 ] . type == SSG_DLIST_UNLOAD_TEX_MATRIX ) + ) + next_dlist-- ; + + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setLoadTexMatrix ( m ) ; +} + + + +void _ssgUnloadTexMatrix () +{ + /* + There is no point in having a unload/unload or a load/unload sequence, + so optimise it to a single unload. + */ + + while ( next_dlist > 0 && + ( dlist [ next_dlist - 1 ] . type == SSG_DLIST_LOAD_TEX_MATRIX || + dlist [ next_dlist - 1 ] . type == SSG_DLIST_UNLOAD_TEX_MATRIX ) + ) + next_dlist-- ; + + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setUnloadTexMatrix () ; +} + + + +void _ssgLoadMatrix ( sgMat4 m ) +{ + /* + There is no point in having a load/load sequence, + so optimise it to a single load. + */ + + while ( next_dlist > 0 && + dlist [ next_dlist - 1 ] . type == SSG_DLIST_LOAD_MATRIX ) + next_dlist-- ; + + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setLoadMatrix ( m ) ; +} + + + +void _ssgDrawLeaf ( ssgLeaf *l ) +{ + if ( next_dlist >= MAX_DLIST ) + ulSetError ( UL_WARNING, "DList stack overflow!" ) ; + else + dlist [ next_dlist++ ] . setDrawLeaf ( l ) ; +} + diff --git a/src/ssg/ssgEntity.cxx b/src/ssg/ssgEntity.cxx new file mode 100644 index 0000000..ba98332 --- /dev/null +++ b/src/ssg/ssgEntity.cxx @@ -0,0 +1,464 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgEntity.cxx 1954 2004-09-13 11:49:36Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +void ssgEntity::copy_from ( ssgEntity *src, int clone_flags ) +{ + ssgBase::copy_from ( src, clone_flags ) ; + + traversal_mask = src -> getTraversalMask () ; + + setTravCallback( SSG_CALLBACK_PRETRAV, src->getTravCallback( SSG_CALLBACK_PRETRAV ) ); + setTravCallback( SSG_CALLBACK_POSTTRAV, src->getTravCallback( SSG_CALLBACK_POSTTRAV ) ); + + dirtyBSphere () ; +} + + +ssgEntity::ssgEntity (void) +{ + traversal_mask = 0xFFFFFFFF ; + type = ssgTypeEntity () ; + bsphere_is_invalid = TRUE ; + + preTravCB = NULL ; + postTravCB = NULL ; +} + + +ssgEntity::~ssgEntity (void) +{ +} + + +void ssgEntity::dirtyBSphere () +{ + if ( bsphere_is_invalid ) return ; + + bsphere_is_invalid = TRUE ; + + int np = getNumParents () ; + + for ( int i = 0 ; i < np ; i++ ) + getParent ( i ) -> dirtyBSphere () ; +} + +void ssgEntity::visualiseBSphere () +{ + if ( bsphere.isEmpty () ) + return ; + + glDisable ( GL_LIGHTING ) ; + + glTranslatef ( bsphere.getCenter()[0], + bsphere.getCenter()[1], + bsphere.getCenter()[2] ) ; + +#ifdef USE_GLUT_SPHERES + int spherebotch = (int)(this) % 9 ; + + switch ( spherebotch++ ) + { + case 0 : glutWireSphere(bsphere.getRadius(),10,10); break ; + case 1 : glutWireSphere(bsphere.getRadius(),11,10); break ; + case 2 : glutWireSphere(bsphere.getRadius(),12,10); break ; + case 3 : glutWireSphere(bsphere.getRadius(),10,11); break ; + case 4 : glutWireSphere(bsphere.getRadius(),11,11); break ; + case 5 : glutWireSphere(bsphere.getRadius(),12,11); break ; + case 6 : glutWireSphere(bsphere.getRadius(),10,12); break ; + case 7 : glutWireSphere(bsphere.getRadius(),11,12); break ; + case 8 : glutWireSphere(bsphere.getRadius(),12,12); spherebotch=0 ; break ; + } +#endif + + glTranslatef ( -bsphere.getCenter()[0], + -bsphere.getCenter()[1], + -bsphere.getCenter()[2] ) ; + + glEnable ( GL_LIGHTING ) ; +} + + +void ssgEntity::print ( FILE *fd, char *indent, int how_much ) +{ + ssgBase::print ( fd, indent, how_much ) ; + + if ( how_much > 1 ) + fprintf ( fd, "%s Num Parents=%d\n", indent, parents.getNumEntities () ) ; +} + + +/* + Get the entity whos name equals match (case sensitive) or 0 if there isn't + This will be overridden by descendant classes to search trees, etc. + This makes recovery of tagged parts of objects simple. + I envisage it being used to find axles, gimbals etc and to separate many objects + from a single loaded model file. The base version in ssgEntity returns the + ssgEntity if the name refers to this entity or 0 if not. +*/ + +ssgEntity* ssgEntity::getByName ( char *match ) +{ + return ( getName() != NULL && strcmp ( getName(), match ) == 0 ) ? + this : (ssgEntity *) NULL ; +} + + +/* + Get the entity specified by the path. A path is a series of names + separated by '/'. Each sub-unit is searched so long as the path matches. + A single leading '/' on the path is ignored. +*/ + +ssgEntity* ssgEntity::getByPath ( char *path ) +{ + /* ignore leading '/' */ + + if ( *path == '/' ) + ++path ; + + /* return this if the name is the path else NULL */ + + return ( getName() != NULL && strcmp ( getName(), path ) == 0 ) ? + this : (ssgEntity *) NULL ; +} + + +ssgCallback ssgEntity::getCallback ( int cb_type ) +{ + if ( isAKindOf ( ssgTypeLeaf() ) ) + return ((ssgLeaf*)this) -> getCallback ( cb_type ) ; + + /* + * Because of transparency sorting, having a pre/post draw callback + * doesn't make sense for anything but a leaf. + */ + + ulSetError ( UL_WARNING, "getCallback() ignored for non-leaf entity"); + + return NULL ; +} + + +void ssgEntity::setCallback ( int cb_type, ssgCallback cb ) +{ + if ( isAKindOf ( ssgTypeLeaf() ) ) + { + ((ssgLeaf*)this) -> setCallback ( cb_type, cb ) ; + } + else + { + /* + * Because of transparency sorting, having a pre/post draw callback + * doesn't make sense for anything but a leaf. + */ + + ulSetError ( UL_WARNING, "setCallback() ignored for non-leaf entity"); + } +} + + +int ssgEntity::preTravTests ( int *test_needed, int which ) +{ + if ( (getTraversalMask() & which) == 0 ) + { + if ( which & SSGTRAV_HOT ) + stats_hot_no_trav ++ ; + /* + if ( which & SSGTRAV_LOS ) + stats_hot_no_trav ++ ;*/ + return FALSE ; + } + + if ( preTravCB != NULL ) + { + int result = (*preTravCB)(this,which) ; + + if ( result == 0 ) return FALSE ; + if ( result == 2 ) *test_needed = 0 ; + } + + return TRUE ; +} + + +void ssgEntity::postTravTests ( int which ) +{ + if ( postTravCB != NULL ) + (*postTravCB)(this,which) ; +} + + +ssgCullResult ssgEntity::cull_test ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! test_needed ) + return SSG_INSIDE ; + + stats_cull_test++ ; + sgSphere tmp = *(getBSphere()) ; + + if ( tmp.isEmpty () ) + return SSG_OUTSIDE ; + + tmp . orthoXform ( m ) ; + + return (ssgCullResult) f -> contains ( &tmp ) ; +} + + +ssgCullResult ssgEntity::isect_test ( sgSphere *s, sgMat4 m, int test_needed ) +{ + if ( ! test_needed ) + return SSG_INSIDE ; + + stats_isect_test++ ; + sgSphere tmp = *(getBSphere()) ; + + if ( tmp.isEmpty () ) + return SSG_OUTSIDE ; + + tmp . orthoXform ( m ) ; + + /* Check axial distances for trivial reject */ + + sgVec3 center_vec ; + float sum_radii = s->getRadius() + tmp.getRadius() ; + + sgSubVec3 ( center_vec, s->getCenter(), tmp.getCenter() ) ; + + if ( sgAbs(center_vec[0]) > sum_radii || + sgAbs(center_vec[1]) > sum_radii || + sgAbs(center_vec[2]) > sum_radii ) + return SSG_OUTSIDE ; + + float separation_sqd = sgScalarProductVec3 ( center_vec, center_vec ) ; + + float tmp_radius_sqd = sgSquare ( tmp.getRadius() ) ; + float sph_radius_sqd = sgSquare ( s ->getRadius() ) ; + + float sum_radii_sqd = tmp_radius_sqd + sph_radius_sqd ; + + if ( separation_sqd >= sum_radii_sqd ) + return SSG_OUTSIDE ; + + if ( separation_sqd + tmp_radius_sqd <= sph_radius_sqd ) + return SSG_INSIDE ; + + return SSG_STRADDLE ; +} + + +/* + Places the addresses of the entities whose names or paths + are listed in bind into the variables whose addresses are + listed in bind. The bind array must terminate with a + NULL name. Returns TRUE if all names were found and + bound to variables. +*/ + +int ssgEntity::bindEntities ( ssgEntityBinding *bind ) +{ + int success = TRUE ; + + while ( bind -> nameOrPath != NULL ) + { + ssgEntity *e = NULL ; + + if ( strchr ( bind -> nameOrPath, '/' ) ) + e = getByPath ( bind -> nameOrPath ) ; + else + e = getByName ( bind -> nameOrPath ) ; + + if ( e != NULL ) + *( bind -> entity ) = e ; + else + success = FALSE ; + + ++bind ; + } + + return success ; +} + + +ssgCullResult ssgEntity::hot_test ( sgVec3 s, sgMat4 m, int test_needed ) +{ + /* Height-Off-Terrain Test */ + stats_hot_test++ ; + + if ( !test_needed ) + { + stats_hot_triv_accept++ ; + return SSG_INSIDE ; + } + + sgSphere tmp = *(getBSphere()) ; + + if ( tmp.isEmpty () ) + return SSG_OUTSIDE ; + + tmp . orthoXform ( m ) ; + + float d = sgSquare ( s[0] - tmp.getCenter()[0] ) + + sgSquare ( s[1] - tmp.getCenter()[1] ) ; + + if ( d > sgSquare ( tmp.getRadius() ) ) + { + stats_hot_radius_reject++ ; + return SSG_OUTSIDE ; + } + + stats_hot_straddle++ ; + return SSG_STRADDLE ; +} + + + +ssgCullResult ssgEntity::los_test ( sgVec3 s, sgMat4 m, int test_needed ) +{ + /* Line of Sight Test + * Determine whether a line of sight that starts at the origin and proceeds along the + * vector "s" intersects the B-sphere of this entity (I think that's right) + * Algorithm: (please bear with the vector calculus) + * Define the line of sight to be L = O + u S + * where "O" is the origin of the line of sight (always (0.0.0) for this application) + * "u" is a parametric coordinate which measures distance along the line of sight + * "S" is the direction vector, given by the variable "s" in this function + * Define the B-sphere as a sphere of radius "r" centered at a point "C". + * The vector from the center of the sphere to a point on the line is given by + * V = O + u S - C = ( O - C ) + u S + * The line of sight intersects the sphere if the shortest length of this vector is less + * than the radius of the sphere. We will work with the square of the length as it is + * easier and gives the same result. This distance-squared from the point on the line to + * the center of the sphere is given by the dot product of "V" with itself: + * d2 = V dot V = ( O - C ) dot ( O - C ) + 2 u * ( O - C ) dot S + u^2 * S dot S + * To get the shortest distance, differentiate "d2" with respect to "u" and set the + * derivative to zero: + * 2 ( O - C ) dot S + 2 u * S dot S = 0 + * u = - [ ( O - C ) dot S ] / [ S dot S ] + * For this value of "u", the distance squared becomes + * d2 = ( O - C ) dot ( O - C ) - [ ( O - C ) dot S ] ^2 / [ S dot S ] + * If d2 < radius of sphere squared, then the line of sight intersects the sphere. + */ + + stats_los_test++ ; + + if ( !test_needed ) + { + stats_los_triv_accept++ ; + return SSG_INSIDE ; + } + + sgSphere tmp = *(getBSphere()) ; + + /* If there is no B-sphere, the LOS is outside of it by definition */ + if ( tmp.isEmpty () ) + return SSG_OUTSIDE ; + + tmp . orthoXform ( m ) ; + + sgVec3 p1 ; /* End point at eye of Line-Of-Sight */ + + sgSetVec3 ( p1, 0.0, 0.0, 0.0 ) ; /* Eye point always at origin */ + + /* Get the center and square the radius of the sphere */ + sgFloat const *center = tmp.getCenter () ; + sgFloat radius2 = sgSquare ( tmp.getRadius () ) ; + + /* Calculate the distance from the line to the center of the sphere */ + sgVec3 oc ; + sgSubVec3 ( oc, p1, center ) ; + + /* Check whether the eyepoint is within the sphere */ + sgFloat eye_to_center_squared = sgSquare ( oc[0] ) + sgSquare ( oc[1] ) + sgSquare ( oc[2] ) ; + if ( eye_to_center_squared < radius2 ) + { + stats_los_straddle++ ; + return SSG_STRADDLE ; + } + + /* Calculate the dot product of the vector from the center to the eyepoint and the + * line of sight vector. If this dot product is positive or zero, then the point of + * closest approach to the sphere is behind the eyepoint or at the eyepoint. Since the + * eyepoint is outside the sphere, this means that the line of sight forward from the + * eyepoint will not intersect the sphere. + */ + sgFloat oc_dot_s = oc[0] * s[0] + oc[1] * s[1] + oc[2] * s[2] ; + if ( oc_dot_s >= 0.0 ) + { + stats_los_radius_reject++ ; + return SSG_OUTSIDE ; + } + + float dmin2 = eye_to_center_squared - sgSquare ( oc_dot_s ) / + ( sgSquare ( s[0] ) + sgSquare ( s[1] ) + sgSquare ( s[2] ) ) ; + + /* Compare minimum distance squared with sphere radius squared */ + if ( dmin2 > radius2 ) + { + stats_los_radius_reject++; + return SSG_OUTSIDE; + } + + stats_los_straddle++ ; + return SSG_STRADDLE ; +} + + +int ssgEntity::load ( FILE *fd ) +{ + dirtyBSphere(); + _ssgReadInt ( fd, &traversal_mask ) ; + return ssgBase::load(fd) ; +} + + + +int ssgEntity::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, traversal_mask ) ; + return ssgBase::save(fd) ; +} + + + +void ssgEntity::getNetTransform ( sgMat4 xform ) +{ + if ( getNumParents () > 0 ) + getParent ( 0 ) -> getNetTransform ( xform ) ; + else + sgMakeIdentMat4 ( xform ) ; +} + + +void ssgEntity::getLastNetTransform ( sgMat4 xform ) +{ + if ( getNumParents () > 0 ) + getParent ( 0 ) -> getLastNetTransform ( xform ) ; + else + sgMakeIdentMat4 ( xform ) ; +} + + diff --git a/src/ssg/ssgIO.cxx b/src/ssg/ssgIO.cxx new file mode 100644 index 0000000..680b199 --- /dev/null +++ b/src/ssg/ssgIO.cxx @@ -0,0 +1,244 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgIO.cxx 1673 2002-09-15 01:29:12Z ude $ +*/ + + +#include "ssgLocal.h" + + +static int read_error = FALSE ; +static int write_error = FALSE ; + + +int _ssgReadError (void) { return read_error ; } +int _ssgWriteError (void) { return write_error ; } + + +void _ssgReadFloat ( FILE *fd, float *var ) +{ + if ( fread ( var, sizeof(float), 1, fd ) == 1 ) return ; + read_error = TRUE ; +} + + +void _ssgWriteFloat ( FILE *fd, const float var ) +{ + if ( fwrite ( & var, sizeof(float), 1, fd ) == 1 ) return ; + write_error = TRUE ; +} + + +void _ssgReadUInt ( FILE *fd, unsigned int *var ) +{ + if ( fread ( var, sizeof(unsigned int), 1, fd ) == 1 ) return ; + read_error = TRUE ; +} + + +void _ssgWriteUInt ( FILE *fd, const unsigned int var ) +{ + if ( fwrite ( & var, sizeof(unsigned int), 1, fd ) == 1 ) return ; + write_error = TRUE ; +} + + +void _ssgReadInt ( FILE *fd, int *var ) +{ + if ( fread ( var, sizeof(int), 1, fd ) == 1 ) return ; + read_error = TRUE ; +} + + +void _ssgWriteInt ( FILE *fd, const int var ) +{ + if ( fwrite ( & var, sizeof(int), 1, fd ) == 1 ) return ; + write_error = TRUE ; +} + + +void _ssgReadUShort ( FILE *fd, unsigned short *var ) +{ + if ( fread ( var, sizeof(unsigned short), 1, fd ) == 1 ) return ; + read_error = TRUE ; +} + + +void _ssgWriteUShort ( FILE *fd, const unsigned short var ) +{ + if ( fwrite ( & var, sizeof(unsigned short), 1, fd ) == 1 ) return ; + write_error = TRUE ; +} + + +void _ssgReadShort ( FILE *fd, short *var ) +{ + if ( fread ( var, sizeof(short), 1, fd ) == 1 ) return ; + read_error = TRUE ; +} + + +void _ssgWriteShort ( FILE *fd, const short var ) +{ + if ( fwrite ( & var, sizeof(short), 1, fd ) == 1 ) return ; + write_error = TRUE ; +} + + +void _ssgReadFloat ( FILE *fd, const unsigned int n, float *var ) +{ + if ( fread ( var, sizeof(float), n, fd ) == n ) return ; + read_error = TRUE ; +} + + +void _ssgWriteFloat ( FILE *fd, const unsigned int n, const float *var ) +{ + if ( fwrite ( var, sizeof(float), n, fd ) == n ) return ; + write_error = TRUE ; +} + +void _ssgReadBytes ( FILE *fd, const unsigned int n, void *var ) +{ + if ( n == 0) + return; + if ( fread ( var, n, 1, fd ) == 1 ) + return ; + read_error = TRUE ; +} + +void _ssgWriteBytes ( FILE *fd, const unsigned int n, const void *var ) +{ + if ( n == 0) + return; + if ( fwrite ( var, n, 1, fd ) == 1 ) + return ; + write_error = TRUE ; +} + + +void _ssgReadUShort ( FILE *fd, const unsigned int n, unsigned short *var ) +{ + if ( fread ( var, sizeof(unsigned short), n, fd ) == n ) return ; + read_error = TRUE ; +} + + +void _ssgWriteUShort ( FILE *fd, const unsigned int n, const unsigned short *var ) +{ + if ( fwrite ( var, sizeof(unsigned short), n, fd ) == n ) return ; + write_error = TRUE ; +} + + + +void _ssgReadShort ( FILE *fd, const unsigned int n, short *var ) +{ + if ( fread ( var, sizeof(short), n, fd ) == n ) return ; + read_error = TRUE ; +} + + +void _ssgWriteShort ( FILE *fd, const unsigned int n, const short *var ) +{ + if ( fwrite ( var, sizeof(short), n, fd ) == n ) return ; + write_error = TRUE ; +} + + +void _ssgReadUInt ( FILE *fd, const unsigned int n, unsigned int *var ) +{ + if ( fread ( var, sizeof(unsigned int), n, fd ) == n ) return ; + read_error = TRUE ; +} + + +void _ssgWriteUInt ( FILE *fd, const unsigned int n, const unsigned int *var ) +{ + if ( fwrite ( var, sizeof(unsigned int), n, fd ) == n ) return ; + write_error = TRUE ; +} + + + +void _ssgReadInt ( FILE *fd, const unsigned int n, int *var ) +{ + if ( fread ( var, sizeof(int), n, fd ) == n ) return ; + read_error = TRUE ; +} + + +void _ssgWriteInt ( FILE *fd, const unsigned int n, const int *var ) +{ + if ( fwrite ( var, sizeof(int), n, fd ) == n ) return ; + write_error = TRUE ; +} + + + +#define MAX_ENTITY_NAME_LENGTH 1024 + +void _ssgReadString ( FILE *fd, char **var ) +{ + int i ; + char s [ MAX_ENTITY_NAME_LENGTH ] ; + + for ( i = 0 ; i < MAX_ENTITY_NAME_LENGTH ; i++ ) + { + int c = getc ( fd ) ; + s [ i ] = c ; + + if ( c == '\0' ) + break ; + } + + if ( i >= MAX_ENTITY_NAME_LENGTH-1 ) + s [ MAX_ENTITY_NAME_LENGTH-1 ] = '\0' ; + + + if ( s[0] == '\0' ) + *var = NULL ; + else + *var = ulStrDup ( s ) ; +} + + +void _ssgWriteString ( FILE *fd, const char *var ) +{ + if ( var != NULL ) + fputs ( var, fd ) ; + + putc ( '\0', fd ) ; +} + + +void _ssgReadVec2 ( FILE *fd, sgVec2 var ) { _ssgReadFloat ( fd, 2, var ) ; } +void _ssgWriteVec2 ( FILE *fd, const sgVec2 var ) { _ssgWriteFloat ( fd, 2, var ) ; } + +void _ssgReadVec3 ( FILE *fd, sgVec3 var ) { _ssgReadFloat ( fd, 3, var ) ; } +void _ssgWriteVec3 ( FILE *fd, const sgVec3 var ) { _ssgWriteFloat ( fd, 3, var ) ; } + +void _ssgReadVec4 ( FILE *fd, sgVec4 var ) { _ssgReadFloat ( fd, 4, var ) ; } +void _ssgWriteVec4 ( FILE *fd, const sgVec4 var ) { _ssgWriteFloat ( fd, 4, var ) ; } + +void _ssgReadMat4 ( FILE *fd, sgMat4 var ) { _ssgReadFloat ( fd, 16, (float *)var ) ; } +void _ssgWriteMat4 ( FILE *fd, const sgMat4 var ) { _ssgWriteFloat ( fd, 16, (float *)var ) ; } + diff --git a/src/ssg/ssgInvisible.cxx b/src/ssg/ssgInvisible.cxx new file mode 100644 index 0000000..8404f54 --- /dev/null +++ b/src/ssg/ssgInvisible.cxx @@ -0,0 +1,67 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgInvisible.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgInvisible::copy_from ( ssgInvisible *src, int clone_flags ) +{ + ssgBranch::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgInvisible::clone ( int clone_flags ) +{ + ssgInvisible *b = new ssgInvisible ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + + +ssgInvisible::ssgInvisible (void) +{ + type = ssgTypeInvisible () ; +} + + +ssgInvisible::~ssgInvisible (void) +{ +} + + +void ssgInvisible::cull ( sgFrustum *, sgMat4, int /* test_needed */ ) +{ +} + + + +int ssgInvisible::load ( FILE *fd ) +{ + return ssgBranch::load(fd) ; +} + +int ssgInvisible::save ( FILE *fd ) +{ + return ssgBranch::save(fd) ; +} + diff --git a/src/ssg/ssgIsect.cxx b/src/ssg/ssgIsect.cxx new file mode 100644 index 0000000..32b4b22 --- /dev/null +++ b/src/ssg/ssgIsect.cxx @@ -0,0 +1,107 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgIsect.cxx 1719 2002-10-26 19:00:56Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +#define MAX_HITS 100 +static ssgHit hitlist [ MAX_HITS ] ; +static int next_hit = 0 ; +static ssgEntity *pathlist [ SSG_MAXPATH ] ; +static int next_path = 0 ; + +int _ssgIsHotTest = FALSE ; +int _ssgIsLosTest = FALSE ; + +void _ssgPushPath ( ssgEntity *e ) +{ + if ( next_path + 1 >= SSG_MAXPATH ) + { + next_path++ ; /* So pop works! */ + return ; + } + + pathlist [ next_path++ ] = e ; +} + + +void _ssgPopPath () +{ + next_path-- ; +} + + +void _ssgAddHit ( ssgLeaf *l, int trinum, sgMat4 mat, sgVec4 pl ) +{ + if ( next_hit + 1 >= MAX_HITS ) + return ; + + ssgHit *h = & hitlist [ next_hit++ ] ; + + h -> leaf = l ; + h -> triangle = trinum ; + + h -> num_entries = (next_path>=SSG_MAXPATH) ? SSG_MAXPATH : next_path ; + memcpy ( h -> path, pathlist, h->num_entries * sizeof ( ssgEntity * ) ) ; + + sgCopyMat4 ( h -> matrix, mat ) ; + sgCopyVec4 ( h -> plane, pl ) ; +} + + +int ssgIsect ( ssgBranch *root, sgSphere *s, sgMat4 mat, ssgHit **results ) +{ + _ssgIsHotTest = FALSE ; + _ssgIsLosTest = FALSE ; + next_hit = 0 ; + next_path = 0 ; + root -> isect ( s, mat, TRUE ) ; + *results = & hitlist [ 0 ] ; + return next_hit ; +} + + +int ssgHOT ( ssgBranch *root, sgVec3 s, sgMat4 mat, ssgHit **results ) +{ + _ssgIsHotTest = TRUE ; + _ssgIsLosTest = FALSE ; + next_hit = 0 ; + next_path = 0 ; + root -> hot ( s, mat, TRUE ) ; + *results = & hitlist [ 0 ] ; + return next_hit ; +} + + +int ssgLOS ( ssgBranch *root, sgVec3 s, sgMat4 mat, ssgHit **results ) +{ + _ssgIsHotTest = FALSE ; + _ssgIsLosTest = TRUE ; + next_hit = 0 ; + next_path = 0 ; + root -> los ( s, mat, TRUE ) ; + *results = & hitlist [ 0 ] ; + return next_hit ; +} + + diff --git a/src/ssg/ssgKeyFlier.h b/src/ssg/ssgKeyFlier.h new file mode 100644 index 0000000..cf64a03 --- /dev/null +++ b/src/ssg/ssgKeyFlier.h @@ -0,0 +1,265 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgKeyFlier.h 1924 2004-04-06 13:32:26Z sjbaker $ +*/ + + +#ifndef _SSG_KEYFLIER_H_ +#define _SSG_KEYFLIER_H_ + +/***********************************************\ +* * +* SSG_KEYFLIER.H * +* * +* This file contains inline functions that * +* provide an ESIG-like keyboard-driven * +* flying carpet flight mode. * +* * +* Simply declare an instance of this class * +* somewhere in your program - and call the * +* 'incoming_keystroke' routine every time a * +* relevent character is typed - call the * +* 'update' function once per frame and the * +* 'get_coord' function whenever you need new * +* coordinates. Other handy functions are also * +* available. * +* * +* It's handy to use the 'Keyboard' class in * +* 'tty_port.h' to provide the incoming keys. * +* * +\***********************************************/ + +typedef void (*queryFunc)(float,float,float,float,float,float) ; +typedef void (*exitFunc)(int) ; + +class ssgKeyFlier +{ +protected: + float tscale ; + float rscale ; + sgCoord curr_pos ; + sgCoord reset_pos ; + sgCoord last_pos ; + sgCoord curr_vel ; + sgCoord velocity ; + + queryFunc query_action ; + exitFunc exit_action ; + +public: + ssgKeyFlier () + { + query_action = NULL ; + exit_action = NULL ; + + tscale = 1.0 ; + rscale = 1.0 ; + sgZeroVec3 ( reset_pos.xyz ) ; sgZeroVec3 ( reset_pos.hpr ) ; + sgZeroVec3 ( last_pos.xyz ) ; sgZeroVec3 ( last_pos.hpr ) ; + sgZeroVec3 ( velocity.xyz ) ; sgZeroVec3 ( velocity.hpr ) ; + update () ; + } + + void set_query_action ( queryFunc f ) { query_action = f ; } + void set_exit_action ( exitFunc f ) { exit_action = f ; } + + void stop_some () + { + velocity.xyz[0]=velocity.xyz[2]=0.0 ; + sgZeroVec3 ( velocity.hpr ) ; + update () ; + } + + void stop_all () + { + sgZeroVec3 ( velocity.xyz ) ; sgZeroVec3 ( velocity.hpr ) ; + update () ; + } + + void reset () + { + stop_all () ; + sgCopyVec3 ( last_pos.xyz, reset_pos.xyz ) ; + sgCopyVec3 ( last_pos.hpr, reset_pos.hpr ) ; + update () ; + } + + void get_scale_factors ( float *translation_scale_factor, float *rotation_scale_factor ) + { + if ( rotation_scale_factor != NULL ) + *rotation_scale_factor = rscale ; + + if ( translation_scale_factor != NULL ) + *translation_scale_factor = tscale ; + } + + /* Scale factors are in meters-per-update and degrees-per-update */ + + void set_scale_factors ( float translation_scale_factor, float rotation_scale_factor ) + { + rscale = rotation_scale_factor ; + tscale = translation_scale_factor ; + } + + + void set_xyz_scale ( float translation_scale_factor ) + { + tscale = translation_scale_factor ; + } + + + void set_hpr_scale ( float rotation_scale_factor ) + { + rscale = rotation_scale_factor ; + } + + + void set_reset ( sgCoord *pos ) + { + sgCopyCoord ( & reset_pos, pos ) ; + } + + sgCoord *get_velocity () + { + sgCopyCoord ( & curr_vel, & velocity ) ; + return & curr_vel ; + } + + void set_velocity ( sgCoord *vel ) + { + stop_all () ; + sgCopyCoord ( & velocity, vel ) ; + update () ; + } + + sgCoord *get_coord () + { + sgCopyCoord ( & curr_pos, & last_pos ) ; + return & curr_pos ; + } + + void set_coord ( sgCoord *pos, int noStop = 0 ) + { + if ( ! noStop ) stop_all () ; + sgCopyCoord ( & last_pos, pos ) ; + update () ; + } + + void show_help ( FILE *fd = stderr ) + { + fprintf ( fd, "ssgKeyFlier HELP.\n" ) ; + fprintf ( fd, "=================\n\n" ) ; + fprintf ( fd, "f : Faster - Make translational controls more sensitive.\n" ) ; + fprintf ( fd, "F : Faster - Make rotational controls more sensitive.\n" ) ; + fprintf ( fd, "s : Slower - Make translational controls less sensitive.\n" ) ; + fprintf ( fd, "S : Slower - Make rotational controls less sensitive.\n" ) ; + fprintf ( fd, "*,/: Same as 'f' and 's'.\n" ) ; + fprintf ( fd, "r/R: Reset - Reset eyepoint to pre-defined position.\n" ) ; + fprintf ( fd, "h/H: Help - Display this help message.\n" ) ; + fprintf ( fd, "^C : - Exit.\n" ) ; + fprintf ( fd, "? : - Where am I?\n" ) ; + fprintf ( fd, "5 : Stop rotation & slew 0 : Stop all movement.\n"); + fprintf ( fd, "2 : Pitch Up 8 : Pitch Down \n"); + fprintf ( fd, "4 : Yaw Left 6 : Yaw Right\n"); + fprintf ( fd, "7 : Roll Left 9 : Roll Right \n"); + fprintf ( fd, "1 : Translate Left. 3 : Translate Right\n"); + fprintf ( fd, "+/,: Translate Down - : Translate Up\n"); + fprintf ( fd, " . : Translate Backwards ENTER : Translate Forwards\n"); + fprintf ( fd, "[Note: Some of these controls may be overridden by the\n"); + fprintf ( fd, " application program.].\n"); + } + + void incoming_keystroke ( char k ) + { + switch ( k ) + { + case 'h' : + case 'H' : show_help () ; break ; + case '/' : + case 'f' : tscale *= 2.0 ; break ; + case 'F' : rscale *= 2.0 ; break ; + case '*' : + case 's' : tscale /= 2.0 ; break ; + case 'S' : rscale /= 2.0 ; break ; + + case 0x03: if ( exit_action ) exit_action( 0 ) ; exit ( 1 ) ; + + case '?' : if ( query_action ) + query_action ( last_pos.xyz[0], last_pos.xyz[1], last_pos.xyz[2], + last_pos.hpr[0], last_pos.hpr[1], last_pos.hpr[2] ) ; + else + ulSetError ( UL_DEBUG, "ssgKeyFlier: XYZ=(%g,%g,%g), HPR=(%g,%g,%g)", + last_pos.xyz[0], last_pos.xyz[1], last_pos.xyz[2], + last_pos.hpr[0], last_pos.hpr[1], last_pos.hpr[2] ) ; + + break ; + case 'r' : + case 'R' : reset () ; break ; + case '5' : stop_some () ; break ; + case '0' : stop_all () ; break ; + + case '1' : velocity.xyz[0] -= tscale ; break ; + case '3' : velocity.xyz[0] += tscale ; break ; + case '.' : velocity.xyz[1] -= tscale ; break ; + case '\n' : + case '\r' : velocity.xyz[1] += tscale ; break ; + case '+' : velocity.xyz[2] -= tscale ; break ; + case '-' : velocity.xyz[2] += tscale ; break ; + + case '6' : velocity.hpr[0] -= rscale ; break ; + case '4' : velocity.hpr[0] += rscale ; break ; +/* Kludge to try to prevent *exact* +/-90 degree pitch terms */ + case '8' : velocity.hpr[1] -= rscale+0.00001f ; break ; + case '2' : velocity.hpr[1] += rscale+0.00001f ; break ; + + case '7' : velocity.hpr[2] -= rscale ; break ; + case '9' : velocity.hpr[2] += rscale ; break ; + } + } + + int isStationary () + { + return ( velocity.hpr[0] == 0.0 && + velocity.hpr[1] == 0.0 && + velocity.hpr[2] == 0.0 && + velocity.xyz[0] == 0.0 && + velocity.xyz[1] == 0.0 && + velocity.xyz[2] == 0.0 ) ; + } + + virtual void update () + { + sgMat4 mat ; + sgMat4 result ; + sgMat4 delta ; + + /* Form new matrix */ + + sgMakeCoordMat4 ( delta, & velocity ) ; + sgMakeCoordMat4 ( mat , & last_pos ) ; + sgMultMat4 ( result, mat, delta ) ; + sgSetCoord ( &last_pos, result ) ; + } +} ; + + +#endif + diff --git a/src/ssg/ssgLeaf.cxx b/src/ssg/ssgLeaf.cxx new file mode 100644 index 0000000..7b28741 --- /dev/null +++ b/src/ssg/ssgLeaf.cxx @@ -0,0 +1,247 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLeaf.cxx 1952 2004-09-13 11:33:58Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +//~~ T.G. moved hee from header amd ref counting accounted for + +void ssgLeaf::setState ( ssgState *st ) +{ + ssgDeRefDelete ( state ) ; + + state = st ; + + if ( state != NULL ) + state->ref() ; +} + +void ssgLeaf::copy_from ( ssgLeaf *src, int clone_flags ) +{ + ssgEntity::copy_from ( src, clone_flags ) ; + + cull_face = src -> getCullFace () ; + + ssgState *s = src -> getState () ; + + //~~ T.G. Deref + ssgDeRefDelete(state); + + if ( s != NULL && ( clone_flags & SSG_CLONE_STATE ) ) + state = (ssgState *)( s -> clone ( clone_flags ) ) ; + else + state = s ; + + //~~ T.G. increment ref counter + if (state != NULL) + state->ref(); + + setCallback( SSG_CALLBACK_PREDRAW, src->getCallback( SSG_CALLBACK_PREDRAW ) ); + setCallback( SSG_CALLBACK_POSTDRAW, src->getCallback( SSG_CALLBACK_POSTDRAW ) ); +} + + + +ssgLeaf::ssgLeaf (void) +{ + cull_face = TRUE ; + state = NULL ; + type = ssgTypeLeaf () ; + +#ifdef _SSG_USE_DLIST + dlist = 0 ; +#endif + + preDrawCB = NULL ; + postDrawCB = NULL ; +} + +ssgLeaf::~ssgLeaf (void) +{ + //~~ T.G. ssgDeRefDelete checks for null case + ssgDeRefDelete ( state ) ; + +#ifdef _SSG_USE_DLIST + deleteDList () ; +#endif +} + + +#ifdef _SSG_USE_DLIST +void ssgLeaf::deleteDList () +{ + if ( dlist != 0 ) + glDeleteLists ( dlist, 1 ) ; + + dlist = 0 ; +} + +void ssgLeaf::makeDList () +{ + deleteDList () ; /* Just to be sure */ + dlist = glGenLists ( 1 ) ; + glNewList ( dlist, GL_COMPILE ) ; + draw_geometry () ; + glEndList () ; +} +#endif // #ifdef _SSG_USE_DLIST + + +void ssgLeaf::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + if ( isTranslucent () ) + _ssgDrawLeaf ( this ) ; + else + draw () ; +} + +void ssgLeaf::hot ( sgVec3 s, sgMat4 m, int test_needed ) +{ + int hot_result = hot_test ( s, m, test_needed ) ; + + if ( hot_result == SSG_OUTSIDE ) + return ; + + /* Add polygons to hit list! */ + + hot_triangles ( s, m, hot_result != SSG_INSIDE ) ; +} + +void ssgLeaf::los ( sgVec3 s, sgMat4 m, int test_needed ) +{ + int los_result = los_test ( s, m, test_needed ) ; + + if ( los_result == SSG_OUTSIDE ) + return ; + + /* Add polygons to hit list! */ + + los_triangles ( s, m, los_result != SSG_INSIDE ) ; +} + + + +void ssgLeaf::isect ( sgSphere *s, sgMat4 m, int test_needed ) +{ + int isect_result = isect_test ( s, m, test_needed ) ; + + if ( isect_result == SSG_OUTSIDE ) + return ; + + /* Add polygons to hit list! */ + + isect_triangles ( s, m, isect_result != SSG_INSIDE ) ; +} + + +void ssgLeaf::getStats ( int *num_branches, int *num_leaves, int *num_tris, int *num_verts ) +{ + *num_branches = 0 ; + *num_leaves = 1 ; /* this! */ + *num_tris = getNumTriangles () ; + *num_verts = getNumVertices () ; +} + + +void ssgLeaf::print ( FILE *fd, char *indent, int how_much ) +{ + if ( how_much == 0 ) + return ; + + ssgEntity::print ( fd, indent, how_much ) ; + + if ( getNumParents () != getRef () ) + fprintf ( fd, "****** WARNING: Ref count doesn't equal parent count!\n" ) ; + + if ( state != NULL ) + { + char in [ 100 ] ; + sprintf ( in, "%s ", indent ); + + if ( how_much == 1 ) + fprintf ( fd, "%s %s: %p\n", indent , state->getTypeName(), state) ; + else + state -> print ( fd, in, how_much ) ; + } + else + fprintf ( fd, "%s No State assigned to this node\n", indent ) ; +} + + +int ssgLeaf::preDraw () +{ + if ( preDrawCB != NULL && ! (*preDrawCB)(this) ) + return FALSE ; + + _ssgCurrentContext->setCullface ( getCullFace() ) ; + + return TRUE ; +} + + +int ssgLeaf::load ( FILE *fd ) +{ + _ssgReadInt ( fd, &cull_face ) ; + + ssgState *st; + + if ( ! _ssgLoadObject ( fd, (ssgBase **) &st, ssgTypeState () ) ) + return FALSE ; + + if ( st != NULL && st -> isAKindOf ( ssgTypeSimpleState () ) ) + { + ssgSimpleState *ss = (ssgSimpleState *) st ; + char *tfname = ss -> getTextureFilename () ; + + if ( tfname != NULL ) + { + ssgState *new_st = ssgGetCurrentOptions () -> createState ( tfname ) ; + + if ( new_st != NULL ) + st = new_st ; + } + } + + setState ( st ) ; + + return ssgEntity::load(fd) ; +} + + +int ssgLeaf::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, cull_face ) ; + + if ( ! _ssgSaveObject ( fd, getState () ) ) + return FALSE ; + + return ssgEntity::save(fd) ; +} + + + diff --git a/src/ssg/ssgList.cxx b/src/ssg/ssgList.cxx new file mode 100644 index 0000000..cbace21 --- /dev/null +++ b/src/ssg/ssgList.cxx @@ -0,0 +1,133 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgList.cxx 1949 2004-09-13 11:16:43Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +ssgList::ssgList ( int init ) +{ + total = 0 ; + next = 0 ; + entity_list = new ssgEntity * [ limit = (init <= 0) ? 1 : init ] ; +} + + +ssgKidList::ssgKidList ( int init ) : ssgList ( init ) +{ +} + + +ssgList::~ssgList (void) +{ + removeAllEntities () ; + delete [] entity_list ; +} + + +ssgKidList::~ssgKidList (void) +{ +} + + +void ssgList::addEntity ( ssgEntity *entity ) +{ +entity->deadBeefCheck() ; + sizeChk () ; + entity_list [ total++ ] = entity ; +} + +void ssgKidList::addEntity ( ssgEntity *entity ) +{ +entity->deadBeefCheck() ; + entity -> ref () ; + ssgList::addEntity ( entity ) ; +} + +void ssgList::sizeChk (void) +{ + /* Room for one more Entity? */ + + if ( total >= limit ) + { + limit += limit ; + ssgEntity **nlist = new ssgEntity * [ limit ] ; + memmove ( nlist, entity_list, sizeof(ssgEntity *) * total ) ; + delete [] entity_list ; + entity_list = nlist ; + } +} + + +int ssgList::searchForEntity ( ssgEntity *entity ) +{ + for ( unsigned int i = 0 ; i < total ; i++ ) + if ( entity_list [ i ] == entity ) + return (int) i ; + + return -1 ; +} + + +void ssgList::removeAllEntities () +{ + while ( total > 0 ) + removeEntity ( (unsigned int) total-1 ) ; +} + +void ssgList::removeEntity ( unsigned int n ) +{ + memmove ( &(entity_list[n]), &(entity_list[n+1]), sizeof(ssgEntity *) * (total-n-1) ) ; + total-- ; + + if ( next >= n ) + next-- ; +} + + +void ssgKidList::removeEntity ( unsigned int n ) +{ +entity_list[n]->deadBeefCheck(); + ssgEntity *e = entity_list [ n ] ; + + ssgList::removeEntity ( n ) ; + + e -> deadBeefCheck () ; + // wk 20.4.2002 problems with mywalk... when merging hiearchy nodes: + ssgDeRefDelete ( e ) ; +} + + +void ssgList::replaceEntity ( unsigned int n, ssgEntity *new_entity ) +{ + new_entity -> deadBeefCheck () ; + entity_list [ n ] -> deadBeefCheck () ; + entity_list [ n ] = new_entity; +} + +void ssgKidList::replaceEntity ( unsigned int n, ssgEntity *new_entity ) +{ + ssgEntity *old_entity = entity_list [ n ] ; + new_entity -> ref () ; + ssgList::replaceEntity ( n, new_entity ) ; + ssgDeRefDelete ( old_entity ) ; +} diff --git a/src/ssg/ssgLoad.cxx b/src/ssg/ssgLoad.cxx new file mode 100644 index 0000000..7608075 --- /dev/null +++ b/src/ssg/ssgLoad.cxx @@ -0,0 +1,260 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoad.cxx 1848 2004-01-24 18:25:45Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + + +static ssgLoaderOptions default_options ; + +ssgLoaderOptions *_ssgCurrentOptions = &default_options ; + + + +char* ssgLoaderOptions::make_path ( char* path, + const char* dir, const char* fname ) const +{ + if ( fname != NULL && fname [ 0 ] != '\0' ) + { + if ( ! ulIsAbsolutePathName ( fname ) && + dir != NULL && dir[0] != '\0' ) + { + strcpy ( path, dir ) ; + strcat ( path, "/" ) ; + strcat ( path, fname ) ; + } + else + strcpy ( path, fname ) ; + + //convert backward slashes to forward slashes + for ( char* ptr = path ; *ptr ; ptr ++ ) + { + if ( *ptr == '\\' ) + *ptr = '/' ; + } + } + else + path [0] = 0 ; + return( path ); +} + + +void ssgLoaderOptions::makeModelPath ( char *path, const char *fname ) const +{ + make_path ( path, model_dir, fname ) ; +} + + +void ssgLoaderOptions::makeTexturePath ( char *path, const char *fname ) const +{ + /* Remove all leading path information. */ + const char* seps = "\\/" ; + const char* fn = & fname [ strlen ( fname ) - 1 ] ; + for ( ; fn != fname && strchr(seps,*fn) == NULL ; fn-- ) + /* Search back for a seperator */ ; + if ( strchr(seps,*fn) != NULL ) + fn++ ; + fname = fn ; + + make_path ( path, texture_dir, fname ) ; +} + + +ssgLeaf* ssgLoaderOptions::createLeaf ( ssgLeaf* leaf, + const char* parent_name ) +{ + if ( leaf != NULL ) + { + /* try to do some state sharing */ + ssgState* st = leaf -> getState () ; + if ( st != NULL && st -> isA ( ssgTypeSimpleState () ) ) + { + ssgSimpleState *ss = (ssgSimpleState*) st ; + ssgSimpleState *match = shared_states.findMatch ( ss ) ; + if ( match != NULL ) + leaf -> setState ( match ) ; + else + shared_states.add ( ss ) ; + } + } + return leaf ; +} + + +ssgTexture* ssgLoaderOptions::createTexture ( char* tfname, + int wrapu, + int wrapv, + int mipmap ) +{ + + char filename [ 1024 ] ; + makeTexturePath ( filename, tfname ) ; + + ssgTexture *tex = shared_textures.findByFilename ( filename ) ; + if ( tex ) + return tex ; + + tex = new ssgTexture ( filename, wrapu, wrapv, mipmap ) ; + if ( tex ) + shared_textures.add ( tex ) ; + return tex ; +} + +ssgTransform* ssgLoaderOptions::createTransform ( ssgTransform* tr, + ssgTransformArray* ta ) const +{ + if ( ta != NULL ) + tr -> setUserData ( ta ) ; + return tr ; +} + +ssgSelector* ssgLoaderOptions::createSelector ( ssgSelector* s ) const +{ + return s ; +} + + +void ssgLoaderOptions::setModelDir ( const char *s ) +{ + delete [] model_dir ; + model_dir = ulStrDup ( s ) ; +} + +void ssgLoaderOptions::setTextureDir ( const char *s ) +{ + delete [] texture_dir ; + texture_dir = ulStrDup ( s ) ; +} + + +static const char *file_extension ( const char *fname ) +{ + const char *p = & ( fname [ strlen(fname) ] ) ; + + while ( p != fname && *p != '/' && *p != '.' ) + p-- ; + + return p ; +} + + +struct _ssgModelFormat +{ + const char *extension ; + ssgLoadFunc *loadfunc ; + ssgSaveFunc *savefunc ; +} ; + + +enum { MAX_FORMATS = 100 } ; + +static _ssgModelFormat formats [ MAX_FORMATS ] ; +static int num_formats = 0 ; + + +void ssgAddModelFormat ( const char* extension, + ssgLoadFunc *loadfunc , ssgSaveFunc *savefunc ) +{ + for ( int i = 0 ; i < num_formats ; i++ ) + { + if ( ulStrEqual ( formats [ i ] . extension, extension ) ) + { + formats [ i ] . extension = extension ; + formats [ i ] . loadfunc = loadfunc ; + formats [ i ] . savefunc = savefunc ; + return ; + } + } + + if ( num_formats < MAX_FORMATS ) + { + formats [ num_formats ] . extension = extension ; + formats [ num_formats ] . loadfunc = loadfunc ; + formats [ num_formats ] . savefunc = savefunc ; + num_formats ++ ; + } + else + { + ulSetError ( UL_WARNING, "ssgAddModelFormat: too many formats" ); + } +} + + + +ssgEntity *ssgLoad ( const char *fname, const ssgLoaderOptions* options ) +{ + if ( fname == NULL || *fname == '\0' ) + return NULL ; + + // find appropiate loader and call its loadfunc + const char *extn = file_extension ( fname ) ; + if ( *extn != '.' ) + { + ulSetError ( UL_WARNING, "ssgLoad: Cannot determine file type for '%s'", fname ); + return NULL ; + } + +extern int g_noLoDs; + g_noLoDs = 1; + + _ssgModelFormat *f = formats ; + for ( int i=0; iloadfunc != NULL && + ulStrEqual ( extn, f->extension ) ) + { + ssgEntity* entity = f -> loadfunc( fname, options ) ; + ssgGetCurrentOptions () -> endLoad () ; + return entity ; + } + } + + ulSetError ( UL_WARNING, "ssgLoad: Unrecognised file type '%s'", extn ) ; + return NULL ; +} + + +int ssgSave ( const char *fname, ssgEntity *ent ) +{ + if ( fname == NULL || ent == NULL || *fname == '\0' ) + return FALSE ; + + const char *extn = file_extension ( fname ) ; + + if ( *extn != '.' ) + { + ulSetError ( UL_WARNING, "ssgSave: Cannot determine file type for '%s'", fname ); + return FALSE ; + } + + _ssgModelFormat *f = formats ; + for ( int i=0; isavefunc != NULL && + ulStrEqual ( extn, f->extension ) ) + return f->savefunc( fname, ent ) ; + } + + ulSetError ( UL_WARNING, "ssgSave: Unrecognised file type '%s'", extn ) ; + return FALSE ; +} diff --git a/src/ssg/ssgLoad3ds.cxx b/src/ssg/ssgLoad3ds.cxx new file mode 100644 index 0000000..21ca8ff --- /dev/null +++ b/src/ssg/ssgLoad3ds.cxx @@ -0,0 +1,1315 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoad3ds.cxx 2110 2006-12-12 15:43:45Z fayjf $ +*/ + +/******************************************************* + ** ssgLoad3ds.cxx + ** + ** Written by Per Liedman (liedman@home.se) + ** Last updated: 2001-02-09 + ** + ** This was written to be a part of Stephen J Bakers + ** PLIB (http://plib.sourceforge.net) + *******************************************************/ + +/* KNOWN ISSUES: + + * Some models (one of the test cases) gets turned "inside out" - obviously + the triangle winding has been reversed so that all triangles that should be + culled are shown and vice versa. I don't know where this information can + be found in the file. + + * Models that uses double-sided materials sometimes look strange. My approach + to double sided materials is to add a copy of each triangle with the + winding reversed and also negate the normals...this seems to *almost* work. + + * The transformation matrix in the 3DS files is still a mystery to me - + whatever I do with it seems to make things worse than ignoring it, although + that doesn't work perfect either. I've taken a look at other loaders, and + it seems that no one really knows what to do with them. + +*/ + +#define USE_VTXARRAYS +const int maxFacesPerLeaf = 10922; // = 32768 / 3 + +#include "ssgLocal.h" +#include "ssg3ds.h" + +#define MAX_MATERIALS 512 + +#define PARSE_OK 1 +#define PARSE_ERROR 0 +#define CHUNK_HEADER_SIZE (2 + 4) + +#define IS_DOUBLESIDED 1 + +/* Define DEBUG if you want debug output + (this might be a nice way of looking at the + structure of a 3DS file). */ +//#define DEBUG 1 + + +#ifdef DEBUG +#define DEBUGPRINT(f, x, y, z) ulSetError(UL_DEBUG, f, debug_indent, x, y, z) +#else +#define DEBUGPRINT(f, x, y, z) +#endif + +#ifdef DEBUG +static char debug_indent[256]; +#endif + +/* this is the minimum value of the dot product for + to faces if their normals should be smoothed, if + they don't use smooth groups. */ +// Until we are able to route such param through the LoaderOptions mechanism, +// make it globally accessible +/*static const*/ float _ssg_smooth_threshold = 0.8f; + +// parsing functions for chunks that need separate treatment. +static int parse_material( unsigned int length); +static int parse_objblock( unsigned int length); +static int parse_rgb1( unsigned int length); +static int parse_rgb2( unsigned int length); +static int parse_material_name( unsigned int length); +static int parse_ambient( unsigned int length); +static int parse_diffuse( unsigned int length); +static int parse_specular( unsigned int length); +static int parse_shininess( unsigned int length); +static int parse_transparency( unsigned int length); +static int parse_doublesided( unsigned int length); +static int parse_vert_list( unsigned int length); +static int parse_face_list( unsigned int length); +static int parse_map_list( unsigned int length); +static int parse_tra_matrix( unsigned int length); +static int parse_trimesh( unsigned int length); +static int parse_smooth_list( unsigned int length); +static int parse_face_materials( unsigned int length); +static int parse_mapname( unsigned int length); +static int parse_mapoptions( unsigned int length); +static int parse_uscale( unsigned int length); +static int parse_vscale( unsigned int length); +static int parse_uoffst( unsigned int length); +static int parse_voffst( unsigned int length); +static int parse_oneunit( unsigned int length); +static int parse_version( unsigned int length); +static int parse_frame ( unsigned int length); +static int parse_frame_objname ( unsigned int length); +static int parse_frame_hierarchy( unsigned int length); +static int identify_face_materials( unsigned int length ); +static int parse_frame_dummyname( unsigned int length ); + +/* _ssg3dsChunk defines how a certain chunk is handled when encountered - + what subchunks it might have and what parse function should be used + for it. */ +struct _ssg3dsChunk { + unsigned short id; + _ssg3dsChunk *subchunks; + int (*parse_func) ( unsigned int ); +}; + +// following arrays define the structure of the chunks in the 3ds file. +static _ssg3dsChunk FaceListDataChunks[] = +{ { CHUNK_SMOOLIST, NULL, parse_smooth_list }, + { CHUNK_FACEMAT, NULL, identify_face_materials }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk FaceListChunks[] = +{ { CHUNK_FACEMAT, NULL, parse_face_materials }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk TriMeshDataChunks[] = +{ { CHUNK_VERTLIST, NULL, parse_vert_list }, + { CHUNK_MAPLIST, NULL, parse_map_list }, + { 0, NULL, NULL } +}; /* these chunks have to be known *before* we call parse_face_list + (see parse_trimesh for more info) */ + +static _ssg3dsChunk TriMeshChunks[] = +{ { CHUNK_FACELIST, FaceListChunks, parse_face_list }, + { CHUNK_TRMATRIX, NULL, parse_tra_matrix }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk ObjBlockChunks[] = +{ { CHUNK_TRIMESH, TriMeshChunks, parse_trimesh }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk TextureChunks[] = +{ { CHUNK_MAPFILENAME, NULL, parse_mapname }, + { CHUNK_MAP_USCALE, NULL, parse_uscale }, + { CHUNK_MAP_VSCALE, NULL, parse_vscale }, + { CHUNK_MAP_UOFFST, NULL, parse_uoffst }, + { CHUNK_MAP_VOFFST, NULL, parse_voffst }, + { CHUNK_MAPOPTIONS, NULL, parse_mapoptions }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk MaterialColourChunks[] = +{ { CHUNK_RGB1, NULL, parse_rgb1 }, + { CHUNK_RGB2, NULL, parse_rgb2 }, + { CHUNK_RGB3, NULL, parse_rgb2 }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk MaterialChunks[] = +{ { CHUNK_MATNAME, NULL, parse_material_name }, + { CHUNK_AMBIENT, MaterialColourChunks, parse_ambient }, + { CHUNK_DIFFUSE, MaterialColourChunks, parse_diffuse }, + { CHUNK_SPECULAR, MaterialColourChunks, parse_specular}, + { CHUNK_SHINE_STRENGTH, NULL, parse_shininess }, + { CHUNK_TRANSPARENCY, NULL, parse_transparency }, + { CHUNK_TEXTURE, TextureChunks, NULL }, + { CHUNK_DOUBLESIDED, NULL, parse_doublesided }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk ObjMeshChunks[] = +{ { CHUNK_MATERIAL, MaterialChunks, parse_material }, + { CHUNK_OBJBLOCK, ObjBlockChunks, parse_objblock }, + { CHUNK_ONEUNIT, NULL, parse_oneunit }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk FrameChunks[] = +{ { CHUNK_FRAME_OBJNAME , NULL, parse_frame_objname }, + { CHUNK_FRAME_HIERARCHY, NULL, parse_frame_hierarchy }, + { CHUNK_FRAME_DUMMYNAME, NULL, parse_frame_dummyname }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk KeyframerChunks[] = +{ { CHUNK_FRAMES , NULL , parse_frame }, + { CHUNK_KEYFRAME_MESH, FrameChunks, NULL }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk MainChunks[] = +{ { CHUNK_OBJMESH, ObjMeshChunks , NULL }, + { CHUNK_KEYFRAMER, KeyframerChunks, NULL }, + { CHUNK_VERSION, NULL, parse_version }, + { 0, NULL, NULL } +}; + +static _ssg3dsChunk TopChunk[] = +{ { CHUNK_MAIN, MainChunks, NULL }, + { 0, NULL, NULL } +}; + +/* The material properties are temporarily stored in this structure before + creating ssgSimpleStates out of them. */ +struct _3dsMat { + char *name ; + int flags; + sgVec3 colour[4]; + float shi, alpha; + + char *tex_name; + sgVec2 tex_scale, tex_offset; + bool wrap_s, wrap_t; +}; +/* + These are the indices used for identifying the materials colour-properties: +*/ +#define _3DSMAT_AMB 0 +#define _3DSMAT_DIF 1 +#define _3DSMAT_EMI 2 +#define _3DSMAT_SPE 3 + + +/* Some 3ds files does not have any materials defined. This material is + used in that case: */ +static _3dsMat default_material= { "ssgLoad3ds default material", + 0, + { { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, + 0.0f, 1.0f, + NULL, + {1.0f, 1.0f}, {0.0f, 0.0f}, + false, false }; + +/* A _ssg3dsStructureNode holds a mesh or a transformation node. All + geometry is collected into structure nodes before actually being assembled + into the scene-graph, since the hierarchy information is at the end of the + 3ds file. */ +struct _ssg3dsStructureNode { + _ssg3dsStructureNode() { + id = -1; + object = NULL; + has_been_used = false; + next = NULL; + } + + short id; + ssgBranch *object; + bool has_been_used; + _ssg3dsStructureNode *next; +}; + +static _ssg3dsStructureNode *findStructureNode( char *name ); +static _ssg3dsStructureNode *findStructureNode( short id ); + +static int parse_chunks( _ssg3dsChunk *chunk_list, unsigned int length); +static void add_leaf( _3dsMat *material, int listed_faces, + unsigned short *face_indices ); + +/* Each vertex in a mesh has a face list associated with it, which + contains all faces that use this vertex. This list is used when + smoothing normals, since the adjacent faces of each vertex is needed. */ +struct _ssg3dsFaceList { + int face_index; + _ssg3dsFaceList *next; +}; + +static _ssg3dsFaceList *addFaceListEntry( _ssg3dsFaceList *face_list, + int face_index ) { + _ssg3dsFaceList *new_entry = new _ssg3dsFaceList; + new_entry -> face_index = face_index; + new_entry -> next = face_list; + + return new_entry; +} + +static void freeFaceList( _ssg3dsFaceList *face_list ) { + for (_ssg3dsFaceList *i = face_list, *temp = NULL; i != NULL; i = temp) { + temp = i -> next; + delete i; + } +} + +// globals +static FILE *model; + +static int num_objects, num_materials, num_textures; +static int double_sided; // is there some double sided material? + +static ssgBranch *top_object, *current_branch; + +static ssgLoaderOptions* current_options = NULL ; + +static _3dsMat **materials, *current_material; + +static unsigned short *vertex_index, *normal_index, num_vertices, num_faces; +static unsigned int *smooth_list; +static _ssg3dsFaceList **face_lists; + +static ssgTransform *current_transform, *last_dummy_object; + +static sgVec3 *vertex_list; +static sgVec3 *face_normals, *vertex_normals; +static sgVec2 *texcrd_list; +static int smooth_found, facemat_found; + +static int colour_mode; + +static _ssg3dsStructureNode *object_list = NULL; +static short current_structure_id = -1; +static short dummy_id = 0; +static short last_id = 0; + +/* flag to control whether to use smooting groups found in the file + or not */ +// Until we are able to route such param through the LoaderOptions mechanism, +// make it globally accessible +int _3DS_use_smoothing_groups = 0; + +// convenient functions +static unsigned char get_byte() { + unsigned char b; + fread( &b, 1, 1, model ); + return b; +} + +/* NOTE: This string has to be freed by the caller + Also note: You can't fetch strings longer than 256 + characters! */ +static char* get_string() { + char *s = new char[256], read; + int c = 0; + + while ( (read = getc(model)) != 0 ) { + if (c < 255) + s[c++] = read; + } + s[c] = 0; + + return s; +} + +//========================================================== +// STRUCTURE NODE FUNCTIONS + +static _ssg3dsStructureNode *findStructureNode( char *name ) { + for ( _ssg3dsStructureNode *n = object_list; n != NULL; n = n->next ) { + if ( strcmp( n->object->getName(), name ) == 0 ) { + return n; + } + } + + return NULL; +} + +static _ssg3dsStructureNode *findStructureNode( short id ) { + for ( _ssg3dsStructureNode *n = object_list; n != NULL; n = n->next ) { + if ( n->id == id ) { + return n; + } + } + + return NULL; +} + +static void addStructureNode( _ssg3dsStructureNode *node ) { + if (object_list == NULL) { + object_list = node; + } else { + node -> next = object_list; + object_list = node; + } +} + +//========================================================== +// MATERIAL PARSERS +static int parse_mapname( unsigned int length ) +{ + current_material->tex_name = get_string(); + DEBUGPRINT("%sMap name: %s %s%s", current_material->tex_name, "", ""); + return PARSE_OK; +} + +static int parse_mapoptions( unsigned int length ) +{ + unsigned short value = ulEndianReadLittle16(model); + // bit 4: 0=tile (default), 1=do not tile (a single bit for both u and v) + current_material->wrap_s = current_material->wrap_t = ((value & 0x10) == 0); + DEBUGPRINT("%sMap options (wrap): %c %s%s", + (current_material->wrap_s)?'Y':'N', "", ""); + + return PARSE_OK; +} + +static int parse_uscale( unsigned int length ) +{ + current_material->tex_scale[1] = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sU-scale: %.3f %s%s", current_material->tex_scale[1], "", ""); + return PARSE_OK; +} + +static int parse_vscale( unsigned int length ) +{ + current_material->tex_scale[0] = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sV-scale: %.3f %s%s", current_material->tex_scale[0], "", ""); + return PARSE_OK; +} + +static int parse_uoffst( unsigned int length ) +{ + current_material->tex_offset[1] = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sU-offset: %.3f %s%s", + current_material->tex_offset[1], "", ""); + return PARSE_OK; +} + +static int parse_voffst( unsigned int length ) +{ + current_material->tex_offset[0] = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sV-offset: %.3f %s%s", + current_material->tex_offset[0], "", ""); + return PARSE_OK; +} + +static int parse_material( unsigned int length ) { + materials[num_materials] = current_material = new _3dsMat; + current_material->flags = 0; + current_material->tex_name = NULL; + num_materials++; + + /* set default value for material colours (taken from glMaterial man page) */ + sgSetVec3(current_material -> colour[_3DSMAT_AMB], 0.2f, 0.2f, 0.2f ); + sgSetVec3(current_material -> colour[_3DSMAT_DIF], 0.8f, 0.8f, 0.8f ); + sgSetVec3(current_material -> colour[_3DSMAT_SPE], 0.0f, 0.0f, 0.0f ); + sgSetVec3(current_material -> colour[_3DSMAT_EMI], 0.0f, 0.0f, 0.0f ); + current_material -> shi = 0.0f; + current_material -> alpha = 1.0f; + + /* set up texture info */ + sgSetVec2(current_material -> tex_scale , 1.0f, 1.0f); + sgSetVec2(current_material -> tex_offset, 0.0f, 0.0f); + current_material -> wrap_s = current_material -> wrap_t = true; + + DEBUGPRINT("%sNew material found.%s%s%s", "", "", ""); + return PARSE_OK; +} + +static int parse_material_name( unsigned int length ) { + current_material -> name = get_string(); + DEBUGPRINT("%sMaterial name:%s%s%s", current_material->name, "", ""); + + return PARSE_OK; +} + +static int parse_rgb1( unsigned int length ) { + float r, g, b; + + r = ulEndianReadLittleFloat(model); + g = ulEndianReadLittleFloat(model); + b = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sColour: R:%.2f, G:%.2f, B:%.2f", r, g, b); + + sgSetVec3(current_material->colour[colour_mode], r, g, b); + + return PARSE_OK; +} + +static int parse_rgb2( unsigned int length ) { + float r, g, b; + + r = (float)get_byte() / 255.0f; + g = (float)get_byte() / 255.0f; + b = (float)get_byte() / 255.0f; + DEBUGPRINT("%sColour: R:%.2f, G:%.2f, B:%.2f", r, g, b); + + sgSetVec3(current_material->colour[colour_mode], r, g, b); + return PARSE_OK; +} + +static int parse_ambient( unsigned int length ) { + colour_mode = _3DSMAT_AMB; + DEBUGPRINT("%sAmbient colour%s%s%s", "", "", ""); + return PARSE_OK; +} + +static int parse_diffuse( unsigned int length ) { + colour_mode = _3DSMAT_DIF; + DEBUGPRINT("%sDiffuse colour%s%s%s", "", "", ""); + return PARSE_OK; +} + +static int parse_specular( unsigned int length ) { + colour_mode = _3DSMAT_SPE; + DEBUGPRINT("%sSpecular colour%s%s%s", "", "", ""); + return PARSE_OK; +} + +static int parse_shininess( unsigned int length ) { + // this chunk contains a percentage chunk, + // so just read that chunks header + ulEndianReadLittle16(model); ulEndianReadLittle32(model); + current_material -> shi = (float)ulEndianReadLittle16(model) * 128.0f / 100.0f; + DEBUGPRINT("%sShininess:%.1f%s%s", current_material->shi, "", ""); + return PARSE_OK; +} + +static int parse_transparency( unsigned int length ) { + // this chunk contains a percentage chunk, + // so just read that chunks header + ulEndianReadLittle16(model); ulEndianReadLittle32(model); + current_material->alpha = 1.0f - (float)ulEndianReadLittle16(model) / 100.0f; + DEBUGPRINT("%sAlpha:%.1f%s%s", current_material->alpha, "", ""); + return PARSE_OK; +} + +static int parse_doublesided( unsigned int length ) { + double_sided = current_material->flags |= IS_DOUBLESIDED; + + DEBUGPRINT("%sMaterial is double sided.%s%s%s", "", "", ""); + + return PARSE_OK; +} + +static ssgSimpleState *get_state( _3dsMat *mat ) { + + if ( mat -> name != NULL ) + { + ssgSimpleState *st = current_options -> createSimpleState ( mat -> name ) ; + if ( st != NULL ) + return st ; + } + + ssgSimpleState *st = new ssgSimpleState () ; + + st -> setName( mat -> name ); + + st -> setMaterial ( GL_AMBIENT, + mat->colour[_3DSMAT_AMB][0], + mat->colour[_3DSMAT_AMB][1], + mat->colour[_3DSMAT_AMB][2], mat->alpha ) ; + st -> setMaterial ( GL_DIFFUSE, + mat->colour[_3DSMAT_DIF][0], + mat->colour[_3DSMAT_DIF][1], + mat->colour[_3DSMAT_DIF][2], mat->alpha ) ; + st -> setMaterial ( GL_SPECULAR, + mat->colour[_3DSMAT_SPE][0], + mat->colour[_3DSMAT_SPE][1], + mat->colour[_3DSMAT_SPE][2], mat->alpha ) ; + st -> setMaterial ( GL_EMISSION, + mat->colour[_3DSMAT_EMI][0], + mat->colour[_3DSMAT_EMI][1], + mat->colour[_3DSMAT_EMI][2], mat->alpha ) ; + st -> setShininess( mat -> shi ) ; + + st -> disable ( GL_COLOR_MATERIAL ) ; + st -> enable ( GL_LIGHTING ) ; + st -> setShadeModel ( GL_SMOOTH ) ; + + if ( mat -> alpha < 0.99f ) + { + st -> disable ( GL_ALPHA_TEST ) ; + st -> enable ( GL_BLEND ) ; + st -> setTranslucent () ; + } + else + { + st -> disable ( GL_BLEND ) ; + st -> setOpaque () ; + } + + if (mat -> tex_name != NULL) { + st -> setTexture( current_options -> createTexture(mat->tex_name, + mat->wrap_s, + mat->wrap_t) ) ; + st -> enable( GL_TEXTURE_2D ); + } else { + st -> disable( GL_TEXTURE_2D ); + } + + // don't show specular if exponent is 0, more info at: + // http://sf.net/mailarchive/forum.php?thread_id=6481452&forum_id=4479 + if ( ! mat->shi ) + st -> setMaterial ( GL_SPECULAR, 0,0,0,1 ); + + return st ; +} + + +//========================================================== +// TRIMESH PARSERS + +static void free_trimesh() +{ + DEBUGPRINT("%sFreeing trimesh object%s%s%s", "","",""); + + if (vertex_list) + delete [] vertex_list; + + if (face_normals) + delete [] face_normals; + + if (vertex_normals) + delete [] vertex_normals; + + if (texcrd_list) + delete [] texcrd_list; + + if (smooth_list) + delete [] smooth_list; + + if (vertex_index) + delete [] vertex_index; + + if (face_lists) { + for (int i = 0; i < num_vertices; i++) { + freeFaceList( face_lists[i] ); + } + + delete [] face_lists; + } + + vertex_list = NULL; + face_normals = NULL; + vertex_normals = NULL; + texcrd_list = NULL; + smooth_list = NULL; + vertex_index = NULL; + face_lists = NULL; +} + +static int parse_trimesh( unsigned int length ) { + free_trimesh(); + +#if 0 + current_transform = new ssgTransform(); + current_branch -> addKid( current_transform ); +#else // avoid introducing a useless ssgTransform + current_transform = (ssgTransform*) current_branch; +#endif + + /* Before we parse CHUNK_FACEMAT, we have to know vertices and texture + coordinates. To ensure this, we make a special pass of the Trimesh + chunks, just extracting this information. + This is kind of a kludge, but it was the easiest way to solve this problem + */ + DEBUGPRINT("%sPrescanning sub-chunks for vertices and texture coords." \ + "%s%s%s", "","",""); +#ifdef DEBUG + strcat(debug_indent, " "); +#endif + + unsigned long p = ftell(model); + int parse_ok = parse_chunks( TriMeshDataChunks, length ); + fseek(model, p, SEEK_SET); + +#ifdef DEBUG + debug_indent[strlen(debug_indent)-4] = 0; +#endif + DEBUGPRINT("%sDone prescanning.%s%s%s", "","",""); + + return parse_ok; +} + +static int parse_vert_list( unsigned int length ) { + num_vertices = ulEndianReadLittle16(model); + vertex_list = new sgVec3[num_vertices]; + face_lists = new _ssg3dsFaceList*[num_vertices]; + + DEBUGPRINT("%sReading %d vertices.%s%s", num_vertices, "", ""); + + for (int i = 0; i < num_vertices; i++) { + vertex_list[i][0] = ulEndianReadLittleFloat(model); + vertex_list[i][1] = ulEndianReadLittleFloat(model); + vertex_list[i][2] = ulEndianReadLittleFloat(model); + + face_lists [i] = NULL; + } + + return PARSE_OK; +} + +static int parse_smooth_list( unsigned int length ) +{ + int i; + smooth_found = TRUE; + + smooth_list = new unsigned int[num_faces]; + DEBUGPRINT("%sReading smoothlist%s%s%s", "", "", ""); + + for (i = 0; i < num_faces; i++) + smooth_list[i] = ulEndianReadLittle32(model); + + return PARSE_OK; +} + +static void smooth_normals( int use_smooth_list ) { + for (int i = 0; i < num_faces; i++) { + for (int j = 0; j < 3; j++) { + int nindex = i * 3 +j; + int vindex = vertex_index[ nindex ]; + sgCopyVec3( vertex_normals[nindex], face_normals[i] ); + + // find all faces containing vertex vindex + for ( _ssg3dsFaceList *l = face_lists[vindex]; l != NULL; l = l->next ) { + int findex = l -> face_index; + + if ( findex != i ) { + int should_smooth; + if (use_smooth_list) { + should_smooth = (smooth_list[i] & smooth_list[findex]); + } else { + float scalar = sgScalarProductVec3( face_normals[i], + face_normals[findex] ); + should_smooth = ( scalar > _ssg_smooth_threshold ); + } + + if (should_smooth) { + sgAddVec3( vertex_normals[nindex], face_normals[findex] ); + } + } + } + + sgNormaliseVec3( vertex_normals[nindex] ); + } + } +} + +static int identify_face_materials( unsigned int length ) { + facemat_found = TRUE; + DEBUGPRINT("%sFace materials found.%s%s%s", "","",""); + + fseek( model, length, SEEK_CUR ); + + return PARSE_OK; +} + +static int parse_face_list( unsigned int length ) { + int i; + num_faces = ulEndianReadLittle16(model); + + DEBUGPRINT("%sReading %d faces.%s%s", num_faces, "", ""); + + vertex_index = new unsigned short[num_faces*3]; + face_normals = new sgVec3[num_faces]; + + vertex_normals = new sgVec3[num_faces * 3]; + + for (i = 0; i < num_faces; i++) { + int v1 = ulEndianReadLittle16(model); + int v2 = ulEndianReadLittle16(model); + int v3 = ulEndianReadLittle16(model); + vertex_index[i*3 ] = v1; + vertex_index[i*3 + 1] = v2; + vertex_index[i*3 + 2] = v3; + + face_lists[ v1 ] = addFaceListEntry( face_lists[ v1 ], i ); + face_lists[ v2 ] = addFaceListEntry( face_lists[ v2 ], i ); + face_lists[ v3 ] = addFaceListEntry( face_lists[ v3 ], i ); + + unsigned short flags = ulEndianReadLittle16(model); + + if (flags & 7 == 0) { // Triangle vertices order should be swapped + unsigned short tmp = vertex_index[i*3 + 1]; + vertex_index[i*3 + 1] = vertex_index[i*3 + 2]; + vertex_index[i*3 + 2] = tmp; + } + + sgMakeNormal( face_normals[i], vertex_list[vertex_index[i*3 ]], + vertex_list[vertex_index[i*3 + 1]], + vertex_list[vertex_index[i*3 + 2]] ); + + } + + /* this is a special "hack" for the face list chunk + because we HAVE to know the smooth list (if there is one) + before building the ssgVtxTable objects, this parsing has to + be done first...*ugh*/ + + smooth_found = FALSE; + facemat_found = FALSE; + DEBUGPRINT("%sPrescanning sub-chunks for smooth list...%s%s%s", "","",""); +#ifdef DEBUG + strcat(debug_indent, " "); +#endif + + unsigned long p = ftell(model); + parse_chunks( FaceListDataChunks, length - (2 + 8*num_faces) ); + fseek(model, p, SEEK_SET); + +#ifdef DEBUG + debug_indent[strlen(debug_indent)-4] = 0; +#endif + DEBUGPRINT("%sDone prescanning.%s%s%s", "","",""); + + /* now apply correct smoothing. If smooth list has been found, + use it, otherwise use threshold value. */ + smooth_normals( _3DS_use_smoothing_groups && smooth_found ); + + if (!facemat_found) { + DEBUGPRINT("%sNo CHUNK_FACEMAT found. Adding default faces of material " \ + "\"%s\"%s%s.", materials[0]->name, "", ""); + unsigned short *face_indices = new unsigned short[num_faces]; + for (i = 0; i < num_faces; i++) { + face_indices[i] = i; + } +#if 0 + add_leaf(materials[0], num_faces, face_indices); +#else + if( num_faces > maxFacesPerLeaf ) { + float num_objs = (float)num_faces / maxFacesPerLeaf; int int_num_objs = int(num_objs); + ulSetError( UL_DEBUG, "\tgeometry objects '%s' split into %d leaves", + current_branch->getName(), num_objs > int_num_objs ? int_num_objs+1 : int_num_objs); + } + for ( int il = 0; il < num_faces; il += maxFacesPerLeaf ) { + add_leaf(materials[0], ulMin(num_faces-il,maxFacesPerLeaf), &face_indices[il]); + } +#endif + } + + return PARSE_OK; +} + +static int parse_map_list( unsigned int length ) { + unsigned short num_v = ulEndianReadLittle16(model); + texcrd_list = new sgVec2[num_v]; + + DEBUGPRINT("%sReading %d texture coords.%s%s", num_v, "", ""); + + for (int i = 0; i < num_v; i++) { + texcrd_list[i][0] = ulEndianReadLittleFloat(model); + texcrd_list[i][1] = ulEndianReadLittleFloat(model); + } + + return PARSE_OK; +} + +static int parse_tra_matrix( unsigned int length ) { + sgMat4 m; + + sgMakeIdentMat4( m ); + + DEBUGPRINT("%sReading transformation matrix.%s%s%s", "","",""); + + /* Strange things seems to be going on with the + local coordinate system in 3ds - I have commented + this out, but things seems to work better without + it (which is odd).*/ + + int i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 3; j++) { + m[j][i] = ulEndianReadLittleFloat(model); + } + } + +#if 0 + m[3][3] = 1.0f; + sgTransposeNegateMat4( m ); + +#ifdef DEBUG + for (int a = 0; a < 4; a++) { + fputs(debug_indent, stderr); + for (int b = 0; b < 4; b++) { + fprintf(stderr, "%.2f\t", m[b][a]); + } + putc('\n', stderr); + } +#endif + +// current_transform -> setTransform( m ); +#endif + return PARSE_OK; +} + +static void add_leaf( _3dsMat *material, int listed_faces, + unsigned short *face_indices ) { + int is_ds = material->flags & IS_DOUBLESIDED; + int has_texture = material->tex_name != NULL; + + ssgVertexArray *vertices = new ssgVertexArray(); + ssgNormalArray *normals = new ssgNormalArray(); + ssgTexCoordArray *texcrds = NULL; +#ifdef USE_VTXARRAYS + ssgIndexArray* indices = new ssgIndexArray(); +#endif + + if (has_texture) { + if (texcrd_list == NULL) { + ulSetError(UL_WARNING, "ssgLoad3ds: Texture coords missing."); + } else { + texcrds = new ssgTexCoordArray(); + } + } + + int tri_idx = 0; + + for (int i = 0; i < listed_faces; i++) { + unsigned short faceindex = face_indices[i]; + int v1 = faceindex * 3, + v2 = faceindex * 3 + 1, + v3 = faceindex * 3 + 2; + +#ifdef USE_VTXARRAYS + indices->add( tri_idx++ ); + indices->add( tri_idx++ ); + indices->add( tri_idx++ ); +#endif + + vertices->add( vertex_list[ vertex_index[v1] ] ); + vertices->add( vertex_list[ vertex_index[v2] ] ); + vertices->add( vertex_list[ vertex_index[v3] ] ); + + normals ->add( vertex_normals[ v1 ] ); + normals ->add( vertex_normals[ v2 ] ); + normals ->add( vertex_normals[ v3 ] ); + + if (has_texture && texcrd_list != NULL) { + int num_texcrds = 3; + sgVec2 _texcrds[6]; + sgCopyVec2( _texcrds[0], texcrd_list[ vertex_index[v1] ] ); + sgCopyVec2( _texcrds[1], texcrd_list[ vertex_index[v2] ] ); + sgCopyVec2( _texcrds[2], texcrd_list[ vertex_index[v3] ] ); + if (is_ds) { + num_texcrds = 6; + sgCopyVec2( _texcrds[3], texcrd_list[ vertex_index[v1] ] ); + sgCopyVec2( _texcrds[4], texcrd_list[ vertex_index[v3] ] ); + sgCopyVec2( _texcrds[5], texcrd_list[ vertex_index[v2] ] ); + } + + for (int j = 0; j < num_texcrds; j++) { + _texcrds[j][0] *= material->tex_scale[0]; + _texcrds[j][1] *= material->tex_scale[1]; + sgAddVec2( _texcrds[j], material->tex_offset ); + texcrds->add( _texcrds[j] ); + } + } + + if (is_ds) { + sgVec3 n[3]; /* we have to use the *negated* normals for back faces */ + + vertices->add( vertex_list[ vertex_index[v1] ] ); + vertices->add( vertex_list[ vertex_index[v3] ] ); + vertices->add( vertex_list[ vertex_index[v2] ] ); + + sgCopyVec3( n[0], vertex_normals[v1] ); + sgCopyVec3( n[1], vertex_normals[v3] ); + sgCopyVec3( n[2], vertex_normals[v2] ); + + for (int j = 0; j < 3; j++) { + sgNegateVec3( n[j] ); + normals->add( n[j] ); + } + +#ifdef USE_VTXARRAYS + indices->add( tri_idx++ ); + indices->add( tri_idx++ ); + indices->add( tri_idx++ ); +#endif + } + + } + +#ifdef USE_VTXARRAYS + ssgVtxArray* vtab = new ssgVtxArray ( GL_TRIANGLES, vertices, normals, texcrds, NULL, indices ); +#else + ssgVtxTable* vtab = new ssgVtxTable ( GL_TRIANGLES, vertices, normals, texcrds, NULL ) ; +#endif + vtab -> setState ( get_state( material ) ) ; + vtab -> setCullFace ( TRUE ) ; + + ssgLeaf* leaf = current_options -> createLeaf ( vtab, 0 ) ; + + if ( leaf ) + current_transform -> addKid( leaf ); +} + +static int parse_face_materials( unsigned int length ) { + int mat_num; + char *mat_name = get_string(); + _3dsMat *material = NULL; + + // find the material + for (mat_num = 0; mat_num < num_materials; mat_num++) { + if ( strcmp( mat_name, materials[mat_num]->name ) == 0 ) { + material = materials[mat_num]; + break; + } + } + + if (material == NULL) { + ulSetError(UL_WARNING, "ssgLoad3ds: Undefined reference to material " \ + "\"%s\" found.", mat_name); + return PARSE_ERROR; + } + + unsigned short listed_faces = ulEndianReadLittle16(model); + + DEBUGPRINT("%sFaces of \"%s\" list with %d faces.%s", + mat_name, listed_faces, ""); + + delete mat_name; // no longer needed + + unsigned short *face_indices = new unsigned short[listed_faces]; + for (int i = 0; i < listed_faces; i++) { + face_indices[i] = ulEndianReadLittle16(model); + } + +#if 0 + add_leaf(material, listed_faces, face_indices); +#else + if( listed_faces > maxFacesPerLeaf ) { + float num_objs = (float)listed_faces / maxFacesPerLeaf; int int_num_objs = int(num_objs); + ulSetError( UL_DEBUG, "\tgeometry objects '%s' split into %d leaves", + current_branch->getName(), num_objs > int_num_objs ? int_num_objs+1 : int_num_objs); + } + for ( int il = 0; il < listed_faces; il += maxFacesPerLeaf ) { + add_leaf(material, ulMin(listed_faces-il,maxFacesPerLeaf), &face_indices[il]); + } +#endif + + delete [] face_indices; + + return PARSE_OK; +} + +//========================================================== +// OBJBLOCK PARSER + +static int parse_objblock( unsigned int length ) { + char *object_name = get_string(); + current_branch = new ssgTransform; + current_branch -> setName( object_name ); + + _ssg3dsStructureNode *object_node = new _ssg3dsStructureNode; + object_node -> object = current_branch; + + addStructureNode( object_node ); + + DEBUGPRINT("%sObject block \"%s\"%s%s", object_name, "", ""); + delete object_name; + + return PARSE_OK; +} + +static int parse_oneunit( unsigned int length ) { +#ifdef DEBUG + float oneunit = ulEndianReadLittleFloat(model); + DEBUGPRINT("%sOne unit: %.3f%s%s", oneunit, "", ""); +#else + ulEndianReadLittleFloat(model) ; +#endif + + return PARSE_OK; +} + +static int parse_version( unsigned int length ) { +#ifdef DEBUG + unsigned int version = ulEndianReadLittle32(model); + DEBUGPRINT("%s3DS Version: %d%s%s", version, "", ""); +#else + ulEndianReadLittle32(model) ; +#endif + + return PARSE_OK; +} + +//========================================================== +// KEYFRAME CHUNK PARSER + +static int parse_frame( unsigned int length ) { + // this chunk is not used for anything right now +#ifdef DEBUG + DEBUGPRINT("%sFrame start: %d, end: %d%s", + ulEndianReadLittle32(model), ulEndianReadLittle32(model), ""); +#else + ulEndianReadLittle32(model); + ulEndianReadLittle32(model); +#endif + + return PARSE_OK; +} + + +static int parse_frame_dummyname( unsigned int length ) { + char *dummyname = get_string(); + if ( last_dummy_object != (ssgTransform*)NULL ) + last_dummy_object->setName( dummyname ); + delete dummyname; + + return PARSE_OK; +} + + +static int parse_frame_objname( unsigned int length ) { + /* This chunk defines a hierarchy elements name and its parent object's + identifier. + + This function assumes that the hierarchy is defined from + root to leaf, i.e. a nodes parent must have been declared before the + node itself is declared. */ + + char *objname = get_string(), dummyname[255]; + ulEndianReadLittle16(model); + ulEndianReadLittle16(model); + short parent_id = ulEndianReadLittle16(model); + int dummy = 0; + + DEBUGPRINT("%sObject name: %s, parent: %d%s", + objname, parent_id, ""); + + if ( strcmp(objname,"$$$DUMMY") == 0 ) { + sprintf( dummyname, "%s_%d", objname, dummy_id++ ); + objname = dummyname; + dummy = 1; + } + + _ssg3dsStructureNode *current_structure_node = findStructureNode( objname ); + + if ( current_structure_node == NULL ) { + current_structure_node = new _ssg3dsStructureNode; + current_structure_node -> object = last_dummy_object = new ssgTransform; + current_structure_node -> object -> setName( objname ); + + addStructureNode( current_structure_node ); + } + + if ( current_structure_id >= 0 ) { + current_structure_node -> id = current_structure_id; + current_structure_id = -1; + } + else + current_structure_node -> id = last_id++; + + if ( parent_id != -1 ) { + _ssg3dsStructureNode *parent = findStructureNode( parent_id ); + if (parent == NULL) { + ulSetError( UL_WARNING, "ssgLoad3ds: Hierarchy entry \"%d\" does "\ + "not match any defined objects.", parent_id ); + } else { + parent -> object -> addKid( current_structure_node -> object ); + current_structure_node -> has_been_used = true; + } + } else { + top_object -> addKid( current_structure_node -> object ); + current_structure_node -> has_been_used = true; + } + + if ( !dummy ) delete objname; + + return PARSE_OK; +} + +static int parse_frame_hierarchy( unsigned int length ) { + current_structure_id = ulEndianReadLittle16(model); + DEBUGPRINT("%sThis object's hierarchy id: %d.%s%s", + current_structure_id, "", ""); + + return PARSE_OK; +} + +//========================================================== +// GENERAL CHUNK PARSER + +static int parse_chunks( _ssg3dsChunk *chunk_list, unsigned int length ) +{ + int parse_ok = PARSE_OK; + unsigned short id; + unsigned int sub_length; + unsigned int p = 0; + _ssg3dsChunk *t; + + while (parse_ok && p < length) { + id = ulEndianReadLittle16(model); + sub_length = ulEndianReadLittle32(model); + + if (p + sub_length > length) { + ulSetError(UL_WARNING, "ssgLoad3ds: Illegal chunk %X of length %i. " \ + "Chunk is longer than parent chunk.", (int)id, sub_length); + return PARSE_ERROR; + } + + p += sub_length; + sub_length -= CHUNK_HEADER_SIZE; + + for (t = chunk_list; t->id != 0 && t->id != id; t++); + + if (t->id == id) { + DEBUGPRINT("%sFound chunk %X of length %d%s (known)", + id, sub_length,""); + // this is a known chunk + // do chunk-specific parsing if available + unsigned long cp = ftell(model); + + if (t->parse_func) + parse_ok = t->parse_func( sub_length ); + +#ifdef DEBUG + strcat(debug_indent, " "); +#endif + + // if chunk can have subchunks, parse these + if (t->subchunks && parse_ok) { + parse_ok = parse_chunks( t->subchunks, + sub_length - (ftell(model)-cp) ); + } + +#ifdef DEBUG + debug_indent[strlen(debug_indent)-4] = 0; +#endif + + } else { + DEBUGPRINT("%sFound chunk %X of length %d%s (unknown)", + id, sub_length,""); + fseek( model, sub_length, SEEK_CUR ); + } + + } + + return parse_ok; +} + + +ssgEntity *ssgLoad3ds( const char *filename, const ssgLoaderOptions* options ) { + int i; + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + char filepath [ 1024 ] ; + current_options -> makeModelPath ( filepath, filename ) ; + + model = fopen ( filepath, "rb" ); + if ( model == NULL ) { + ulSetError(UL_WARNING, "ssgLoad3ds: Failed to open '%s' for reading", + filepath ) ; + return NULL ; + } + + fseek(model, 0, SEEK_END); + unsigned long size = ftell(model); + rewind(model); + + current_structure_id = -1; + dummy_id = last_id = 0; + last_dummy_object = (ssgTransform*)NULL; + num_objects = num_materials = num_textures = 0; + object_list = NULL; + vertex_list = NULL; + texcrd_list = NULL; + face_normals = NULL; + face_lists = NULL; + vertex_index = normal_index = NULL; + top_object = new ssgBranch(); + + // initialize some storage room for materials + // (ok, could be implemented as linked list, but...well I'm lazy) + materials = new _3dsMat*[MAX_MATERIALS]; + // strange enough, the 3ds file does not have to include any materials, + // in which case we will use this one. + materials[0] = & default_material; + + parse_chunks( TopChunk, size ); + + fclose(model); + + // clean up the materials array + for (i = 0; i < num_materials; i++) { + if (materials[i] -> name != NULL) { + delete [] materials[i] -> name; + } + if (materials[i] -> tex_name != NULL) { + delete [] materials[i] -> tex_name; + } + + delete materials[i]; + } + + for ( _ssg3dsStructureNode *n = object_list, *temp; n != NULL; n = temp ) { + if ( !n -> has_been_used ) { + top_object -> addKid( n -> object ); + } + temp = n -> next; + delete n; + } + + delete [] materials; + + free_trimesh(); + + return top_object; +} + + diff --git a/src/ssg/ssgLoadAC.cxx b/src/ssg/ssgLoadAC.cxx new file mode 100644 index 0000000..f060f45 --- /dev/null +++ b/src/ssg/ssgLoadAC.cxx @@ -0,0 +1,952 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadAC.cxx 2086 2006-09-30 08:41:21Z bram $ +*/ + + +#include "ssgLocal.h" +#include "ssgVertSplitter.h" + +static FILE *loader_fd ; + +struct _ssgMaterial +{ + sgVec4 rgba ; + sgVec4 spec ; + sgVec4 emis ; + sgVec4 amb ; + float shi ; +} ; + +static int num_materials = 0 ; + +static ssgLoaderOptions *current_options = NULL ; +static int current_materialind = 0 ; +static ssgBranch *current_branch = NULL ; +static ssgVertexArray *current_vertexarray = NULL ; +static ssgTexCoordArray *current_texcoordarray = NULL ; +static ssgIndexArray *current_triindexarray = NULL ; +static ssgIndexArray *current_matindexarray = NULL ; +static ssgIndexArray *current_flagsarray = NULL ; +static char *current_tfname = NULL ; +static char *current_data = NULL ; +static float current_crease = 61.0 ; + +#define MAX_MATERIALS 1000 /* This *ought* to be enough! */ +static _ssgMaterial *mlist [ MAX_MATERIALS ] ; + +static sgMat4 current_matrix ; +static sgVec2 texrep ; +static sgVec2 texoff ; + +static sgVec2 invalidTexture = { 1e30f, } ; +static sgVec3 zero = { 0.0, } ; + +static int do_material ( char *s ) ; +static int do_object ( char *s ) ; +static int do_name ( char *s ) ; +static int do_data ( char *s ) ; +static int do_texture ( char *s ) ; +static int do_texrep ( char *s ) ; +static int do_texoff ( char *s ) ; +static int do_crease ( char *s ) ; +static int do_rot ( char *s ) ; +static int do_loc ( char *s ) ; +static int do_url ( char *s ) ; +static int do_numvert ( char *s ) ; +static int do_numsurf ( char *s ) ; +static int do_surf ( char *s ) ; +static int do_mat ( char *s ) ; +static int do_refs ( char *s ) ; +static int do_kids ( char *s ) ; + +/*static int do_obj_world ( char *s ) ; +static int do_obj_poly ( char *s ) ; +static int do_obj_group ( char *s ) ; +static int do_obj_light ( char *s ) ;*/ + +#define PARSE_CONT 0 +#define PARSE_POP 1 + +struct Tag +{ + const char *token ; + int (*func) ( char *s ) ; +} ; + + +static void skip_spaces ( char **s ) +{ + while ( **s == ' ' || **s == '\t' ) + (*s)++ ; +} + + +static void skip_quotes ( char **s ) +{ + skip_spaces ( s ) ; + + if ( **s == '\"' ) + { + (*s)++ ; + + char *t = *s ; + + while ( *t != '\0' && *t != '\"' ) + t++ ; + + if ( *t != '\"' ) + ulSetError ( UL_WARNING, "ac_to_gl: Mismatched double-quote ('\"') in '%s'", *s ) ; + + *t = '\0' ; + } + else + ulSetError ( UL_WARNING, "ac_to_gl: Expected double-quote ('\"') in '%s'", *s ) ; +} + + + +static int search ( Tag *tags, char *s ) +{ + skip_spaces ( & s ) ; + + for ( int i = 0 ; tags[i].token != NULL ; i++ ) + if ( ulStrNEqual ( tags[i].token, s, strlen(tags[i].token) ) ) + { + s += strlen ( tags[i].token ) ; + + skip_spaces ( & s ) ; + + return (*(tags[i].func))( s ) ; + } + + ulSetError ( UL_FATAL, "ac_to_gl: Unrecognised token '%s'", s ) ; + + return 0 ; /* Should never get here */ +} + +static Tag top_tags [] = +{ + { "MATERIAL", do_material }, + { "OBJECT" , do_object }, + { NULL, NULL } +} ; + + +static Tag object_tags [] = +{ + { "name" , do_name }, + { "data" , do_data }, + { "texture" , do_texture }, + { "texrep" , do_texrep }, + { "texoff" , do_texoff }, + { "crease" , do_crease }, + { "rot" , do_rot }, + { "loc" , do_loc }, + { "url" , do_url }, + { "numvert" , do_numvert }, + { "numsurf" , do_numsurf }, + { "kids" , do_kids }, + { NULL, NULL } +} ; + +static Tag surf_tag [] = +{ + { "SURF" , do_surf }, + { NULL, NULL } +} ; + +static Tag surface_tags [] = +{ + { "mat" , do_mat }, + { "refs" , do_refs }, + { NULL, NULL } +} ; + +/*static Tag obj_type_tags [] = +{ + { "world", do_obj_world }, + { "poly" , do_obj_poly }, + { "group", do_obj_group }, + { "light", do_obj_light }, + { NULL, NULL } +} ;*/ + + +#define OBJ_WORLD 0 +#define OBJ_POLY 1 +#define OBJ_GROUP 2 +#define OBJ_LIGHT 3 + +/*static int do_obj_world ( char * ) { return OBJ_WORLD ; } +static int do_obj_poly ( char * ) { return OBJ_POLY ; } +static int do_obj_group ( char * ) { return OBJ_GROUP ; } +static int do_obj_light ( char * ) { return OBJ_LIGHT ; }*/ + +static int last_num_kids = -1 ; +static int current_flags = -1 ; + +static ssgState *get_state ( _ssgMaterial *mat ) +{ + if (current_tfname != NULL) { + ssgState *st = current_options -> createState ( current_tfname ) ; + if ( st != NULL ) + return st ; + } + + ssgSimpleState *st = new ssgSimpleState () ; + + st -> setMaterial ( GL_DIFFUSE, mat -> rgba ) ; + st -> setMaterial ( GL_AMBIENT, mat -> amb ) ; + st -> setMaterial ( GL_EMISSION, mat -> emis ) ; + st -> setMaterial ( GL_SPECULAR, mat -> spec ) ; + st -> setShininess ( mat -> shi ) ; + + st -> enable ( GL_COLOR_MATERIAL ) ; + st -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + st -> enable ( GL_LIGHTING ) ; + st -> setShadeModel ( GL_SMOOTH ) ; + + bool has_alpha = false ; + + if ( current_tfname != NULL ) + { + ssgTexture *tex = current_options -> createTexture ( current_tfname ) ; + has_alpha = tex -> hasAlpha () ; + st -> setTexture( tex ) ; + st -> enable( GL_TEXTURE_2D ) ; + } + else + { + st -> disable( GL_TEXTURE_2D ) ; + } + + if ( mat -> rgba[3] < 0.99 || has_alpha ) + { + st -> disable ( GL_ALPHA_TEST ) ; + st -> enable ( GL_BLEND ) ; + st -> setTranslucent () ; + } + else + { + st -> disable ( GL_ALPHA_TEST ) ; + st -> disable ( GL_BLEND ) ; + st -> setOpaque () ; + } + + return st ; +} + + +static int do_material ( char *s ) +{ + char name [ 1024 ] ; + sgVec4 rgba ; + sgVec4 amb ; + sgVec4 emis ; + sgVec4 spec ; + int shi ; + float trans ; + + if ( sscanf ( s, + "%s rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f", + name, + &rgba [0], &rgba [1], &rgba [2], + &amb [0], &amb [1], &amb [2], + &emis[0], &emis[1], &emis[2], + &spec[0], &spec[1], &spec[2], + &shi, + &trans ) != 15 ) + { + ulSetError ( UL_WARNING, "ac_to_gl: Can't parse this MATERIAL:" ) ; + ulSetError ( UL_WARNING, "ac_to_gl: MATERIAL %s", s ) ; + } + else + { + char *nm = name ; + + skip_quotes ( &nm ) ; + + amb [ 3 ] = emis [ 3 ] = spec [ 3 ] = 1.0f ; + rgba [ 3 ] = 1.0f - trans ; + + mlist [ num_materials ] = new _ssgMaterial ; + _ssgMaterial *current_material ; + current_material = mlist [ num_materials ] ; + sgCopyVec4 ( current_material -> rgba , rgba ) ; + sgCopyVec4 ( current_material -> amb , amb ) ; + sgCopyVec4 ( current_material -> emis , emis ) ; + sgCopyVec4 ( current_material -> spec , spec ) ; + current_material -> shi = (float) shi ; + } + + num_materials++ ; + return PARSE_CONT ; +} + +static int do_object ( char * /* s */ ) +{ +/* + int obj_type = search ( obj_type_tags, s ) ; +*/ + + delete [] current_tfname ; + current_tfname = NULL ; + + char buffer [ 1024 ] ; + + sgSetVec2 ( texrep, 1.0f, 1.0f ) ; + sgSetVec2 ( texoff, 0.0f, 0.0f ) ; + + sgMakeIdentMat4 ( current_matrix ) ; + + ssgEntity *old_cb = current_branch ; + + ssgTransform *tr = new ssgTransform () ; + + tr -> setTransform ( current_matrix ) ; + + current_branch -> addKid ( tr ) ; + current_branch = tr ; + + current_matindexarray = new ssgIndexArray ; + current_flagsarray = new ssgIndexArray ; + current_texcoordarray = new ssgTexCoordArray ; + current_vertexarray = new ssgVertexArray ; + current_triindexarray = new ssgIndexArray ; + + while ( fgets ( buffer, 1024, loader_fd ) != NULL ) + if ( search ( object_tags, buffer ) == PARSE_POP ) + break ; + + // If he have read some surfaces belonging to that object, + // compute normals and distribute the triangels across the materials. + int ntris = current_triindexarray -> getNum () / 3; + if ( 0 < ntris ) { + int i; + int nvert = current_vertexarray -> getNum () ; + + // Put the triangles and the current crease angle into the vertex splitter ... + ssgVertSplitter split( nvert, ntris ); + split.setSharpAngle ( current_crease ); + + for ( i = 0 ; i < nvert ; i++ ) + sgCopyVec3 ( split.vert ( i ), current_vertexarray -> get ( i ) ) ; + for ( i = 0 ; i < ntris ; i++ ) + split.setTri ( i, + * ( current_triindexarray -> get ( 3*i ) ), + * ( current_triindexarray -> get ( 3*i+1 ) ), + * ( current_triindexarray -> get ( 3*i+2 ) ) ); + + // ... and let it compute the normals. + split.splitAndCalcNormals () ; + + // Cycle through all ssgState/colour combinations and emit leafs from them. + for ( int cullface = 0 ; cullface < 2 ; cullface++ ) { + for ( int material = 0 ; material < num_materials ; material++ ) { + ssgVertexArray* vertexarray = (ssgVertexArray *) current_vertexarray -> clone () ; + vertexarray -> ref () ; + ssgNormalArray* normalarray = new ssgNormalArray ( nvert ) ; + normalarray -> ref () ; + ssgTexCoordArray* texcoordarray = (ssgTexCoordArray *) current_texcoordarray -> clone () ; + texcoordarray -> ref () ; + ssgIndexArray* triindexarray = new ssgIndexArray ( nvert ) ; + triindexarray -> ref () ; + // Mark all normals as unset using a zero vector. + for ( i = 0 ; i < nvert ; i++ ) + normalarray -> add ( zero ) ; + + bool has_texture = current_tfname != NULL ; + + // For every material/state cycle through all triangles and pick + // out those ones with that maternial/state. + for ( int tri = 0 ; tri < ntris ; tri++ ) { + int triMatindex = * ( current_matindexarray -> get ( tri ) ) ; + int triFlags = * ( current_flagsarray -> get ( tri ) ) ; + int triCullface = ! ( triFlags & 0x20 ) ; + + if ( triMatindex == material && triCullface == cullface ) { + // get the original indices + int* triind = split.getTri ( tri ) ; + int origtriind[3]; + for ( int k = 0 ; k < 3 ; k++ ) + origtriind[k] = triind[k] < nvert ? triind[k] : split.origVert( triind[k] - nvert ) ; + + // Note that copying those values prevents us from a race condion + // which occurs when doing something like: + // texcoordarray -> add ( texcoordarray -> get ( origtriind[i] ) ) ; + + // make a local copy of texcoords ... + sgVec2 texcoords[3]; + for ( i = 0 ; i < 3 ; i++ ) + sgCopyVec2 ( texcoords[i], texcoordarray -> get ( origtriind[i] ) ) ; + + // ... of vertices ... + sgVec3 vertices[3]; + for ( i = 0 ; i < 3 ; i++ ) + sgCopyVec3 ( vertices[i], split.vert ( origtriind[i] ) ) ; + + int triSmooth = ( triFlags & 0x10 ) ; + + // ... and of normals. + sgVec3 normals[3]; + if ( triSmooth ) { + for ( i = 0 ; i < 3 ; i++ ) + sgCopyVec3 ( normals[i], split.norm ( triind[i] ) ) ; + } else { + // If we have flat shading, compute one normal for all. + sgSubVec3 ( normals[1], vertices[1], vertices[0] ) ; + sgSubVec3 ( normals[2], vertices[2], vertices[0] ) ; + sgVectorProductVec3 ( normals[0], normals[1], normals[2] ) ; + sgNormaliseVec3 ( normals[0] ) ; + sgCopyVec3 ( normals[1], normals[0] ) ; + sgCopyVec3 ( normals[2], normals[0] ) ; + } + + + for ( i = 0 ; i < 3 ; i++ ) { + + if ( sgEqualVec3 ( normalarray -> get ( origtriind[i] ), zero ) ) { + // Case: not yet initialized. + + sgCopyVec3 ( normalarray -> get ( origtriind[i] ), normals[i] ) ; + + triindexarray -> add ( origtriind[i] ) ; + + } else if ( sgEqualVec3 ( normalarray -> get ( origtriind[i] ), normals[i] ) ) { + // Case: initialized and the same as before. + + triindexarray -> add ( origtriind[i] ) ; + + } else { + // Case: initialized and different. + + // Scan for a vertex/normal/texcoord triple matching the required one. + // If there exist one, use that. + int num = vertexarray -> getNum () ; + int replind = -1 ; + for ( int l = nvert ; l < num ; l++ ) { + if ( sgEqualVec3( vertices[i], vertexarray -> get ( l ) ) && + ( !has_texture || sgEqualVec2( texcoordarray -> get ( origtriind[i] ), texcoordarray -> get ( l ) ) ) && + sgEqualVec3( normals[i], normalarray -> get ( l ) ) ) { + replind = l ; + } + } + + // If we have not yet the required triple in our dataset, add it. + if ( replind < 0 ) { + vertexarray -> add ( vertices[i] ) ; + normalarray -> add ( normals[i] ) ; + texcoordarray -> add ( texcoords[i] ) ; + replind = num ; + } + triindexarray -> add ( replind ) ; + } + } + } + } + + if ( 0 < triindexarray -> getNum () ) { + ssgColourArray *colour = new ssgColourArray ( 1 ) ; + colour -> add ( mlist [ material ] -> rgba ) ; + ssgVtxArray* v = new ssgVtxArray ( GL_TRIANGLES, + vertexarray, + normalarray, + has_texture ? texcoordarray : 0, + colour, + triindexarray ) ; + v -> removeUnusedVertices(); + v -> setState ( get_state ( mlist [ material ] ) ) ; + v -> setCullFace ( cullface ) ; + ssgLeaf* leaf = current_options -> createLeaf ( v, 0 ) ; + if ( leaf ) + tr -> addKid ( leaf ) ; + } + + ssgDeRefDelete ( vertexarray ) ; + ssgDeRefDelete ( normalarray ) ; + ssgDeRefDelete ( texcoordarray ) ; + ssgDeRefDelete ( triindexarray ) ; + } + } + } + + // Cleanup + delete current_matindexarray ; + current_matindexarray = NULL ; + delete current_flagsarray ; + current_flagsarray = NULL ; + delete current_vertexarray ; + current_vertexarray = NULL ; + delete current_texcoordarray ; + current_texcoordarray = NULL ; + delete current_triindexarray ; + current_triindexarray = NULL ; + + // Process child nodes + + int num_kids = last_num_kids ; + + for ( int i = 0 ; i < num_kids ; i++ ) + { + fgets ( buffer, 1024, loader_fd ) ; + search ( top_tags, buffer ) ; + } + + current_branch = (ssgBranch *) old_cb ; + + return PARSE_CONT ; +} + + +static int do_name ( char *s ) +{ + skip_quotes ( &s ) ; + + current_branch -> setName ( s ) ; + + return PARSE_CONT ; +} + + +static int do_data ( char *s ) +{ + int len = strtol ( s, NULL, 0 ) ; + + current_data = new char [ len + 1 ] ; + + for ( int i = 0 ; i < len ; i++ ) + current_data [ i ] = getc ( loader_fd ) ; + + current_data [ len ] = '\0' ; + + int c; + while ( ( c = getc( loader_fd ) ) != EOF ) /* Final RETURN */ + if ( c != '\r' && c != '\n' ) { + ungetc ( c, loader_fd ) ; + break ; + } + + ssgBranch *br = current_options -> createBranch ( current_data ) ; + + if ( br != NULL ) + { + current_branch -> addKid ( br ) ; + current_branch = br ; + } + + /* delete [] current_data ; */ + current_data = NULL ; + + return PARSE_CONT ; +} + + +static int do_texture ( char *s ) +{ + skip_quotes ( &s ) ; + + delete [] current_tfname ; + + if ( s == NULL || s[0] == '\0' ) + current_tfname = NULL ; + else + current_tfname = ulStrDup ( s ) ; + + return PARSE_CONT ; +} + + +static int do_texrep ( char *s ) +{ + if ( sscanf ( s, "%f %f", & texrep [ 0 ], & texrep [ 1 ] ) != 2 ) + ulSetError ( UL_WARNING, "ac_to_gl: Illegal texrep record." ) ; + + return PARSE_CONT ; +} + + +static int do_texoff ( char *s ) +{ + if ( sscanf ( s, "%f %f", & texoff [ 0 ], & texoff [ 1 ] ) != 2 ) + ulSetError ( UL_WARNING, "ac_to_gl: Illegal texoff record." ) ; + + return PARSE_CONT ; +} + +static int do_crease ( char *s ) +{ + // the crease angle is not yet used. However, reading the crease line correctly means + // *.ac lines with "crease" can now be read. + if ( sscanf ( s, "%f", & current_crease ) != 1 ) + ulSetError ( UL_WARNING, "ac_to_gl: Illegal crease angle." ) ; + + return PARSE_CONT ; +} + +static int do_rot ( char *s ) +{ + current_matrix [ 0 ][ 3 ] = current_matrix [ 1 ][ 3 ] = current_matrix [ 2 ][ 3 ] = + current_matrix [ 3 ][ 0 ] = current_matrix [ 3 ][ 1 ] = current_matrix [ 3 ][ 2 ] = 0.0f ; + current_matrix [ 3 ][ 3 ] = 1.0f ; + + if ( sscanf ( s, "%f %f %f %f %f %f %f %f %f", + & current_matrix [ 0 ] [ 0 ], & current_matrix [ 0 ] [ 1 ], & current_matrix [ 0 ] [ 2 ], + & current_matrix [ 1 ] [ 0 ], & current_matrix [ 1 ] [ 1 ], & current_matrix [ 1 ] [ 2 ], + & current_matrix [ 2 ] [ 0 ], & current_matrix [ 2 ] [ 1 ], & current_matrix [ 2 ] [ 2 ] ) != 9 ) + ulSetError ( UL_WARNING, "ac_to_gl: Illegal rot record." ) ; + + ((ssgTransform *)current_branch) -> setTransform ( current_matrix ) ; + return PARSE_CONT ; +} + +static int do_loc ( char *s ) +{ + if ( sscanf ( s, "%f %f %f", & current_matrix [ 3 ][ 0 ], & current_matrix [ 3 ][ 2 ], & current_matrix [ 3 ][ 1 ] ) != 3 ) + ulSetError ( UL_WARNING, "ac_to_gl: Illegal loc record." ) ; + + current_matrix [ 3 ][ 1 ] = - current_matrix [ 3 ][ 1 ] ; + current_matrix [ 3 ][ 3 ] = 1.0f ; + ((ssgTransform *)current_branch) -> setTransform ( current_matrix ) ; + + return PARSE_CONT ; +} + +static int do_url ( char *s ) +{ + skip_quotes ( & s ) ; + +#ifdef PRINT_URLS + ulSetError ( UL_DEBUG, "/* URL: \"%s\" */\n", s ) ; +#endif + + return PARSE_CONT ; +} + +static int do_numvert ( char *s ) +{ + char buffer [ 1024 ] ; + + int nv = strtol ( s, NULL, 0 ) ; + + for ( int i = 0 ; i < nv ; i++ ) + { + sgVec3 v; + fgets ( buffer, 1024, loader_fd ) ; + + if ( sscanf ( buffer, "%f %f %f", &v[0], &v[1], &v[2] ) != 3 ) + { + ulSetError ( UL_FATAL, "ac_to_gl: Illegal vertex record." ) ; + } + + float tmp = v[1] ; + v[1] = -v[2] ; + v[2] = tmp ; + + current_vertexarray -> add ( v ) ; + current_texcoordarray -> add ( invalidTexture ) ; + } + + return PARSE_CONT ; +} + +static int do_numsurf ( char *s ) +{ + int ns = strtol ( s, NULL, 0 ) ; + + for ( int i = 0 ; i < ns ; i++ ) + { + char buffer [ 1024 ] ; + + fgets ( buffer, 1024, loader_fd ) ; + search ( surf_tag, buffer ) ; + } + + return PARSE_CONT ; +} + +static int do_surf ( char *s ) +{ + current_flags = strtol ( s, NULL, 0 ) ; + + char buffer [ 1024 ] ; + + while ( fgets ( buffer, 1024, loader_fd ) != NULL ) + if ( search ( surface_tags, buffer ) == PARSE_POP ) + break ; + + return PARSE_CONT ; +} + + +static int do_mat ( char *s ) +{ + int mat = strtol ( s, NULL, 0 ) ; + + current_materialind = mat ; + + return PARSE_CONT ; +} + +static void add_textured_vertex_edge ( short ind, sgVec2 tex ) +{ + // Add a new triangle edge. For that, check for a vertex/texcoord + // pair already in the current dataset. Use the already present index if present. + + bool has_texture = current_tfname != NULL ; + if ( sgEqualVec2( tex, current_texcoordarray -> get ( ind ) ) || !has_texture ) { + // In this case, we have that vertex/texcoord pair already at the index + // within the ac file. + + current_triindexarray -> add ( ind ) ; + + } else if ( sgEqualVec2( invalidTexture, current_texcoordarray -> get ( ind ) ) ) { + // In this case, we have not yet stored a valid texture coordinate + // for the vertex at the given index. Just copy the texturecoordinate + // value into that place. + + sgCopyVec2( current_texcoordarray -> get ( ind ), tex ) ; + + current_triindexarray -> add ( ind ) ; + + } else { + // Texture coordinate do not match the prevous texcoords stored for this vertex. + // Search for a vertex/texcoord pair matching the requested one, if not + // yet present, add a new one. + + int num = current_vertexarray -> getNum () ; + for ( int i = 0 ; i < num ; i++ ) { + if ( ( !has_texture || sgEqualVec2( tex, current_texcoordarray -> get ( i ) ) ) && + sgEqualVec3( current_vertexarray -> get ( ind ), current_vertexarray -> get ( i ) ) ) { + current_triindexarray -> add ( i ) ; + return ; + } + } + + // Need to copy that before, else we run into a racecondition where + // we copy from an location which is already freed. + sgVec3 vertex ; + sgCopyVec3 ( vertex, current_vertexarray -> get ( ind ) ) ; + current_vertexarray -> add ( vertex ) ; + current_texcoordarray -> add ( tex ) ; + + current_triindexarray -> add ( num ) ; + } +} + +static int do_refs ( char *s ) +{ + int nrefs = strtol ( s, NULL, 0 ) ; + char buffer [ 1024 ] ; + + if ( nrefs == 0 ) + return PARSE_POP ; + + int type = ( current_flags & 0x0F ) ; + + // Handle line type objects by creating a single leaf for each line segment. + if ( type == 1 || type == 2 ) { + ssgIndexArray *ind = new ssgIndexArray ; + for ( int i = 0 ; i < nrefs ; i++ ) + { + fgets ( buffer, 1024, loader_fd ) ; + + int vtx ; + float dummy ; + + if ( sscanf ( buffer, "%d %f %f", &vtx, &dummy, &dummy ) != 3 ) + { + ulSetError ( UL_FATAL, "ac_to_gl: Illegal ref record." ) ; + } + + ind -> add ( vtx ) ; + } + + ssgColourArray *col = new ssgColourArray ( 1 ) ; + col -> add ( mlist [ current_materialind ] -> rgba ) ; + + GLenum gltype = ( type == 1 ) ? GL_LINE_LOOP : GL_LINE_STRIP ; + ssgVtxArray *va = new ssgVtxArray ( gltype, (ssgVertexArray *)current_vertexarray -> clone (), + 0, 0, col, ind ); + va -> removeUnusedVertices(); + va -> setState ( get_state ( mlist [ current_materialind ] ) ) ; + + ssgLeaf *leaf = current_options -> createLeaf ( va, 0 ) ; + if ( leaf ) + current_branch -> addKid ( leaf ) ; + } + + if ( type == 0 ) { + // Handle surface datatypes. + // Each surface is triangulated and each triangle is put + // into the index array current_triindexarray. + int first_vertind = -1 ; + int prev_vertind = 0 ; + sgVec2 first_texcoord ; + sgVec2 prev_texcoord ; + for ( int i = 0 ; i < nrefs ; i++ ) + { + fgets ( buffer, 1024, loader_fd ) ; + + int vertind ; + sgVec2 texcoord ; + + if ( sscanf ( buffer, "%d %f %f", &vertind, + &texcoord[0], + &texcoord[1] ) != 3 ) + { + ulSetError ( UL_FATAL, "ac_to_gl: Illegal ref record." ) ; + } + + texcoord[0] *= texrep[0] ; + texcoord[1] *= texrep[1] ; + texcoord[0] += texoff[0] ; + texcoord[1] += texoff[1] ; + + // Store the first index texcoord pair. + // This one is referenced for every triangle. + if ( first_vertind < 0 ) { + first_vertind = vertind ; + sgCopyVec2( first_texcoord, texcoord ); + } + + // When we have read the third vertex index we can emit the first triangle. + if ( 2 <= i ) { + // Store the edges of the triangle + add_textured_vertex_edge ( first_vertind, first_texcoord ) ; + add_textured_vertex_edge ( prev_vertind, prev_texcoord ) ; + add_textured_vertex_edge ( vertind, texcoord ) ; + + // Store the material and flags for this surface. + current_matindexarray -> add ( current_materialind ); + current_flagsarray -> add ( current_flags ); + } + + // Copy current -> previous + prev_vertind = vertind ; + sgCopyVec2( prev_texcoord, texcoord ); + } + } + + return PARSE_POP ; +} + +static int do_kids ( char *s ) +{ + last_num_kids = strtol ( s, NULL, 0 ) ; + + return PARSE_POP ; +} + + +ssgEntity *ssgLoadAC3D ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgEntity *obj = ssgLoadAC ( fname, options ) ; + + if ( obj == NULL ) + return NULL ; + + /* Do some simple optimisations */ + + ssgBranch *model = new ssgBranch () ; + model -> addKid ( obj ) ; + ssgFlatten ( obj ) ; + return model ; +} + +/* + Original function for backwards compatibility... +*/ + +ssgEntity *ssgLoadAC ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + num_materials = 0 ; + + current_tfname = NULL ; + current_branch = NULL ; + + current_crease = 61.0 ; + + sgSetVec2 ( texrep, 1.0, 1.0 ) ; + sgSetVec2 ( texoff, 0.0, 0.0 ) ; + + loader_fd = fopen ( filename, "r" ) ; + + if ( loader_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadAC: Failed to open '%s' for reading", filename ) ; + return NULL ; + } + + char buffer [ 1024 ] ; + int firsttime = TRUE ; + + current_branch = new ssgTransform () ; + + while ( fgets ( buffer, 1024, loader_fd ) != NULL ) + { + char *s = buffer ; + + /* Skip leading whitespace */ + + skip_spaces ( & s ) ; + + /* Skip blank lines and comments */ + + if ( *s < ' ' && *s != '\t' ) continue ; + if ( *s == '#' || *s == ';' ) continue ; + + if ( firsttime ) + { + firsttime = FALSE ; + + if ( ! ulStrNEqual ( s, "AC3D", 4 ) ) + { + fclose ( loader_fd ) ; + ulSetError ( UL_WARNING, "ssgLoadAC: '%s' is not in AC3D format.", filename ) ; + return NULL ; + } + } + else + search ( top_tags, s ) ; + } + + for (int i = 0; i < num_materials; i++) + { + delete mlist[i]; + mlist[i]=0; + } + num_materials=0; + + delete [] current_tfname ; + current_tfname = NULL ; + fclose ( loader_fd ) ; + + return current_branch ; +} + diff --git a/src/ssg/ssgLoadASC.cxx b/src/ssg/ssgLoadASC.cxx new file mode 100644 index 0000000..d8eeea9 --- /dev/null +++ b/src/ssg/ssgLoadASC.cxx @@ -0,0 +1,1015 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +// +// .ASC loader for SSG/PLIB +// ASC = ascii files, used for example by 3DS R4 (the DOS version) +// Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) on 3.10.2004 +// +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" +#include "ssgParser.h" + +#define u32 unsigned int + +// These functions return TRUE on success +typedef int AscHandlerFunctionType(); + + +static char *globEmpty=""; + +static ssgBranch *curr_branch_; + +struct AscEntityType +{ + const char * sName; + AscHandlerFunctionType *HandleEntity; + bool appearsInsideAMesh; +} ; + +void AscLinePreProcessor(char *line) +// there are lines like this +// Page 2 +// in *.asc files. They should be completely ignored. + +{ char *p = line; + while((*p == ' ') || (*p == 9)) + p++; + // p points to the first word (or the line end) now. + if(ulStrNEqual(p, "Page", strlen("Page"))) + { + // Since this loader ignores the line structure, + // we can also ignore the \n or \r and just "delete" the whole line + line[0] = 0; + } + if(ulStrNEqual(p, "Camera", strlen("Camera"))) + line[0] = 0; +} + + +static /* const */ ssgLoaderOptions* current_options = NULL ; + +static _ssgParserSpec parser_spec = +{ + "\r\n\t ", // delim_chars_skipable + "=:", // delim_chars_non_skipable so that things like "red=0.5" or "vertex 0:" work + AscLinePreProcessor, // pre_processor + "{", // open_brace_chars - unused + "}", // close_brace_chars - unused + '"', // quote_char + '#', // comment_char - I am not sure there are comments and if so which char starts them + "//" // comment_string - I am not sure there are comments and if so which string starts them +} ; + + +static _ssgParser parser; +static ssgBranch* top_branch; + +static int Ascii2Int(int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int Ascii2UInt(unsigned int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = (unsigned int)(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int Ascii2Float(SGfloat &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = SGfloat(strtod( token, &endptr)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + + +struct MeshStatusType { + bool isvalid; // rest of this is valid, IOW I am inside reading a mesh + char *sName; + bool mapped; + bool materialed_but_not_mapped; // only defined if mapped = FALSE + bool hidden; // unused + int maxVertex, maxFace, curVertex, curFace; + void set_sName(char *s); + // material + smoothing is per poly!!! +} MeshStatus; + + +void MeshStatusType::set_sName(char *s) +{ + if(sName) + delete [] sName; + sName = new char[strlen(s)+1]; + strcpy(sName, s); +} + +void CheckIndex(int &i, int max) +{ + if((i<0) || (i>=max)) + { fprintf(stderr, "Mesh '%s': Index %d is not in the range 0 to %d!", MeshStatus.sName, i, max); + i = 0; + } +} + +void CheckWhetherMeshHasEnded(); +static int HandleMeshMaterialList(const char *firstToken); +static int HandleMaterialOld(const char *firstToken); +static int HandleTextureFileName(const char *firstToken); + +static int HandleAmbient(); +static int HandleSolid(); +static int HandleNamed(); +static int HandleTriMesh(); +static int HandleHidden(); +static int HandleMapped(); +static int HandleVertex(); +static int HandleFace(); +static int HandleMaterial(); +static int HandleSmoothing(); +static int HandleDirect(); +static int HandlePosition(); +static int HandleLight(); +static int HandleSpotlight(); +static int HandleHotspot (); +static int HandleFalloff (); +static int HandleGlobal (); +// unneeded static int HandleCamera(); +static int HandleTarget(); +static int HandleBank(); +static int HandleNear(); + +static AscEntityType aEntities[] = +{ + { "Spotlight", HandleSpotlight, FALSE}, + { "Hotspot", HandleHotspot , FALSE}, + { "Falloff", HandleFalloff , FALSE}, + { "Global", HandleGlobal , FALSE}, +// unneeded { "Camera", HandleCamera, FALSE}, + { "Target", HandleTarget, FALSE}, + { "Bank", HandleBank, FALSE}, + { "Near", HandleNear, FALSE}, + { "Ambient", HandleAmbient, FALSE}, + { "Solid", HandleSolid, FALSE}, + { "Named", HandleNamed, FALSE}, + { "Tri-mesh,", HandleTriMesh, TRUE}, + { "Hidden", HandleHidden, TRUE}, + { "Mapped", HandleMapped, TRUE}, + { "Vertex", HandleVertex, TRUE}, + { "Face", HandleFace, TRUE}, + { "Material", HandleMaterial, TRUE}, + { "Smoothing", HandleSmoothing}, + { "Direct", HandleDirect, FALSE}, + { "Position", HandlePosition, FALSE}, + { "Light", HandleLight, FALSE}, + { NULL, NULL} +}; + + +static int ParseEntity(char *token) +// called recursively +// copied from ssgLoadX; A bit (?) of overkill for *.asc. +{ int i=0; + + while(aEntities[i].sName!=NULL) + { if (!strcmp(token,aEntities[i].sName)) + { if (aEntities[i].HandleEntity != NULL) + { /*char *sNextToken, *sName=globEmpty; + sNextToken=parser.getNextToken(0); + if ( parser.eof ) + { parser.error("unexpected end of file\n"); + return FALSE; + }*/ + if(!aEntities[i].appearsInsideAMesh) + CheckWhetherMeshHasEnded(); + if (!aEntities[i].HandleEntity(/*sNextToken*/)) + return FALSE; + } + else + { + parser.error("I am sorry, but Entity-type '%s' is not yet implemented.", aEntities[i].sName); + return FALSE ; + } + + break; + } + i++; + } + if (aEntities[i].sName==NULL) + { + parser.error("unexpected token %s", token); + return FALSE ; + } + return TRUE; +} + +#define MAX_ASC_MATERIALS 1024 + +struct AscMaterialType { + char *sName; + ssgSimpleState *ss; +} AscMaterials[MAX_ASC_MATERIALS]; +static int noOfAscmaterials=0; + + +#define MAX_NO_VERTICES_PER_FACE 1000 + +static class ssgLoaderWriterMesh currentMesh; + +static ssgSimpleState *currentState; + +// we only need one of these, since obviously the texture name is the only materialproperty in asc: +static ssgSimpleState *untexturedState = NULL; + +extern sgVec4 currentDiffuse; + + +static int HandleDirect() +// example line: +// "Direct light" +{ + parser.expectNextToken("light"); + return TRUE; +} +/* not needed since handled via the PreProcessor +static int HandleCamera() +// example line: +// Camera (51.944435mm) +{ + float Dummy; + parser.expectNextToken("("); + if (!parser.getNextFloat(Dummy, "Camera")) return FALSE; + parser.expectNextToken("mm)"); + return TRUE; +} +*/ +static int HandleTarget() +// example line: +// Target: X:7.573029 Y:-989.009766 Z:6.24827() +{ + float Dummy; + parser.expectNextToken(":"); + parser.expectNextToken("X"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Target X")) return FALSE; + parser.expectNextToken("Y"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Target Y")) return FALSE; + parser.expectNextToken("Z"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Target Z")) return FALSE; + return TRUE; +} + +static int HandleBank() +// example line: +// Bank angle: 0 degrees +{ + float Dummy; + parser.expectNextToken("angle"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Bank angle")) return FALSE; + parser.expectNextToken("degrees"); + return TRUE; +} + +static int HandleNear() +// example line: +// Near 0 Far 1000 +{ + float Dummy; + if (!parser.getNextFloat(Dummy, "Near")) return FALSE; + parser.expectNextToken("Far"); + if (!parser.getNextFloat(Dummy, "Far")) return FALSE; + return TRUE; +} + +static int HandlePosition() +// example line: +// "Position: X:-534.261475 Y:484.437958 Z:203.464706" +{ + float Dummy; + parser.expectNextToken(":"); + parser.expectNextToken("X"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "light X")) return FALSE; + parser.expectNextToken("Y"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "light Y")) return FALSE; + parser.expectNextToken("Z"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "light Z")) return FALSE; + return TRUE; + +} + +static int HandleSpotlight() +// example line: +// "Spotlight to: X:-1.4 Y:-0.607239 Z:-16.277397" +{ + float Dummy; + parser.expectNextToken("to"); + parser.expectNextToken(":"); + parser.expectNextToken("X"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Spotlight to X")) return FALSE; + parser.expectNextToken("Y"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Spotlight to Y")) return FALSE; + parser.expectNextToken("Z"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Spotlight to Z")) return FALSE; + return TRUE; +} + +static int HandleHotspot () +// example line: +// "Hotspot size: 54 degrees" +{ + float Dummy; + parser.expectNextToken("size"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Hotspot size")) return FALSE; + parser.expectNextToken("degrees"); + return TRUE; +} + +static int HandleFalloff () +// example line: +// "Falloff size: 54.5 degrees" +{ + float Dummy; + parser.expectNextToken("size"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(Dummy, "Falloff size")) return FALSE; + parser.expectNextToken("degrees"); + return TRUE; +} + +static int HandleGlobal () +// example line: +// "Global Shadow" +{ + parser.expectNextToken("Shadow"); + return TRUE; +} + + +static int HandleLight() +// example line: +// "Light color: Red=1 Green=1 Blue=1" +{ + float Dummy; + parser.expectNextToken("color"); + parser.expectNextToken(":"); + parser.expectNextToken("Red"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Light red")) return FALSE; + parser.expectNextToken("Green"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Light Green")) return FALSE; + parser.expectNextToken("Blue"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Light Blue")) return FALSE; + return TRUE; +} + + + +static int HandleAmbient() +// example line: +// "Ambient light color: Red=0.180392 Green=0.180392 Blue=0.180392" +{ + float Dummy; + parser.expectNextToken("light"); + parser.expectNextToken("color"); + parser.expectNextToken(":"); + parser.expectNextToken("Red"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Ambient red")) return FALSE; + parser.expectNextToken("Green"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Ambient green")) return FALSE; + parser.expectNextToken("Blue"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Ambient blue")) return FALSE; + return TRUE; +} + +static int HandleSolid() +// example line: +// "Solid background color: Red=0.345098 Green=0.6 Blue=0.807843" +{ + parser.expectNextToken("background"); + parser.expectNextToken("color"); + parser.expectNextToken(":"); + parser.expectNextToken("Red"); + parser.expectNextToken("="); + float Dummy; + if (!parser.getNextFloat(Dummy, "Background red")) return FALSE; + parser.expectNextToken("Green"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Background green")) return FALSE; + parser.expectNextToken("Blue"); + parser.expectNextToken("="); + if (!parser.getNextFloat(Dummy, "Background blue")) return FALSE; + return TRUE; +} + +static int HandleNamed() +// example line: +// "Named object: "EX1_A"" +{ + parser.expectNextToken("object"); + parser.expectNextToken(":"); +// assert(MeshStatus.isvalid); comes before trimesh ! + char *s; // cleaned up sName + s = parser.getNextToken("Mesh name"); + if(*s=='"') + s++; + if(s[strlen(s)-1]=='"') + s[strlen(s)-1]=0; + + MeshStatus.set_sName(s); + + return TRUE; +} + +static int HandleTriMesh() +// example line: +// "Tri-mesh, Vertices: 22 Faces: 18" +{ + parser.expectNextToken("Vertices"); + parser.expectNextToken(":"); + if (!parser.getNextInt(MeshStatus.maxVertex, "Number vertices")) return FALSE; + parser.expectNextToken("Faces"); + parser.expectNextToken(":"); + if (!parser.getNextInt(MeshStatus.maxFace, "Number faces")) return FALSE; + if(MeshStatus.isvalid) + { + fprintf(stderr, "MeshStatus.isvalid is TRUE. Probably, in the mesh preceding '%s', there are more faces than predicted", MeshStatus.sName); + return FALSE; + } + MeshStatus.isvalid = TRUE; + MeshStatus.curVertex = -1; + MeshStatus.curFace = -1; + MeshStatus.mapped = FALSE; + MeshStatus.materialed_but_not_mapped = FALSE; + MeshStatus.hidden = FALSE; // unused + noOfAscmaterials=0; + currentState = untexturedState; + + + currentMesh.reInit (); + currentMesh.setName( MeshStatus.sName ); + currentMesh.createVertices( MeshStatus.maxVertex ); + currentMesh.createFaces( MeshStatus.maxFace ); + + return TRUE; +} + +static int HandleHidden() +// example line: +// "Hidden" +{ + assert(MeshStatus.isvalid); + MeshStatus.hidden = TRUE; // unused + return TRUE; +} + +static int HandleMapped() +// example line: +// "Mapped" +{ + assert(MeshStatus.isvalid); + currentMesh.createPerVertexTextureCoordinates2( MeshStatus.maxVertex ) ; + currentMesh.createMaterials(); + currentMesh.createMaterialIndices( MeshStatus.maxFace ); + MeshStatus.mapped = TRUE; + return TRUE; +} + +static int HandleSmoothing() +// example line: +// "Smoothing: 1" +{ + int i; + parser.expectNextToken(":"); + if (!parser.getNextInt(i, "Smoothing")) return FALSE; + return TRUE; +} + + +int SetOrGetMaterial(char *sMatName) +// if exists, gets the index. Else sets sName and creates the ss +{ + char *s; // cleaned up sName + s = sMatName; + if(*s=='"') + s++; + if(s[strlen(s)-1]=='"') + s[strlen(s)-1]=0; + // **** search for it **** + for(int i=0; isetOpaque(); + currentState->disable(GL_BLEND); + currentState->disable(GL_ALPHA_TEST); + currentState->disable(GL_TEXTURE_2D); + currentState->enable(GL_COLOR_MATERIAL); + currentState->enable(GL_LIGHTING); + currentState->setShadeModel(GL_SMOOTH); + currentState->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + currentState->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + currentState->setShininess(50); + // set texture + currentState -> setTexture( current_options -> createTexture( s ) ); + currentState -> setName(s); + currentState -> enable( GL_TEXTURE_2D ); + currentMesh.addMaterial(¤tState); + return noOfAscmaterials-1; +} + + +static int HandleMaterial() +// example line: +// "Material:"BEXHUST"" +{ + parser.expectNextToken(":"); + if(!MeshStatus.mapped && !MeshStatus.materialed_but_not_mapped) + { + currentMesh.createMaterials(); + currentMesh.createMaterialIndices( MeshStatus.maxFace ); + MeshStatus.materialed_but_not_mapped = TRUE; + } + + + char *sMaterialName = parser.getNextToken("Material name"); + int i = SetOrGetMaterial(sMaterialName); + currentMesh.addMaterialIndex(i); + + return TRUE; +} + +static int HandleVertex() +// example line: +// "Vertex list:" +// *or* (!!) +// "Vertex 0: X:115.710701 Y:180.000305 Z:-60.796242 U:0.048042 V:0.996284" +{ + char *sNextToken = parser.peekAtNextToken( "vertex list?" ); + if(ulStrEqual("list", sNextToken)) + { + parser.expectNextToken("list"); + parser.expectNextToken(":"); + } + else + { + sgVec3 vert; + int i; + if (!parser.getNextInt(i, "vertex index")) return FALSE; + assert(MeshStatus.isvalid); + assert(MeshStatus.maxVertex > i); + assert(MeshStatus.curVertex == i-1); + MeshStatus.curVertex ++; + parser.expectNextToken(":"); + parser.expectNextToken("X"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(vert[0], "X")) return FALSE; + parser.expectNextToken("Y"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(vert[1], "Y")) return FALSE; + parser.expectNextToken("Z"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(vert[2], "Z")) return FALSE; + if(MeshStatus.mapped) + { + // read texture coordinates + sgVec2 tv; + parser.expectNextToken("U"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(tv[0], "U")) return FALSE; + parser.expectNextToken("V"); + parser.expectNextToken(":"); + if (!parser.getNextFloat(tv[1], "V")) return FALSE; + currentMesh.addPerVertexTextureCoordinate2( tv ) ; + } + currentMesh.addVertex(vert); + } + + return TRUE; +} + +static int HandleFace() +// example line: +// "Face list:" +// *or* (!!) +// "Face 0: A:0 B:10 C:7 AB:1 BC:1 CA:1" +{ + char *sNextToken = parser.peekAtNextToken( "Face list?" ); + if(ulStrEqual("list", sNextToken)) + { + parser.expectNextToken("list"); + parser.expectNextToken(":"); + } + else + { + int iFace, AB, _BC, CA; + int aiVertices[3]; + if (!parser.getNextInt(iFace, "Face index")) return FALSE; + assert(MeshStatus.isvalid); + assert(MeshStatus.maxFace > iFace); + assert(MeshStatus.curFace == iFace-1); + MeshStatus.curFace ++; + parser.expectNextToken(":"); + parser.expectNextToken("A"); + parser.expectNextToken(":"); + if (!parser.getNextInt(aiVertices[0], "A")) return FALSE; + CheckIndex(aiVertices[0], MeshStatus.maxVertex); + parser.expectNextToken("B"); + parser.expectNextToken(":"); + if (!parser.getNextInt(aiVertices[1], "B")) return FALSE; + CheckIndex(aiVertices[1], MeshStatus.maxVertex); + parser.expectNextToken("C"); + parser.expectNextToken(":"); + if (!parser.getNextInt(aiVertices[2], "C")) return FALSE; + CheckIndex(aiVertices[2], MeshStatus.maxVertex); + parser.expectNextToken("AB"); + parser.expectNextToken(":"); + if (!parser.getNextInt(AB, "AB")) return FALSE; + parser.expectNextToken("BC"); + parser.expectNextToken(":"); + if (!parser.getNextInt(_BC, "BC")) return FALSE; // "BC" is already used :-/ + parser.expectNextToken("CA"); + parser.expectNextToken(":"); + if (!parser.getNextInt(CA, "CA")) return FALSE; + currentMesh.addFaceFromIntegerArray(3, aiVertices); + } + return TRUE; +} + + +// end todo ############################################################## + + + +/////////////////////////////////////////////////// old +// start alt todo ############################################################## + + + + +static int HandleTextureFileName(const char *firstToken) +{/* + TextureFilename { + "../image/box_top.gif"; + } #TextureFilename + */ + char *filename_ptr, *filename = new char [ strlen(firstToken)+1 ] ; + assert(filename!=NULL); + strcpy ( filename, firstToken ) ; + filename_ptr = filename ; + + if ( filename_ptr[0] == '"' ) + filename_ptr++; + if (filename_ptr[strlen(filename_ptr)-1] == '"') + filename_ptr[strlen(filename_ptr)-1] = 0; + currentState -> setTexture( current_options -> createTexture( filename_ptr ) ); + currentState -> enable( GL_TEXTURE_2D ); + + + parser.expectNextToken(";"); + parser.expectNextToken("}"); + delete [] filename; + return TRUE; +} + +static int HandleMaterialOld(const char *firstToken) +// return TRUE on success +{ SGfloat power; + int bFoundTextureFileName = FALSE; + sgVec4 specularColour, EmissiveColour; + + // read body + if (! Ascii2Float(currentDiffuse[0], firstToken, "Facecolour R")) + return FALSE; + + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[1], "Facecolour G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[2], "Facecolour B")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[3], "Facecolour A")) return FALSE; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + if (!parser.getNextFloat(power, "power")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[0], "Specular R")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[1], "Specular G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[2], "Specular B")) return FALSE; + specularColour[3] = 0.0; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[0], "Emissive R")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[1], "Emissive G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[2], "Emissive B")) return FALSE; + EmissiveColour[3] = 0.0; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + + // create SimpleState + + currentState = new ssgSimpleState () ; + +// currentState -> setMaterial ( GL_AMBIENT, mat -> amb ) ; + currentState -> setMaterial ( GL_DIFFUSE, currentDiffuse) ; + currentState -> setMaterial ( GL_SPECULAR, specularColour) ; + currentState -> setMaterial ( GL_SPECULAR, specularColour[0], + specularColour[1], specularColour[2], currentDiffuse[3] ) ; + currentState -> setMaterial ( GL_EMISSION, EmissiveColour[0], + EmissiveColour[1], EmissiveColour[2], currentDiffuse[3] ) ; + + currentState -> setShininess ( power ) ; // Fixme, NIV14: Is that correct? + + currentState -> enable ( GL_COLOR_MATERIAL ) ; + currentState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + currentState -> enable ( GL_LIGHTING ) ; + currentState -> setShadeModel ( GL_SMOOTH ) ; + + if ( currentDiffuse[3] > 0.0f ) + { + currentState -> disable ( GL_ALPHA_TEST ) ; + currentState -> enable ( GL_BLEND ) ; + currentState -> setTranslucent () ; + } + else + { + currentState -> disable ( GL_BLEND ) ; + currentState -> setOpaque () ; + } + currentState -> disable( GL_TEXTURE_2D ); + + + + while(TRUE) + { char *nextToken =parser.getNextToken(0); + if (0==strcmp("}", nextToken)) + { currentMesh.addMaterial( ¤tState ); + return TRUE; // Material is finished. success + } + + if ( 0!= strcmp("TextureFilename", nextToken) ) + { parser.error("TextureFilename expected!\n"); + return FALSE; + } + if ( bFoundTextureFileName ) + { parser.error("Only one TextureFileName per Material please!\n"); + return FALSE; + } + if (!ParseEntity(nextToken)) // read "TextureFileName" + return FALSE; + bFoundTextureFileName = TRUE; + } + return TRUE; //lint !e527 +} + + +static int HandleMeshMaterialList(const char *firstToken) +{ + u32 i, nFaceIndexes, nMaterialsRead = 0, nMaterials; + + if (! Ascii2UInt(nMaterials, firstToken, "nMaterials")) + return FALSE; + + parser.expectNextToken(";"); + currentMesh.createMaterials( nMaterials ); + if (!parser.getNextUInt(nFaceIndexes, "number of Face Indexes")) + return FALSE; + currentMesh.createMaterialIndices( nFaceIndexes ) ; + parser.expectNextToken(";"); + + + if ( nFaceIndexes > currentMesh.getNumFaces()) + { parser.error("No of face indexes of materiallist (%d) is greater than then no " + "of faces (%d)!\n" + "Therefore the material list is ignored!", + ( int ) nFaceIndexes, ( int ) currentMesh.getNumFaces()); + return TRUE; // go on parsing + } + if ( nFaceIndexes > currentMesh.getNumFaces()) + parser.message("Informational: No of face indexes of materiallist (%d) is less than then no " + "of faces (%d)\n" , + ( int ) nFaceIndexes, ( int ) currentMesh.getNumFaces()); + for ( i=0 ; i= nMaterials ) + { parser.error("Too many Materials!\n"); + return FALSE; + } + if (!ParseEntity(nextToken)) // read "Material" + return FALSE; + nMaterialsRead++; + } + return TRUE; //lint !e527 +} + + + +// end alt todo ############################################################## + +void CheckWhetherMeshHasEnded() +{ if(MeshStatus.isvalid && + (MeshStatus.curFace == MeshStatus.maxFace-1) && + (MeshStatus.curVertex == MeshStatus.maxVertex-1)) + { + // end mesh PART FROM MATREEIAL AND SMOOTH + //u32 i, j, nNoOfVertices, nNoOfVerticesForThisFace, nNoOfFaces; + + + if ( currentState == NULL ) + { currentState = new ssgSimpleState(); + currentState->setOpaque(); + currentState->disable(GL_BLEND); + currentState->disable(GL_ALPHA_TEST); + currentState->disable(GL_TEXTURE_2D); + currentState->enable(GL_COLOR_MATERIAL); + currentState->enable(GL_LIGHTING); + currentState->setShadeModel(GL_SMOOTH); + currentState->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + currentState->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + currentState->setShininess(50); + /* + currentState -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5); + currentState -> setMaterial ( GL_DIFFUSE, 0.7, 0.7, 0.7); // light grey + currentState -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0); + currentState -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0); + + currentState -> setShininess ( 3 ) ; + + //currentState -> enable ( GL_COLOR_MATERIAL ) ; + //currentState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + currentState -> enable ( GL_LIGHTING ) ; + currentState -> setShadeModel ( GL_SMOOTH ) ; + + currentState -> disable ( GL_BLEND ) ; + currentState -> setOpaque () ; + currentState -> disable( GL_TEXTURE_2D ); + */ + } + + currentMesh.addToSSG( + currentState // Pfusch, kludge. NIV135 + , + current_options, + curr_branch_); + MeshStatus.isvalid = FALSE; + } +} + +static int parse() +{ + char* token; + token = parser.getNextToken(0); // getNextToken -> line structure is ignored (IOW line ends are ignored) + while (! parser.eof ) + { + if (!ParseEntity(token)) + return FALSE; + + token = parser.getNextToken(0); + } + CheckWhetherMeshHasEnded(); + return TRUE ; +} + + +ssgEntity *ssgLoadASC ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + + MeshStatus.isvalid=FALSE; + MeshStatus.sName = NULL; + + + untexturedState = new ssgSimpleState; + untexturedState->setOpaque(); + untexturedState->disable(GL_BLEND); + untexturedState->disable(GL_ALPHA_TEST); + untexturedState->disable(GL_TEXTURE_2D); + untexturedState->enable(GL_COLOR_MATERIAL); + untexturedState->enable(GL_LIGHTING); + untexturedState->setShadeModel(GL_SMOOTH); + untexturedState->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.7f, 1.0f); + untexturedState->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.7f, 1.0f); + untexturedState->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + untexturedState->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + untexturedState->setShininess(50); + untexturedState->ref(); + + currentState = untexturedState; + top_branch = new ssgBranch ; + curr_branch_ = top_branch; + if ( !parser.openFile( fname, &parser_spec )) + { + delete top_branch ; + return 0; + } + if ( !parse() ) + { + delete top_branch ; + top_branch = 0 ; + } +// parse_free(); + parser.closeFile(); + + untexturedState->deRef(); + if(untexturedState->getRef()==0) + delete untexturedState; + return top_branch ; +} diff --git a/src/ssg/ssgLoadASE.cxx b/src/ssg/ssgLoadASE.cxx new file mode 100644 index 0000000..5b63a7f --- /dev/null +++ b/src/ssg/ssgLoadASE.cxx @@ -0,0 +1,1704 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadASE.cxx 1988 2004-10-29 22:41:37Z wolfram_kuss $ +*/ + +/**** +* NAME +* ssgLoadASE -- ASE model loader +* +* DESCRIPTION +* ssgLoadASE will load an ASE model exported from 3dsmax using +* the standard ascii export v2.00 plugin. +* +* The material list and geometry objects are parsed and converted +* into an SSG scene graph. shape, light, camera, and helper +* nodes are ignored. +* +* If the geometry has vertex colours (pre-lit) then lighting is +* disabled for that leaf. +* +* mesh animation creates ssgSelector entities. transform +* animation creates ssgTransform entities with ssgTransformArray +* user_data. see the viewer example for details on controlling +* animation. +* +* NOTE: be sure you _reset the transform_ of the top-level object +* in 3dsmax before exporting transform animation. +* +* Please report any bugs to the author. Complete information about +* the ASE format can be found in the 3dsmax SDK source code. +* +* AUTHOR +* Dave McClurg +* +* CREATION DATE +* Feb-2000 +* +* MODIFICATION HISTORY +* November 2000 - v1.1, Dave McClurg +* o support for transform animation samples +****/ + +#include "ssgLocal.h" +#include "ssgParser.h" + + +#define u32 unsigned int +#define f32 float +#define cchar const char + + +static ssgLoaderOptions* current_options = NULL ; + +struct aseVertexBuffer +{ + bool use_flag ; + int index ; + sgVec3 v ; + sgVec2 tv ; + sgVec3 cv ; +} ; + + +struct aseFace +{ + u32 v[3]; + u32 tv[3]; + u32 cv[3]; + u32 sub_index; + + // SAC: Additional vals to track normals for each face vertex. + // Since each vertex can supposedly have various normals if shared by two + // non-smoothed faces, we track them per face. I haven't checked this as + // good as I should have with smoothed & unsmoothed shapes :) + bool has_vertex_normals; + sgVec3 vn [3]; +}; + + +struct aseTransform +{ + sgVec3 pos ; + sgVec3 axis ; // axis of rotation (unit vector) + f32 angle ; // angle of rotation in radians (always >0) + sgVec3 scale ; +} ; + + +struct aseMesh +{ + u32 num_faces ; + u32 num_verts ; + u32 num_tverts ; + u32 num_cverts ; + + aseFace* faces ; + sgVec3* verts ; + sgVec2* tverts ; + sgVec3* cverts ; + + aseMesh(); + ~aseMesh(); +}; + + +struct aseObject +{ + enum Type { GEOM, HELPER, CAMERA } ; + int type ; + char* name ; + char* parent ; + bool inherit_pos [3] ; + sgVec3 pos ; + sgVec3 target ; + u32 mat_index ; + u32 num_tkeys ; + aseTransform* tkeys ; + + enum { MAX_FRAMES = 256 }; + aseMesh* mesh_list[ MAX_FRAMES ]; + int mesh_count ; + + aseObject( Type type ); + ~aseObject(); +}; + +struct aseMaterial +{ + char* name ; + u32 mat_index ; + u32 sub_index ; + bool sub_flag ; + + sgVec4 amb ; + sgVec4 diff ; + sgVec4 spec ; + f32 shine ; + f32 transparency ; + + char* tfname ; + sgVec2 texrep ; + sgVec2 texoff ; +}; + + +#define MAX_MATERIALS 1000 +static aseMaterial** materials ; +static u32 num_materials ; + +static u32 first_frame ; +static u32 last_frame ; +static u32 frame_speed ; +static u32 ticks_per_frame ; +static u32 num_frames=0 ; + +static _ssgParserSpec parser_spec = +{ + "\r\n\t ", // delim_chars_skipable + 0, // delim_chars_non_skipable + NULL, // pre_processor + "{", // open_brace_chars + "}", // close_brace_chars + '"', // quote_char + 0, // comment_char + 0 // comment_string +} ; + +static _ssgParser parser; +static ssgBranch* top_branch; + +aseMesh::aseMesh() +{ + memset(this,0,sizeof(aseMesh)); +} + + +aseMesh::~aseMesh() +{ + delete[] faces; + delete[] verts; + delete[] tverts; + delete[] cverts; +} + + +aseObject::aseObject( aseObject::Type _type ) +{ + memset(this,0,sizeof(aseObject)); + type = _type ; +} + + +aseObject::~aseObject() +{ + delete[] name; + delete[] parent; + delete[] tkeys; + for ( int i=0; imat_index == mat_index && mat->sub_flag ) + count ++; + } + return count ; +} + + +static aseMaterial* find_material( u32 mat_index, u32 sub_index ) +{ + u32 i; + + //find sub-material + for ( i=0; imat_index == mat_index && mat->sub_index == sub_index ) + return(mat); + } + + //just match material # + for ( i=0; imat_index == mat_index ) + return(mat); + } + + parser.error("unknown material #%d",mat_index); + return(0); +} + + +static ssgSimpleState* make_state( aseMaterial* mat, bool prelit ) +{ + if ( mat -> tfname != NULL) + { + ssgSimpleState *st = current_options -> createSimpleState ( mat -> tfname ) ; + if ( st != NULL ) + return st ; + } + + ssgSimpleState *st = new ssgSimpleState () ; + + bool has_alpha = false ; + + if (mat -> tfname != NULL) + { + ssgTexture* tex = current_options -> createTexture ( mat->tfname ) ; + has_alpha = tex -> hasAlpha () ; + + st -> setTexture ( tex ) ; + st -> enable ( GL_TEXTURE_2D ) ; + } + else + { + st -> disable( GL_TEXTURE_2D ) ; + } + + st -> disable ( GL_ALPHA_TEST ) ; + + if ( sgCompareFloat ( mat -> transparency, 0.0f, 0.01f ) > 0 || has_alpha ) + { + st -> enable ( GL_BLEND ) ; + st -> setTranslucent () ; + } + else + { + st -> disable ( GL_BLEND ) ; + st -> setOpaque () ; + } + + if ( prelit ) + { + st -> disable ( GL_LIGHTING ) ; + } + else + { + st -> setMaterial ( GL_AMBIENT, mat -> amb ) ; + st -> setMaterial ( GL_DIFFUSE, mat -> diff ) ; + st -> setMaterial ( GL_SPECULAR, mat -> spec ) ; + st -> setShininess ( mat -> shine ) ; + st -> disable ( GL_COLOR_MATERIAL ) ; + st -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + st -> enable ( GL_LIGHTING ) ; + } + + st -> setShadeModel ( GL_SMOOTH ) ; + + return st ; +} + + +static ssgSimpleState* get_state( aseMaterial* mat, bool prelit ) +{ + // is material an ifl (image file list) +#ifdef UL_WIN32 + if ( strnicmp ( "ifl_", mat -> name, 4 ) == 0 ) +#else + if ( strncasecmp ( "ifl_", mat -> name, 4 ) == 0 ) +#endif + { + u32 num_subs = count_sub_materials ( mat -> mat_index ); + if ( num_subs < 2 ) + parser.error("ifl material only has <2 frames: %s",mat -> name); + + ssgStateSelector* selector = new ssgStateSelector ( num_subs ) ; + for ( u32 i=0; i mat_index, i ) ; + assert ( mat2 != NULL ) ; + + ssgSimpleState* st = make_state ( mat2, prelit ) ; + selector -> setStep ( i, st ) ; + } + selector -> selectStep ( 0 ) ; + return selector ; + } + return make_state ( mat, prelit ) ; +} + + +static int parse_map( aseMaterial* mat ) +// return TRUE on success +{ + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*BITMAP")) + { + if ( mat->tfname != NULL ) + parser.error("multiple textures for material: %s",mat->name); + else + { + char* fname; + if (! parser.parseString(fname, "bitmap filename") ) + return FALSE; + + //strip existing directory from fname + char* slash = strrchr ( fname, '/' ) ; + if ( !slash ) + slash = strrchr ( fname, '\\' ) ; //for dos + if ( slash ) + fname = slash + 1 ; + + mat->tfname = ulStrDup ( fname ) ; + } + } + else if (!strcmp(token,"*UVW_U_TILING")) + { + if (! parser.parseFloat(mat->texrep[0], "tiling.u")) + return FALSE; + } + else if (!strcmp(token,"*UVW_V_TILING")) + { + if (! parser.parseFloat(mat->texrep[1], "tiling.v")) + return FALSE; + } + else if (!strcmp(token,"*UVW_U_OFFSET")) + { + if (! parser.parseFloat(mat->texoff[0] , "offset.u")) + return FALSE; + } + else if (!strcmp(token,"*UVW_V_OFFSET")) + { + if (! parser.parseFloat(mat->texoff[1] , "offset.v")) + return FALSE; + } + } + return TRUE; +} + + +static int parse_material( u32 mat_index, u32 sub_index, cchar* mat_name ) +// return TRUE on success +{ + if ( num_materials >= MAX_MATERIALS ) + { + parser.error( "too many materials" ); + + // skip material definition + int startLevel = parser.level; + while (parser.getLine( startLevel ) != NULL) + ; + return TRUE; // go on parsing + } + aseMaterial* mat = new aseMaterial; + materials [ num_materials++ ] = mat ; + + memset ( mat, 0, sizeof(aseMaterial) ) ; + mat->mat_index = mat_index ; + mat->sub_index = sub_index ; + mat->sub_flag = ( mat_name != 0 ) ; + mat->texrep[0] = 1.0f ; + mat->texrep[1] = 1.0f ; + mat->texoff[0] = 0.0f ; + mat->texoff[1] = 0.0f ; + + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*MATERIAL_NAME")) + { + char* name; + if (! parser.parseString(name, "mat name") ) + return FALSE; + + if ( mat->sub_flag ) + { + char buff [ 256 ] ; + sprintf( buff, "%s, sub#%d", mat_name, sub_index ); + + mat->name = ulStrDup ( buff ) ; + } + else + mat->name = ulStrDup ( name ) ; + } + else if (!strcmp(token,"*MATERIAL_AMBIENT")) + { + if (! parser.parseFloat(mat->amb[ 0 ], "amb.r")) + return FALSE; + if (! parser.parseFloat(mat->amb[ 1 ], "amb.g")) + return FALSE; + if (! parser.parseFloat(mat->amb[ 2 ], "amb.b")) + return FALSE; + mat->amb[ 3 ] = 1.0f; + } + else if (!strcmp(token,"*MATERIAL_DIFFUSE")) + { + if (! parser.parseFloat(mat->diff[ 0 ], "diff.r")) + return FALSE; + if (! parser.parseFloat(mat->diff[ 1 ], "diff.g")) + return FALSE; + if (! parser.parseFloat(mat->diff[ 2 ], "diff.b")) + return FALSE; + mat->diff[ 3 ] = 1.0f; + } + else if (!strcmp(token,"*MATERIAL_SPECULAR")) + { + if (! parser.parseFloat(mat->spec[ 0 ], "spec.r")) + return FALSE; + if (! parser.parseFloat(mat->spec[ 1 ], "spec.g")) + return FALSE; + if (! parser.parseFloat(mat->spec[ 2 ], "spec.b")) + return FALSE; + mat->spec[ 3 ] = 1.0f; + } + else if (!strcmp(token,"*MATERIAL_SHINE")) + { + if (! parser.parseFloat(mat->shine, "shine")) + return FALSE; + + // SAC: Shine seems off. If I load a 3ds of the same shape + // the shine is 256x as much and works better, so I'll tweak + // the val here :) I couldn't clarify the range used by + // MAX SDK anywhere in the docs (though this assumes 0 - 0.5). + // OpenGL is 0-128. + mat->shine = mat->shine * 256.0f; + if ( mat->shine > 128 ) + mat->shine = 128; + } + else if (!strcmp(token,"*MATERIAL_TRANSPARENCY")) + { + if (! parser.parseFloat(mat->transparency, "transparency")) + return FALSE; + } + else if (!strcmp(token,"*MAP_DIFFUSE")) + { + //Need: what about MAP_GENERIC, MAP_AMBIENT, etc?? + if (! parse_map( mat )) + return FALSE; + } + else if (!strcmp(token,"*SUBMATERIAL")) + { + u32 sub_index; + if (! parser.parseUInt(sub_index, "sub mat #")) + return FALSE; + if (! parse_material( mat_index, sub_index, mat->name )) + return FALSE; + } + } + + //parser.message("material: %s (%s)",mat->name,mat->tfname); + return TRUE; +} + + +static int parse_material_list() +// return TRUE on success +{ + if ( num_materials ) + parser.error("multiple material lists"); + + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*MATERIAL")) + { + u32 mat_index; + if (! parser.parseUInt(mat_index, "mat #")) + return FALSE; + if (! parse_material( mat_index, 9999, NULL )) + return FALSE; + } + } + return TRUE; +} + + +static int parse_mesh( aseObject* obj ) +{ + aseMesh* mesh = NULL ; + u32 mesh_face_normal_index = 0x7fffffff; + u32 mesh_face_normal_count = 0; + + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if ( mesh == NULL ) + { + u32 frame = aseObject::MAX_FRAMES ; + + if (!strcmp(token,"*TIMEVALUE")) + { + u32 time; + if (! parser.parseUInt(time, "time")) + return FALSE; + frame = (time + (ticks_per_frame-1)) / ticks_per_frame - first_frame; + } + else + { + parser.error("missing *TIMEVALUE"); + frame = aseObject::MAX_FRAMES ; + } + + if ( frame >= aseObject::MAX_FRAMES || obj->mesh_list [ frame ] != NULL ) + { + //ignore this mesh + while (parser.getLine( startLevel )) ; + return TRUE; // go on parsing + } + + mesh = new aseMesh ; + obj->mesh_list [ frame ] = mesh ; + obj->mesh_count ++ ; + } + else if (!strcmp(token,"*MESH_NUMFACES")) + { + if (mesh -> faces) + parser.error("%s already seen",token); + else + { + if (! parser.parseUInt(mesh -> num_faces, "num_faces")) + return FALSE; + mesh -> faces = new aseFace[ mesh -> num_faces ]; + + // SAC: Help clear all the has_vertex_normals flags + memset ( mesh -> faces, 0, mesh -> num_faces * sizeof (aseFace) ); + } + } + else if (!strcmp(token,"*MESH_NUMTVFACES")) + { + u32 ntfaces; + if (! parser.parseUInt(ntfaces, "ntfaces")) + return FALSE; + if (ntfaces != mesh -> num_faces) + parser.error("NUMTFACES(%d) != NUMFACES(%d)",ntfaces,mesh -> num_faces); + } + else if (!strcmp(token,"*MESH_NUMCVFACES")) + { + u32 ncfaces; + if (! parser.parseUInt(ncfaces, "ncfaces")) + return FALSE; + if (ncfaces != mesh -> num_faces) + parser.error("NUMCFACES(%d) != NUMFACES(%d)",ncfaces,mesh -> num_faces); + } + else if (!strcmp(token,"*MESH_NUMVERTEX")) + { + if (mesh -> verts) + parser.error("%s already seen",token); + else + { + if (! parser.parseUInt(mesh -> num_verts, "num_verts")) + return FALSE; + mesh -> verts = new sgVec3[ mesh -> num_verts ]; + } + } + else if (!strcmp(token,"*MESH_NUMTVERTEX")) + { + if (mesh -> tverts) + parser.error("%s already seen",token); + else + { + if (! parser.parseUInt(mesh -> num_tverts, "num_tverts")) + return FALSE; + if (mesh -> num_tverts) + mesh -> tverts = new sgVec2[ mesh -> num_tverts ]; + } + } + else if (!strcmp(token,"*MESH_NUMCVERTEX")) + { + if (mesh -> cverts) + parser.error("%s already seen",token); + else + { + if (! parser.parseUInt(mesh -> num_cverts, "num_cverts")) + return FALSE; + if (mesh -> num_cverts) + mesh -> cverts = new sgVec3[ mesh -> num_cverts ]; + } + } + else if (!strcmp(token,"*MESH_FACE")) + { + u32 index; + // wk: The old parseInt converted a "3:" into 3, + // the new would give an error. Therefore I had to change this code: + char *my_endptr, *my_token = parser.parseToken("face #"); + if (my_token[strlen(my_token)-1]==':') + my_token[strlen(my_token)-1]=0; + + index = (unsigned int)(strtol( my_token, &my_endptr, 10)); + if ( (my_endptr != NULL) && (*my_endptr != 0)) + { parser.error("The field face # should contain an integer number but contains %s", my_token) ; + return FALSE; + } + + + if (index >= mesh -> num_faces) + parser.error("bad face #"); + else + { + aseFace& face = mesh -> faces[ index ]; + + parser.expect("A:"); + if (! parser.parseUInt(face.v[0], "face.v[0]")) + return FALSE; + parser.expect("B:"); + if (! parser.parseUInt(face.v[1], "face.v[1]")) + return FALSE; + parser.expect("C:"); + if (! parser.parseUInt(face.v[2], "face.v[2]")) + return FALSE; + + //search for other flags + token = parser.parseToken(0); + while ( ! parser.eol ) + { + if ( strcmp(token,"*MESH_MTLID") == 0 ) + { + if (! parser.parseUInt(face.sub_index, "mat #")) + return FALSE; + } + token = parser.parseToken(0); + } + } + } + else if (!strcmp(token,"*MESH_TFACE")) + { + u32 index; + if (! parser.parseUInt(index, "tface #")) + return FALSE; + if (index >= mesh -> num_faces) + parser.error("bad tface #"); + else + { + aseFace& face = mesh -> faces[ index ]; + + if (! parser.parseUInt(face.tv[0], "tface.tv[0]")) + return FALSE; + if (! parser.parseUInt(face.tv[1], "tface.tv[1]")) + return FALSE; + if (! parser.parseUInt(face.tv[2], "tface.tv[2]")) + return FALSE; + } + } + else if (!strcmp(token,"*MESH_CFACE")) + { + u32 index; + if (! parser.parseUInt(index, "cface #")) + return FALSE; + if (index >= mesh -> num_faces) + parser.error("bad cface #"); + else + { + aseFace& face = mesh -> faces[ index ]; + + if (! parser.parseUInt(face.cv[0], "tface.cv[0]")) + return FALSE; + if (! parser.parseUInt(face.cv[1], "tface.cv[1]")) + return FALSE; + if (! parser.parseUInt(face.cv[2], "tface.cv[2]")) + return FALSE; + } + } + // SAC: START OF VERTEX NORMAL PARSING SECTION + // NOTE: Assumes that three correctly-ordered *MESH_VERTEXNORMAL lines + // come after each *MESH_FACENORMAL line. Which seems to be the case + // in current exporter + else if (!strcmp(token,"*MESH_FACENORMAL")) + { + if (! parser.parseUInt(mesh_face_normal_index, "face normal #")) + return FALSE; + mesh_face_normal_count = 0; + } + else if (!strcmp(token,"*MESH_VERTEXNORMAL")) + { + //int nverts = mesh -> num_verts; + + if ( mesh_face_normal_index >= mesh -> num_faces ) + parser.error("bad mesh_face_normal_index #"); + if ( mesh_face_normal_count >= 3 ) + parser.error("bad mesh_face_normal_count"); + + aseFace& face = mesh -> faces[ mesh_face_normal_index ]; + sgVec3& vn = face . vn [ mesh_face_normal_count ]; + face.has_vertex_normals = true; + u32 vertex_index; + if (! parser.parseUInt(vertex_index, "vertex normal #")) + return FALSE; + if ( face.v [mesh_face_normal_count] != vertex_index ) + parser.error("bad MESH_VERTEXNORMAL entries out of order!"); + if (! parser.parseFloat(vn[0], "vertexNormal.x")) + return FALSE; + if (! parser.parseFloat(vn[1], "vertexNormal.y")) + return FALSE; + if (! parser.parseFloat(vn[2], "vertexNormal.z")) + return FALSE; + + // sgNormaliseVec3 ( vn ) ; + mesh_face_normal_count++; + } + // SAC: END OF VERTEX NORMAL PARSING SECTION + else if (!strcmp(token,"*MESH_VERTEX")) + { + u32 index; + if (! parser.parseUInt(index, "vertex #")) + return FALSE; + if (index >= mesh -> num_verts) + parser.error("bad vertex #"); + else + { + sgVec3& vert = mesh -> verts[ index ]; + + if (! parser.parseFloat(vert[0], "vert.x")) + return FALSE; + if (! parser.parseFloat(vert[1], "vert.y")) + return FALSE; + if (! parser.parseFloat(vert[2], "vert.z")) + return FALSE; + } + } + else if (!strcmp(token,"*MESH_TVERT")) + { + u32 index; + if (! parser.parseUInt(index, "tvertex #")) + return FALSE; + if (index >= mesh -> num_tverts) + parser.error("bad tvertex #"); + else + { + sgVec2& tvert = mesh -> tverts[ index ]; + + if (! parser.parseFloat(tvert[0], "tvert.x")) + return FALSE; + if (! parser.parseFloat(tvert[1], "tvert.y")) + return FALSE; + } + } + else if (!strcmp(token,"*MESH_VERTCOL")) + { + u32 index; + if (! parser.parseUInt(index, "cvertex #")) + return FALSE; + if (index >= mesh -> num_cverts) + parser.error("bad cvertex #"); + else + { + sgVec3& cvert = mesh -> cverts[ index ]; + + if (! parser.parseFloat(cvert[0], "cvert.x")) + return FALSE; + if (! parser.parseFloat(cvert[1], "cvert.y")) + return FALSE; + if (! parser.parseFloat(cvert[2], "cvert.z")) + return FALSE; + } + } + } + return TRUE; +} + + +static void get_texcoord ( aseMaterial* mat, sgVec2 tv ) +{ + //invert Y coordinate + tv[1] = 1.0f - tv[1] ; + + tv[0] *= mat->texrep[0] ; + tv[1] *= mat->texrep[1] ; + tv[0] += mat->texoff[0] ; + tv[1] += mat->texoff[1] ; +} + + +static ssgLeaf* add_points( aseObject* obj, aseMesh* mesh ) +{ + ssgVertexArray* vl = NULL ; + + if ( obj->type == aseObject::CAMERA ) + { + /* compute a normalized target vector */ + sgVec3 target ; + sgCopyVec3 ( target, obj->target ) ; + sgSubVec3 ( target, obj->pos ) ; + + SGfloat len = sgLengthVec3 ( target ) ; + if ( len == SG_ZERO ) + { + vl = new ssgVertexArray ( 1 ) ; + vl -> add ( obj->pos ) ; + } + else + { + vl = new ssgVertexArray ( 2 ) ; + + sgNormaliseVec3 ( target ) ; + sgAddVec3 ( target, obj->pos ) ; + + vl -> add ( obj->pos ) ; + vl -> add ( target ) ; + } + } + else if ( mesh != NULL ) + { + u32 num_verts = mesh -> num_verts ; + if ( num_verts == 0 ) + return NULL; + + vl = new ssgVertexArray ( num_verts ) ; + + //pass the data to ssg + sgVec3* vert = mesh -> verts ; + for ( u32 i=0; i < num_verts; i++, vert++ ) + vl -> add ( *vert ) ; + } + else + { + return NULL ; + } + + ssgVtxTable* leaf = new ssgVtxTable ( GL_POINTS, + vl, NULL, NULL, NULL ) ; + + if ( leaf != NULL ) + { + // leaves with no faces are used for game logic + // and should not be culled and rendered + leaf -> clrTraversalMaskBits ( SSGTRAV_CULL ) ; + } + + return ssgGetCurrentOptions () -> createLeaf ( leaf, obj -> name ) ; +} + + +static ssgLeaf* add_mesh( aseObject* obj, aseMesh* mesh, u32 sub_index ) +{ + u32 i ; + + aseMaterial* mat = find_material ( obj->mat_index, sub_index ) ; + if ( mat == NULL ) + return NULL ; + + //compute number of faces for this sub-material + u32 num_faces = mesh -> num_faces ; + if ( mat->sub_flag ) + { + num_faces = 0 ; + aseFace* face = mesh -> faces ; + for ( i=0; i num_faces; i++, face++ ) + { + if ( face->sub_index == mat->sub_index ) + num_faces ++ ; + } + } + if ( num_faces == 0 ) + return NULL ; + + if ( mesh -> cverts == NULL ) + { + u32 num_verts = num_faces * 3 ; + + //pass the data to ssg + ssgSimpleState* st = get_state ( mat, false ) ; + ssgColourArray* cl = new ssgColourArray ( 1 ) ; + ssgVertexArray* vl = new ssgVertexArray ( num_verts ) ; + ssgNormalArray* nl = new ssgNormalArray ( num_verts ) ; + ssgTexCoordArray* tl = 0 ; + if ( mesh -> tverts ) + tl = new ssgTexCoordArray ( num_verts ) ; + + //set the material colour + sgVec4 c ; + sgCopyVec3 ( c, mat -> diff ) ; + c[3] = 1.0f - mat -> transparency ; + cl -> add ( c ) ; + + aseFace* face = mesh -> faces ; + for ( i=0; i num_faces; i++, face++ ) + { + if ( mat->sub_flag && face->sub_index != mat->sub_index ) + continue ; + + sgVec3 n ; + sgMakeNormal ( n, + mesh -> verts[ face->v[0] ] , + mesh -> verts[ face->v[1] ] , + mesh -> verts[ face->v[2] ] ) ; + + for ( u32 j=0; j<3; j++ ) + { + vl -> add ( mesh -> verts[ face->v[j] ] ) ; + + // SAC: If per-vertex normals were found, use 'em. + // Else use the face normal computed above + if ( face -> has_vertex_normals ) + nl -> add ( face->vn [j] ) ; + else + nl -> add ( n ) ; + + if ( mesh -> tverts ) + { + sgVec2 tv ; + sgCopyVec2 ( tv, mesh -> tverts[ face->tv[j] ] ) ; + get_texcoord ( mat, tv ) ; + tl -> add ( tv ) ; + } + } + } + + ssgVtxTable* leaf = new ssgVtxTable ( GL_TRIANGLES, + vl, nl, tl, cl ) ; + leaf -> setCullFace ( TRUE ) ; + leaf -> setState ( st ) ; + return current_options -> createLeaf ( leaf, obj->name ) ; + } + + //allocate map_index array + int* map_index = new int [ mesh -> num_faces * 3 ] ; + + //allocate the vertex list + u32 max_verts = mesh -> num_verts + mesh -> num_faces * 3 ; + aseVertexBuffer* vert_list = new aseVertexBuffer [ max_verts ] ; + + //mark each vertex as *not* used + aseVertexBuffer* vert = vert_list ; + for ( i=0; i < max_verts; i++, vert++ ) + { + vert -> use_flag = false ; + } + + u32 extra_verts = 0 ; + + //build the vertex list + aseFace* face = mesh -> faces ; + for ( i=0; i num_faces; i++, face++ ) + { + if ( mat->sub_flag && face->sub_index != mat->sub_index ) + continue ; + + for ( u32 j=0; j<3; j++ ) + { + int k = i*3+j; + + map_index [k] = face->v[j] ; + vert = vert_list + map_index[k]; + + if ( vert -> use_flag ) + { + //check for match + bool match = true ; + if ( mesh -> tverts && + sgCompareVec2 ( vert -> tv, mesh -> tverts[ face->tv[j] ], 0.0f ) != 0 ) + match = false ; + else if ( mesh -> cverts && + sgCompareVec3 ( vert -> cv, mesh -> cverts[ face->cv[j] ], 0.0f ) != 0 ) + match = false ; + if ( match ) + continue ; //texcoord and color matches other vertex + + extra_verts ++ ; + + map_index [k] = mesh -> num_verts + k ; + vert = vert_list + map_index[k]; + } + + //add the vertex + vert -> use_flag = true; + sgCopyVec3 ( vert -> v, mesh -> verts[ face->v[j] ] ) ; + if ( mesh -> tverts ) + sgCopyVec2 ( vert -> tv, mesh -> tverts[ face->tv[j] ] ) ; + if ( mesh -> cverts ) + sgCopyVec3 ( vert -> cv, mesh -> cverts[ face->cv[j] ] ) ; + } + } + + //assign a unique index to each vertex + int num_verts = 0 ; + vert = vert_list; + for ( i=0; i < max_verts; i++, vert++ ) + { + if ( vert -> use_flag ) + vert -> index = num_verts ++; + } + + //if ( extra_verts > 0 ) + // ulSetError( UL_DEBUG, "%d verts; %d added", num_verts-extra_verts, extra_verts ); + //else + // ulSetError( UL_DEBUG, "%d verts", num_verts ); + + //pass the data to ssg + ssgSimpleState* st = get_state ( mat, ( mesh -> cverts != NULL ) ) ; + ssgIndexArray* il = new ssgIndexArray ( num_faces * 3 ) ; + ssgVertexArray* vl = new ssgVertexArray ( num_verts ) ; + ssgTexCoordArray* tl = 0 ; + ssgColourArray* cl = 0 ; + if ( mesh -> tverts ) + tl = new ssgTexCoordArray ( num_verts ) ; + if ( mesh -> cverts ) + cl = new ssgColourArray ( num_verts ) ; + + //build the index list + face = mesh -> faces ; + for ( i=0; i num_faces; i++, face++ ) + { + if ( mat->sub_flag && face->sub_index != mat->sub_index ) + continue ; + + for ( u32 j=0; j<3; j++ ) + { + int k = i*3+j; + vert = vert_list + map_index[k]; + + if ( ! vert -> use_flag ) + ulSetError ( UL_FATAL, "internal error" ) ; + + il -> add ( vert -> index ) ; + } + } + + //copy the vertex lists + vert = vert_list; + for ( i=0; i < max_verts; i++, vert++ ) + { + if ( vert -> use_flag ) + { + vl -> add ( vert -> v ) ; + + if ( mesh -> tverts ) + { + sgVec2 tv ; + sgCopyVec2 ( tv, vert -> tv ) ; + get_texcoord ( mat, tv ) ; + tl -> add ( tv ) ; + } + + if ( mesh -> cverts ) + { + sgVec4 c ; + sgCopyVec3 ( c, vert -> cv ) ; + c[3] = 1.0f - mat -> transparency ; + + cl -> add ( c ) ; + } + } + } + + delete[] vert_list ; + delete[] map_index ; + + ssgVtxArray* leaf = new ssgVtxArray ( GL_TRIANGLES, + vl, NULL, tl, cl, il ) ; + leaf -> setCullFace ( TRUE ) ; + leaf -> setState ( st ) ; + return current_options -> createLeaf ( leaf, obj -> name ) ; +} + + +static aseTransform* get_tkey( aseObject* obj, u32 time ) +{ + if ( obj->tkeys == NULL ) + { + obj->num_tkeys = 0; + obj->tkeys = new aseTransform [ num_frames ] ; + + //initialize + aseTransform* tkey = obj->tkeys ; + for ( u32 i=0; ipos, 0, 0, 0 ) ; + sgSetVec3 ( tkey->axis, 0, 0, 1 ) ; + tkey->angle = 0.0f ; + sgSetVec3 ( tkey->scale, 1, 1, 1 ) ; + } + } + + //compute frame number + u32 frame = time / ticks_per_frame - first_frame; + if ((time % ticks_per_frame) != 0 || frame >= num_frames) + parser.error("bad time"); + + if ( frame+1 > obj->num_tkeys ) + obj->num_tkeys = frame+1; + + return( &obj->tkeys[ frame ] ); +} + +#define MAX_TKEY_PER_MESH 256 + +static int parse_tkeys( aseObject* obj ) +{ + bool match = false ; + void *tkeys[MAX_TKEY_PER_MESH]; + int num_tkeys=0; + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*NODE_NAME")) + { + char* name; + if (! parser.parseString(name, "obj name")) + return FALSE; + if ( obj->name && !strcmp(name,obj->name) ) + match = true ; + } + else if (match) + { + if (!strcmp(token,"*CONTROL_POS_SAMPLE")) + { + u32 time; + if (! parser.parseUInt(time, "time")) + return FALSE; + aseTransform* tkey = get_tkey( obj, time ); + + if (! parser.parseFloat(tkey->pos[0], "pos.x")) + return FALSE; + if (! parser.parseFloat(tkey->pos[1], "pos.y")) + return FALSE; + if (! parser.parseFloat(tkey->pos[2], "pos.z")) + return FALSE; + + if ( obj->parent == NULL ) + { + sgSubVec3 ( tkey->pos, obj->pos ) ; + } + else + { + for ( int i=0; i<3; i++ ) + { + if ( obj->inherit_pos[i] ) + tkey->pos[i] -= obj->pos[i] ; + } + } + + //copy the position forward + for ( u32 i=obj->num_tkeys; itkeys[ i ].pos, tkey->pos ) ; + } + else if (!strcmp(token,"*CONTROL_ROT_SAMPLE")) + { + u32 time; + if (!parser.parseUInt(time, "time")) + return FALSE; + aseTransform* tkey = get_tkey( obj, time ); + + if (! parser.parseFloat(tkey->axis[0], "axis.x")) + return FALSE; + if (! parser.parseFloat(tkey->axis[1], "axis.y")) + return FALSE; + if (! parser.parseFloat(tkey->axis[2], "axis.z")) + return FALSE; + if (! parser.parseFloat(tkey->angle, "angle")) + return FALSE; + } + else if (!strcmp(token,"*CONTROL_TCB_ROT_KEY")) + { + u32 time; + if (!parser.parseUInt(time, "time")) + return FALSE; + aseTransform* tkey = new aseTransform; + tkeys[num_tkeys++] = tkey; + assert(num_tkeys < MAX_TKEY_PER_MESH); + sgSetVec3 ( tkey->pos, 0, 0, 0 ) ; // "TCB": only rotation allowed :-/ + sgSetVec3 ( tkey->axis, 0, 0, 1 ) ; + tkey->angle = 0.0f ; + sgSetVec3 ( tkey->scale, 1, 1, 1 ) ; + +// *CONTROL_TCB_ROT_KEY 160 -0.9949 -0.0914 0.0434 3.1336 0.0000 0.0000 0.0000 0.0000 0.0000 + if (! parser.parseFloat(tkey->axis[0], "axis.x")) + return FALSE; + if (! parser.parseFloat(tkey->axis[1], "axis.y")) + return FALSE; + if (! parser.parseFloat(tkey->axis[2], "axis.z")) + return FALSE; + if (! parser.parseFloat(tkey->angle, "angle")) + return FALSE; + float dummy; + if (! parser.parseFloat(dummy, "tens")) + return FALSE; + if (! parser.parseFloat(dummy, "cont")) + return FALSE; + if (! parser.parseFloat(dummy, "bias")) + return FALSE; + if (! parser.parseFloat(dummy, "easeIn")) + return FALSE; + if (! parser.parseFloat(dummy, "easeOut")) + return FALSE; + } + else if (!strcmp(token,"*CONTROL_SCALE_SAMPLE")) + { + u32 time; + if (! parser.parseUInt(time, "time")) + return FALSE; + aseTransform* tkey = get_tkey( obj, time ); + + if (! parser.parseFloat(tkey->scale[0], "scale.x")) + return FALSE; + if (! parser.parseFloat(tkey->scale[1], "scale.y")) + return FALSE; + if (! parser.parseFloat(tkey->scale[2], "scale.z")) + return FALSE; + } + } + } + if(num_tkeys) + { + obj->num_tkeys = num_tkeys; + obj->tkeys = new aseTransform [ num_tkeys ] ; + for(int i=0; itkeys[i] = *((aseTransform *)tkeys[i]); + } + return TRUE; +} + + +static int parse_object( aseObject::Type type ) +{ + aseObject* obj = new aseObject ( type ) ; + + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*NODE_NAME")) + { + if ( !obj->name ) + { + char* name; + if (! parser.parseString(name, "obj name")) + return FALSE; + + + obj->name = ulStrDup ( name ) ; + } + } + else if (!strcmp(token,"*NODE_PARENT")) + { + if ( !obj->parent ) + { + char* name; + if (! parser.parseString(name, "parent name")) + return FALSE; + + + obj->parent = ulStrDup ( name ) ; + } + } + else if (!strcmp(token,"*NODE_TM")) + { + bool target = false ; + bool match = false ; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*NODE_NAME")) + { + char* name; + if (! parser.parseString(name, "obj name")) + return FALSE; + if ( obj->name && !strcmp(name,obj->name) ) + match = true ; + if ( strstr(name,".Target") != NULL ) + target = true ; + } + else if (match) + { + if (!strcmp(token,"*TM_POS")) + { + if (! parser.parseFloat(obj->pos[0], "pos.x")) + return FALSE; + if (! parser.parseFloat(obj->pos[1], "pos.y")) + return FALSE; + if (! parser.parseFloat(obj->pos[2], "pos.z")) + return FALSE; + sgCopyVec3 ( obj->target, obj->pos ) ; + } + else if (!strcmp(token,"*INHERIT_POS")) + { + int temp ; + if (! parser.parseInt(temp, "inherit_pos.x")) + return FALSE; + obj->inherit_pos[0] = ( temp != 0 ) ; + if (! parser.parseInt(temp, "inherit_pos.y")) + return FALSE; + obj->inherit_pos[1] = ( temp != 0 ) ; + if (! parser.parseInt(temp, "inherit_pos.z")) + return FALSE; + obj->inherit_pos[2] = ( temp != 0 ) ; + } + } + else if (target) + { + if (!strcmp(token,"*TM_POS")) + { + if (! parser.parseFloat(obj->target[0], "pos.x")) + return FALSE; + if (! parser.parseFloat(obj->target[1], "pos.y")) + return FALSE; + if (! parser.parseFloat(obj->target[2], "pos.z")) + return FALSE; + } + } + } + } + else if (!strcmp(token,"*MESH")) + { + if (! parse_mesh( obj )) + return FALSE; + } + else if (!strcmp(token,"*MESH_ANIMATION")) + { + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*MESH")) + { + if (! parse_mesh( obj )) + return FALSE; + } + } + } + else if (!strcmp(token,"*TM_ANIMATION")) + { + if (! parse_tkeys( obj )) + return FALSE; + } + else if (!strcmp(token,"*MATERIAL_REF")) + { + if (! parser.parseUInt(obj->mat_index, "mat #")) + return FALSE; + } + } + + //if ( obj->parent != NULL ) + // ulSetError( UL_DEBUG, "add_mesh: %s, parent=%s", obj->name, obj->parent ) ; + //else + // ulSetError( UL_DEBUG, "add_mesh: %s", obj->name ) ; + + ssgEntity* mesh_entity = NULL ; + + if ( obj->mesh_count > 1 ) + { + //how many frames? + int num_frames = 0 ; + int i ; + for ( i=0; imesh_list [ i ] ; + if ( mesh != NULL ) + num_frames ++ ; + } + + //allocate selector + ssgSelector* selector = new ssgSelector ( num_frames ) ; + + //init + for ( i=0; imesh_list [ i ] ; + if ( mesh == NULL ) + continue ; + + u32 num_subs = count_sub_materials ( obj->mat_index ); + if ( num_subs > 1 ) + { + //break apart the mesh for each sub material + ssgBranch* branch = new ssgBranch ; + for ( u32 sub_index=0; sub_index addKid ( leaf ) ; + } + selector -> addKid ( branch ) ; + } + else + { + ssgLeaf* leaf = add_mesh ( obj, mesh, 0 ) ; + if ( leaf ) + selector -> addKid ( leaf ) ; + } + } + + selector -> selectStep ( 0 ) ; + mesh_entity = current_options -> createSelector ( selector ) ; + } + else if ( obj->mesh_list [ 0 ] != NULL ) + { + aseMesh* mesh = obj->mesh_list [ 0 ] ; + ssgBranch* branch = new ssgBranch ; + + u32 num_faces = mesh -> num_faces ; + if ( num_faces == 0 ) + { + ssgLeaf* leaf = add_points( obj, mesh ) ; + if ( leaf ) + branch -> addKid ( leaf ) ; + } + else + { + u32 num_subs = count_sub_materials ( obj->mat_index ); + if ( num_subs > 1 ) + { + //break apart the mesh for each sub material + for ( u32 sub_index=0; sub_index addKid ( leaf ) ; + } + } + else + { + ssgLeaf* leaf = add_mesh ( obj, mesh, 0 ); + if ( leaf ) + branch -> addKid ( leaf ) ; + } + } + + mesh_entity = branch ; + } + else + { + ssgBranch* branch = new ssgBranch ; + ssgLeaf* leaf = add_points( obj, NULL ) ; + if ( leaf ) + branch -> addKid ( leaf ) ; + mesh_entity = branch ; + } + + if ( mesh_entity != NULL ) + { + //add to graph -- find parent branch + ssgBranch* parent_branch ; + if ( obj->parent ) + { + ssgEntity* found = top_branch -> getByName ( obj->parent ) ; + if ( found != NULL ) + { + assert ( found -> isAKindOf ( ssgTypeBranch() ) ) ; + parent_branch = (ssgBranch *) found ; + } + else + { + //parser.error("object %s: parent %s not seen",obj->name,obj->parent); + parent_branch = top_branch ; + } + } + else + { + parent_branch = top_branch ; + } + + if ( obj->num_tkeys > 0 ) + { + ssgAnimTransform * at = new ssgAnimTransform; + at->setNum( obj->num_tkeys ); + + /* + Build the transforms + */ + sgMat4 rmat; + sgMakeIdentMat4 ( rmat ) ; + at->setATransform ( rmat, 0); + + for ( u32 i = 1 ; i < obj->num_tkeys ; i++ ) + { + aseTransform* tkey = obj->tkeys + i ; + + sgVec3 pos ; + sgMat4 tmp ; + sgMat4 mat ; + + /* + * compute rmat + * the key rotation is additive to the last rotation + */ + sgMakeRotMat4 ( tmp, -tkey->angle * SG_RADIANS_TO_DEGREES, tkey->axis ) ; + sgPostMultMat4 ( rmat, tmp ) ; + + /* + * rotation is around the mesh pivot point (obj->pos) + * translate -obj->pos + */ + sgCopyVec3 ( pos, obj->pos ) ; + sgNegateVec3 ( pos ) ; + sgMakeTransMat4 ( mat, pos ) ; + + /* + * perform the rotation + */ + sgPostMultMat4 ( mat, rmat ) ; + + /* + * translate obj->pos + tkey->pos + */ + sgCopyVec3 ( pos, obj->pos ) ; + sgAddVec3 ( pos, tkey->pos ) ; + sgMakeTransMat4 ( tmp, pos ) ; + sgPostMultMat4 ( mat, tmp ) ; + + at->setATransform ( mat, i); + } + // insert the ssgAnimTransform into the scene graph: + at-> addKid ( mesh_entity ) ; + mesh_entity = at; + } + + parent_branch -> addKid ( mesh_entity ) ; + mesh_entity -> setName ( obj->name ) ; + } + + delete obj ; + return TRUE; +} + + +static bool parse() +{ + materials = new aseMaterial* [ MAX_MATERIALS ]; + if ( !materials ) + { + parser.error("not enough memory"); + return false ; + } + + num_materials = 0 ; + + first_frame = 0 ; + last_frame = 0 ; + frame_speed = 0 ; + ticks_per_frame = 0 ; + num_frames = 0 ; + + bool firsttime = true; + char* token; + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (firsttime) + { + if (strcmp(token,"*3DSMAX_ASCIIEXPORT")) + { + parser.error("not ASE format"); + return false ; + } + u32 version_number; + if (! parser.parseUInt(version_number, "version number") ) + return false ; + if ( version_number != 200) + { + parser.error("invalid %s version",token); + return false ; + } + firsttime = false; + } + else if (!strcmp(token,"*SCENE")) + { + int startLevel = parser.level; + while ((token = parser.getLine( startLevel )) != NULL) + { + if (!strcmp(token,"*SCENE_FIRSTFRAME")) + { + if (! parser.parseUInt(first_frame, "FIRSTFRAME #")) + return FALSE; + } + else if (!strcmp(token,"*SCENE_LASTFRAME")) + { + if (! parser.parseUInt(last_frame, "LASTFRAME #")) + return FALSE; + num_frames = last_frame - first_frame + 1; + } + else if (!strcmp(token,"*SCENE_FRAMESPEED")) + { + if (! parser.parseUInt(frame_speed, "FRAMESPEED #")) + return FALSE; + } + else if (!strcmp(token,"*SCENE_TICKSPERFRAME")) + { + if (! parser.parseUInt(ticks_per_frame, "TICKSPERFRAME #")) + return FALSE; + } + } + } + else if (!strcmp(token,"*MATERIAL_LIST")) + { + if (! parse_material_list()) + return FALSE; + } + else if (!strcmp(token,"*GEOMOBJECT")) + { + if (! parse_object( aseObject::GEOM )) + return FALSE; + } + else if (!strcmp(token,"*HELPEROBJECT")) + { + if (! parse_object( aseObject::HELPER )) + return FALSE; + } + else if (!strcmp(token,"*CAMERAOBJECT")) + { + if (! parse_object( aseObject::CAMERA )) + return FALSE; + } + } + return true ; +} + + +static void parse_free() +{ + u32 i ; + for ( i = 0 ; i < num_materials ; i++ ) + { + delete [] materials[ i ] -> name ; + delete [] materials[ i ] -> tfname ; + delete materials[ i ] ; + } + delete[] materials ; + materials = 0 ; +} + + +ssgEntity *ssgLoadASE ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + top_branch = new ssgBranch ; + if ( !parser.openFile( fname, &parser_spec ) ) + { + delete top_branch ; + return 0; + } + if ( !parse() ) + { + delete top_branch ; + top_branch = 0 ; + } + parse_free(); + parser.closeFile(); + + return top_branch ; +} diff --git a/src/ssg/ssgLoadATG.cxx b/src/ssg/ssgLoadATG.cxx new file mode 100644 index 0000000..2ebc2ea --- /dev/null +++ b/src/ssg/ssgLoadATG.cxx @@ -0,0 +1,497 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadATG.cxx 1970 2004-10-04 08:11:26Z wolfram_kuss $ +*/ +/* + .scenery loader for SSG/PLIB + ATG = ascii Terra Gear + + Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in May 2001 + +*/ + +/* +General info on working with FG scenery files: + +If you want to add stuff to the scenery think about adding it via the *.ind files. +Editing the scenery manually is always "dangerous", since your effort is lost the +next time the scenery is re-generated. + +ssgLoadATG and ssgSaveATG are not finished at all and there are some known bugs: +- texture coordinate reading doesn't work 100%, writing is not implemented yet. +- Only faces with "f". TriStrips ("ts") and TriFans ("tf") not yet supported. + +First of all, make sure the FG textures are in your texture path. If +your FG is in, then add +C:/FG/Textures/$(...) +to the string in viewer.setTexturePath in .ppe_rc. +If you run PPE, it will tell you where you .pee_rc is (and create one if you don't have one). + + +As an example, say you want to add a tower to the airport KLVK. +It goes something like this: + +1. Unzip KLVK.gz, you get a file KLVK. +2. delete KLVK.gz or move it away so that it isn't used anymore. +3. Rename KLVK to KLV.atg. PLIB finds out the fileformat via the extension, + so PLIB can't handle files without extension. +4. Go into PPE (or another PLIB based program :-)) and load KLVK.atg. +5. Say "make everything visible". You can look at it from all sides via + the middle mouse button and move the camery via the numKeyPad. For more, see the PPE docu. +6. Now, using some magic like a future PPE-version, add whatever you want to add. + I tried this and by coding a few small functions, have succeeded partly. Some hints: + Pressing "w" gets you into a mode where you can get the 3D coordinates of stuff. + What I want to add is centred around 0,0,0, so writing a small, but non-trivial :-( + translateAllVertices, moved the other object that I load via "merge". The next problem + is that the tile is not axisparalell, so you have to rotate what you loaded a certain way. +7. Save the tile as KLVK.atg +8. From the original KLVK, get the line with "# gbs..." and copy it to KLVK.atg +9. Rename KLVK.atg to KLVK. You can now view it in FG by going to + this airport. The texture coords will be broken (see above). + + + + */ + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" +#include "ssgParser.h" + + +extern sgVec4 currentDiffuse; + + +static /* const */ ssgLoaderOptions* current_options = NULL ; +static class ssgTexCoordArray *linearListTCPFAV=NULL; // TCPFAV = TextureCoordinatesPerFaceAndVertex + +static _ssgParserSpec parser_spec = +{ + "\r\n\t ", // delim_chars_skipable + NULL, // delim_chars_non_skipable + NULL, // pre_processor + NULL, // open_brace_chars + NULL, // close_brace_chars + '"', // quote_char. not used for scenery + 0, // comment_char # is handled in this module, since it may contain + // important info like the name of the material or the gbs + NULL // comment_string +} ; + + +static _ssgParser parser; +static ssgBranch* top_branch; + +static int Ascii2Int(int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + + + +#define MAX_NO_VERTICES_PER_FACE 10000 + +static class ssgLoaderWriterMesh _theMesh; + + + +static int _ssgNoFaces, _ssgNoVertices, _ssgNoVertexNormals, _ssgNoVertexTC; //lint !e551 + +static char * _current_usemtl = NULL, * _last_usemtl = NULL; +static int _current_material_index = -1; + +static double _ssg_gbs_x = 0.0, _ssg_gbs_y = 0.0, _ssg_gbs_z = 1.0, _ssg_gbs_r = 0.0; + +void ssgGetValuesFromLastATGFile(double *x, double *y, double *z, double *r) +// These values are the values from the "# gbs" line from the last loaded +// ATG file, or 0, 0, 1, 0 if there is no ATG file with a # gbs line loaded yet +// The values are the vector from the centre of the earth to the tile and +// a radius. +{ *x = _ssg_gbs_x; + *y = _ssg_gbs_y; + *z = _ssg_gbs_z; + *r = _ssg_gbs_r; +} + +static char* parser_getLine() +// replaces parser.getLine, but handles '#' +// Some "comment lines" starting with '#' have important info like material/texture name inside +// if this function finds a #-line with a materialname, it sets _current_usemtl +{ char *token; + token = parser.getLine(0); + if ( token == NULL ) + return NULL; + + while ( token[0] == '#' ) + { char * token0 = parser.parseToken( 0 ); + if ( ulStrEqual ("usemtl" , token0)) // name has to be 0 + { char * usemtl = parser.parseToken( 0 ); + if ( usemtl != NULL) + if ( 0 != usemtl[0] ) + { + if ( _current_usemtl != NULL ) + delete [] _current_usemtl ; + _current_usemtl = ulStrDup ( usemtl ) ; + } + } + //# gbs -2710586.16105 -4275323.15743 3867065.62925 7623.60 + else if ( ulStrEqual ("gbs" , token0)) // name has to be 0 + { char * temp; + temp = parser.parseToken( 0 ); + _ssg_gbs_x = atof (temp); + temp = parser.parseToken( 0 ); + _ssg_gbs_y = atof (temp); + temp = parser.parseToken( 0 ); + _ssg_gbs_z = atof (temp); + temp = parser.parseToken( 0 ); + _ssg_gbs_r = atof (temp); + } + token = parser.getLine(0); + if ( token == NULL ) + return NULL; + } + return token; +} + +static int reduce_numbers; // some ATG files (from BTG converted ones?) have vertex coordinates that include gbs, for example + // 2713547.89986. Others have coordinates excluding gbs, for example 2961.50684 +// I need to reduce the big numbers beacuse float a,b; a-b is dangerous if a large and b small. +// Therefore I reduce the numbers by gbs while I have vertex coords and gbs as double and add it back in using double in PPE + + +static int parse() +{ + // ************* Init ************************ + char* token; + _ssgNoFaces= 0; + _ssgNoVertices = 0; + _ssgNoVertexNormals = 0; + _ssgNoVertexTC = 0; + + _theMesh.reInit (); + _theMesh.createVertices(); + _theMesh.createFaces(); + _theMesh.createPerFaceAndVertexTextureCoordinates2(); + _theMesh.createMaterials(); + _theMesh.createMaterialIndices() ; + + token = parser_getLine(); + if ( token == NULL ) + { parser.error("The file seems to be empty"); + return FALSE; + } + // ****** read vertices ************ + int first = TRUE; + while ( 0 == strcmp( token, "v") ) + { + double vx, vy, vz; + if (! parser.parseDouble( vx, "vertex.x") ) + return FALSE; + if (! parser.parseDouble( vy, "vertex.y") ) + return FALSE; + if (! parser.parseDouble( vz, "vertex.z") ) + return FALSE; + if (first) + { first = FALSE; + +#ifndef ABS +#define ABS(x) (((x)>0)?(x):-(x)) +#endif + // Mega-kludge: + reduce_numbers = (ABS(vx)>50000); + } + if (reduce_numbers) + { assert( _ssg_gbs_x != 0.0); // kludge + vx += _ssg_gbs_x; + vy += _ssg_gbs_y; + vz += _ssg_gbs_z; + } + sgVec3 vert; + sgSetVec3( vert, (float) vx, (float) vy, (float) vz); + _theMesh.addVertex(vert); // add to mesh + _ssgNoVertices++; + token = parser_getLine(); + if ( token == NULL ) + { parser.error("There are only vertices in the scenery, no faces"); + return FALSE; + } + } + // ****** read vertex normals ************ + while ( 0 == strcmp( token, "vn") ) + { sgVec3 vert_norm; + + if (! parser.parseFloat( vert_norm[0], "vertex normal.x") ) + return FALSE; + if (! parser.parseFloat( vert_norm[1], "vertex normal.y") ) + return FALSE; + if (! parser.parseFloat( vert_norm[2], "vertex normal.z") ) + return FALSE; + //_theMesh.addVertex(vert); + _ssgNoVertexNormals++; + token = parser_getLine(); + if ( token == NULL ) + { parser.error("There are only vertices in the scenery, no faces"); + return FALSE; + } + } + /*if ( _ssgNoVertexNormals != _ssgNoVertices) + { parser.error("The number of vertex normals and vertices doesn't match"); + return FALSE; + }*/ + // ***************** read texture coords. ********************** + // this format has a "linear" list of texture coords, that is indexed from the faces. + while ( 0 == strcmp( token, "vt") ) + { sgVec2 vert_tc; + + if (! parser.parseFloat( vert_tc[0], "vertex texture.x") ) + return FALSE; + if (! parser.parseFloat( vert_tc[1], "vertex texture.y") ) + return FALSE; + linearListTCPFAV->add(vert_tc); + _ssgNoVertexTC++; + token = parser_getLine(); + if ( token == NULL ) + { parser.error("There are only vertices in the scenery, no faces"); + return FALSE; + } + } + /*if ( _ssgNoVertexTC != _ssgNoVertices) + { parser.error("The number of texture coords and vertices doesn't match"); + return FALSE; + }*/ + + // ********* Read Faces ********** + int aiVertices[MAX_NO_VERTICES_PER_FACE], aiTCs[MAX_NO_VERTICES_PER_FACE]; + unsigned int nNoOfVerticesForThisFace; + + while ( 0 == strcmp( token, "f") ) + { char *ptr, buffer[1024], *ptr2Slash; + + if ( !ulStrEqual (_current_usemtl, _last_usemtl)) + // ***************+ material has changed. *********** + { + // bring _last_usemtl up to date + if ( _last_usemtl != NULL ) + delete [] _last_usemtl; + _last_usemtl= ulStrDup ( _current_usemtl ) ; + + + // create SimpleState + + ssgSimpleState * currentState = new ssgSimpleState () ; // (0) ? + + currentState -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5, 1.0); + currentState -> setMaterial ( GL_DIFFUSE, 1.0, 1.0, 1.0, 1.0) ; // 0.8, 0.8, 1.0, 1.0f + currentState -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + currentState -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0, 1.0); + currentState -> setShininess ( 20 ) ; // Fixme, NIV14: Is that correct? + + currentState -> disable ( GL_COLOR_MATERIAL ) ; + //currentState -> enable ( GL_COLOR_MATERIAL ) ; + //currentState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + currentState -> enable ( GL_LIGHTING ) ; + currentState -> setShadeModel ( GL_SMOOTH ) ; + + currentState ->disable(GL_ALPHA_TEST); //needed? + +/* if ( currentDiffuse[3] > 0.0f ) + { + currentState -> disable ( GL_ALPHA_TEST ) ; + currentState -> enable ( GL_BLEND ) ; + currentState -> setTranslucent () ; + } + else */ + { + currentState -> disable ( GL_BLEND ) ; + currentState -> setOpaque () ; + } + currentState -> disable( GL_TEXTURE_2D ); +// if (textured) + { + char * fileName = new char [ strlen ( _current_usemtl ) + 5 ] ; + assert( fileName != NULL ); + strcpy ( fileName, _current_usemtl ) ; + strcat( fileName, ".rgb" ); + + currentState -> setTexture( current_options -> createTexture( fileName ) ); + delete [] fileName; + currentState -> enable( GL_TEXTURE_2D ); + } + // add SimpleState + _theMesh.addMaterial( ¤tState ); + + // bring _current_material_index up to date + _current_material_index ++; + + } // if new material + + // ************* parse vertex and TC indices of this face ****************** + nNoOfVerticesForThisFace = 0; + ptr = parser.parseToken( "vertex index"); + while ( ptr != NULL ) + { strncpy(buffer, ptr, 1024); + ptr2Slash = strchr(buffer, '/'); + if ( ptr2Slash != NULL) + { *ptr2Slash = 0; + ptr2Slash++; + if ( ! Ascii2Int( aiTCs[ nNoOfVerticesForThisFace ], ptr2Slash, "texture coord. index")) + return FALSE; + } + else + aiTCs[ nNoOfVerticesForThisFace ]=0; + if ( ! Ascii2Int( aiVertices[ nNoOfVerticesForThisFace ], buffer, "vertex index")) + return FALSE; + nNoOfVerticesForThisFace++; + assert(nNoOfVerticesForThisFaceref(); + for(j=0;j<3;j++) + { + if ( ! linearListTCPFAV->get(aiTCs[l*3+j]) ) + { parser.error("Internal error while reading *.atg-file: aiTCs[j] == NULL \n"); + return FALSE; + } + sca->add(linearListTCPFAV->get(aiTCs[l*3+j])); + } + + // ****** add face to mesh ***** + _theMesh.addPerFaceAndVertexTextureCoordinate2( &sca ) ; + int carray[3]; + carray[0]=aiVertices[3*l+0]; + carray[1]=aiVertices[3*l+1]; + carray[2]=aiVertices[3*l+2]; + _theMesh.addFaceFromIntegerArray(3, carray); + _theMesh.addMaterialIndex ( _current_material_index ) ; + } +#else + unsigned int j; + ssgTexCoordArray * sca = new ssgTexCoordArray (nNoOfVerticesForThisFace); + sca->ref(); + for(j=0;jget(aiTCs[j]) ) + { parser.error("Internal error while reading *.atg-file: aiTCs[j] == NULL \n"); + return FALSE; + } + sca->add(linearListTCPFAV->get(aiTCs[j])); + } + + // ****** add face to mesh ***** + _theMesh.addPerFaceAndVertexTextureCoordinate2 ( &sca ) ; + _theMesh.addFaceFromIntegerArray(nNoOfVerticesForThisFace, aiVertices); + _theMesh.addMaterialIndex ( _current_material_index ) ; + +#endif + + token = parser_getLine(); + if ( token == NULL ) + break; + } // ******* end of reading faces ***** + if ( token != NULL ) + ulSetError ( UL_WARNING, "f expected, got %s\n", token); + + if (! parser.eof ) + ulSetError ( UL_WARNING, "Warning: no eof\n"); + + + // kludge. We need a state for add2SSG: + + ssgSimpleState * ss = new ssgSimpleState () ; // (0) ? + ss -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5, 1.0); + ss -> setMaterial ( GL_DIFFUSE, 1.0, 1.0, 1.0, 1.0) ; // 0.8, 0.8, 1.0, 1.0f + ss -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + ss -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0, 1.0); + ss -> setShininess ( 20 ) ; // Fixme, NIV14: Is that correct? + + ss -> disable ( GL_COLOR_MATERIAL ) ; + //ss -> enable ( GL_COLOR_MATERIAL ) ; + //ss -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + ss -> enable ( GL_LIGHTING ) ; + ss -> setShadeModel ( GL_SMOOTH ) ; + + ss ->disable(GL_ALPHA_TEST); //needed? + + ss -> disable ( GL_BLEND ) ; + ss -> setOpaque () ; + + ss -> disable( GL_TEXTURE_2D ); + _theMesh.checkMe(); //lint !e534 // For debug + _theMesh.addToSSG( + ss, // super kludge. NIV135 + current_options, + top_branch); + + return TRUE ; +} + + +ssgEntity *ssgLoadATG ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + top_branch = new ssgBranch ; + _current_usemtl = NULL; + _last_usemtl = NULL; + _current_material_index = -1; + + + if ( !parser.openFile( fname, &parser_spec )) + { + delete top_branch ; + return 0; + } + linearListTCPFAV=new ssgTexCoordArray(); + if ( !parse() ) + { + delete linearListTCPFAV; + delete top_branch ; + top_branch = 0 ; + } + delete linearListTCPFAV; +// parse_free(); + parser.closeFile(); + + return top_branch ; +} diff --git a/src/ssg/ssgLoadBMP.cxx b/src/ssg/ssgLoadBMP.cxx new file mode 100644 index 0000000..4e20a50 --- /dev/null +++ b/src/ssg/ssgLoadBMP.cxx @@ -0,0 +1,383 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadBMP.cxx 2166 2011-03-22 12:00:57Z fayjf $ +*/ + + +#include "ssgLocal.h" + +#ifdef SSG_LOAD_BMP_SUPPORTED + +static FILE *curr_image_fd ; +static char curr_image_fname [ 512 ] ; +static int isSwapped ; + + +static void swab_short ( unsigned short *x ) +{ + if ( isSwapped ) + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + +static void swab_int ( unsigned int *x ) +{ + if ( isSwapped ) + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + +/*static void swab_int_array ( int *x, int leng ) +{ + if ( ! isSwapped ) + return ; + + for ( int i = 0 ; i < leng ; i++ ) + { + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; + x++ ; + } +}*/ + + +static unsigned char readByte () +{ + unsigned char x ; + fread ( & x, sizeof(unsigned char), 1, curr_image_fd ) ; + return x ; +} + +static unsigned short readShort () +{ + unsigned short x ; + fread ( & x, sizeof(unsigned short), 1, curr_image_fd ) ; + swab_short ( & x ) ; + return x ; +} + +static unsigned int readInt () +{ + unsigned int x ; + fread ( & x, sizeof(unsigned int), 1, curr_image_fd ) ; + swab_int ( & x ) ; + return x ; +} + + +/* + Original source for BMP loader kindly + donated by "Sean L. Palmer" +*/ + + +struct BMPHeader +{ + unsigned short FileType ; + unsigned int FileSize ; + unsigned short Reserved1 ; + unsigned short Reserved2 ; + unsigned int OffBits ; + unsigned int Size ; + unsigned int Width ; + unsigned int Height ; + unsigned short Planes ; + unsigned short BitCount ; + unsigned int Compression ; + unsigned int SizeImage ; + unsigned int XPelsPerMeter ; + unsigned int YPelsPerMeter ; + unsigned int ClrUsed ; + unsigned int ClrImportant ; +} ; + + +struct RGBA +{ + unsigned char r,g,b,a ; +} ; + + +bool ssgLoadBMP ( const char *fname, ssgTextureInfo* info ) +{ + int w, h, bpp ; + int index=0; + bool old_format = false; + RGBA pal [ 256 ] ; + + BMPHeader bmphdr ; + + /* Open file & get size */ + + strcpy ( curr_image_fname, fname ) ; + + if ( ( curr_image_fd = fopen ( curr_image_fname, "rb" ) ) == NULL ) + { + char *p = strrchr(curr_image_fname,'_'); + if (p != 0) { + *p = '\0'; + p++; + index = atoi (p); + old_format = true; + if ( ( curr_image_fd = fopen(curr_image_fname, "rb")) == NULL) { + perror ( "ssgLoadTexture" ) ; + ulSetError( UL_WARNING, "ssgLoadTexture: Failed to load '%s' for reading.", curr_image_fname ); + return false ; + } + p--; + *p = '_'; + } + else { + perror ( "ssgLoadTexture" ) ; + ulSetError( UL_WARNING, "ssgLoadTexture: Failed to open '%s' for reading.", curr_image_fname ); + return false ; + } + } + + /* + Load the BMP piecemeal to avoid struct packing issues + */ + + isSwapped = FALSE ; + bmphdr.FileType = readShort () ; + + if ( bmphdr.FileType == ((int)'B' + ((int)'M'<<8)) ) + isSwapped = FALSE ; + else + if ( bmphdr.FileType == ((int)'M' + ((int)'B'<<8)) ) + isSwapped = TRUE ; + else + { + ulSetError ( UL_WARNING, "%s: Unrecognised magic number 0x%04x", + curr_image_fname, bmphdr.FileType ) ; + return false ; + } + + bmphdr.FileSize = readInt () ; + bmphdr.Reserved1 = readShort () ; + bmphdr.Reserved2 = readShort () ; + bmphdr.OffBits = readInt () ; + bmphdr.Size = readInt () ; + bmphdr.Width = readInt () ; + bmphdr.Height = readInt () ; + bmphdr.Planes = readShort () ; + bmphdr.BitCount = readShort () ; + bmphdr.Compression = readInt () ; + bmphdr.SizeImage = readInt () ; + bmphdr.XPelsPerMeter = readInt () ; + bmphdr.YPelsPerMeter = readInt () ; + bmphdr.ClrUsed = readInt () ; + bmphdr.ClrImportant = readInt () ; + + w = bmphdr.Width ; + h = bmphdr.Height ; + bpp = bmphdr.Planes * bmphdr.BitCount ; + + bool top_down = false ; + if ( h < 0 ) + { + top_down = true ; + h = -1 * h ; + } + +#ifdef PRINT_BMP_HEADER_DEBUG + ulSetError ( UL_DEBUG, "Filetype %04x", bmphdr.FileType ) ; + ulSetError ( UL_DEBUG, "Filesize %08x", bmphdr.FileSize ) ; + ulSetError ( UL_DEBUG, "R1 %04x", bmphdr.Reserved1 ) ; + ulSetError ( UL_DEBUG, "R2 %04x", bmphdr.Reserved2 ) ; + ulSetError ( UL_DEBUG, "Offbits %08x", bmphdr.OffBits ) ; + ulSetError ( UL_DEBUG, "Size %08x", bmphdr.Size ) ; + ulSetError ( UL_DEBUG, "Width %08x", bmphdr.Width ) ; + ulSetError ( UL_DEBUG, "Height %08x", bmphdr.Height ) ; + ulSetError ( UL_DEBUG, "Planes %04x", bmphdr.Planes ) ; + ulSetError ( UL_DEBUG, "Bitcount %04x", bmphdr.BitCount ) ; + ulSetError ( UL_DEBUG, "Compression %08x", bmphdr.Compression ) ; + ulSetError ( UL_DEBUG, "SizeImage %08x", bmphdr.SizeImage ) ; + ulSetError ( UL_DEBUG, "XPelsPerMeter %08x", bmphdr.XPelsPerMeter ) ; + ulSetError ( UL_DEBUG, "YPelsPerMeter %08x", bmphdr.YPelsPerMeter ) ; + ulSetError ( UL_DEBUG, "ClrUsed %08x", bmphdr.ClrUsed ) ; + ulSetError ( UL_DEBUG, "ClrImportant %08x", bmphdr.ClrImportant ) ; +#endif + + int isMonochrome = TRUE ; + int isOpaque = TRUE ; + + if ( bpp <= 8 ) + { + for ( int i = 0 ; i < 256 ; i++ ) + { + pal[i].b = readByte () ; + pal[i].g = readByte () ; + pal[i].r = readByte () ; + + /* According to BMP specs, this fourth value is not really alpha value + but just a filler byte, so it is ignored for now. */ + pal[i].a = readByte () ; + if (old_format == true) { + pal[i].a = (i= 0 ; y-- ) + { + GLubyte *row_ptr ; + if ( top_down ) + { + /* store flipped image */ + row_ptr = &data [ y * row_size ] ; + } + else + { + /* store without flipping */ + row_ptr = &data [ ( h - ( y + 1 ) ) * row_size ] ; + } + if ( fread ( row_ptr, 1, row_size, curr_image_fd ) != (unsigned)row_size ) + { + ulSetError ( UL_WARNING, "Premature EOF in '%s'", curr_image_fname ) ; + return false ; + } + // padding bytes + if ( pad ) + fread ( buff, 1, pad, curr_image_fd ); + } + } + + fclose ( curr_image_fd ) ; + + GLubyte *image ; + int z ; + + if ( bpp == 8 ) + { + int i ; + + // check for diffrent alpha values in the bitmap + // assume blending if that's the case + for ( i = 1 ; i < w * h ; i++ ) { + if (pal[data[i]].a != pal[data[i-1]].a) { + isOpaque = FALSE ; + break; + } + } + + if ( isMonochrome ) + z = isOpaque ? 1 : 2 ; + else + z = isOpaque ? 3 : 4 ; + + image = new GLubyte [ w * h * z ] ; + + for ( i = 0 ; i < w * h ; i++ ) + switch ( z ) + { + case 1 : image [ i ] = pal[data[i]].r ; break ; + case 2 : image [ i*2 ] = pal[data[i]].r ; + image [ i*2 + 1 ] = pal[data[i]].a ; break ; + case 3 : image [ i*3 ] = pal[data[i]].r ; + image [ i*3 + 1 ] = pal[data[i]].g ; + image [ i*3 + 2 ] = pal[data[i]].b ; break ; + case 4 : image [ i*4 ] = pal[data[i]].r ; + image [ i*4 + 1 ] = pal[data[i]].g ; + image [ i*4 + 2 ] = pal[data[i]].b ; + image [ i*4 + 3 ] = pal[data[i]].a ; break ; + default : break ; + } + + delete [] data ; + } + else + if ( bpp == 24 ) + { + z = 3 ; + isOpaque = TRUE ; + image = data ; + + /* BGR --> RGB */ + + for ( int i = 0 ; i < w * h ; i++ ) + { + GLubyte tmp = image [ 3 * i ] ; + image [ 3 * i ] = image [ 3 * i + 2 ]; + image [ 3 * i + 2 ] = tmp ; + } + } + else + if ( bpp == 32 ) + { + z = 4 ; + isOpaque = FALSE ; + image = data ; + + /* BGRA --> RGBA */ + + for ( int i = 0 ; i < w * h ; i++ ) + { + GLubyte tmp = image [ 4 * i ] ; + image [ 4 * i ] = image [ 4 * i + 2 ]; + image [ 4 * i + 2 ] = tmp ; + } + } + else + { + ulSetError ( UL_WARNING, "ssgLoadTexture: Can't load %d bpp BMP textures.", bpp ) ; + return false ; + } + + if ( info != NULL ) + { + info -> width = w ; + info -> height = h ; + info -> depth = z ; + info -> alpha = ( isOpaque == FALSE ) ; + } + + return ssgMakeMipMaps ( image, w, h, z ) ; +} + +#else + +bool ssgLoadBMP ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - BMP support not configured", fname ) ; + return false ; +} + +#endif diff --git a/src/ssg/ssgLoadDOF.cxx b/src/ssg/ssgLoadDOF.cxx new file mode 100644 index 0000000..7382dac --- /dev/null +++ b/src/ssg/ssgLoadDOF.cxx @@ -0,0 +1,638 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2003 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadDOF.cxx 1748 2003-01-29 19:33:41Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +static ssgLoaderOptions* current_options = NULL ; + +struct _NameValuePair +{ + char *name ; + int value ; +} ; + +#define TOKEN_DOF1 1 +#define TOKEN_EDOF 2 +#define TOKEN_MATS 3 +#define TOKEN_GEOB 4 +#define TOKEN_MAT0 5 +#define TOKEN_GOB1 6 +#define TOKEN_MHDR 7 +#define TOKEN_MCOL 8 +#define TOKEN_MUVW 9 +#define TOKEN_MTRA 10 +#define TOKEN_MTEX 11 +#define TOKEN_MSUB 12 +#define TOKEN_MEND 13 +#define TOKEN_GHDR 14 +#define TOKEN_INDI 15 +#define TOKEN_VERT 16 +#define TOKEN_TVER 17 +#define TOKEN_NORM 18 +#define TOKEN_BRST 19 +#define TOKEN_GEND 20 +#define TOKEN_ILLEGAL 998 +#define TOKEN_EOF 999 + + +static _NameValuePair tags [] = +{ + { "DOF1", TOKEN_DOF1 }, + { "EDOF", TOKEN_EDOF }, + { "MATS", TOKEN_MATS }, + { "GEOB", TOKEN_GEOB }, + { "MAT0", TOKEN_MAT0 }, + { "GOB1", TOKEN_GOB1 }, + { "MHDR", TOKEN_MHDR }, + { "MCOL", TOKEN_MCOL }, + { "MUVW", TOKEN_MUVW }, + { "MTRA", TOKEN_MTRA }, + { "MTEX", TOKEN_MTEX }, + { "MSUB", TOKEN_MSUB }, + { "MEND", TOKEN_MEND }, + { "GHDR", TOKEN_GHDR }, + { "INDI", TOKEN_INDI }, + { "VERT", TOKEN_VERT }, + { "TVER", TOKEN_TVER }, + { "NORM", TOKEN_NORM }, + { "BRST", TOKEN_BRST }, + { "GEND", TOKEN_GEND }, + { NULL, 0 } +} ; + + +struct dofMaterial +{ + char *name ; + char *className ; + + sgVec4 ambient ; + sgVec4 diffuse ; + sgVec4 specular ; + sgVec4 emission ; + float shininess ; + + float uvwUoffset ; + float uvwVoffset ; + float uvwUtiling ; + float uvwVtiling ; + float uvwAngle ; + float uvwBlur ; + float uvwBlurOffset ; + + float transparency ; /* Unused */ + int blendMode ; /* 0 == No blend, + 1 == BLEND_SRC_ALPHA, + 2 == BLEND_CONST (unused) */ + + int textures ; + char **mapName ; + + int subMaterials ; /* Should be zero */ + dofMaterial *MAT0 ; /* Should be unused */ + + ssgSimpleState *ssg_material ; +} ; + + +static ulList *materials ; + + +static int getToken ( FILE *fd ) +{ + char s [ 5 ] ; + + if ( fread ( s, 1, 4, fd ) != 4 ) + return TOKEN_EOF ; + + s [ 4 ] = '\0' ; + + for ( int i = 0 ; tags [ i ] . name != 0 ; i++ ) + if ( strcmp ( tags[i].name, s ) == 0 ) + return tags[i].value ; + + ulSetError ( UL_WARNING, "Illegal token '%s'", s ) ; + return TOKEN_ILLEGAL ; +} + + +static short getShort ( FILE *fd ) +{ + short val ; + + if ( fread ( & val, sizeof(short), 1, fd ) != 1 ) + return 0 ; + + val = ulEndianLittle16 ( (unsigned short) val ) ; + return val ; +} + + +static void getColour ( FILE *fd, float *colour ) +{ + if ( fread ( colour, sizeof(float), 4, fd ) != 4 ) + return ; + + ulEndianLittleArrayFloat ( colour, 4 ) ; +} + + +static float getFloat ( FILE *fd ) +{ + float val ; + + if ( fread ( & val, sizeof(float), 1, fd ) != 1 ) + return 0 ; + + val = ulEndianLittleFloat ( val ) ; + return val ; +} + + +static int getInteger ( FILE *fd ) +{ + int val ; + + if ( fread ( & val, sizeof(int), 1, fd ) != 1 ) + return 0 ; + + val = ulEndianLittle32 ( (unsigned int) val ) ; + return val ; +} + + +static int getLength ( FILE *fd ) +{ + return getInteger ( fd ) ; +} + + +static int getTokenAndLength ( FILE *fd, int *len ) +{ + int tok = getToken ( fd ) ; + + if ( tok == TOKEN_EOF ) + { + *len = 0 ; + return tok ; + } + + *len = getLength ( fd ) ; + return tok ; +} + + +static char *getQString ( FILE *fd, int *l ) +{ + short len = getShort ( fd ) ; + + (*l) -= len + sizeof ( short ) ; + + char *res = new char [ len + 1 ] ; + + fread ( res, sizeof(char), len, fd ) ; + res [ len ] = '\0' ; + + return res ; +} + + +static void getMAT0 ( FILE *fd ) +{ + dofMaterial *mat = new dofMaterial ; + + mat -> ssg_material = new ssgSimpleState () ; + + materials -> addEntity ( mat ) ; + + while ( 1 ) + { + int len ; + + switch ( getToken ( fd ) ) + { + case TOKEN_MHDR : + { + len = getLength ( fd ) ; + mat -> name = getQString ( fd, &len ) ; + mat -> className = getQString ( fd, &len ) ; +fprintf ( stderr, "MAT0: Name='%s', className='%s'\n", + mat -> name, mat -> className ) ; + + } + break ; + + case TOKEN_MTEX : + { + len = getLength ( fd ) ; + mat -> textures = getInteger ( fd ) ; + len -= sizeof ( int ) ; + mat -> mapName = new char * [ mat -> textures ] ; + for ( int i = 0 ; i < mat -> textures ; i++ ) + mat -> mapName [ i ] = getQString ( fd, & len ) ; + } + break ; + + case TOKEN_MCOL : + { + len = getLength ( fd ) ; + getColour ( fd, mat -> ambient ) ; + getColour ( fd, mat -> diffuse ) ; + getColour ( fd, mat -> specular ) ; + getColour ( fd, mat -> emission ) ; + mat -> shininess = getFloat ( fd ) ; + len -= 17 * sizeof(float) ; + } + break ; + + case TOKEN_MUVW : + len = getLength ( fd ) ; + mat -> uvwUoffset = getFloat ( fd ) ; + mat -> uvwVoffset = getFloat ( fd ) ; + mat -> uvwUtiling = getFloat ( fd ) ; + mat -> uvwVtiling = getFloat ( fd ) ; + mat -> uvwAngle = getFloat ( fd ) ; + mat -> uvwBlur = getFloat ( fd ) ; + mat -> uvwBlurOffset = getFloat ( fd ) ; + len -= 7 * sizeof(float) ; + break ; + + case TOKEN_MTRA : + len = getLength ( fd ) ; + mat -> transparency = getFloat ( fd ) ; + mat -> blendMode = getInteger ( fd ) ; + len -= sizeof(float) + sizeof(int) ; + break ; + + case TOKEN_MSUB : + { + len = getLength ( fd ) ; + + mat -> subMaterials = getInteger ( fd ) ; + + if ( mat -> subMaterials != 0 ) + ulSetError ( UL_WARNING, + "ssgLoadDOF: Sub-Materials are not supported." ) ; + + len -= sizeof ( int ) ; + } + break ; + + case TOKEN_MEND : + { + ssgSimpleState *m = mat -> ssg_material ; + + if ( mat -> textures > 0 ) + { + m -> enable ( GL_TEXTURE_2D ) ; + m -> setTexture ( mat -> mapName[0] ) ; + } + else + m -> disable ( GL_TEXTURE_2D ) ; + + m -> disable ( GL_COLOR_MATERIAL ) ; + m -> setMaterial ( GL_AMBIENT , mat -> ambient ) ; + m -> setMaterial ( GL_DIFFUSE , mat -> diffuse ) ; + m -> setMaterial ( GL_SPECULAR, mat -> specular ) ; + m -> setMaterial ( GL_EMISSION, mat -> emission ) ; + m -> setShininess ( mat -> shininess ) ; + } + return ; + + case TOKEN_EOF : return ; + + default : + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: Material contains an unrecognised token?!?" ) ; + + len = getLength ( fd ) ; + } + break ; + } + + for ( int i = 0 ; i < len ; i++ ) getc ( fd ) ; + } +} + + +static void getMaterials ( FILE *fd, int howMany ) +{ +ulSetError ( UL_WARNING, "Getting %d materials", howMany ) ; + + for ( int i = 0 ; i < howMany ; i++ ) + { + int len ; + + switch ( getToken ( fd ) ) + { + case TOKEN_MAT0 : + len = getLength ( fd ) ; + getMAT0 ( fd ) ; + break ; + + + case TOKEN_EOF : return ; + + default : + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: Material contains an unrecognised token?!?" ) ; + + len = getLength ( fd ) ; + + for ( int i = 0 ; i < len ; i++ ) + getc ( fd ) ; + } + return ; + } + } +} + + +static ssgEntity *getGOB1 ( FILE *fd ) +{ + int flags = 0 ; + int paintflags = 0 ; + int materialRef = 0 ; + int indices = 0 ; short *index = NULL ; + int vertices = 0 ; float *vertex = NULL ; + int tvertices = 0 ; float *tvertex = NULL ; + int normals = 0 ; float *normal = NULL ; + int bursts = 0 ; + int *burstStart = NULL ; + int *burstCount = NULL ; + int *burstMtlID = NULL ; + int *burstVperP = NULL ; + + while ( 1 ) + { + int len ; + + switch ( getToken ( fd ) ) + { + case TOKEN_GHDR : + { + len = getLength ( fd ) ; + + flags = getInteger ( fd ) ; + paintflags = getInteger ( fd ) ; + materialRef = getInteger ( fd ) ; + + len -= 3 * sizeof ( int ) ; + } + break ; + + case TOKEN_INDI : + { + len = getLength ( fd ) ; + indices = getInteger ( fd ) ; + index = new short [ indices ] ; + fread ( index, sizeof(short), indices, fd ) ; + ulEndianLittleArray16 ( (unsigned short *) index, indices ) ; + len -= sizeof(int) + sizeof(short) * indices ; + } + break ; + + case TOKEN_VERT : + { + len = getLength ( fd ) ; + vertices = getInteger ( fd ) ; + vertex = new float [ vertices * 3 ] ; + fread ( vertex, sizeof(float), vertices * 3, fd ) ; + ulEndianLittleArrayFloat ( vertex, vertices * 3 ) ; + len -= sizeof(int) + sizeof(float) * vertices * 3 ; + } + break ; + + case TOKEN_TVER : + { + len = getLength ( fd ) ; + tvertices = getInteger ( fd ) ; + tvertex = new float [ tvertices * 2 ] ; + fread ( tvertex, sizeof(float), tvertices * 2, fd ) ; + ulEndianLittleArrayFloat ( tvertex, tvertices * 2 ) ; + len -= sizeof(int) + sizeof(float) * tvertices * 2 ; + } + break ; + + case TOKEN_NORM : + { + len = getLength ( fd ) ; + normals = getInteger ( fd ) ; + normal = new float [ normals * 3 ] ; + fread ( normal, sizeof(float), normals * 3, fd ) ; + ulEndianLittleArrayFloat ( normal, normals * 3 ) ; + len -= sizeof(int) + sizeof(float) * normals * 3 ; + } + break ; + + case TOKEN_BRST : + { + len = getLength ( fd ) ; + bursts = getInteger ( fd ) ; + burstStart = new int [ bursts ] ; + burstCount = new int [ bursts ] ; + burstMtlID = new int [ bursts ] ; + burstVperP = new int [ bursts ] ; + fread ( burstStart, sizeof(int), bursts, fd ) ; + fread ( burstCount, sizeof(int), bursts, fd ) ; + fread ( burstMtlID, sizeof(int), bursts, fd ) ; + fread ( burstVperP, sizeof(int), bursts, fd ) ; + ulEndianLittleArray32 ( (unsigned int *) burstStart, bursts ) ; + ulEndianLittleArray32 ( (unsigned int *) burstCount, bursts ) ; + ulEndianLittleArray32 ( (unsigned int *) burstMtlID, bursts ) ; + ulEndianLittleArray32 ( (unsigned int *) burstVperP, bursts ) ; + + len -= sizeof(int) + sizeof(int) * bursts * 4 ; + } + break ; + + case TOKEN_GEND : + { + float *colours = new float [ vertices * 4 ] ; + + for ( int i = 0 ; i < vertices ; i++ ) + { + colours [ i*4 + 0 ] = 0.5f ; + colours [ i*4 + 1 ] = 0.5f ; + colours [ i*4 + 2 ] = 0.5f ; + colours [ i*4 + 3 ] = 1.0f ; + + float tmp ; + tmp = vertex [ i*3 + 1 ] ; + vertex [ i*3 + 1 ] = -vertex [ i*3 + 2 ] ; + vertex [ i*3 + 2 ] = tmp ; + tmp = normal [ i*3 + 1 ] ; + normal [ i*3 + 1 ] = -normal [ i*3 + 2 ] ; + normal [ i*3 + 2 ] = tmp ; + } + ssgVertexArray *vx = new ssgVertexArray ( vertices , (sgVec3 *) vertex ) ; + ssgColourArray *co = new ssgColourArray ( vertices , (sgVec4 *) colours ) ; + ssgNormalArray *nm = new ssgNormalArray ( normals , (sgVec3 *) normal ) ; + ssgTexCoordArray *tx = new ssgTexCoordArray ( tvertices, (sgVec2 *) tvertex ) ; + ssgIndexArray *ix = new ssgIndexArray ( indices , index ) ; + + ssgVtxArray *va = new ssgVtxArray ( GL_TRIANGLES, + vx, nm, tx, co, ix ) ; + + va -> setState ( ( (dofMaterial *) + ( materials -> getEntity ( materialRef ) ) + ) -> ssg_material ) ; + delete [] burstStart ; + delete [] burstCount ; + delete [] burstMtlID ; + delete [] burstVperP ; + + return va ; + } + + case TOKEN_EOF : return NULL ; + + default : + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: GOB1 contains an unrecognised token?!?" ) ; + + len = getLength ( fd ) ; + } + break ; + } + + for ( int i = 0 ; i < len ; i++ ) getc ( fd ) ; + } +} + + + +static ssgBranch *getGeode ( FILE *fd, int howMany ) +{ + ssgBranch *br = new ssgBranch () ; + +ulSetError ( UL_WARNING, "Getting %d geodes", howMany ) ; + + for ( int i = 0 ; i < howMany ; i++ ) + { + int len ; + + switch ( getToken ( fd ) ) + { + case TOKEN_GOB1 : + len = getLength ( fd ) ; + + br -> addKid ( getGOB1 ( fd ) ) ; + + break ; + + case TOKEN_EOF : return br ; + + default : + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: Geode contains an unrecognised token?!?" ) ; + + len = getLength ( fd ) ; + + for ( int i = 0 ; i < len ; i++ ) + getc ( fd ) ; + } + return br ; + } + } + + return br ; +} + + +static void cleanUp () +{ + for ( int i = 0 ; i < materials -> getNumEntities () ; i++ ) + delete (dofMaterial *) ( materials -> getEntity ( i ) ) ; + delete materials ; +} + + +ssgEntity *ssgLoadDOF ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgBranch *model ; + + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE *fd = fopen ( filename, "ra" ) ; + + if ( fd == NULL ) + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: Failed to open '%s' for reading", filename ) ; + return NULL ; + } + + int len ; + + if ( getTokenAndLength ( fd, &len ) != TOKEN_DOF1 ) + { + ulSetError ( UL_WARNING, + "ssgLoadDOF: '%s' does not start with 'DOF1'?!?", filename ) ; + return NULL ; + } + + materials = new ulList () ; + model = new ssgBranch () ; + + while ( 1 ) + { + switch ( getToken ( fd ) ) + { + case TOKEN_MATS : + getLength ( fd ) ; + getMaterials ( fd, getInteger ( fd ) ) ; + break ; + + case TOKEN_GEOB : + getLength ( fd ) ; + model -> addKid ( getGeode ( fd, getInteger ( fd ) ) ) ; + break ; + + case TOKEN_EDOF : + cleanUp () ; + return model ; + + case TOKEN_EOF : + cleanUp () ; + return NULL ; + + default : + ulSetError ( UL_WARNING, + "ssgLoadDOF: '%s' contains an unrecognised token?!?", filename ) ; + cleanUp () ; + return NULL ; + } + } +} + + diff --git a/src/ssg/ssgLoadDXF.cxx b/src/ssg/ssgLoadDXF.cxx new file mode 100644 index 0000000..b23b8e3 --- /dev/null +++ b/src/ssg/ssgLoadDXF.cxx @@ -0,0 +1,952 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadDXF.cxx 1673 2002-09-15 01:29:12Z ude $ +*/ + +// +// DXF loader for SSG/PLIB +// Ported from IVCON by Dave McClurg (dpm@efn.org) in March-2000 +// IVCON::dxf_read() by John Burkardt, 23 May 1999 +// Added better support for meshes, August 2001 +// Added support for colors, blocks and block references, September 2001 +// + +#include "ssgLocal.h" + + +#define MAX_LINE_LEN 1024 + +class dxfVert { +public: + sgVec3 pos ; + int color_index ; + int isEqual ( class dxfVert * other ); +} ; + +class dxfVertArray : public ssgSimpleList +{ +public: + dxfVertArray () : ssgSimpleList ( sizeof(dxfVert), 0, NULL ) {} + dxfVert *get ( unsigned int n ) { return (dxfVert *) raw_get ( n ) ; } + void add ( const dxfVert* thing ) { raw_add ( (char *) thing ) ; } ; +} ; + +struct dxfMesh +{ + int flags; + int size[2]; + int vlist[4]; + int vnum; +} ; + +enum Entities { + ENT_NONE, + ENT_LINE, + ENT_FACE, + ENT_POLYLINE, + ENT_VERTEX, + ENT_INSERT +}; + +static int ent_type = ENT_NONE ; + +static const ssgLoaderOptions* current_options = NULL ; +static ssgState* current_state = NULL ; +static ssgBranch* top_branch = NULL ; +static ssgBranch* current_block = NULL ; +static ssgBranch* blocks = NULL ; + +static int num_line; +static int num_face; +static dxfVertArray tempvert; +static dxfVertArray linevert; +static dxfVertArray facevert; +static dxfVertArray meshvert; +static dxfMesh mesh; + +static int cflags; +static sgVec3 cvec; +static sgVec3 scale_vec; +static float rot_angle; +static char* block_name; +static int color_index ; + +static float dxf_colors [256][3] = +{ + { 0.0000f, 0.0000f, 0.0000f }, + { 1.0000f, 0.0000f, 0.0000f }, + { 1.0000f, 1.0000f, 0.0000f }, + { 0.0000f, 1.0000f, 0.0000f }, + { 0.0000f, 1.0000f, 1.0000f }, + { 0.0000f, 0.0000f, 1.0000f }, + { 1.0000f, 0.0000f, 1.0000f }, + { 0.0000f, 0.0000f, 0.0000f }, + { 0.0000f, 0.0000f, 0.0000f }, + { 0.0000f, 0.0000f, 0.0000f }, + { 1.0000f, 0.0000f, 0.0000f }, + { 1.0000f, 0.5000f, 0.5000f }, + { 0.6500f, 0.0000f, 0.0000f }, + { 0.6500f, 0.3250f, 0.3250f }, + { 0.5000f, 0.0000f, 0.0000f }, + { 0.5000f, 0.2500f, 0.2500f }, + { 0.3000f, 0.0000f, 0.0000f }, + { 0.3000f, 0.1500f, 0.1500f }, + { 0.1500f, 0.0000f, 0.0000f }, + { 0.1500f, 0.0750f, 0.0750f }, + { 1.0000f, 0.2500f, 0.0000f }, + { 1.0000f, 0.6250f, 0.5000f }, + { 0.6500f, 0.1625f, 0.0000f }, + { 0.6500f, 0.4063f, 0.3250f }, + { 0.5000f, 0.1250f, 0.0000f }, + { 0.5000f, 0.3125f, 0.2500f }, + { 0.3000f, 0.0750f, 0.0000f }, + { 0.3000f, 0.1875f, 0.1500f }, + { 0.1500f, 0.0375f, 0.0000f }, + { 0.1500f, 0.0938f, 0.0750f }, + { 1.0000f, 0.5000f, 0.0000f }, + { 1.0000f, 0.7500f, 0.5000f }, + { 0.6500f, 0.3250f, 0.0000f }, + { 0.6500f, 0.4875f, 0.3250f }, + { 0.5000f, 0.2500f, 0.0000f }, + { 0.5000f, 0.3750f, 0.2500f }, + { 0.3000f, 0.1500f, 0.0000f }, + { 0.3000f, 0.2250f, 0.1500f }, + { 0.1500f, 0.0750f, 0.0000f }, + { 0.1500f, 0.1125f, 0.0750f }, + { 1.0000f, 0.7500f, 0.0000f }, + { 1.0000f, 0.8750f, 0.5000f }, + { 0.6500f, 0.4875f, 0.0000f }, + { 0.6500f, 0.5688f, 0.3250f }, + { 0.5000f, 0.3750f, 0.0000f }, + { 0.5000f, 0.4375f, 0.2500f }, + { 0.3000f, 0.2250f, 0.0000f }, + { 0.3000f, 0.2625f, 0.1500f }, + { 0.1500f, 0.1125f, 0.0000f }, + { 0.1500f, 0.1313f, 0.0750f }, + { 1.0000f, 1.0000f, 0.0000f }, + { 1.0000f, 1.0000f, 0.5000f }, + { 0.6500f, 0.6500f, 0.0000f }, + { 0.6500f, 0.6500f, 0.3250f }, + { 0.5000f, 0.5000f, 0.0000f }, + { 0.5000f, 0.5000f, 0.2500f }, + { 0.3000f, 0.3000f, 0.0000f }, + { 0.3000f, 0.3000f, 0.1500f }, + { 0.1500f, 0.1500f, 0.0000f }, + { 0.1500f, 0.1500f, 0.0750f }, + { 0.7500f, 1.0000f, 0.0000f }, + { 0.8750f, 1.0000f, 0.5000f }, + { 0.4875f, 0.6500f, 0.0000f }, + { 0.5688f, 0.6500f, 0.3250f }, + { 0.3750f, 0.5000f, 0.0000f }, + { 0.4375f, 0.5000f, 0.2500f }, + { 0.2250f, 0.3000f, 0.0000f }, + { 0.2625f, 0.3000f, 0.1500f }, + { 0.1125f, 0.1500f, 0.0000f }, + { 0.1313f, 0.1500f, 0.0750f }, + { 0.5000f, 1.0000f, 0.0000f }, + { 0.7500f, 1.0000f, 0.5000f }, + { 0.3250f, 0.6500f, 0.0000f }, + { 0.4875f, 0.6500f, 0.3250f }, + { 0.2500f, 0.5000f, 0.0000f }, + { 0.3750f, 0.5000f, 0.2500f }, + { 0.1500f, 0.3000f, 0.0000f }, + { 0.2250f, 0.3000f, 0.1500f }, + { 0.0750f, 0.1500f, 0.0000f }, + { 0.1125f, 0.1500f, 0.0750f }, + { 0.2500f, 1.0000f, 0.0000f }, + { 0.6250f, 1.0000f, 0.5000f }, + { 0.1625f, 0.6500f, 0.0000f }, + { 0.4063f, 0.6500f, 0.3250f }, + { 0.1250f, 0.5000f, 0.0000f }, + { 0.3125f, 0.5000f, 0.2500f }, + { 0.0750f, 0.3000f, 0.0000f }, + { 0.1875f, 0.3000f, 0.1500f }, + { 0.0375f, 0.1500f, 0.0000f }, + { 0.0938f, 0.1500f, 0.0750f }, + { 0.0000f, 1.0000f, 0.0000f }, + { 0.5000f, 1.0000f, 0.5000f }, + { 0.0000f, 0.6500f, 0.0000f }, + { 0.3250f, 0.6500f, 0.3250f }, + { 0.0000f, 0.5000f, 0.0000f }, + { 0.2500f, 0.5000f, 0.2500f }, + { 0.0000f, 0.3000f, 0.0000f }, + { 0.1500f, 0.3000f, 0.1500f }, + { 0.0000f, 0.1500f, 0.0000f }, + { 0.0750f, 0.1500f, 0.0750f }, + { 0.0000f, 1.0000f, 0.2500f }, + { 0.5000f, 1.0000f, 0.6250f }, + { 0.0000f, 0.6500f, 0.1625f }, + { 0.3250f, 0.6500f, 0.4063f }, + { 0.0000f, 0.5000f, 0.1250f }, + { 0.2500f, 0.5000f, 0.3125f }, + { 0.0000f, 0.3000f, 0.0750f }, + { 0.1500f, 0.3000f, 0.1875f }, + { 0.0000f, 0.1500f, 0.0375f }, + { 0.0750f, 0.1500f, 0.0938f }, + { 0.0000f, 1.0000f, 0.5000f }, + { 0.5000f, 1.0000f, 0.7500f }, + { 0.0000f, 0.6500f, 0.3250f }, + { 0.3250f, 0.6500f, 0.4875f }, + { 0.0000f, 0.5000f, 0.2500f }, + { 0.2500f, 0.5000f, 0.3750f }, + { 0.0000f, 0.3000f, 0.1500f }, + { 0.1500f, 0.3000f, 0.2250f }, + { 0.0000f, 0.1500f, 0.0750f }, + { 0.0750f, 0.1500f, 0.1125f }, + { 0.0000f, 1.0000f, 0.7500f }, + { 0.5000f, 1.0000f, 0.8750f }, + { 0.0000f, 0.6500f, 0.4875f }, + { 0.3250f, 0.6500f, 0.5688f }, + { 0.0000f, 0.5000f, 0.3750f }, + { 0.2500f, 0.5000f, 0.4375f }, + { 0.0000f, 0.3000f, 0.2250f }, + { 0.1500f, 0.3000f, 0.2625f }, + { 0.0000f, 0.1500f, 0.1125f }, + { 0.0750f, 0.1500f, 0.1313f }, + { 0.0000f, 1.0000f, 1.0000f }, + { 0.5000f, 1.0000f, 1.0000f }, + { 0.0000f, 0.6500f, 0.6500f }, + { 0.3250f, 0.6500f, 0.6500f }, + { 0.0000f, 0.5000f, 0.5000f }, + { 0.2500f, 0.5000f, 0.5000f }, + { 0.0000f, 0.3000f, 0.3000f }, + { 0.1500f, 0.3000f, 0.3000f }, + { 0.0000f, 0.1500f, 0.1500f }, + { 0.0750f, 0.1500f, 0.1500f }, + { 0.0000f, 0.7500f, 1.0000f }, + { 0.5000f, 0.8750f, 1.0000f }, + { 0.0000f, 0.4875f, 0.6500f }, + { 0.3250f, 0.5688f, 0.6500f }, + { 0.0000f, 0.3750f, 0.5000f }, + { 0.2500f, 0.4375f, 0.5000f }, + { 0.0000f, 0.2250f, 0.3000f }, + { 0.1500f, 0.2625f, 0.3000f }, + { 0.0000f, 0.1125f, 0.1500f }, + { 0.0750f, 0.1313f, 0.1500f }, + { 0.0000f, 0.5000f, 1.0000f }, + { 0.5000f, 0.7500f, 1.0000f }, + { 0.0000f, 0.3250f, 0.6500f }, + { 0.3250f, 0.4875f, 0.6500f }, + { 0.0000f, 0.2500f, 0.5000f }, + { 0.2500f, 0.3750f, 0.5000f }, + { 0.0000f, 0.1500f, 0.3000f }, + { 0.1500f, 0.2250f, 0.3000f }, + { 0.0000f, 0.0750f, 0.1500f }, + { 0.0750f, 0.1125f, 0.1500f }, + { 0.0000f, 0.2500f, 1.0000f }, + { 0.5000f, 0.6250f, 1.0000f }, + { 0.0000f, 0.1625f, 0.6500f }, + { 0.3250f, 0.4063f, 0.6500f }, + { 0.0000f, 0.1250f, 0.5000f }, + { 0.2500f, 0.3125f, 0.5000f }, + { 0.0000f, 0.0750f, 0.3000f }, + { 0.1500f, 0.1875f, 0.3000f }, + { 0.0000f, 0.0375f, 0.1500f }, + { 0.0750f, 0.0938f, 0.1500f }, + { 0.0000f, 0.0000f, 1.0000f }, + { 0.5000f, 0.5000f, 1.0000f }, + { 0.0000f, 0.0000f, 0.6500f }, + { 0.3250f, 0.3250f, 0.6500f }, + { 0.0000f, 0.0000f, 0.5000f }, + { 0.2500f, 0.2500f, 0.5000f }, + { 0.0000f, 0.0000f, 0.3000f }, + { 0.1500f, 0.1500f, 0.3000f }, + { 0.0000f, 0.0000f, 0.1500f }, + { 0.0750f, 0.0750f, 0.1500f }, + { 0.2500f, 0.0000f, 1.0000f }, + { 0.6250f, 0.5000f, 1.0000f }, + { 0.1625f, 0.0000f, 0.6500f }, + { 0.4063f, 0.3250f, 0.6500f }, + { 0.1250f, 0.0000f, 0.5000f }, + { 0.3125f, 0.2500f, 0.5000f }, + { 0.0397f, 0.0000f, 0.3000f }, + { 0.1875f, 0.1500f, 0.3000f }, + { 0.0375f, 0.0000f, 0.1500f }, + { 0.0938f, 0.0750f, 0.1500f }, + { 0.5000f, 0.0000f, 1.0000f }, + { 0.7500f, 0.5000f, 1.0000f }, + { 0.3250f, 0.0000f, 0.6500f }, + { 0.4875f, 0.3250f, 0.6500f }, + { 0.2500f, 0.0000f, 0.5000f }, + { 0.3750f, 0.2500f, 0.5000f }, + { 0.1500f, 0.0000f, 0.3000f }, + { 0.2250f, 0.1500f, 0.3000f }, + { 0.0750f, 0.0000f, 0.1500f }, + { 0.1125f, 0.0750f, 0.1500f }, + { 0.7500f, 0.0000f, 1.0000f }, + { 0.8750f, 0.5000f, 1.0000f }, + { 0.4875f, 0.0000f, 0.6500f }, + { 0.5688f, 0.3250f, 0.6500f }, + { 0.3750f, 0.0000f, 0.5000f }, + { 0.4375f, 0.2500f, 0.5000f }, + { 0.2250f, 0.0000f, 0.3000f }, + { 0.2625f, 0.1500f, 0.3000f }, + { 0.1125f, 0.0000f, 0.1500f }, + { 0.1313f, 0.0750f, 0.1500f }, + { 1.0000f, 0.0000f, 1.0000f }, + { 1.0000f, 0.5000f, 1.0000f }, + { 0.6500f, 0.0000f, 0.6500f }, + { 0.6500f, 0.3250f, 0.6500f }, + { 0.5000f, 0.0000f, 0.5000f }, + { 0.5000f, 0.2500f, 0.5000f }, + { 0.3000f, 0.0000f, 0.3000f }, + { 0.3000f, 0.1500f, 0.3000f }, + { 0.1500f, 0.0000f, 0.1500f }, + { 0.1500f, 0.0750f, 0.1500f }, + { 1.0000f, 0.0000f, 0.7500f }, + { 1.0000f, 0.5000f, 0.8750f }, + { 0.6500f, 0.0000f, 0.4875f }, + { 0.6500f, 0.3250f, 0.5688f }, + { 0.5000f, 0.0000f, 0.3750f }, + { 0.5000f, 0.2500f, 0.4375f }, + { 0.3000f, 0.0000f, 0.2250f }, + { 0.3000f, 0.1500f, 0.2625f }, + { 0.1500f, 0.0000f, 0.1125f }, + { 0.1500f, 0.0750f, 0.1313f }, + { 1.0000f, 0.0000f, 0.5000f }, + { 1.0000f, 0.5000f, 0.7500f }, + { 0.6500f, 0.0000f, 0.3250f }, + { 0.6500f, 0.3250f, 0.4875f }, + { 0.5000f, 0.0000f, 0.2500f }, + { 0.5000f, 0.2500f, 0.3750f }, + { 0.3000f, 0.0000f, 0.1500f }, + { 0.3000f, 0.1500f, 0.2250f }, + { 0.1500f, 0.0000f, 0.0750f }, + { 0.1500f, 0.0750f, 0.1125f }, + { 1.0000f, 0.0000f, 0.2500f }, + { 1.0000f, 0.5000f, 0.6250f }, + { 0.6500f, 0.0000f, 0.1625f }, + { 0.6500f, 0.3250f, 0.4063f }, + { 0.5000f, 0.0000f, 0.1250f }, + { 0.5000f, 0.2500f, 0.3125f }, + { 0.3000f, 0.0000f, 0.0750f }, + { 0.3000f, 0.1500f, 0.1875f }, + { 0.1500f, 0.0000f, 0.0375f }, + { 0.1500f, 0.0750f, 0.0938f }, + { 0.3300f, 0.3300f, 0.3300f }, + { 0.4640f, 0.4640f, 0.4640f }, + { 0.5980f, 0.5980f, 0.5980f }, + { 0.7320f, 0.7320f, 0.7320f }, + { 0.8660f, 0.8660f, 0.8660f }, + { 1.0000f, 1.0000f, 1.0000f }, +}; + +int dxfVert::isEqual ( class dxfVert * other ) +// compares strictly (without epsilon) +{ + if ( color_index != other->color_index ) + return FALSE; + return sgCompareVec3 ( pos, other->pos, 0.0 ); + +} + +/*static void copy_vert ( dxfVert& dst, const dxfVert& src ) +{ + dst.color_index = src.color_index ; + sgCopyVec3 ( dst.pos, src.pos ) ; +}*/ + +static void add_tri ( const dxfVert* p, const dxfVert* q, const dxfVert* r ) +{ + facevert.add ( p ) ; + facevert.add ( q ) ; + facevert.add ( r ) ; + num_face ++; +} + +static void add_face ( void ) +{ + int num_vert = tempvert.getNum() ; + + if ( num_vert >= 4 ) //quad? + if ( tempvert.get(3)->isEqual ( tempvert.get(2) ) ) + // pseudo quad. + num_vert=3; + + if ( num_vert >= 3 ) + { + if ( num_vert >= 4 ) //quad? + { + add_tri ( tempvert.get(0), tempvert.get(1), tempvert.get(3) ) ; + add_tri ( tempvert.get(3), tempvert.get(1), tempvert.get(2) ) ; + } + else //triangle + { + add_tri ( tempvert.get(0), tempvert.get(1), tempvert.get(2) ) ; + } + } +} + +static void dxf_flush ( void ) +{ + int num_vert = tempvert.getNum() ; + if ( ent_type == ENT_LINE ) + { + if ( num_vert >= 2 ) + { + linevert.add( tempvert.get(0) ) ; + linevert.add( tempvert.get(1) ) ; + num_line ++; + } + } + else if ( ent_type == ENT_FACE ) + { + add_face () ; + } + else if ( ent_type == ENT_POLYLINE ) + { + mesh.flags = cflags; + meshvert.removeAll(); + mesh.size[0] = mesh.vlist[0]; + mesh.size[1] = mesh.vlist[1]; + } + else if ( ent_type == ENT_INSERT ) + { + if ( block_name != NULL ) + { + //block_name + ssgEntity* found = blocks -> getByName ( block_name ) ; + if ( found != NULL ) + { + //cvec + //scale_vec + //rot_angle + + sgMat4 mat, tmp; + sgVec3 axis = { 0.0f, 0.0f, 1.0f }; + sgMakeRotMat4 ( mat, rot_angle, axis ) ; + + sgMakeIdentMat4 ( tmp ) ; + sgScaleVec4 ( tmp[0], scale_vec[0] ); + sgScaleVec4 ( tmp[1], scale_vec[1] ); + sgScaleVec4 ( tmp[2], scale_vec[2] ); + sgPostMultMat4 ( mat, tmp ) ; + + sgMakeTransMat4 ( tmp, cvec ) ; + sgPostMultMat4 ( mat, tmp ) ; + + //printf("cvec(%.2f,%.2f,%.2f), scale(%.2f,%.2f,%.2f), rot(%.2f)\n", + // cvec[0], cvec[1], cvec[2], cvec[0], cvec[1], cvec[2], rot_angle ) ; + //found -> print ( stderr, "", 99 ); + + ssgTransform* block_tr = new ssgTransform ; + block_tr -> setName ( block_name ) ; + block_tr -> setTransform ( mat ) ; + block_tr -> addKid ( found ) ; + top_branch -> addKid ( block_tr ) ; + } + } + } + else if ( ent_type == ENT_VERTEX ) + { + if ( (mesh.flags & 8) != 0 ) + { + //This is a 3D Polyline + if ( (cflags & 32) != 0 ) + { + //This is a 3D Polyline vertex + meshvert.add ( tempvert.get(0) ) ; + } + } + else if ( (mesh.flags & 16) != 0 ) + { + //This is a 3D polygon MxN mesh. (uniform grid) + if ( (cflags & 64) != 0 ) + { + //This is a 3D polygon mesh vertex + meshvert.add ( tempvert.get(0) ) ; + } + } + else if ( (mesh.flags & 64) != 0 ) + { + //This Polyline is a polyface mesh. + if ( (cflags & 128) != 0 ) + { + if ( (cflags & 64) != 0 ) + { + //This is a 3D polygon mesh vertex + meshvert.add ( tempvert.get(0) ) ; + } + else if ( mesh.vnum >= 3 ) + { + //copy each vertex where the first is numbered 1 + tempvert.removeAll(); + int error = 0; + for ( int i=0; i 0 && ival <= meshvert.getNum() ) + tempvert.add ( meshvert.get(ival-1) ) ; + else + error = 1; + } + + if ( error == 0 ) + add_face () ; + } + } + } + } + + cflags = 0; + cvec[0] = 0.0f; + cvec[1] = 0.0f; + cvec[2] = 0.0f; + scale_vec[0] = 1.0f; + scale_vec[1] = 1.0f; + scale_vec[2] = 1.0f; + rot_angle = 0.0f; + color_index = 7 ; + + tempvert.removeAll(); + + mesh.vnum = 0 ; + mesh.vlist[0] = 0; + mesh.vlist[1] = 0; + mesh.vlist[2] = 0; + mesh.vlist[3] = 0; + + delete[] block_name; + block_name = NULL; +} + + +static void dxf_free () +{ + tempvert.removeAll(); + linevert.removeAll() ; + facevert.removeAll() ; + meshvert.removeAll() ; + + num_line = 0; + num_face = 0; + + ent_type = ENT_NONE; +} + + +static sgVec4& get_color ( int ci ) +{ + if ( ci < 0 ) + ci = 0 ; + else if ( ci > 255 ) + ci = 255 ; + float* rgb = dxf_colors[ci]; + static sgVec4 color ; + sgSetVec4 ( color, rgb[0], rgb[1], rgb[2], 1.0f ) ; + return color ; +} + + +static void dxf_create ( ssgBranch* br ) +{ + dxf_flush () ; + + //create ssg nodes + if ( num_face ) + { + int num_vert = facevert.getNum () ; + ssgVertexArray* vlist = new ssgVertexArray ( num_vert ) ; + ssgColourArray* clist = new ssgColourArray ( num_vert ) ; + ssgNormalArray* nlist = new ssgNormalArray ( num_vert ) ; + sgVec3 normal ; + for ( int i=0; i pos, + facevert.get(i+1) -> pos, + facevert.get(i+2) -> pos ) ; + } + + vlist -> add ( facevert.get(i) -> pos ) ; + nlist -> add ( normal ) ; + clist -> add ( get_color( facevert.get(i) -> color_index ) ) ; + } + ssgVtxTable *vtab = new ssgVtxTable ( GL_TRIANGLES, vlist, nlist, 0, clist ); + vtab -> setState ( current_state ) ; + br -> addKid ( vtab ) ; + } + + if ( num_line ) + { + int num_vert = linevert.getNum () ; + ssgVertexArray* vlist = new ssgVertexArray ( num_vert ) ; + ssgColourArray* clist = new ssgColourArray ( num_vert ) ; + for ( int i=0; i add ( linevert.get(i) -> pos ) ; + clist -> add ( get_color(linevert.get(i) -> color_index) ) ; + } + ssgVtxTable *vtab = new ssgVtxTable ( GL_LINES, vlist, 0, 0, clist ); + vtab -> setState ( current_state ) ; + br -> addKid ( vtab ) ; + } + + dxf_free () ; +} + + +static int dxf_read ( FILE *filein ) +{ + dxf_free () ; + +/* + Read the next two lines of the file into INPUT1 and INPUT2. +*/ + for ( ;; ) { + + int count; + int code; + int width; + char input1[MAX_LINE_LEN]; + char input2[MAX_LINE_LEN]; + +/* + INPUT1 should contain a single integer, which tells what INPUT2 + will contain. +*/ + if ( fgets ( input1, MAX_LINE_LEN, filein ) == NULL ) { + break; + } + + count = sscanf ( input1, "%d%n", &code, &width ); + if ( count <= 0 ) { + break; + } +/* + Read the second line, and interpret it according to the code. +*/ + if ( fgets ( input2, MAX_LINE_LEN, filein ) == NULL ) { + break; + } + + if ( code == 0 ) { + + dxf_flush () ; + + //set ent_type + if ( strncmp( input2, "LINE", 4 ) == 0 ) + ent_type = ENT_LINE ; + else if ( strncmp( input2, "3DFACE", 6 ) == 0 ) + ent_type = ENT_FACE ; + else if ( strncmp( input2, "POLYLINE", 8 ) == 0 ) + ent_type = ENT_POLYLINE ; + else if ( strncmp( input2, "VERTEX", 6 ) == 0 ) + ent_type = ENT_VERTEX; + else if ( strncmp( input2, "INSERT", 6 ) == 0 ) + ent_type = ENT_INSERT; + else if ( strncmp( input2, "BLOCK", 5 ) == 0 ) { + if ( current_block == NULL ) { + current_block = new ssgBranch ; + blocks -> addKid ( current_block ) ; + } + } + else if ( strncmp( input2, "ENDBLK", 6 ) == 0 ) { + if ( current_block != NULL ) { + dxf_create ( current_block ) ; + current_block = NULL ; + } + } + else if ( strncmp( input2, "SEQEND", 6 ) == 0 ) { + +#define PL_CLOSED_IN_M 0x01 +#define PL_CURVE_FIT_ADDED 0x02 +#define PL_SPLINE_FIT_ADDED 0x04 +#define PL_3D_POLYLINE 0x08 +#define PL_3D_MESH 0x10 +#define PL_CLOSED_IN_N 0x20 +#define PL_POLYFACE_MESH 0x40 +#define PL_USE_LINETYPE 0x80 + + int polyline_flags = mesh.flags ; + + if ( (polyline_flags & PL_3D_POLYLINE) != 0 ) { + + //This is a 3D Polyline + int last = 0; + int i = 1; + int num_vert = meshvert.getNum () ; + + if ( (polyline_flags & (PL_CLOSED_IN_M|PL_CLOSED_IN_N)) != 0 ) { + + //Polyline is closed + last = num_vert - 1; + i = 0; + } + + for ( ; i= ( mesh.size[0] * mesh.size[1] ) ) { + + int mesh_m = mesh.size[0]; + int mesh_n = mesh.size[1]; + + dxfVert *buff[2]; + buff[0] = meshvert.get(0); + buff[1] = meshvert.get(mesh_n); + + /* create triangles */ + int i,j; + for (i=1;igetName() == NULL ) { + char* name = ulStrDup ( input2 ) ; + current_block->setName( name ) ; + } + } + } + else if ( ch == '0' || ch == '1' || ch == '2' || ch == '3' ) { + + float rval; + count = sscanf ( input2, "%e%n", &rval, &width ); + + switch ( input1[cpos] ) + { + case '1': + cvec[0] = rval; + break; + + case '2': + cvec[1] = rval; + break; + + case '3': + cvec[2] = rval; + + { + dxfVert vert ; + vert.color_index = color_index ; + sgCopyVec3 ( vert.pos, cvec ) ; + + tempvert.add ( &vert ) ; + } + break; + } + } + } + else if ( input1[cpos] == '4' ) { + + float rval; + count = sscanf ( input2, "%e%n", &rval, &width ); + + switch ( input1[cpos+1] ) + { + case '1': + scale_vec[0] = rval; + break; + + case '2': + scale_vec[1] = rval; + break; + + case '3': + scale_vec[2] = rval; + break; + } + } + else if ( input1[cpos] == '5' ) { + + char ch = input1[cpos+1]; + if ( ch == '0' ) { + + count = sscanf ( input2, "%e%n", &rot_angle, &width ); + } + } + else if ( input1[cpos] == '6' ) { + + if ( input1[cpos+1] == '2' ) { + + count = sscanf ( input2, "%d%n", &color_index, &width ); + } + } + else if ( input1[cpos] == '7' ) { + + int ival ; + count = sscanf ( input2, "%d%n", &ival, &width ); + + switch ( input1[cpos+1] ) + { + case '0': + cflags = ival; + break; + + case '1': + mesh.vlist[0] = ival; + mesh.vnum = 1; + break; + + case '2': + mesh.vlist[1] = ival; + mesh.vnum = 2; + break; + + case '3': + mesh.vlist[2] = ival; + mesh.vnum = 3; + break; + + case '4': + mesh.vlist[3] = ival; + mesh.vnum = 4; + break; + } + } + } + } + + dxf_create ( top_branch ) ; + + return TRUE; +} + + +static ssgState *make_state () +{ + ssgSimpleState *st = new ssgSimpleState () ; + + sgVec4 spec = { 0.0f, 0.0f, 0.0f, 1.0f }; + sgVec4 emis = { 0.0f, 0.0f, 0.0f, 1.0f } ; + float shi = { 0.0f } ; + + st -> setMaterial ( GL_SPECULAR, spec ) ; + st -> setMaterial ( GL_EMISSION, emis ) ; + st -> setShininess ( shi ) ; + st -> enable ( GL_COLOR_MATERIAL ) ; + st -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + st -> enable ( GL_LIGHTING ) ; + st -> setShadeModel ( GL_SMOOTH ) ; + + st -> disable ( GL_BLEND ) ; + st -> setOpaque () ; + st -> disable( GL_TEXTURE_2D ) ; + + return st ; +} + + +ssgEntity *ssgLoadDXF ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + top_branch = NULL ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE *loader_fd = fopen ( filename, "ra" ) ; + + if ( loader_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadDXF: Failed to open '%s' for reading", filename ) ; + return NULL ; + } + + top_branch = new ssgTransform () ; + + blocks = new ssgBranch ; + current_state = make_state () ; + + blocks -> ref () ; + current_state -> ref () ; + + dxf_read ( loader_fd ) ; + + fclose ( loader_fd ) ; + + ssgDeRefDelete ( current_state ) ; + ssgDeRefDelete ( blocks ) ; + + return top_branch ; +} diff --git a/src/ssg/ssgLoadFLT.cxx b/src/ssg/ssgLoadFLT.cxx new file mode 100644 index 0000000..0a088d0 --- /dev/null +++ b/src/ssg/ssgLoadFLT.cxx @@ -0,0 +1,3173 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadFLT.cxx 2134 2008-09-29 18:44:27Z fayjf $ +*/ + +// +// $Id: ssgLoadFLT.cxx 2134 2008-09-29 18:44:27Z fayjf $ +// +// +// OpenFlight loader for SSG +// +// +// The loading of FLT files can to some degree be controlled by +// setting environment variables: +// +// FLTNOCLEAN - if set disables all kinds of scene graph optimisations +// FLTNOEXT - ignore external references +// FLTNOTEX - disables textures altogether +// FLTNOMIPMAP - configure textures for bilinear filtering (no mipmaps) +// +// +// Some known issues: +// +// 1. The color table in v14.0 and earlier is defined differently and +// cannot be parsed. It may consist of 16 colours of 16 brightnesses +// each, and the RGB values are passed as shorts. +// +// 2. Geometry in v11.0 and earlier are defined using obsolete records +// (opcodes 6, 7, 8 and 9). Need specifications to parse them. +// +// 3. How to find external files properly. Some loaders (most notably +// IRIS Performer and MultiGen) uses the variable FLTEXTERNPATH. +// (MultiGen also uses FLTPATH and TXTPATH I think). +// +// 4. How to do mmap on Mac and PS2. +// +// 5. No optimisations are done on the geometry. New triangle strip +// generation code planned for SSG, and should be used when ready. +// +// 6. Optimisation done on the scene graph level are minimal. +// +// +// +/* + * Another OpenFlight loader for Performer + * Copyright (C) 1998-2000 Marten Stromberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "ssg.h" +#include "ssgLoaderWriterStuff.h" + +/* define this to compile against older versions of SSG */ +/*#define NO_LOADER_OPTIONS*/ + +/* debug */ +/*#define NO_LIGHTING*/ +/*#define NO_TEXTURES*/ +/*#define NO_COLORS*/ + +/* Try to figure out how to do mmap. */ +#ifdef UL_WIN32 +# define USE_WIN32_MMAP +#else +# include +# ifdef _POSIX_MAPPED_FILES +# define USE_POSIX_MMAP +# endif +#endif + +#include +#include +#include +#include +#ifdef UL_IRIX +# include +#endif +#ifdef USE_POSIX_MMAP +# include +# include /* Need both for Mandrake 8.0 !! */ +# include +# include +#endif +#ifdef UL_MSVC +# include +#endif + + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifndef R_OK +# define R_OK 4 +#endif + +#ifndef ABS +# define ABS(x) ((x)>=0?(x):-(x)) +#endif +#ifndef MIN +# define MIN(a,b) ((a)<=(b)?(a):(b)) +#endif +#ifndef MAX +# define MAX(a,b) ((a)>=(b)?(a):(b)) +#endif +#define MIN3(a,b,c) ((a)<=(b)?MIN(a,c):MIN(b,c)) +#define MAX3(a,b,c) ((a)>=(b)?MAX(a,c):MAX(b,c)) +#define CLAMP(x,lo,hi) ((x)<=(lo)?(lo):(x)>=(hi)?(hi):(x)) + +#define template _template /* trams */ + +typedef unsigned char ubyte; + +#if defined(UL_WIN32) || defined(UL_MAC_OSX) +typedef unsigned short ushort; +typedef unsigned int uint; +#endif + +// 525 = negative identation, 539= did not expect positive identation +//lint -save -e525 -e539 + +/* + * byte sex + */ + +/* XXX what about PDP_ENDIAN? */ + +/* Help! Is this correct? */ +#if (!defined(BYTE_ORDER) && defined(UL_WIN32) && !defined(NOT_INTEL_BYTE_ORDER)) +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +#endif + +/* #undef BYTE_ORDER */ + +#if !defined(BYTE_ORDER) || BYTE_ORDER != BIG_ENDIAN + +#if !defined(BYTE_ORDER) +static void _swab16(const void *src, void *dst, int n) +{ + ushort *s = (ushort *)src; + ushort *d = (ushort *)dst; + while (n--) { + ushort t = *s++; + *d++ = (((t & 0xff00U) >> 8) | + ((t & 0x00ffU) << 8)); + } +} +#endif + +static void _swab32(const void *src, void *dst, int n) +{ + uint *s = (uint *)src; + uint *d = (uint *)dst; + while (n--) { + uint t = *s++; + *d++ = (((t & 0xff000000U) >> 24) | + ((t & 0x00ff0000U) >> 8) | + ((t & 0x0000ff00U) << 8) | + ((t & 0x000000ffU) << 24)); + } +} + +static void _swab64(const void *src, void *dst, int n) +{ + /* XXX how to check if 64-bit integers are available?? */ + uint *s = (uint *)src; + uint *d = (uint *)dst; + while (n--) { + uint t0 = *s++; + uint t1 = *s++; + *d++ = (((t1 & 0xff000000U) >> 24) | + ((t1 & 0x00ff0000U) >> 8) | + ((t1 & 0x0000ff00U) << 8) | + ((t1 & 0x000000ffU) << 24)); + *d++ = (((t0 & 0xff000000U) >> 24) | + ((t0 & 0x00ff0000U) >> 8) | + ((t0 & 0x0000ff00U) << 8) | + ((t0 & 0x000000ffU) << 24)); + } +} + +#endif + +#if !defined(BYTE_ORDER) || BYTE_ORDER != LITTLE_ENDIAN + +static void _copy16(const void *src, void *dst, int n) +{ + memcpy(dst, src, 2*n); +} + +static void _copy32(const void *src, void *dst, int n) +{ + memcpy(dst, src, 4*n); +} + +static void _copy64(const void *src, void *dst, int n) +{ + memcpy(dst, src, 8*n); +} + +#endif + + +#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN + +/* big endian */ + +inline uint get16u(const void *ptr) +{ + return *(ushort *)ptr; +} + +inline uint get32u(const void *ptr) +{ + return *(uint *)ptr; +} + +#define get16v _copy16 +#define get32v _copy32 +#define get64v _copy64 + +#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN + +/* little endian */ + +inline uint get16u(const void *ptr) +{ + ushort tmp = *(ushort *)ptr; + return (((tmp & 0xff00U) >> 8) | + ((tmp & 0x00ffU) << 8)); +} + +inline uint get32u(const void *ptr) +{ + uint tmp = *(uint *)ptr; + return (((tmp & 0xff000000U) >> 24) | + ((tmp & 0x00ff0000U) >> 8) | + ((tmp & 0x0000ff00U) << 8) | + ((tmp & 0x000000ffU) << 24)); +} + +#define get16v _swab16 +#define get32v _swab32 +#define get64v _swab64 + +#else + +/* any endian */ + +inline uint get16u(const void *ptr) +{ + ubyte *u = (ubyte *)ptr; + return (((uint)u[0] << 8) | + ((uint)u[1] << 0)); +} + +inline uint get32u(const void *ptr) +{ + ubyte *u = (ubyte *)ptr; + return (((uint)u[0] << 24) | + ((uint)u[1] << 16) | + ((uint)u[2] << 8) | + ((uint)u[3] << 0)); +} + +static void (*get16v)(const void *, void *, int); +static void (*get32v)(const void *, void *, int); +static void (*get64v)(const void *, void *, int); + +#define PROBE_ENDIAN 1 + +#endif + +#define get16i(ptr) (short)get16u(ptr) +#define get32i(ptr) (int)get32u(ptr) + + +/* splay trees + * + * for further details, see "Self-adjusting Binary Search Trees" + * by Sleator and Tarjan, JACM Volume 32, No 3, July 1985, pp 652-686. + */ + +struct snode { + struct snode *left, *right; + void *key; + void *data; +}; + +typedef int (*sfunc)(const void *key1, const void *key2); + +static struct snode *splay(struct snode *t, const void *key, sfunc comp) +{ + struct snode N, *l, *r, *y; + + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + + for (;;) { + if (comp(key, t->key) < 0) { + if (t->left == NULL) break; + if (comp(key, t->left->key) < 0) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } + else if (comp(key, t->key) > 0) { + if (t->right == NULL) break; + if (comp(key, t->right->key) > 0) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } + else { + break; + } + } + + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + + return t; +} + +static struct snode *sinsert(struct snode *root, void *key, size_t size, sfunc comp) +{ + struct snode *t, *x; + t = splay(root, key, comp); + if (t != NULL && comp(t->key, key) == 0) + return t; + //x = (struct snode *)malloc(sizeof(struct snode)); + x = new snode; + assert( x != NULL ); + if (t == NULL) { + x->left = x->right = NULL; + } + else if (comp(key, t->key) < 0) { + x->left = t->left; + x->right = t; + t->left = NULL; + } + else { + x->right = t->right; + x->left = t; + t->right = NULL; + } + if (size > 0) { + //x->key = malloc(size); + x->key = new ubyte[size]; + memcpy(x->key, key, size); + } + else { + x->key = key; + } + x->data = (void *)-1; + return x; +} + +#define S_KEY 1 /* free key */ +#define S_DATA 2 /* free data */ +#define S_TREE 4 /* delete scene graph (SSG special) */ + +static void deltree(ssgEntity *node) +{ + if (node->getRef() <= 1 && node->isAKindOf(ssgTypeBranch())) { + ssgBranch *grp = (ssgBranch *)node; + int n = grp->getNumKids(); + while (n--) { + deltree(grp->getKid(n)); + grp->removeKid(n); // delete kid (iff ref == 0) + } + } +} + +static void sfree(struct snode *x, int flags) +{ + if (x) { + sfree(x->left, flags); + sfree(x->right, flags); + if ((flags & S_KEY)) + //free(x->key); + delete [] (ubyte *)x->key; + if (x->data != (void *)-1 && x->data != 0) { + if ((flags & S_DATA)) + //free(x->data); + delete [] (ubyte *)x->data; + if ((flags & S_TREE)) { + deltree((ssgEntity *)x->data); + ssgDeRefDelete((ssgEntity *)x->data); + } + } + } +} + +static int ptrcmp(const void *key1, const void *key2) +{ + return (const char *)key1 - (const char *)key2; +} + + +static void hexdump(enum ulSeverity severity, const ubyte *buf, int size, int offset) +{ +#define COLS 16 + char line[1024], *lp; + while (size > 0) { + int i, n = MIN(size, COLS); + lp = line; + lp += sprintf(lp, "%04x ", offset); + for (i = 0; i < n; i++) + lp += sprintf(lp, " %02x", buf[i]); + for (; i < COLS; i++) + lp += sprintf(lp, " "); + *lp++ = ' '; + *lp++ = ' '; + for (i = 0; i < n; i++) + *lp++ = (buf[i] & 0x7f) < 0x20 ? '.' : buf[i]; + *lp = 0; + ulSetError(severity, line); + buf += COLS; + offset += COLS; + size -= COLS; + } +} + + + +#define MAXDEPTH 256 + +#define UNPACK_ABGR(dst, src) \ + ((dst)[0] = (1.0f/255.0f)*(src)[3], \ + (dst)[1] = (1.0f/255.0f)*(src)[2], \ + (dst)[2] = (1.0f/255.0f)*(src)[1], \ + (dst)[3] = 1.0f) + +#define UNPACK_ABGR2(dst, src, intensity) \ + ((dst)[0] = (1.0f/255.0f/127.0f)*(src)[3]*(intensity), \ + (dst)[1] = (1.0f/255.0f/127.0f)*(src)[2]*(intensity), \ + (dst)[2] = (1.0f/255.0f/127.0f)*(src)[1]*(intensity), \ + (dst)[3] = 1.0f) + +/* triangle flags */ +#define TRI_COLOR_MATERIAL 0x01 /* --> apply material on vertex colors */ +#define TRI_TRANSLUCENT 0x02 +#define TRI_SUBFACE 0x04 /* offset geometry */ +#define TRI_BBOARD_AXIAL 0x08 +#define TRI_BBOARD_POINT 0x10 +/*#define TRI_WIREFRAME 0x20*/ +#define TRI_BBOARD (TRI_BBOARD_AXIAL | TRI_BBOARD_POINT) + +/* vertex bind */ +#define BIND_COLOR 0x01 +#define BIND_NORMAL 0x02 +#define BIND_TEXCOORD 0x04 +#define BIND_FLAT_COLOR 0x08 +#define BIND_FLAT_NORMAL 0x10 +#define BIND_FLAT (BIND_FLAT_COLOR | BIND_FLAT_NORMAL) + +struct fltTriangle { + + ssgState *state; + + ubyte flags; + ubyte bind; + +#if 0 + short priority; /* affects the ordering within the geoset */ + ushort transparency; /* eventually applied to colors when done */ +#endif + + int index[3]; + sgVec4 color; /* face color *or* material contributions to vertex colors */ + +}; + +struct fltTexture { + char *file; + ssgState *state; /* user-defined state */ + ssgTexture *tex; + int alpha; +}; + +struct fltState { + + fltState() { + memset(this, 0, sizeof(*this)); + notex_state = (ssgState *)-1; + atris = 256; + //tris = (fltTriangle *)malloc(sizeof(fltTriangle) * atris); + tris = new fltTriangle[atris]; + } + + ~fltState() { + sfree(texs, S_DATA); + sfree(mtls, S_DATA); + sfree(refs, S_TREE); + if (vtab) { + delete [] offset; + delete [] bind; + delete [] coord; + delete [] color; + delete [] normal; + delete [] texcoord; + } + //free(tris); + delete [] tris; + } + + const char *filename; + int revision; + int major; + int minor; + + /* Vertex Table */ + ubyte *vtab; /* start of vertex table */ + int vnum; + int *offset; /* chunk offset (used as index) */ + ubyte *bind; /* 1 - color, 2 - normal, 4 - texcoord */ + sgVec3 *coord; + sgVec4 *color; + sgVec3 *normal; + sgVec2 *texcoord; + + /* Other Tables (these may be sparse, that is why arrays are not used) */ + struct snode *mtls; /* index --> float[14] */ + struct snode *texs; /* index --> fltTexture */ + struct snode *refs; /* index --> ssgEntity */ + ubyte (*ctab)[4]; /* packed ABGR color table */ + int cnum; + ssgState *notex_state; /* special user-defined state if no texture */ + + /* Collected Geometry */ + fltTriangle *tris; + int ntris, atris; + fltTriangle *temp; + char *parent_name; +}; + +struct fltNodeAttr { + + /* allocated using new/delete for convenience */ + fltNodeAttr() { + memset(this, 0, sizeof(*this)); + } + ~fltNodeAttr() { + if (name) delete name; + if (mask_words) delete mask_words; + } + + /* properies that are not applied immediately */ + + char *name; + + int replicate; + int transform; + int islod; + + sgMat4 mat; + sgVec2 range; + sgVec3 center; + + /* switch record info */ + int num_masks; + int num_words; + uint *mask_words; + int current_mask; + +}; + +static int Inited = 0; +static int NoTextures = 0; +static int NoMipmaps = 0; +static int NoExternals = 0; +static int NoClean = 0; + +#ifndef NO_LOADER_OPTIONS +static ssgLoaderOptions *LoaderOptions; +#endif + +static struct snode *TexCache; /* filename --> fltTexture */ + +static fltTexture *LoadTex(char *fname) +{ + TexCache = sinsert(TexCache, fname, strlen(fname) + 1, (sfunc)strcmp); + if (TexCache->data == (void *)-1) { + //fltTexture *tex = (fltTexture *)malloc(sizeof(fltTexture)); + fltTexture *tex = new fltTexture; + assert ( tex != NULL ); + tex->file = fname; +#ifdef NO_LOADER_OPTIONS + tex->state = 0; + tex->tex = new ssgTexture(fname, 1, 1, !NoMipmaps); +#else + tex->state = LoaderOptions->createState(fname); + tex->tex = tex->state ? 0 : LoaderOptions->createTexture(fname, 1, 1, !NoMipmaps); +#endif + tex->alpha = tex->tex ? tex->tex->hasAlpha() : 0; + TexCache->data = tex; + } + return (fltTexture *)TexCache->data; +} + +struct StateInfo { + int cf; /* cull face */ + int tr; /* translucent */ + int cm; /* color material */ + ssgTexture *tex; + float *mtl; + float alpha; +}; + +static int StateCompare(const void *key1, const void *key2) +{ + const StateInfo *s1 = (const StateInfo *)key1; + const StateInfo *s2 = (const StateInfo *)key2; + int d; + d = s1->cf - s2->cf; + if (d == 0) { + d = s1->tr - s2->tr; + if (d == 0) { + d = s1->cm - s2->cm; + if (d == 0) { + d = (char *)s1->tex - (char *)s2->tex; + if (d == 0) { + if (s1->mtl == 0 || s2->mtl == 0) + d = (char *)s1->mtl - (char *)s2->mtl; + else { + int i = s1->cm ? 6 : 0; + for (; i < 12 && d == 0; i++) + if (s1->mtl[i] < s2->mtl[i] - 0.01f) + d = -1; + else if (s1->mtl[i] > s2->mtl[i] + 0.01f) + d = 1; + if (d == 0) { + if (s1->alpha < s2->alpha - 0.01f) + d = -1; + else if (s1->alpha > s2->alpha + 0.01f) + d = 1; + } + } + } + } + } + } + return d; +} + +static struct snode *StateCache; /* StateInfo --> ssgSimpleState */ + +static ssgSimpleState *ConstructState(StateInfo *key) +{ + StateCache = sinsert(StateCache, key, sizeof(StateInfo), StateCompare); + if (StateCache->data == (void *)-1) { + ssgSimpleState *s; + + /*ulSetError(UL_DEBUG, "new state --");*/ + + s = new ssgSimpleState; + + if (key->cf) { + s->enable(GL_CULL_FACE); + } + else { + s->disable(GL_CULL_FACE); + /*ulSetError(UL_DEBUG, " two-sided");*/ + } + + if (key->tr) { + s->setTranslucent(); + s->enable(GL_BLEND); /* XXX what about multisampling? */ + /*ulSetError(UL_DEBUG, " transparent");*/ + } + else { + s->setOpaque(); + s->disable(GL_BLEND); + } + /* leave the alpha test to the application */ + + if (key->mtl) { + float *m = key->mtl; + sgVec4 c; + /*ulSetError(UL_DEBUG, "%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %d", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], key->cm);*/ + if (key->cm) { + s->enable(GL_COLOR_MATERIAL); + s->setColourMaterial(GL_AMBIENT_AND_DIFFUSE); + //ulSetError(UL_DEBUG, " color material"); + } + else { + s->disable(GL_COLOR_MATERIAL); + sgSetVec4(c, m[0], m[1], m[2], key->alpha); s->setMaterial(GL_AMBIENT, c); + sgSetVec4(c, m[3], m[4], m[5], key->alpha); s->setMaterial(GL_DIFFUSE, c); + //ulSetError(UL_DEBUG, " ambient %.2f %.2f %.2f", m[0], m[1], m[2]); + //ulSetError(UL_DEBUG, " diffuse %.2f %.2f %.2f", m[3], m[4], m[5]); + } + sgSetVec4(c, m[6], m[7], m[8], key->alpha); s->setMaterial(GL_SPECULAR, c); + sgSetVec4(c, m[9], m[10], m[11], key->alpha); s->setMaterial(GL_EMISSION, c); + //ulSetError(UL_DEBUG, " specular %.2f %.2f %.2f", m[6], m[7], m[8]); + //ulSetError(UL_DEBUG, " emission %.2f %.2f %.2f", m[9], m[10], m[11]); + //ulSetError(UL_DEBUG, " alpha %.2f", key->alpha); + s->setShininess(m[12]); + s->enable(GL_LIGHTING); + } + else { + s->disable(GL_LIGHTING); + /*ulSetError(UL_DEBUG, " no lighting");*/ + } + + if (key->tex) { + s->enable(GL_TEXTURE_2D); + s->setTexture(key->tex); + /*ulSetError(UL_DEBUG, " texture %s", key->tex->getFilename());*/ + } + else { + s->disable(GL_TEXTURE_2D); + } + + s->setShadeModel(GL_SMOOTH); + + s->ref(); + + StateCache->data = s; + } + return (ssgSimpleState *)StateCache->data; +} + +static int tricmp(const void *a, const void *b) +{ + /* this comparison is used to sort the list of triangles in order to + * generate as few geosets as possible. + */ + fltTriangle *ta = (fltTriangle *)a; + fltTriangle *tb = (fltTriangle *)b; + int d = (char *)ta->state - (char *)tb->state; + if (d == 0) { + d = ta->flags - tb->flags; + if (d == 0) + d = ta->bind - tb->bind; + } + return d; +} + +#if 0 /* no longer needed since this is now the default blend equation */ +static int PreDrawTranslucent(ssgEntity *) +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + return 1; +} +#endif + +static int PreDrawSubface(ssgEntity *) +{ +#ifdef GL_VERSION_1_1 + glPolygonOffset(-2.0f, -1.0f); + glEnable(GL_POLYGON_OFFSET_FILL); +#else + glPolygonOffsetEXT(-0.1f, -0.002f); + glEnable(GL_POLYGON_OFFSET_EXT); +#endif + return 1; +} + +static int PostDrawSubface(ssgEntity *) +{ +#ifdef GL_VERSION_1_1 + glDisable(GL_POLYGON_OFFSET_FILL); +#else + glDisable(GL_POLYGON_OFFSET_EXT); +#endif + return 1; +} + +/* convert a list of triangles to geosets using the pfdMesher + * need a seperate geostate for each comb of tex/mtl/bind + */ +static ssgEntity *Build(fltState *state) +{ + /*const char t[9] = "plsTQSfFP";*/ + ssgBranch *grp = 0; + ssgCutout *bboard1 = 0; + ssgCutout *bboard2 = 0; + ssgLeaf *leaf = 0; + fltTriangle *arr; + int num, i, j, k, m, n; + + //int *index; + //int *vertex; + + arr = state->tris; + num = state->ntris; + state->ntris = 0; + if (num == 0) + return 0; + + /* sort the triangles (put them in bins according to their attributes) + * one geoset will be generated for each such bin + */ + qsort(arr, num, sizeof(fltTriangle), tricmp); + + int *index = new int [ state->vnum ]; + int *vertex = new int [ 32768 ]; + + for (i = 0; i < num; ) { + + ssgVertexArray *va; + ssgColourArray *ca = 0; + ssgNormalArray *na = 0; + ssgTexCoordArray *ta = 0; + ssgVtxTable *geom; + + int flags = arr[i].flags; + int bind = arr[i].bind; + + /* see how many tris that belong to this group */ + for (j = i + 1; (j < num && + arr[j].state == arr[i].state && + arr[j].flags == flags && + arr[j].bind == bind + ); j++) + ; + /*fprintf(stderr, " %d", j - i);*/ + + /* simply construct a triangle soup */ + /* XXX fixme! */ + + if ((bind & BIND_FLAT) || (flags & TRI_COLOR_MATERIAL)) { + + n = 3*(j - i); + + va = new ssgVertexArray(n); + for (k = i; k < j; k++) { + va->add(state->coord[arr[k].index[0]]); + va->add(state->coord[arr[k].index[1]]); + va->add(state->coord[arr[k].index[2]]); + } + + if ((bind & BIND_FLAT_COLOR)) { + ca = new ssgColourArray(n); + for (k = i; k < j; k++) { + ca->add(arr[k].color); + ca->add(arr[k].color); + ca->add(arr[k].color); + } + } + else if ((bind & BIND_COLOR)) { + ca = new ssgColourArray(n); + if ((flags & TRI_COLOR_MATERIAL)) { + for (k = i; k < j; k++) { + for (m = 0; m < 3; m++) { + sgVec4 color; + float *mtl_color = arr[k].color; + float *vtx_color = state->color[arr[k].index[m]]; + color[0] = mtl_color[0] * vtx_color[0]; + color[1] = mtl_color[1] * vtx_color[1]; + color[2] = mtl_color[2] * vtx_color[2]; + color[3] = mtl_color[3] * vtx_color[3]; + ca->add(color); + } + } + } + else { + for (k = i; k < j; k++) { + ca->add(state->color[arr[k].index[0]]); + ca->add(state->color[arr[k].index[1]]); + ca->add(state->color[arr[k].index[2]]); + } + } + } + else if ((flags & TRI_TRANSLUCENT)) { + ca = new ssgColourArray(1); + ca->add(arr[i].color); + } + + if ((bind & BIND_FLAT_NORMAL)) { + na = new ssgNormalArray(n); + for (k = i; k < j; k++) { + sgVec3 a, b, normal; + int *w = arr[k].index; + sgSubVec3(a, state->coord[w[1]], state->coord[w[0]]); + sgSubVec3(b, state->coord[w[2]], state->coord[w[0]]); + sgVectorProductVec3(normal, a, b); + sgNormalizeVec3(normal); + na->add(normal); + na->add(normal); + na->add(normal); + } + } + else if ((bind & BIND_NORMAL)) { + na = new ssgNormalArray(n); + for (k = i; k < j; k++) { + na->add(state->normal[arr[k].index[0]]); + na->add(state->normal[arr[k].index[1]]); + na->add(state->normal[arr[k].index[2]]); + } + } + + if ((bind & BIND_TEXCOORD)) { + ta = new ssgTexCoordArray(n); + for (k = i; k < j; k++) { + ta->add(state->texcoord[arr[k].index[0]]); + ta->add(state->texcoord[arr[k].index[1]]); + ta->add(state->texcoord[arr[k].index[2]]); + } + } + + geom = new ssgVtxTable(GL_TRIANGLES, va, na, ta, ca); + + } + else { + + ssgIndexArray *ia = new ssgIndexArray(3*(j - i)); + + memset(index, -1, sizeof(index[0]) * state->vnum); + n = 0; + for (k = i; k < j; k++) { + int *tri = arr[k].index; + int save = n; + for (m = 0; m < 3; m++) { + if (index[tri[m]] == -1) { + if (n == 65536) { + ulSetError(UL_DEBUG, "[flt] More than 65536 vertices, split forced."); + n = save; + j = k; + //goto hopp; + break; + } + vertex[n] = tri[m]; + index[tri[m]] = n++; + } + ia->add((short)index[tri[m]]); + } + if (m < 3) break; + } + //hopp: + + va = new ssgVertexArray(n); + for (k = 0; k < n; k++) + va->add(state->coord[vertex[k]]); + + if ((bind & BIND_COLOR)) { + ca = new ssgColourArray(n); + for (k = 0; k < n; k++) + ca->add(state->color[vertex[k]]); + } + else if ((flags & TRI_TRANSLUCENT)) { + ca = new ssgColourArray(1); + ca->add(arr[i].color); + } + + if ((bind & BIND_NORMAL)) { + na = new ssgNormalArray(n); + for (k = 0; k < n; k++) + na->add(state->normal[vertex[k]]); + } + + if ((bind & BIND_TEXCOORD)) { + ta = new ssgTexCoordArray(n); + for (k = 0; k < n; k++) + ta->add(state->texcoord[vertex[k]]); + } + + if (n == 3*(j - i)) { + + delete ia; + + geom = new ssgVtxTable(GL_TRIANGLES, va, na, ta, ca); + } + else { + + geom = new ssgVtxArray(GL_TRIANGLES, va, na, ta, ca, ia); + } + + } + + geom->setState(arr[i].state); + + /*ulSetError(UL_DEBUG, "build: [%d..%d] %02x %d indices, %d vertices", + i, j - 1, bind, + 3*(j - i), n);*/ + + if (leaf) { + if (grp == 0) + grp = new ssgBranch; + grp->addKid(leaf); + } + + leaf = geom; + +#if 0 + if ((flags & TRI_TRANSLUCENT)) { + leaf->setCallback(SSG_CALLBACK_PREDRAW, PreDrawTranslucent); + } + else +#endif + if ((flags & TRI_SUBFACE)) { + leaf->setCallback(SSG_CALLBACK_PREDRAW, PreDrawSubface); + leaf->setCallback(SSG_CALLBACK_POSTDRAW, PostDrawSubface); + } + +#ifndef NO_LOADER_OPTIONS + leaf = LoaderOptions->createLeaf(leaf, state->parent_name); +#endif + + if ((flags & TRI_BBOARD)) { + if ((flags & TRI_BBOARD_AXIAL)) { + if (bboard1 == 0) + bboard1 = new ssgCutout(0); + bboard1->addKid(leaf); + } + else { + if (bboard2 == 0) + bboard2 = new ssgCutout(1); + bboard2->addKid(leaf); + } + leaf = 0; + } + + i = j; + } + + delete [] vertex; + delete [] index; + + if (((leaf != 0) + (bboard1 != 0) + (bboard2 != 0)) > 1 && !grp) //lint !e514 Lint warning "unusual use of a boolean" + grp = new ssgBranch; + + if (grp) { + if (bboard1) + grp->addKid(bboard1); + if (bboard2) + grp->addKid(bboard2); + if (leaf) + grp->addKid(leaf); + return grp; + } + + if (bboard1) + return bboard1; + + if (bboard2) + return bboard2; + + return leaf; +} + +/* add a triangle */ +static void AddTri(fltState *state, int v0, int v1, int v2) +{ + fltTriangle *tri; + if (state->ntris == state->atris) { + state->atris += state->atris; + //state->tris = (fltTriangle *)realloc(state->tris, sizeof(fltTriangle) * state->atris); + fltTriangle *old = state->tris; + state->tris = new fltTriangle[state->atris]; + memcpy(state->tris, old, sizeof(fltTriangle) * state->atris / 2); + delete [] old; + } + tri = state->tris + state->ntris++; + memcpy(tri, state->temp, sizeof(fltTriangle)); + tri->index[0] = v0; + tri->index[1] = v1; + tri->index[2] = v2; +} + +static void Triangulate(int *w, int n, fltState *state) +{ + int buf[3 * (16 - 2)]; + int *tris = (n > 16) ? new int [ 3 * (n - 2) ] : buf; + int num_tris = _ssgTriangulate(state->coord, w, n, tris); + for (int i = 0; i < num_tris; i++) + AddTri(state, tris[3*i + 0], tris[3*i + 1], tris[3*i + 2]); + if (tris != buf) + delete [] tris; +} + +static int ObsoleteFlag; +static int NotImplementedFlag; + +static void Obsolete(int op) +{ + if (!ObsoleteFlag) { + ulSetError(UL_WARNING, "[flt] This file is probably rather old (obsolete opcodes ignored)."); + ObsoleteFlag = 1; + } + //printf("op %d obsolete\n", op); +} + +static void NotImplemented(int op) +{ + if (!NotImplementedFlag) { + ulSetError(UL_WARNING, "[flt] This file contains opcodes that are not implemented."); + NotImplementedFlag = 1; + } + //printf("op %d not implemented\n", op); +} + +static void ReportBadChunk(const ubyte *ptr, const char *name) +{ + int op = get16u(ptr); + int len = get16u(ptr + 2); + ulSetError(UL_WARNING, "[flt] Bad record, opcode %d (%s), length %d:", op, name, len); + hexdump(UL_WARNING, ptr, len, 0); + ulSetError(UL_WARNING, "Please report this, either at http://plib.sourceforge.net/,"); + ulSetError(UL_WARNING, "or by email to plib-devel@lists.sourceforge.net. Thanks."); +} + +#define BAD_CHUNK(ptr, name)\ + do {\ + static int first = 1;\ + if (first) {\ + ReportBadChunk(ptr, name);\ + first = 0;\ + }\ + } while (0) + +static int intcmp(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +/* polygon chunks */ +static int GeomChunks(ubyte *ptr0, ubyte *end, fltState *state, ssgEntity **nodep, int objflags, int objtrans) +{ + ubyte *ptr = ptr0; + int sp = 0, op, len, done = 0, triflg = 0; + int w[512]; /* enough? */ + fltTriangle tri; + StateInfo info; + int subface = 0; + char long_id[256] = {0}; + + state->temp = &tri; + memset(&tri, 0, sizeof(tri)); + + /* read as many polygon chunks as possible. + * can be done since objects may not contain groups. + * color table etc should also be defined before any polygon. + * (the purpose is to keep the switch(op) relatively small to speed up this "inner loop") + */ + + do { + + if (ptr + 4 > end) + break; + op = get16u(ptr); + len = get16u(ptr+2); + if (len < 4 || (len & 3) != 0 || ptr + len > end) + break; + + switch (op) { + + case 5: { /* Face (appearence) */ + + /* + * Face Record Format + * + * Int 0 2 Face Opcode 5 + * Unsigned 2 2 Length of the record + * Char 4 8 7 char ASCII ID; 0 terminates + * Int 12 4 IR color code + * Int 16 2 Relative priority + * Int 18 1 Draw type + * 0 = Draw solid with backface culling + * 1 = Draw solid, no backface culling + * 2 = Draw wireframe + * 3 = Draw wireframe and close + * 4 = Surround with wireframe in alternate color + * 8 = Omnidirectional light + * 9 = Unidirectional light + * 10 = Bidirectional light + * Int 19 1 Texture white = if TRUE, draw textured face white + * Unsigned 20 2 Color name index + * Unsigned 22 2 Alternate color name index + * Int 24 1 Reserved + * Int 25 1 Template (billboard) + * 0 = Fixed, no alpha blending + * 1 = Fixed, alpha blending + * 2 = Axial rotate + * 4 = Point rotate + * Int 26 2 Detail texture pattern index, -1 if none + * Int 28 2 Texture pattern index, -1 if none + * Int 30 2 Material index, -1 if none + * Int 32 2 Surface material code (for DFAD) + * Int 34 2 Feature ID (for DFAD) + * Int 36 4 IR material code + * Unsigned 40 2 Transparency 0 = Opaque 65535 = Totally clear + * Unsigned 42 1 LOD generation control + * Unsigned 43 1 Line style index + * Boolean 44 4 Flags (bits from left to right) + * 0 = Terrain + * 1 = No color + * 2 = No alternate color + * 3 = Packed color + * 4 = Terrain culture cutout (footprint) + * 5 = Hidden, not drawn + * 6-31 = Spare + * Unsigned 48 1 Light mode + * 0 = Use face color, not illuminated + * 1 = Use vertex colors, not illuminated + * 2 = Use face color and vertex normal + * 3 = Use vertex color and vertex normal + * Unsigned 49 1 Reserved + * Unsigned 50 2 Reserved + * Boolean 52 4 Reserved + * Unsigned 56 4 Packed color, primary (A, B, G, R) + * Unsigned 60 4 Packed color, alternate (A, B, G, R) + * Int 64 2 Texture mapping index + * Int 66 2 Reserved + * Unsigned 68 4 Primary color index + * Unsigned 72 4 Alternate color index + * Int 76 2 Reserved + * Int 78 2 Reserved + * + */ + + static float default_mtl[14] = { + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 8.0f, 1.0f + }; + int flags = len < 48 ? 0 : get32i(ptr + 44); + int drawtype = ptr[18]; + + if (drawtype > 4 || (flags & (1 << 5)) || len < 32) { + triflg = 0; + } + else { + int index; + struct snode *t; + int tex_alpha = 0; + + tri.state = 0; + tri.flags = 0; + +#ifdef NO_TEXTURES + info.tex = 0; +#else + /* texture */ + info.tex = 0; + index = get16i(ptr + 28); /* texture index */ + t = 0; + if (index != -1) { + state->texs = t = splay(state->texs, (void *)index, ptrcmp); + if (t == 0 || t->key != (void *)index) { + /*ulSetError(UL_DEBUG, "[flt] undefined texture %d", index);*/ + t = 0; + } + } + if (t) { + fltTexture *tex = (fltTexture *)t->data; + if (tex->state == (ssgState *)-1) { + fltTexture *tmp = LoadTex(tex->file); + tex->state = tmp->state; + tex->tex = tmp->tex; + tex->alpha = tmp->alpha; + } + tri.state = tex->state; + info.tex = tex->tex; + tex_alpha = tex->alpha; + } +#ifndef NO_LOADER_OPTIONS + else { + if (state->notex_state == (ssgState *)-1) + state->notex_state = LoaderOptions->createState(0); + tri.state = state->notex_state; + } +#endif +#endif + + if (tri.state) { + + tri.bind = BIND_COLOR | BIND_NORMAL | BIND_TEXCOORD; + + } + else { + + int template = ptr[25]; + int white = ptr[19] || template > 1; /* OK ?? */ + int lmode = len >= 49 ? ptr[48] : 3; + int trans = get16u(ptr + 40); + + //ulSetError(UL_DEBUG, "light mode %d", lmode); + + /*lmode = 0;*/ + + /* wireframe */ + /*tri.wireframe = (drawtype > 1);*/ + + /* backface culling */ + info.cf = (drawtype == 0); + + /* alpha component */ +#if 0 + if (state->major >= 15) { + /* this is according to the spec, but it sure is suspicious */ + int a = trans >> 1; + int b = objtrans >> 1; + info.alpha = 1.0f - 1.0f/(32767*32767) * (a * b); + } + else +#endif + { + info.alpha = 1.0f - 1.0f/65535.0f * trans; + } + sgSetVec4(tri.color, 1, 1, 1, info.alpha); + +#ifdef NO_LIGHTING + info.mtl = 0; +#else + /* material */ + if (/*lmode < 2 ||*/ /* (objflags & (1 << 3)) || */ white) { + info.mtl = 0; + } + else { + index = get16i(ptr + 30); /* material index */ + state->mtls = t = splay(state->mtls, (void *)index, ptrcmp); + if (t && t->key == (void *)index) { + info.mtl = (float *)t->data; + } + else { + info.mtl = default_mtl; + } + info.alpha *= info.mtl[13]; + tri.color[3] = 1.0f; + } +#endif + + /* transparency */ + info.tr = (tex_alpha > 0 || info.alpha < 0.999f); /* && template > 0 */ + if (info.tr) + tri.flags |= TRI_TRANSLUCENT; + + /* initial bindings */ + tri.bind = 0; + if ((lmode & 1) && !white) + tri.bind |= BIND_COLOR; + if (info.mtl) { + tri.bind |= BIND_NORMAL; + if (!(lmode & 2) || (objflags & (1 << 4))) + tri.bind |= BIND_FLAT_NORMAL; + } + if (info.tex) + tri.bind |= BIND_TEXCOORD; + +#ifdef NO_COLORS + tri.bind &= ~BIND_COLOR; +#else + + /* face color */ + if (!(lmode & 1) && !(flags & 2) && !white && state->major >= 14) { /* will probably not work with old files */ + if ((flags & 8) && len >= 60) { + UNPACK_ABGR(tri.color, ptr + 56); /* packed ABGR */ + tri.bind |= BIND_FLAT_COLOR; + } + else if (state->revision > 1400) { + int color; + if (state->revision > 1500 && len >= 72) + color = get32i(ptr + 68); + else { + color = get16u(ptr + 20); + if (color == 65535) + color = -1; + } + int index = color / 128; + int intensity = color % 128; + if (color >= 0 && state->ctab && index < state->cnum) { + UNPACK_ABGR2(tri.color, state->ctab[index], intensity); + tri.bind |= BIND_FLAT_COLOR; + } + else if (color != -1) + ulSetError(UL_DEBUG, "[flt] Bad face color %d.", color); + } + tri.color[3] = info.alpha; + } +#endif + + /* + if (tri.bind & BIND_COLOR) + ulSetError(UL_DEBUG, "use vertex colors"); + if (tri.bind & BIND_FLAT_COLOR) + ulSetError(UL_DEBUG, "face color %.2f %.2f %.2f", + tri.color[0], tri.color[1], tri.color[2]); + */ + + /* billboard */ + if (template == 4) + tri.flags |= TRI_BBOARD_POINT; + else if (template == 2) + tri.flags |= TRI_BBOARD_AXIAL; + + /* cannot create the state yet, + * need to know the vertex bindings. */ + } + + triflg = 1; + } + ptr += len; + break; + } + + case 72: /* Vertex List (aka polygon) */ + if (triflg) { + int i, n = (len - 4) >> 2; + if (state->vtab && n > 2 && n < 512) { /* silently ignore if suspicious.. */ + int bind = tri.bind; + ubyte *p = ptr + 4; + for (i = 0; i < n; ++i) { + /* could this be done any faster? */ + /* it is tempting to reparse the vertex at the given offset */ + int offset = get32i(p), *ptr; + ptr = (int *)bsearch(&offset, state->offset, state->vnum, + sizeof(int), intcmp); + if (!ptr) { + ulSetError(UL_DEBUG, "[flt] Bad vertex offset %i.", offset); + break; + } + w[i] = ptr - state->offset; + bind &= state->bind[w[i]] | BIND_FLAT; + p += 4; + } + if (i == n) { + + tri.bind = bind; + + if (subface) + tri.flags |= TRI_SUBFACE; + else + tri.flags &= ~TRI_SUBFACE; + + if (tri.state) { + + Triangulate(w, n, state); + + } + else { + /* finalize state */ + + /* setup color material */ + if ((bind & (BIND_COLOR | BIND_FLAT_COLOR)) && + (bind & (BIND_NORMAL | BIND_FLAT_NORMAL))) { + sgVec4 color; + color[0] = 0.2f*info.mtl[0] + 0.8f*info.mtl[3]; + color[1] = 0.2f*info.mtl[1] + 0.8f*info.mtl[4]; + color[2] = 0.2f*info.mtl[2] + 0.8f*info.mtl[5]; + color[3] = info.alpha; + if ((tri.bind & BIND_FLAT_COLOR)) { + tri.color[0] *= color[0]; + tri.color[1] *= color[1]; + tri.color[2] *= color[2]; + tri.color[3] *= color[3]; + } + else { + sgCopyVec4(tri.color, color); /* apply to vertex colors */ + } + info.cm = 1; + tri.flags |= TRI_COLOR_MATERIAL; + } + else { + info.cm = 0; + tri.flags &= ~TRI_COLOR_MATERIAL; + } + + tri.state = ConstructState(&info); + + Triangulate(w, n, state); + + tri.state = 0; + } + } + } + } + ptr += len; + break; + + case 89: /* Morph Vertex List */ + ptr += len; + break; + + case 10: /* Push */ + sp++; + ptr += len; + break; + + case 11: /* Pop */ + if (sp == 0) { + done = 1; + break; + } + sp--; + ptr += len; + /*if (sp == 0) + done = 1;*/ + break; + + case 19: /* Push Subface */ + subface++; + ptr += len; + break; + + case 20: /* Pop Subface */ + subface--; + ptr += len; + break; + + case 33: /* Long ID */ + if (long_id[0] == 0) { + int n = CLAMP(len - 4, 0, 255); + memcpy(long_id, ptr + 4, n); + long_id[n] = 0; + } + ptr += len; + break; + + case 31: /* Text Comment */ + case 50: /* Vector (for light points) */ + case 21: /* Push Extension */ + case 22: /* Pop Extension */ + case 97: /* Line Style Record */ + case 122: /* Push Attribute */ + case 123: /* Pop Attribute */ + ptr += len; + break; + + case 6: /* Vertex with ID (obsolete) */ + case 7: /* Short Vertex (obsolete) */ + case 8: /* Vertex with Color (obsolete) */ + case 9: /* Vertex with Color and Normal (obsolete) */ + Obsolete(op); + ptr += len; + break; + + default: + if (sp == 0) { /* you never know what this might be. better let someone else take car of it. */ + done = 1; + break; + } + NotImplemented(op); + ptr += len; + } + + } while (!done); + + *nodep = Build(state); + + if (long_id[0] != 0 && *nodep != NULL) + (*nodep)->setName(long_id); + + return ptr - ptr0; +} + +struct LODInfo { + ssgTransform *scs; + ssgRangeSelector *lod; +}; + +static int LODCompare(const void *a, const void *b) +{ + float d = ((LODInfo *)a)->lod->getRange(0) - ((LODInfo *)b)->lod->getRange(0); + return d < 0 ? -1 : d > 0 ? 1 : 0; +} + +static void MergeLODs(ssgBranch *grp) +{ + LODInfo info[64]; + int i, j, k, m, n; + + n = grp->getNumKids(); + if (n <= 1) + return; + + m = 0; + for (i = 0; i < n && m < 64; i++) { + ssgEntity *kid; + kid = grp->getKid(i); + info[m].scs = 0; + if (kid->isA(ssgTypeTransform()) && kid->getNumParents() == 1) { + info[m].scs = (ssgTransform *)kid; + if (info[m].scs->getNumKids() != 1) + continue; + kid = info[m].scs->getKid(0); + } + if (!kid->isA(ssgTypeRangeSelector()) || kid->getNumParents() != 1) + continue; + info[m].lod = (ssgRangeSelector *)kid; + m++; + } + + if (m > 1) { + + qsort(info, m, sizeof(info[0]), LODCompare); + + //grp->print(); + + for (i = 0; i < m; i++) { + sgMat4 mat1, mat2; + float ranges[33], range2; + int num1, num2; + int flag = 0; + + if (info[i].lod == 0) + continue; + + if (info[i].scs) + info[i].scs->getTransform(mat1); + else + sgMakeIdentMat4(mat1); + num1 = info[i].lod->getNumKids(); + for (k = 0; k <= num1; k++) + ranges[k] = info[i].lod->getRange(k); + + for (j = i + 1; j < m; j++) { + + if (info[j].lod == 0) + continue; + + if (info[j].scs) + info[j].scs->getTransform(mat2); + else + sgMakeIdentMat4(mat2); + num2 = info[j].lod->getNumKids(); + range2 = info[j].lod->getRange(0); + + if (sgDistanceVec3(mat1[3], mat2[3]) > 0.1f * range2) /* XXX */ + continue; + if (num1 + num2 > 32) /* XXX */ + continue; + if (ABS(ranges[num1] - range2) > 0.1f * range2) /* XXX */ + continue; + + for (k = 0; k < num2; k++) { + info[i].lod->addKid(info[j].lod->getKid(k)); + ranges[num1 + k + 1] = info[j].lod->getRange(k + 1); + num1++; + } + + if (info[j].scs == 0 || info[j].scs->getRef() == 1) { + if (info[j].lod->getRef() == 1) { + while (num2--) + info[j].lod->removeKid(num2); + } + if (info[j].scs) + info[j].scs->removeKid(info[j].lod); // delete info[j].lod + else + grp->removeKid(info[j].lod); + } + if (info[j].scs) + grp->removeKid(info[j].scs); // delete info[j].scs + + info[j].lod = 0; + flag = 1; + } + + if (flag) { + info[i].lod->setRanges(ranges, num1 + 1); + assert(info[i].scs == 0 || !info[i].scs->isA(0xDeadBeef)); + assert(!info[i].lod->isA(0xDeadBeef)); + } + } + } +} + +static ssgEntity *PostClean(ssgEntity *node, fltNodeAttr *attr) +{ + + if (node && attr && attr->name) + node->setName(attr->name); + +#if 1 + /* remove empty or redundant groups */ + while (!NoClean && node && node->isA(ssgTypeBranch())) { + ssgBranch *grp = (ssgBranch *)node; + int num = grp->getNumKids(); + if (num > 1) + break; + if (num == 1) { + ssgEntity *kid = grp->getKid(0); + if (grp->getName()) { + if (kid->getName()) + break; + kid->setName(grp->getName()); + } + if (grp->getRef() == 0) { + kid->ref(); + grp->removeKid(0); + kid->deRef(); + delete grp; + } + node = kid; + assert(!node->isA(0xDeadBeef)); + } + else { + if (grp->getRef() == 0) + delete grp; + node = 0; + } + } +#endif + +#if 1 + /* see if we can merge LOD nodes */ + if (node && node->isAKindOf(ssgTypeBranch()) && + !node->isAKindOf(ssgTypeSelector())) { + MergeLODs((ssgBranch *)node); + assert(!node->isA(0xDeadBeef)); + } +#endif + + /* set limits on animated nodes */ + if (node && node->isA(ssgTypeTimedSelector())) { + ssgTimedSelector *sw = (ssgTimedSelector *) node; + if (sw->getNumKids() > 1) { + sw->setDuration(30.0f); + sw->setLimits(0, sw->getNumKids() - 1); + sw->control(SSG_ANIM_START); + } + } + + /* apply node attributes */ + if (node && attr) { + + if (attr->transform) { + + ssgTransform *scs = new ssgTransform; + scs->setTransform(attr->mat); + scs->addKid(node); + + if (attr->replicate > 0) { + ssgBranch *grp; + sgMat4 mat; + int i; + grp = new ssgBranch; + grp->addKid(scs); + sgCopyMat4(mat, attr->mat); +#if 0 + ulSetError(UL_DEBUG, "replicating %d times", attr->replicate); + for (i = 0; i < 4; i++) + ulSetError(UL_DEBUG, " %6.2f %6.2f %6.2f %6.2f", mat[i][0], mat[i][1], mat[i][2], mat[i][3]); +#endif + for (i = 0; i < attr->replicate; ++i) { + sgPostMultMat4(mat, attr->mat); + scs = new ssgTransform; + scs->setTransform(mat); + scs->addKid(node); + grp->addKid(scs); + } + node = grp; /* there's no point in cleaning this construction */ + /*ulSetError(UL_DEBUG, "[flt] replicated %d times", attr->replicate);*/ + } + else { + node = scs; // PostClean(scs, 0); + /*ulSetError(UL_DEBUG, "[flt] added transformation");*/ + } + } + + /* apply level of detail */ + if (attr->islod) { + float ranges[2]; + ranges[0] = MIN(attr->range[0], attr->range[1]); + ranges[1] = MAX(attr->range[0], attr->range[1]); + if (ranges[1] > MAX(0, ranges[0])) { + ssgRangeSelector *lod = new ssgRangeSelector; + lod->setRanges(ranges, 2); + + if (sgLengthVec3(attr->center) > 0.01f) { /* XXX */ + ssgTransform *t1, *t2; + sgMat4 mat; + sgMakeTransMat4(mat, attr->center[0], attr->center[1], attr->center[2]); + t1 = new ssgTransform; + t1->setTransform(mat); + sgMakeTransMat4(mat, -attr->center[0], -attr->center[1], -attr->center[2]); + t2 = new ssgTransform; + t2->setTransform(mat); + t1->addKid(lod); + lod->addKid(t2); + t2->addKid(node); + node = t1; + } + else { + lod->addKid(node); + node = lod; + } + } + } + + /* setup switch records */ + if (attr->num_masks > 0) { + ssgTimedSelector *sw = new ssgTimedSelector; + int n = ((ssgBranch *) node) -> getNumKids(); + if (n > 32 * attr->num_words) n = 32 * attr->num_words; + for (int i = 0; i < attr->num_masks; i++) { + ssgBranch *br = new ssgBranch; + for (int j = 0; j < n; j++) { + if (attr->mask_words[j / 32] & (1u << (j % 32))) + br -> addKid(((ssgBranch *) node) -> getKid(j)); + } + sw -> addKid(br); + } + if (node->getRef() == 0) { + ((ssgBranch *) node) -> removeAllKids(); + delete node; + } + sw->setMode(SSG_ANIM_SHUTTLE); + sw->setDuration(30.0f); + sw->setLimits(0, attr->num_masks - 1); + sw->control(SSG_ANIM_START); + node = sw; + } + } + + if (attr) + delete attr; + + assert(node == 0 || !node->isA(0xDeadBeef)); + + return node; +} + +/* link nodes while removing crap (post traversal) */ +static void PostLink(ssgEntity **stack, fltNodeAttr **attr, + int instance, fltState *state) +{ +#if 0 + ulSetError(UL_DEBUG, "PostLink: %s %s <-- %s %s", + stack[0] ? pfGetTypeName(stack[0]) : "0", stack[0] ? pfGetNodeName(stack[0]) : "0", + stack[1] ? pfGetTypeName(stack[1]) : "0", stack[1] ? pfGetNodeName(stack[1]) : "0"); +#endif + + if (stack[1] == 0) { + if (attr[1]) { + delete attr[1]; + attr[1] = 0; + } + return; + } + assert(!stack[1]->isA(0xDeadBeef)); + + stack[1] = PostClean(stack[1], attr[1]); + + if (stack[1] && instance >= 0) { + state->refs = sinsert(state->refs, (void *)instance, 0, ptrcmp); + if (state->refs->data != (void *)-1) { + ulSetError(UL_WARNING, "[flt] Instance %d redefined.", instance); + ssgDeRefDelete((ssgEntity *) state->refs->data); + } + state->refs->data = stack[1]; + stack[1]->ref(); + } + + if (stack[0] == 0) { + stack[0] = stack[1]; + if (attr[0]) { + delete attr[0]; + attr[0] = 0; + } + } + else { + assert(!stack[0]->isA(0xDeadBeef)); + if (stack[1]) { + if (stack[0]->isAKindOf(ssgTypeBranch())) { + ((ssgBranch *)stack[0])->addKid(stack[1]); + } + else { + ssgBranch *grp = new ssgBranch; + grp->addKid(stack[0]); + grp->addKid(stack[1]); + stack[0] = PostClean(grp, 0); + } + } + } + + stack[1] = 0; + attr[1] = 0; +} + +/* group/object ancillary chunks */ +static int AttrChunks(ubyte *ptr0, ubyte *end, fltNodeAttr **attrp) +{ + ubyte *ptr = ptr0; + int op, len, done = 0; + fltNodeAttr *attr; + + attr = *attrp; + + while (!done) { + + if (ptr + 4 > end) + break; + op = get16u(ptr); + len = get16u(ptr + 2); + if (len < 4 || (len & 3) != 0 || ptr + len > end) + break; + + switch (op) { + + case 31: /* Comment */ + ptr += len; + break; + + case 33: { /* Long ID */ + int n = len - 4; + if (n > 0 && n < 256) { + if (attr == 0) + attr = new fltNodeAttr; + //attr->name = (char *)malloc(n + 1); + attr->name = new char[n + 1]; + memcpy(attr->name, ptr + 4, n); + attr->name[n] = 0; + } + ptr += len; + break; + } + + case 60: { /* Replicate */ + if (attr == 0) + attr = new fltNodeAttr; + attr->replicate = get16u(ptr + 4); + ptr += len; + break; + } + + case 49: { /* Transfomation Matrix */ + sgMat4 mat; + int i, j; + get32v(ptr + 4, mat, 16); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + float d = mat[i][j] - (i == j ? 1.0f : 0.0f); + if (ABS(d) > 0.001f) + break; + } + if (j < 4) + break; + } + if (i < 4) { + if (attr == 0) + attr = new fltNodeAttr; + attr->transform = 1; + sgCopyMat4(attr->mat, mat); + } + ptr += len; + break; + } + + case 12: /* Translate (obsolete) */ + case 40: /* Translate (obsolete) */ + case 41: /* Rotate about Point (obsolete) */ + case 42: /* Rotate about Edge (obsolete) */ + case 43: /* Scale (obsolete) */ + case 44: /* Translate (obsolete) */ + case 45: /* Scale (obsolete) */ + case 46: /* Rotate about Point (obsolete) */ + case 47: /* Rotate and/or Scale about Point (obsolete) */ + case 48: /* Put Transform (obsolete) */ + case 51: /* Bounding Box (obsolete) */ + case 77: /* Scale (obsolete) */ + case 110: /* Histogram Bounding Volume (obsolete) */ + Obsolete(op); + ptr += len; + break; + + case 74: /* Bounding Box */ + case 76: /* Rotate about Edge */ + case 78: /* Translate */ + case 79: /* Scale */ + case 80: /* Rotate about Point */ + case 81: /* Rotate and/or Scale */ + case 82: /* Put */ + case 88: /* Road Zone */ + case 94: /* General Matrix */ + case 100: /* Extension Attribute */ + case 105: /* Bounding Sphere */ + case 106: /* Bounding Cylinder */ + case 108: /* Bounding Volume Center */ + case 109: /* Bounding Volume Orientation */ + case 116: /* CAT Data */ + /* these are safe to ignore */ + ptr += len; + break; + + default: + done = 1; + break; + } + } + + *attrp = attr; + + return ptr - ptr0; +} + +static ssgEntity *LoadFLT(const char *path); + +/* object hierarchy chunks */ +static ssgEntity *HierChunks(ubyte *ptr, ubyte *end, fltState *state) +{ + ssgEntity *stack[MAXDEPTH + 1]; + fltNodeAttr *attr[MAXDEPTH + 1]; + int instance[MAXDEPTH + 1]; + int sp, op, len, k; + + stack[0] = new ssgBranch; + stack[0]->setName("reserved"); + attr[0] = 0; + instance[0] = -1; + stack[1] = 0; + attr[1] = 0; + instance[1] = -1; + sp = 1; + + for (;;) { + + if (ptr + 4 > end) + break; + op = get16u(ptr); + if (op < 1 || op > 150) + break; + len = get16u(ptr + 2); + if (len < 4 || (len & 3) != 0 || ptr + len > end) + break; + +#if 0 + for (int i = 0; i < sp; i++) + putchar('\t'); + printf("op %d\n", op); +#endif + + switch (op) { + + case 10: /* Push */ + if (stack[sp] && !stack[sp]->isAKindOf(ssgTypeBranch())) { + /* shouldn't happen */ + ulSetError(UL_DEBUG, "[flt] Objects are not allowed to contain other objects or groups."); + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + } + if (sp >= MAXDEPTH) { + ulSetError(UL_WARNING, "[flt] Stack overflow."); + } + else { + sp++; + stack[sp] = 0; + attr[sp] = 0; + instance[sp] = -1; + } + ptr += len; + break; + + case 11: /* Pop */ + if (sp == 1) + ulSetError(UL_WARNING, "[flt] Stack underflow."); + else { + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + sp--; + } + ptr += len; + break; + + case 4: { /* Object */ + + /* + * Object Record Format + * + * Int 0 2 Object Opcode 4 + * Unsigned 2 2 Length of the record + * Char 4 8 7 char ASCII ID; 0 terminates + * Boolean 12 4 Flags (bits from to right) + * 0 = Don't display in daylight + * 1 = Don't display at dusk + * 2 = Don't display at night + * 3 = Don't illuminate + * 4 = Flat shaded + * 5 = Group's shadow object + * 6-31 = Spare + * Int 16 2 Relative priority + * Unsigned 18 2 Transparency + * 0 = Opaque + * 65535 = Totally clear + * Int 20 2 Special effect ID1 - application defined + * Int 22 2 Special effect ID2 - application defined + * Int 24 2 Significance + * Int 26 2 Spare + * + */ + + int flags = get32i(ptr + 12); + int trans = get16u(ptr + 18); + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + ptr += len; + ptr += AttrChunks(ptr, end, &attr[sp]); + state->parent_name = (char *)ptr + 4; /* OK?? */ + ptr += GeomChunks(ptr, end, state, &stack[sp], flags, trans); + state->parent_name = 0; + if (stack[sp] && stack[sp]->getName() == 0 && ptr[4]) + stack[sp]->setName((char *)ptr + 4); + break; + } + + case 5: /* Face */ + /* polygons are not allowed to be outside objects, but this rule is not always respected */ + //ulSetError(UL_DEBUG, "[flt] Implicit object."); + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + ptr += GeomChunks(ptr, end, state, &stack[sp], 0, 0); + break; + + case 2: /* Group */ + case 14: /* Degree of Freedom */ + case 73: /* Level of Detail */ + case 96: /* Switch */ + case 98: /* Clip Region */ + + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + + switch (op) { + + case 2: { /* Group */ +#if 0 /* group animation - seems wrong... */ + int flags = (len >= 20) ? get32i(ptr + 16) : 0; + if ((flags & 3) != 0) { + ssgTimedSelector *sw = new ssgTimedSelector; + sw->setMode((flags & 2) ? SSG_ANIM_SWING : SSG_ANIM_SHUTTLE); + stack[sp] = sw; + } + else +#endif + { + stack[sp] = new ssgBranch; + } + break; + } + + case 14: { /* Degree of Freedom */ + stack[sp] = new ssgTransform; + break; + } + + case 73: { /* Level of Detail */ + fltNodeAttr *a; + double v[3]; + attr[sp] = a = new fltNodeAttr; + a->islod = 1; + get64v(ptr + 16, v, 2); + sgSetVec2(a->range, (float)v[0], (float)v[1]); + get64v(ptr + 40, v, 3); + sgSetVec3(a->center, (float)v[0], (float)v[1], (float)v[2]); + stack[sp] = new ssgBranch; + break; + } + + case 96: { /* Switch */ + int num_words = (len > 28) ? get32i(ptr + 20) : -1; + int num_masks = (len > 28) ? get32i(ptr + 24) : -1; + if (num_words <= 0 || num_masks <= 0 || len < 28 + 4 * num_words * num_masks) { + ulSetError(UL_DEBUG, "[flt] Incomplete switch record."); + } + else { + fltNodeAttr *a = new fltNodeAttr; + a->current_mask = get32i(ptr + 16); + a->num_masks = num_masks; + a->num_words = num_words; + a->mask_words = new uint [ num_masks * num_words ]; + get32v(ptr + 28, a->mask_words, num_masks * num_words); + attr[sp] = a; + } + stack[sp] = new ssgBranch; + break; + } + + default: { /* Others (i.e. 98 Clip Region) */ + stack[sp] = new ssgBranch; + break; + } + + } + + if (ptr[4]) + stack[sp]->setName((char *)ptr + 4); + + ptr += len; + ptr += AttrChunks(ptr, end, &attr[sp]); + + break; + + case 61: /* Instance Reference */ + k = get16u(ptr + 6); + if (k != instance[sp]) { // current instance inserted by default + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + if (state->refs) { + state->refs = splay(state->refs, (void *)k, ptrcmp); + if (state->refs->key == (void *)k) { + stack[sp] = (ssgEntity *)state->refs->data; + } + } + } + ptr += len; + ptr += AttrChunks(ptr, end, &attr[sp]); + break; + + case 62: /* Instance Definition */ + k = get16u(ptr + 6); +#if 0 + if (stack[sp]) { + state->refs = sinsert(state->refs, (void *)k, 0, ptrcmp); + if (state->refs->data == (void *)-1) { + state->refs->data = stack[sp]; + stack[sp]->ref(); + } + } + else + { + ulSetError(UL_DEBUG, "[flt] yeah, but the node is empty!"); + } +#else + instance[sp] = k; +#endif + ptr += len; + break; + + case 63: /* External Reference */ + /*ulSetError(UL_DEBUG, "external reference %s", ptr + 4);*/ + PostLink(stack + sp - 1, attr + sp - 1, instance[sp], state); + instance[sp] = -1; + if (!NoExternals) { + char *file = (char *)ptr + 4, *p; + if ((p = strrchr(file, '/'))) + file = p + 1; + //stack[sp] = LoadFLT(file); + stack[sp] = ssgLoad (file); + } + ptr += len; + ptr += AttrChunks(ptr, end, &attr[sp]); + break; + + case 31: /* Text Comment */ + case 21: /* Push Extension */ + case 22: /* Pop Extension */ + case 55: /* Binary Separating Plane */ + case 87: /* Road Segment */ + case 90: /* Linkage */ + case 91: /* Sound Bead */ + case 92: /* Path Bead */ + case 95: /* Text */ + case 101: /* Light Source Bead */ + case 122: /* Push Attribute */ + case 123: /* Pop Attribute */ + //printf("op %d ignored\n", op); + ptr += len; + break; + + case 3: /* Level of Detail (obsolete) */ + case 13: /* Degree of Freedom (obsolete) */ + case 16: /* Instance Reference (obsolete) */ + case 17: /* Instance Definition (obsolete) */ + Obsolete(op); + ptr += len; + break; + + default: + NotImplemented(op); + ptr += len; + } + } + + /* pop stack (expected one iteration but may be more for incomplete databases) */ + while (sp-- > 0) + PostLink(stack + sp, attr + sp, -1, 0); + + if (stack[0]) + stack[0] = PostClean(stack[0], attr[0]); + else if (attr[0]) + delete attr[0]; // possible? + + return stack[0]; +} + +/* parse the vertex table */ +static int VertexTable(ubyte *ptr0, ubyte *end, fltState *state) +{ + int len, size, num, i; + ubyte *ptr = ptr0; + + assert(get16u(ptr) == 67); /* vtx tab op code */ + len = get16u(ptr + 2); + + size = get32i(ptr + 4); + /*ulSetError(UL_DEBUG, "vertex table header len %d, total size %d", len, size);*/ + num = (size - len)/40; /* max # of vertices */ + if (num <= 0 || state->vtab) { + if (state->vtab) + ulSetError(UL_WARNING, "[flt] Multiple vertex tables not allowed."); + return size; + } + state->vtab = ptr; + end = MIN(end, ptr + size); + ptr += len; + + state->offset = new int [num]; + state->bind = new ubyte [num]; + state->coord = new sgVec3 [num]; + state->color = new sgVec4 [num]; + state->normal = new sgVec3 [num]; + state->texcoord = new sgVec2 [num]; + + for (i = 0; i < num && ptr + 40 <= end; ++i) { + double tmp[3]; + ubyte *p = ptr; + int op = get16u(p); + int len = get16u(p + 2); + int flags = get16u(p + 6); + int bind = 0; + if (ptr + len > end) + break; + state->offset[i] = ptr - ptr0; + get64v(p + 8, tmp, 3); + sgSetVec3(state->coord[i], (float)tmp[0], (float)tmp[1], (float)tmp[2]); + sgSetVec4(state->color[i], 1, 1, 1, 1); + sgSetVec3(state->normal[i], 0, 0, 1); + sgSetVec2(state->texcoord[i], 0, 0); + p += 32; + if (op == 69 || op == 70) { + get32v(p, state->normal[i], 3); + sgNormalizeVec3(state->normal[i]); + p += 12; + bind |= BIND_NORMAL; + } + if (op == 70 || op == 71) { + get32v(p, state->texcoord[i], 2); + p += 8; + bind |= BIND_TEXCOORD; + } + if (!(flags & 4)) { + if ((flags & 8) && p + 4 <= ptr + len) { + UNPACK_ABGR(state->color[i], p); + //ulSetError(UL_DEBUG, "packed ABGR: %d %d %d %d", p[0], p[1], p[2], p[3]); + bind |= BIND_COLOR; + } + else if (state->revision > 1400) { + int color; + if (state->revision > 1500 && p + 8 <= ptr + len) { + color = get32i(p + 4); + } + else { + color = get16u(ptr + 4); + if (color == 65535) + color = -1; + } + int index = color / 128; + int intensity = color % 128; + if (color >= 0 && state->ctab && index < state->cnum) { + //ulSetError(UL_DEBUG, "colour %d: index %d, intensity %d", color, index, intensity); + UNPACK_ABGR2(state->color[i], state->ctab[index], intensity); + bind |= BIND_COLOR; + } + } + } + /* + if (bind & BIND_COLOR) + ulSetError(UL_DEBUG, "vertex #%d color %.2f %.2f %.2f", i, + state->color[i][0], + state->color[i][1], + state->color[i][2]); + */ + state->bind[i] = bind; + ptr += len; + } + /*ulSetError(UL_DEBUG, "got %d vertices", i);*/ + + state->vnum = i; + + return size; +} + +/* header ancillary chunks */ +static int TableChunks(ubyte *ptr0, ubyte *end, fltState *state) +{ + ubyte *ptr = ptr0; + int op, len, index, done = 0; + + while (!done) { + + if (ptr + 4 > end) + break; + op = get16u(ptr); + len = get16u(ptr + 2); + if (len < 4 || (len & 3) != 0 || ptr + len > end) + break; + + switch (op) { + + case 32: /* Color Table */ + if (len < 132 + 4 * 512) { + /* note: in older files it appears that the + * color table is stored as 16-bit integers. + * i have no documentation on this. + * maybe 16 x 16 colors (rather than 512 or 1024 x 128). + */ + if (state->revision <= 1400) { + ulSetError(UL_WARNING, "[flt] Color table ignored (unknown format)."); + /*hexdump(UL_DEBUG, ptr, len, 0);*/ + } + else + BAD_CHUNK(ptr, "Color Table"); + } + else if (state->ctab == 0) { + int max = (len - 132) / 4; + state->ctab = (ubyte (*)[4])(ptr + 132); + state->cnum = MIN(state->revision > 1500 ? 1024 : 512, max); +#if 0 + int i; + for (i = 0; i < state->cnum; i++) + ulSetError(UL_DEBUG, "%d %02x %02x %02x %02x", i, + state->ctab[i][0], state->ctab[i][1], + state->ctab[i][2], state->ctab[i][3]); +#endif + } + else { + ulSetError(UL_WARNING, "[flt] Multiple color tables are not allowed."); + } + ptr += len; + break; + + case 64: /* Texture Reference */ + if (!NoTextures) { + if (len == 96 || len == 216) { + char *file = (char *)ptr + 4, *p; + if ((p = strrchr(file, '/'))) + file = p + 1; + index = get32i(ptr + len - 12); + state->texs = sinsert(state->texs, (void *)index, 0, ptrcmp); + if (state->texs->data == (void *)-1) { + //fltTexture *tex = (fltTexture *)malloc(sizeof(fltTexture)); + fltTexture *tex = new fltTexture; + assert ( tex != NULL ); + tex->file = file; + tex->state = (ssgState *)-1; + tex->tex = (ssgTexture *)-1; + state->texs->data = tex; + } + } + else { + BAD_CHUNK(ptr, "Texture Reference"); + } + } + ptr += len; + break; + + case 66: /* Material Table */ + if ((len - 4) % 184 != 0) { + BAD_CHUNK(ptr, "Material Table"); + } + else if (state->mtls == 0) { + ubyte *p = ptr + 4; + int i, j, n = (len - 4) / 184; + for (i = 0; i < n; ++i) { + float *mtl; + state->mtls = sinsert(state->mtls, (void *)i, 0, ptrcmp); + //state->mtls->data = malloc(sizeof(float)*14); + state->mtls->data = new float[14]; + mtl = (float *)state->mtls->data; + get32v(p, mtl, 14); + for (j = 0; j < 12; j++) + mtl[j] = CLAMP(mtl[j], 0, 1); + mtl[12] = CLAMP(mtl[12], 8, 128); + mtl[13] = CLAMP(mtl[13], 0, 1); +#if 0 + float r, g, b, s; + r = mtl[0] + mtl[3] + mtl[6] + mtl[9]; + g = mtl[1] + mtl[4] + mtl[7] + mtl[10]; + b = mtl[2] + mtl[5] + mtl[8] + mtl[11]; + s = MAX3(r, g, b); + if (s > 1.0f) { + s = 1.0f/s; + for (j = 0; j < 12; j++) + mtl[j] *= s; + } +#endif + p += 184; + } + } + else { + ulSetError(UL_WARNING, "[flt] Multiple material tables are not allowed"); + } + ptr += len; + break; + + case 67: /* Vertex Table (header) */ + ptr += VertexTable(ptr, end, state); + break; + + case 113: /* Material */ + index = get32i(ptr + 4); + state->mtls = sinsert(state->mtls, (void *)index, 0, ptrcmp); + if (state->mtls->data == (void *)-1) { + float *mtl; + int i; + //state->mtls->data = malloc(sizeof(float)*14); + state->mtls->data = new float[14]; + mtl = (float *)state->mtls->data; + get32v(ptr + 24, mtl, 14); + for (i = 0; i < 12; i++) + mtl[i] = CLAMP(mtl[i], 0, 1); + mtl[12] = CLAMP(mtl[12], 8, 128); + mtl[13] = CLAMP(mtl[13], 0, 1); +#if 0 + float r, g, b, s; + r = mtl[0] + mtl[3] + mtl[6] + mtl[9]; + g = mtl[1] + mtl[4] + mtl[7] + mtl[10]; + b = mtl[2] + mtl[5] + mtl[8] + mtl[11]; + s = MAX3(r, g, b); + if (s > 1.0f) { + s = 1.0f/s; + for (i = 0; i < 12; i++) + mtl[i] *= s; + } +#endif + } + ptr += len; + break; + + case 65: /* Eyepoint Palette (obsolete) */ + Obsolete(op); + ptr += len; + break; + + case 31: /* Text Comment */ + case 83: /* Eyepoint and Trackplane */ + case 90: /* Linkage Palette */ + case 93: /* Sound Palette */ + case 97: /* Line Style Palette */ + case 102: /* Light Source Palette */ + case 103: /* Reserved */ + case 104: /* Reserved */ + case 112: /* Texture Mapping */ + case 114: /* Name Table */ + /* these are safe to ignore */ + ptr += len; + break; + + default: + //printf("op %d: end of table chunks\n", op); + done = 1; + } + } + + return ptr - ptr0; +} + +static int CheckHeader(ubyte *ptr, ubyte *end, fltState *state) +{ + /*const char *unit[9] = {"meters", "kilometers", "", "", "feet", "inches", "", "", "nautical miles"};*/ + int len, k; + if (get16u(ptr) != 1) { + ulSetError(UL_WARNING, "[flt] Wrong header opcode (%d).", get16i(ptr)); + return -1; + } + len = get16u(ptr + 2); + if (len < 128 || len > 1024) { + ulSetError(UL_WARNING, "[flt] Suspicious header record length (%d).", len); + return -1; + } + k = get32i(ptr + 12); + if (k < 100) { + state->revision = 100 * k; + state->major = k; + state->minor = 0; + } + else { + state->revision = k; + state->major = k / 100; + state->minor = k % 100; + } + if (state->major < 11 || state->major > 16) { + ulSetError(UL_WARNING, "[flt] Suspicious format revision number (%d).", k); + return -1; + } +/* + ulSetError(UL_DEBUG, "[flt] Loading %s %sFlight v%d.%d", + state->filename, + state->major > 13 ? "Open" : "", + state->major, + state->minor); +*/ + return len; +} + +#if 0 +struct snode *refs; +static int nrefs; + +static void ptree(ssgEntity *node, FILE *f, int d) +{ + int i; + for (i = 0; i < d; ++i) + fprintf(f, " "); + { + const char *p = node->getName(); + fprintf(f, "%s \"%s\"", node->getTypeName(), p ? p : ""); + } + if (node->getNumParents() > 1) { + refs = sinsert(refs, node, 0, ptrcmp); + if (refs->data == (void *)-1) { + fprintf(f, " (%d)", nrefs); + refs->data = (void *)nrefs++; + } + else { + fprintf(f, " (%d) ...\n", (int)refs->data); + return; + } + } + assert(!node->isA(0xDeadBeef)); + if (node->isAKindOf(ssgTypeLeaf())) { + ssgLeaf *leaf = (ssgLeaf *)node; + ssgState *st = leaf->getState(); + fprintf(f, " %d tris", leaf->getNumTriangles()); + if (st && st->isAKindOf(ssgTypeSimpleState())) { + ssgSimpleState *ss = (ssgSimpleState *)st; + char *file = ss->getTextureFilename(); + if (file) { + char *p; + if ((p = strrchr(file, '/'))) + file = p + 1; + fprintf(f, " %s", file); + } + } + } + if (node->isAKindOf(ssgTypeTransform())) { + sgMat4 mat; + ((ssgTransform *)node)->getTransform(mat); + fprintf(f, " pos %.2f %.2f %.2f", mat[3][0], mat[3][1], mat[3][2]); + } + if (node->isAKindOf(ssgTypeRangeSelector())) { + ssgRangeSelector *lod = (ssgRangeSelector *)node; + fprintf(f, " ranges"); + for (i = 0; i <= lod->getNumKids(); i++) + fprintf(f, " %.2f", lod->getRange(i)); + } + putc('\n', f); + if (node->isAKindOf(ssgTypeBranch())) { + ssgBranch *grp = (ssgBranch *)node; + int n = grp->getNumKids(); + for (i = 0; i < n; ++i) + ptree(grp->getKid(i), f, d+1); + } +} +#endif + +static const char *FindFile(const char *file) +{ + /* XXX fixme! */ + static char path[1024]; + + if (ulFileExists((char *)file)) + return file; + + ssgGetCurrentOptions () -> makeModelPath ( path, file ) ; + if (ulFileExists(path)) + return path; + + ulSetError(UL_WARNING, "[flt] %s not found.", file); + + return 0; +} + +static struct snode *FltCache; + +#ifdef UL_WIN32 + +static void win32_perror(const char *s) +{ + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + fprintf(stderr, "%s: %s\n", s, lpMsgBuf); + LocalFree( lpMsgBuf ); +} + +#endif + +static ssgEntity *LoadFLT(const char *file) +{ + struct snode *cache; + cache = FltCache = sinsert(FltCache, (void *)file, strlen(file) + 1, (sfunc)strcmp); + if (cache->data == (void *)-1) { + cache->data = NULL; /* avoid looping */ + + fltState *state = 0; + int size = 0, len; + ubyte *ptr = 0, *end; + ssgEntity *node = 0; + +#ifdef USE_WIN32_MMAP + + HANDLE fd = INVALID_HANDLE_VALUE; + HANDLE map = 0; + +#else + + int fd = -1; +#ifdef USE_POSIX_MMAP + struct stat st; +#else + ubyte buf[256]; +#endif + +#endif + + do { /* dummy loop */ + + const char *path, *name; + + path = FindFile(file); + if (path == 0) + break; + +#ifdef USE_WIN32_MMAP + + fd = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + if (fd == INVALID_HANDLE_VALUE) { + win32_perror(path); + break; + } + + size = GetFileSize(fd, NULL); + if (size < 256) + break; + + map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, size, NULL); + if (map == 0) { + win32_perror(path); + break; + } + + ptr = (ubyte *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if (ptr == 0) { + win32_perror(path); + break; + } + +#else /* USE_WIN32_MAP */ + + if ((fd = open(path, O_RDONLY|O_BINARY)) == -1) { + perror(path); + break; + } + +#ifdef USE_POSIX_MMAP + if (fstat(fd, &st)) { + perror(file); + break; + } + size = st.st_size; + if (size < 256) + break; + ptr = (ubyte *)mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == (ubyte *)-1) { + perror(file); + break; + } + close(fd); + fd = -1; +#else /* USE_POSIX_MMAP */ + if (read(fd, buf, 256) != 256) { + perror(file); + break; + } + ptr = buf; +#endif /* ! USE_POSIX_MMAP */ + +#endif /* ! USE_WIN32_MMAP */ + + /*putc('\n', stderr);*/ + + name = strrchr(file, '/'); + if (name) + name++; + else + name = file; + + state = new fltState; + state->filename = name; + + len = CheckHeader(ptr, ptr + 256, state); + if (len == -1) + break; + +#if !defined(USE_POSIX_MMAP) && !defined(USE_WIN32_MMAP) + + if ((size = lseek(fd, 0, SEEK_END)) == -1) { + perror(file); + break; + } + //ptr = (ubyte *)malloc(size); + ptr = new ubyte[size]; + lseek(fd, 0, SEEK_SET); + if (read(fd, ptr, size) != size) { + perror(file); + break; + } + close(fd); + fd = -1; + +#endif + + end = ptr + size; + len += TableChunks(ptr + len, end, state); + node = HierChunks(ptr + len, end, state); + + if (node) + node->setName((char *)name); + + } while (0); + + delete state; + +#ifdef USE_WIN32_MMAP + + if (ptr) + UnmapViewOfFile(ptr); + if (map) + CloseHandle(map); + if (fd != INVALID_HANDLE_VALUE) + CloseHandle(fd); + +#else /* USE_WIN32_MMAP */ + + if (fd != -1) + close(fd); + +#ifdef USE_POSIX_MMAP + if (ptr != 0 && ptr != (ubyte *)-1) + munmap((char *)ptr, size); +#else + if (ptr != 0 && ptr != buf) + //free(ptr); + delete [] ptr; +#endif + +#endif /* ! USE_WIN32_MMAP */ + + cache->data = node; + if (node) node->ref(); + } + + return (ssgEntity *)cache->data; +} + +#if 0 +static struct snode *Flattened; + +static ssgEntity *Flatten(ssgEntity *node, float (*mat)[4]) +{ + if (node == 0) + return 0; + + assert(!node->isA(0xDeadBeef)); + + /* some nodes cannot be flattened. only types that may be + * generated by the loader are considered here. + */ + if (node->getRef() > 0 || + node->isAKindOf(ssgTypeRangeSelector()) || + node->isAKindOf(ssgTypeCutout())) { + if (node->isAKindOf(ssgTypeBranch())) { + /* see if we have flattened beyond this node already */ + int completed = 0; + if (node->getNumParents() > 0) { + Flattened = sinsert(Flattened, node, 0, ptrcmp); + if (Flattened->data == (void *)-1) { + Flattened->data = 0; + } + else { + completed = 1; + } + } + if (!completed) { + /* traverse */ + ssgBranch *grp = (ssgBranch *)node; + int i, n = grp->getNumKids(); + if (n > 0) { + ssgEntity *kids[32]; + for (i = n; i--;) { + kids[i] = grp->getKid(i); + kids[i]->ref(); grp->removeKid(i); kids[i]->deRef(); + kids[i] = Flatten(kids[i], 0); + assert(kids[i] == 0 || !kids[i]->isA(0xDeadBeef)); + } + for (i = 0; i < n; i++) + if (kids[i]) + grp->addKid(kids[i]); + grp->recalcBSphere(); + } + } + } + /* insert a transform if required */ + if (mat) { + ssgTransform *scs = new ssgTransform; + scs->setTransform(mat); + scs->addKid(node); + node = scs; + } + return node; + } + + assert(node->getNumParents() == 0); + + /* accumulate transformations */ + if (node->isAKindOf(ssgTypeTransform())) { + ssgTransform *scs = (ssgTransform *)node; + int i, n = scs->getNumKids(); + if (n <= 0) { + delete scs; + return 0; + } + else { + sgMat4 tmp; + scs->getTransform(tmp); + if (mat) + sgPostMultMat4(tmp, mat); + if (n == 1) { + node = scs->getKid(0); + node->ref(); scs->removeKid(0); node->deRef(); + delete scs; + assert(!node->isA(0xDeadBeef)); + return Flatten(node, tmp); + } + else { + ssgBranch *grp = new ssgBranch; + grp->setName(scs->getName()); + for (i = 0; i < n; i++) + grp->addKid(scs->getKid(i)); + while (n--) + scs->removeKid(n); + delete scs; + return Flatten(grp, tmp); + } + } + } + + /* recurse the scene graph */ + if (node->isAKindOf(ssgTypeBranch())) { + ssgBranch *grp = (ssgBranch *)node; + int i, n = grp->getNumKids(); + if (n <= 0) { + delete grp; + return 0; + } + else if (n == 1 && grp->isA(ssgTypeBranch())) { + ssgEntity *kid = grp->getKid(0); + kid->ref(); grp->removeKid(0); kid->deRef(); + delete grp; + assert(!kid->isA(0xDeadBeef)); + return Flatten(kid, mat); + } + else { + ssgEntity *kids[32]; + for (i = n; i--;) { + kids[i] = grp->getKid(i); + kids[i]->ref(); grp->removeKid(i); kids[i]->deRef(); + kids[i] = Flatten(kids[i], mat); + assert(kids[i] == 0 || !kids[i]->isA(0xDeadBeef)); + } + assert(!grp->isA(0xDeadBeef)); + for (i = 0; i < n; i++) { + if (kids[i]) { + assert(!kids[i]->isA(0xDeadBeef)); + grp->addKid(kids[i]); + } + } + grp->recalcBSphere(); + return grp; + } + } + + /* transform geometry */ + if (node->isAKindOf(ssgTypeVtxTable()) && mat) { + ssgVtxTable *g = (ssgVtxTable *)node; + sgVec3 *a; + int i, n; + n = g->getNumVertices(); + if (n > 0) { + g->getVertexList((void **)&a); + for (i = 0; i < n; i++) + sgXformPnt3(a[i], a[i], mat); + } + n = g->getNumNormals(); + if (n > 0) { + g->getNormalList((void **)&a); + for (i = 0; i < n; i++) + sgXformVec3(a[i], a[i], mat); + } + g->recalcBSphere(); + } + assert(!node->isA(0xDeadBeef)); + return node; +} +#endif + +static void Init(void) +{ + if (Inited) + return; + Inited = 1; + +#if !defined(USE_POSIX_MMAP) && !defined(USE_WIN32_MMAP) + ulSetError(UL_DEBUG, "[flt] Memory mapped files not enabled, please check source code."); +#endif + +#ifdef PROBE_ENDIAN + { + short tmp = 42; + if (*(char *)&tmp == 42) { + /* little endian */ + get16v = _swab16; + get32v = _swab32; + get64v = _swab64; + ulSetError(UL_DEBUG, "[flt] Little endian architecture."); + } + else { + /* big endian */ + get16v = _copy16; + get32v = _copy32; + get64v = _copy64; + ulSetError(UL_DEBUG, "[flt] Big endian architecture."); + } + } +#endif + + if (getenv("FLTNOTEX")) NoTextures = 1; + if (getenv("FLTNOMIPMAP")) NoMipmaps = 1; + if (getenv("FLTNOEXT")) NoExternals = 1; + if (getenv("FLTNOCLEAN")) NoClean = 1; + + return; +} + +ssgEntity *ssgLoadFLT(const char *filename, +#ifdef NO_LOADER_OPTIONS + ssgHookFunc +#else + const ssgLoaderOptions *options +#endif + ) +{ + static int depth = 0; + ssgEntity *node; + + if (depth == 0) { + + Init(); + + ObsoleteFlag = 0; + NotImplementedFlag = 0; + + TexCache = 0; + StateCache = 0; + FltCache = 0; + +#ifndef NO_LOADER_OPTIONS + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + LoaderOptions = ssgGetCurrentOptions () ; +#endif + + } + + // no longjmp()'s please! (or this recursion test will fail) + + depth++; + + node = LoadFLT(filename); + + depth--; + + if (depth == 0) { + + sfree(TexCache, S_KEY | S_DATA); + sfree(StateCache, S_KEY); + + if (node) node->ref(); // prevent this node from being deleted! + sfree(FltCache, S_KEY | S_TREE); + if (node) node->deRef(); + + } + +#if 0 + if (node && !NoClean) { + Flattened = 0; + node = Flatten(node, 0); + sfree(Flattened, 0); + } +#endif + +#if 0 /* debug */ + if (node && getenv("FLTDUMP")) { + const char *file = "/tmp/tree.txt"; + FILE *f = fopen(file, "w"); + if (f == 0) + perror(file); + else { + refs = 0; + nrefs = 0; + ptree(node, f, 0); + sfree(refs, 0); + fclose(f); + ulSetError(UL_DEBUG, "wrote %s", file); + } + } +#endif + + return node; +} +//lint -restore diff --git a/src/ssg/ssgLoadIV.cxx b/src/ssg/ssgLoadIV.cxx new file mode 100644 index 0000000..633b962 --- /dev/null +++ b/src/ssg/ssgLoadIV.cxx @@ -0,0 +1,381 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 William Lachance, Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net +*/ + +#include "ssgLocal.h" +#include "ssgParser.h" +#include "ssgLoaderWriterStuff.h" + +#include "ssgLoadVRML.h" + +static _ssgParserSpec parser_spec = +{ + "\r\n\t, ", // delim_chars_skipable + 0, // delim_chars_non_skipable + NULL, // pre_processor + "{[", // open_brace_chars + "}]", // close_brace_chars + '"', // quote_char + '#', // comment_char + 0 // comment_string +}; + +static ssgLoaderOptions* currentOptions = NULL ; +static _nodeIndex *definedNodes = NULL; + +static bool iv_parseSeparator( ssgBranch *parentBranch, _traversalState *parentData, char *defName ); +static bool iv_parseSwitch( ssgBranch *parentBranch, _traversalState *parentData, char *defName ); +static bool iv_parseIndexedFaceSet( ssgBranch *parentBranch, _traversalState *parentData, char *defName ); +static bool iv_parseTexture2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); + +static _parseTag ivTags [] = +{ + { "Separator", iv_parseSeparator }, + { "Switch", iv_parseSwitch }, + { "IndexedFaceSet", iv_parseIndexedFaceSet }, + { "Coordinate3", vrml1_parseCoordinate3 }, + { "TextureCoordinate2", vrml1_parseTextureCoordinate2 }, + { "Texture2", iv_parseTexture2 }, + { "ShapeHints", vrml1_parseShapeHints }, + { "MatrixTransform", vrml1_parseMatrixTransform }, + { "Scale", vrml1_parseScale }, + { "Rotation", vrml1_parseRotation }, + { "Translation", vrml1_parseTranslation }, + { NULL, NULL }, +}; + +ssgEntity *ssgLoadIV( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + currentOptions = ssgGetCurrentOptions () ; + + if ( !vrmlParser.openFile( fname, &parser_spec ) ) { + ulSetError ( UL_WARNING, "ssgLoadIV: Failed to open '%s' for reading", fname ) ; + return 0; + } + + definedNodes = new _nodeIndex(); + + // check for a valid header header + char *token; + if( !(token = vrmlParser.getRawLine()) ) + return 0; + if( strstr( token, "#Inventor V2.1 ascii" ) == NULL ) // should we handle different flavours of inventor? + { + ulSetError ( UL_WARNING, "ssgLoadIV: valid iv header not found" ); + return 0; + } + + // creating a root node.. + ssgBranch *rootBranch = new ssgBranch(); + + vrmlParser.expectNextToken( "Separator" ); + + if( !iv_parseSeparator( rootBranch, NULL, NULL ) ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML: Failed to extract valid object(s) from %s", fname ) ; + delete( rootBranch ); + delete( definedNodes ); + return NULL ; + } + + vrmlParser.closeFile(); + delete( definedNodes ); + + return rootBranch ; +} + +static bool iv_parseSeparator( ssgBranch *parentBranch, _traversalState *parentData, char *defName ) +{ + char *childDefName = NULL; + + char *token; + + vrmlParser.expectNextToken( "{" ); + + // create a branch for this node + ssgBranch *currentBranch = new ssgBranch(); + + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + _traversalState *currentData; + if( parentData == NULL ) + currentData = new _traversalState(); + else + currentData = parentData->clone(); + + token = vrmlParser.getNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + int i=0; bool tokenFound = FALSE; + while( ivTags[i].token != NULL && !tokenFound ) + { + if( !strcmp( token, ivTags[i].token ) ) + { + if( !(ivTags[i].func( currentBranch, currentData, childDefName ) ) ) + { + delete( currentBranch ); + delete( currentData ); + return FALSE; + } + tokenFound = TRUE; + } + + i++; + } + if( !tokenFound ) + parseUnidentified(); + + token = vrmlParser.getNextToken( NULL ); + } + + parentBranch->addKid( currentBranch ); + + delete( currentData ); // delete the currentData structure (we may use its content, but not its form) + + return TRUE; +} + +static bool iv_parseSwitch( ssgBranch *parentBranch, _traversalState *parentData, char *defName ) +// UNSUPPORTED BEHAVIOUR: does not do a check for a whichChild parameter. Assumes that a switch +// "hides" all of its children. +{ + char *childDefName = NULL; + + char *token; + + vrmlParser.expectNextToken( "{" ); + + // create a branch for this node + ssgBranch *currentBranch = new ssgSelector(); + ((ssgSelector *)currentBranch)->select( 0 ); // fixme: allow for children to be traversed + + + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + _traversalState *currentData; + if( parentData == NULL ) + currentData = new _traversalState(); + else + currentData = parentData->clone(); + + token = vrmlParser.getNextToken( NULL ); + + while( strcmp( token, "}" ) ) + { + int i=0; bool tokenFound = FALSE; + while( ivTags[i].token != NULL && !tokenFound ) + { + if( !strcmp( token, ivTags[i].token ) ) + { + if( !(ivTags[i].func( currentBranch, currentData, childDefName ) ) ) + { + delete( currentBranch ); + delete( currentData ); + return FALSE; + } + + tokenFound = TRUE; + } + i++; + } + if( !tokenFound ) + parseUnidentified(); + + token = vrmlParser.getNextToken( NULL ); + } + + parentBranch->addKid( currentBranch ); + + delete( currentData ); // delete the currentData structure (we may use its content, but not its form) + + return TRUE; +} + +static bool iv_parseIndexedFaceSet( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + bool texCoordIndexGiven = FALSE; + + ssgBranch *currentBranch = new ssgBranch(); + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + ssgLoaderWriterMesh *loaderMesh = new ssgLoaderWriterMesh(); + loaderMesh->createFaces(); + loaderMesh->setVertices( currentData->getVertices() ); + if( currentData->getTexture() != NULL && currentData->getTextureCoordinates() != NULL ) + loaderMesh->createPerFaceAndVertexTextureCoordinates2(); + + vrmlParser.expectNextToken("{"); + + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "coordIndex" ) ) + { + vrmlParser.expectNextToken("coordIndex"); + if( !vrml1_parseCoordIndex( loaderMesh, currentData ) ) + { + delete( loaderMesh ); + return FALSE; + } + } + + else if( !strcmp( token, "textureCoordIndex" ) ) + { + texCoordIndexGiven = TRUE; + vrmlParser.expectNextToken("textureCoordIndex"); + if( !vrml1_parseTextureCoordIndex( loaderMesh, currentData ) ) + { + delete( loaderMesh ); + return FALSE; + } + } + else + token = vrmlParser.getNextToken( NULL ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + + //ulSetError(UL_DEBUG, "Level: %i. Found %i faces here.\n", vrmlParser.level, numFaces); + + vrmlParser.expectNextToken( "}" ); + + // ------------------------------------------------------- + // add the face set to ssg + // ------------------------------------------------------- + + // kludge. We need a state for addToSSG: + ssgSimpleState * ss = new ssgSimpleState () ; // (0) ? + ss -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5, 1.0); + ss -> setMaterial ( GL_DIFFUSE, 1.0, 1.0, 1.0, 1.0) ; // 0.8, 0.8, 1.0, 1.0f + ss -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + ss -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0, 1.0); + ss -> setShininess ( 20 ) ; // Fixme, NIV14: Is that correct? + + // ------------------------------------------------------- + // texturing stuff + // ------------------------------------------------------- + // todo: give an implicit mapping if texture coordinates are not given + // todo: add support for per-vertex texturing + if( currentData->getTexture() != NULL && currentData->getTextureCoordinates() != NULL && texCoordIndexGiven ) + { + ss -> setTexture ( currentData->getTexture() ); + ss -> enable( GL_TEXTURE_2D ); + } + else + ss -> disable( GL_TEXTURE_2D ); + + ss -> disable ( GL_COLOR_MATERIAL ) ; + //ss -> enable ( GL_COLOR_MATERIAL ) ; + //ss -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + ss -> enable ( GL_LIGHTING ) ; + ss -> setShadeModel ( GL_SMOOTH ) ; + + ss ->disable(GL_ALPHA_TEST); //needed? + + ss -> disable ( GL_BLEND ) ; + + ss -> setOpaque () ; + + if( !currentData->getEnableCullFace() ) + ss->disable( GL_CULL_FACE ); + + if( !loaderMesh->checkMe() ) + { + delete( loaderMesh ); + return FALSE; + } + + if( currentData->getTransform() != NULL ) + { + currentBranch->addKid( currentData->getTransform() ); // FIXME: in case we're reusing transforms, perhaps they should be reinstanced? (currently we don't allow this) + loaderMesh->addToSSG( ss, currentOptions, currentData->getTransform() ); + } + else + loaderMesh->addToSSG( ss, currentOptions, currentBranch ); + + parentBranch->addKid( currentBranch ); + + return TRUE; +} + +static bool iv_parseTexture2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + char *fileName = NULL; bool wrapU = FALSE, wrapV = FALSE; + + vrmlParser.expectNextToken("{"); + + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "filename") ) + { + vrmlParser.expectNextToken("filename"); + if( !vrmlParser.getNextString( token, NULL ) ) + return FALSE; + fileName = ulStrDup ( token ) ; + } + else if( !strcmp( token, "wrapS") ) + { + vrmlParser.expectNextToken("wrapS"); + token = vrmlParser.getNextToken( NULL ); + if( !strcmp( token, "REPEAT") ) + wrapU = TRUE; + } + else if( !strcmp( token, "wrapT") ) + { + vrmlParser.expectNextToken("wrapT"); + token = vrmlParser.getNextToken( NULL ); + if( !strcmp( token, "REPEAT") ) + wrapV = TRUE; + } + else + token = vrmlParser.getNextToken( NULL ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + + if( fileName == NULL ) + return FALSE; + + //ssgTexture *currentTexture = new ssgTexture( fileName, wrapU, wrapV ); + ssgTexture *currentTexture = currentOptions -> createTexture ( fileName, wrapU, wrapV ); + currentData->setTexture( currentTexture ); + vrmlParser.expectNextToken("}"); + + delete [] fileName; + + return TRUE; +} diff --git a/src/ssg/ssgLoadM.cxx b/src/ssg/ssgLoadM.cxx new file mode 100644 index 0000000..a2dad41 --- /dev/null +++ b/src/ssg/ssgLoadM.cxx @@ -0,0 +1,152 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadM.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include +#include "ssgLocal.h" + +static ssgLoaderOptions* current_options; + +ssgEntity* ssgLoadM( const char* fname, + const ssgLoaderOptions* options ) { + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE* model_file = fopen(filename, "r"); + if(!model_file) { + ulSetError(UL_WARNING, "ssgLoadM: Couldn't open file '%s'.", + filename); + return NULL; + } + + ssgVertexArray* vertices = new ssgVertexArray; + ssgNormalArray* normals = new ssgNormalArray; + ssgIndexArray* indices = new ssgIndexArray; + + int i, index; + char line[256]; + sgVec3 zero = {0.0f, 0.0f, 0.0f}; + + fgets(line, 256, model_file); + while ( !feof(model_file) ) { + char* token; + + switch (line[0]) { + case '#': // comment, skip this line + break; + case 'V': + sgVec3 vtx; + token = strtok(line, " "); // token should now be "Vertex" + token = strtok(NULL, " "); // token is vertex index now + + index = atoi(token) - 1; + + // fill out non-declared vertices with zero vectors + while (index > vertices->getNum()) { + vertices->add(zero); + normals ->add(zero); + } + + // get vertex coordinate + for (i = 0; i < 3; i++) { + token = strtok(NULL, " "); + vtx[i] = (float) atof(token); + } + + vertices->add(vtx) ; + normals ->add(zero); + + break; + + case 'F': // face + token = strtok(line, " "); // token should now be "Face" + token = strtok(NULL, " "); // face index, ignored + + for (i = 0; i < 3; i++) { + token = strtok(NULL, " "); + indices->add( atoi(token)-1 ); + } + + break; + + case 'E': // Edge, ignored + break; + + default: + ulSetError(UL_WARNING, "ssgLoadM: Syntax error on line \"%s\".", line); + } + + fgets(line, 256, model_file); + } + + ssgSimpleState* state = new ssgSimpleState(); + state->setOpaque(); + state->disable(GL_BLEND); + state->disable(GL_ALPHA_TEST); + state->disable(GL_TEXTURE_2D); + state->enable(GL_COLOR_MATERIAL); + state->enable(GL_LIGHTING); + state->setShadeModel(GL_SMOOTH); + state->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.0f, 1.0f); + state->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.0f, 1.0f); + state->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + state->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + state->setShininess(50); + + // now calculate smooth normals (this code belongs elsewhere) + for (i = 0; i < indices->getNum(); i += 3) { + short idx0 = *indices->get(i ); + short idx1 = *indices->get(i + 1); + short idx2 = *indices->get(i + 2); + + sgVec3 normal; + sgMakeNormal( normal, + vertices->get(idx0), + vertices->get(idx1), + vertices->get(idx2) ); + + sgAddVec3( normals->get(idx0), normal ); + sgAddVec3( normals->get(idx1), normal ); + sgAddVec3( normals->get(idx2), normal ); + } + + for (i = 0; i < vertices->getNum(); i++) { + sgNormaliseVec3( normals->get(i) ); + } + + ssgVtxArray* leaf = new ssgVtxArray( GL_TRIANGLES, + vertices, + normals, + NULL, + NULL, + indices ); + + leaf->setCullFace( TRUE ); + leaf->setState( state ); + + ssgLeaf* model = current_options -> createLeaf( leaf, NULL ); + + return model; +} diff --git a/src/ssg/ssgLoadMD2.cxx b/src/ssg/ssgLoadMD2.cxx new file mode 100644 index 0000000..99a18a1 --- /dev/null +++ b/src/ssg/ssgLoadMD2.cxx @@ -0,0 +1,402 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadMD2.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +/*************************************************************************** + ssgloadmd2.cpp - description + ------------------- + begin : Thu Sep 7 2000 + copyright : (C) 2000 by Jon Anderson + email : janderson@onelink.com + ***************************************************************************/ +/******************************************************* + ** This was written to be a part of Stephen J Bakers + ** PLIB (http://plib.sourceforge.net) + *******************************************************/ + +#include "ssgLocal.h" + +//defaults +#define FRAME_DELAY 3 +#define NUM_SEQUENCES 22 + +/** + MD2 file loader for SSG. + + ssgLoadMD2 loads a given MD2 file into an + ssgSelector hiearchy. Each animation sequence is + loaded into a ssgTimedSelector. Each sequence is + then put into a top level ssgSelector. In addition, + all the skins are loaded into a ssgStateSelector that + is attached to each frame's ssgVtxTable. Animation + sequences can then be played by: + + ssgSelector::selectStep ( SSG_MD2_STAND ); + ssgTimedSelector *ts = (ssgTimedSelector *)s -> getKid ( i ); + ts -> setMode ( SSG_ANIM_SHUTTLE ); + ts -> control ( SSG_ANIM_START ); + + + Notes: + -Not all md2 files will contain the complete animation + sequences. + -Not all md2 files contain skin info/locations. + -By default, it displays each animation frame for + 3 screen frames. MD2 animations were designed to be + played at ~ 10hz + + MD2 File format documentaion: + http://www.ugrad.cs.jhu.edu/~dansch/md2 + + MD2 Frames documentation: + http://www.planetquake.com/polycount/resources/quake2/q2frameslist.shtml + + MD2 Models: + http://www.planetquake.com/polycount/ + +*/ + + +/* +int seq_sizes[][2] = { + { SSG_MD2_STAND, 40 }, + { SSG_Md2_RUN, 6 }, + { SSG_MD2_ATTACK, 8 }, + { SSG_MD2_PAIN_1, 4 }, + { SSG_MD2_PAIN_2, 4 }, + { SSG_MD2_PAIN_3, 4 }, + { SSG_MD2_JUMP, 6 }, + { SSG_MD2_FLIPOFF,12 }, + { SSG_MD2_SALUTE, 11 }, + { SSG_MD2_TAUNT, 17 }, + { SSG_MD2_WAVE, 11 }, + { SSG_MD2_POINT, 12 }, + { SSG_MD2_CROUCH_STAND, 19 }, + { SSG_MD2_CROUCH_WALK, 9 }, + { SSG_MD2_CROUCH_ATTACK, 9 }, + { SSG_MD2_CROUCH_PAIN, 4 }, + { SSG_MD2_CROUCH_DEATH, 5 }, + { SSG_MD2_DEATH_1, 6 }, + { SSG_MD2_DEATH_2, 6 }, + { SSG_MD2_DEATH_3, 6 }, + { SSG_MD2_ALL, 1 }, + { SSG_MD2_POSE, 1 } +}; + +*/ + +static int seq_frames[] = { + 40, + 6, + 8, + 4, + 4, + 4, + 6, + 12, + 11, + 17, + 11, + 12, + 19, + 9, + 9, + 4, + 5, + 6, + 6, + 6, + 1, //this may or may not be in the file. + 1 //this may or may not be in the file. +}; + + +typedef struct +{ + int magic; + int version; + int skinWidth; + int skinHeight; + int frameSize; + int numSkins; + int numVertices; + int numTexCoords; + int numTriangles; + int numGlCommands; + int numFrames; + int offsetSkins; + int offsetTexCoords; + int offsetTriangles; + int offsetFrames; + int offsetGlCommands; + int offsetEnd; +} t_model; + +typedef struct +{ + unsigned char vertex[3]; + unsigned char lightNormalIndex; +} t_vertex; + + +typedef struct +{ + short vertexIndices[3]; + short textureIndices[3]; + +} t_triangle; + +typedef struct +{ + short s, t; +} t_tcoord; + +typedef struct +{ + float s, t; + int vertexIndex; +} t_glCommandVertex; + +typedef struct +{ + float scale[3]; + float translate[3]; + char name[16]; +} t_frame; + + +static int is_little_endian; + +static ssgLoaderOptions* current_options = NULL ; +static FILE *loader_fd; + +static t_tcoord *uvs; +static t_frame *frames; +static t_vertex **vertices; +static t_triangle *triangles; +static char skins[32][1024]; +static t_model header; + + +static void read_header() +{ + fread(&header, sizeof(t_model), 1, loader_fd); +} + +static void read_frames(int offset) +{ + fseek(loader_fd, offset, SEEK_SET); + + frames = new t_frame[header.numFrames]; + vertices = new t_vertex* [header.numFrames]; + + for(int i=0; i enable ( GL_TEXTURE_2D ); + //state -> setTexture( filepath ) ; + state -> setTexture( current_options -> createTexture ( skins[i] ) ); + states -> setStep ( i, state ); + } + + /**Activate the first state*/ + if(stated) + states -> selectStep( 0 ); + else { + ulSetError(UL_WARNING, "ssgLoadMD2: No skins specified in MD2 file!"); + ssgSimpleState *state = new ssgSimpleState(); + state -> disable ( GL_TEXTURE_2D ); + states -> setStep(0, state); + states -> selectStep( 0 ); + } + + ssgSelector *sequences; + + if(header.numFrames == 200) + sequences = new ssgSelector(NUM_SEQUENCES); + else + sequences = new ssgSelector(NUM_SEQUENCES - 2); + + int current_sequence_num = 0; + int current_sequence_frame = 0; + + ssgTimedSelector *current_sequence = new ssgTimedSelector(seq_frames[current_sequence_num]); + sequences->addKid(current_sequence); + + for(i=0; i-1; k--){ + int vert_index = triangles[j].vertexIndices[k]; + + int bx = vertices[i][vert_index].vertex[0]; + int by = vertices[i][vert_index].vertex[1]; + int bz = vertices[i][vert_index].vertex[2]; + + vert[0] = ((float)bx)*frames[i].scale[0] + frames[i].translate[0]; + vert[1] = ((float)by)*frames[i].scale[1] + frames[i].translate[1]; + vert[2] = ((float)bz)*frames[i].scale[2] + frames[i].translate[2]; + + uv[0] = ((float)uvs[triangles[j].textureIndices[k]].s)/header.skinWidth; + uv[1] = 1 - ((float)uvs[triangles[j].textureIndices[k]].t)/header.skinHeight; + + vlist -> add( vert ); + tlist -> add( uv ); + } + } + ssgVtxTable *vtab = new ssgVtxTable ( GL_TRIANGLES, vlist, 0, tlist, 0 ) ; + vtab -> setState(states); + + current_sequence ->addKid(vtab); + current_sequence_frame++; + + if( current_sequence_frame > seq_frames[current_sequence_num] -1){ + current_sequence -> setLimits( 0, seq_frames[current_sequence_num]-1 ); + current_sequence -> setDuration( FRAME_DELAY ); + current_sequence -> setMode ( SSG_ANIM_ONESHOT ); + + current_sequence_num ++; + current_sequence_frame = 0; + current_sequence = new ssgTimedSelector(seq_frames[current_sequence_num]); + sequences -> addKid( current_sequence ); + } + + } + + sequences -> selectStep( 0 ); + return sequences; + +} + + +ssgEntity * ssgLoadMD2( const char *filename, const ssgLoaderOptions* options) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + int j = 1 ; + is_little_endian = *((char *) &j ); + + char filepath [ 1024 ] ; + current_options -> makeModelPath ( filepath, filename ) ; + + loader_fd = fopen ( filepath, "rb" ); + + if ( loader_fd == NULL ) { + ulSetError ( UL_WARNING, "ssgLoadMD2: Failed to open '%s' for reading", filepath ) ; + return NULL ; + } + + + read_header(); + read_skins(header.offsetSkins); + read_uvcoords(header.offsetTexCoords); + read_triangles(header.offsetTriangles); + read_frames(header.offsetFrames); + read_glcommands(header.offsetGlCommands); + + + ssgEntity * model = convert_to_ssg(); + + //clean up the memory used by the MD2 structure; + delete[] uvs; + delete[] frames; + delete[] triangles; + for(int i=0; i +// +// Revision: $Id: ssgLoadMDL.cxx 2012 2005-01-15 20:36:46Z sjbaker $ +// +// Description: +// +//=========================================================================== +// Copyright (c) 2000 Thomas E. Sevaldrud +//=========================================================================== + +#include "ssgLocal.h" + +// kludge: global +int g_noLoDs =1; + +#ifdef SSG_LOAD_MDL_SUPPORTED + +#include "ssgLoadMDL.h" + +#define DEF_SHININESS 50 + +//#define DEBUG + +#ifdef DEBUG +#include +#define DEBUGPRINT(x) std::cerr << x +#else +#define DEBUGPRINT(x) +#endif + +static ssgLoaderOptions *current_options; + +// Temporary vertex arrays +static ssgVertexArray *curr_vtx_; +static ssgNormalArray *curr_norm_; +static ssgIndexArray *curr_index_; + +// Vertex arrays +static ssgVertexArray *vertex_array_; +static ssgNormalArray *normal_array_; +static ssgTexCoordArray *tex_coords_; + +// Current part (index array) +static ssgLeaf *curr_part_; +static ssgBranch *model_; + +// Moving parts +static ssgBranch *ailerons_grp_, *elevator_grp_, *rudder_grp_; +static ssgBranch *gear_grp_, *spoilers_grp_, *flaps_grp_; +static ssgBranch *prop_grp_; + +static sgMat4 curr_matrix_; +static sgVec3 curr_rot_pt_; +static sgVec4 curr_col_; +static char *curr_tex_name_; +static ssgAxisTransform *curr_xfm_; + +int noGT=0, noLT=0, no0=0; + +#define EXPERIMENTAL_CULL_FACE_CODE +#ifdef EXPERIMENTAL_CULL_FACE_CODE +static bool curr_cull_face_; +#endif + +// File Address Stack +static const int MAX_STACK_DEPTH = 128; // wk: 32 is too small +static long stack_ [MAX_STACK_DEPTH]; // adress part +static short lod_ [MAX_STACK_DEPTH]; // lod part of the stack +static int stack_depth_; +static short noLoDs; +static short curr_lod; + +// static sgMat4 matrix_stack_ [MAX_STACK_DEPTH]; +// static char *textures_ [MAX_STACK_DEPTH]; +// static ssgBranch *groups_ [MAX_STACK_DEPTH]; + +static int start_idx_, last_idx_; +static int curr_var_; + +static bool has_normals_, vtx_dirty_, tex_vtx_dirty_; +//static bool join_children_, override_normals_; + +//static char *tex_fmt_; + +//=========================================================================== + +/*static void initLoader() +{ + start_idx_ = 0; + join_children_ = true; + override_normals_ = true; + tex_fmt_ = "tif"; + stack_depth_ = 0; +#ifdef EXPERIMENTAL_CULL_FACE_CODE + curr_cull_face_ = false ; +#endif +}*/ + +#ifdef DEBUG +FILE *wkfp; + +#define PRINT_STRUCTURE(a, b) fprintf(wkfp, a, b); +#define PRINT_STRUCTURE1(a ) fprintf(wkfp, a); + +#else + +#define PRINT_STRUCTURE(a, b) +#define PRINT_STRUCTURE1(a ) + +#endif + +//=========================================================================== + +static void newPart() +{ + vtx_dirty_ = true; + tex_vtx_dirty_ = true; + curr_tex_name_ = NULL; + sgSetVec4( curr_col_, 1.0f, 1.0f, 1.0f, 1.0f ); + + delete curr_vtx_; + delete curr_norm_; + curr_vtx_ = new ssgVertexArray ; + curr_norm_ = new ssgNormalArray ; +} + +//=========================================================================== + +static void push_stack( long entry, short lod ) { + assert( stack_depth_ < MAX_STACK_DEPTH - 1 ); + + lod_ [stack_depth_] = lod; + stack_[stack_depth_++] = entry; +} + +static long pop_stack(short &lod) { + assert( stack_depth_ > 0 ); + + lod = lod_[--stack_depth_]; + return stack_[stack_depth_]; +} + +//=========================================================================== + +static void recalcNormals() { + DEBUGPRINT( "Calculating normals." << std::endl); + sgVec3 n; + + for (int i = 0; i < curr_index_->getNum() - 2; i++) { + unsigned short ix0 = *curr_index_->get(i ); + unsigned short ix1 = *curr_index_->get(i + 1); + unsigned short ix2 = *curr_index_->get(i + 2); + + sgMakeNormal( n, + vertex_array_->get(ix0), + vertex_array_->get(ix1), + vertex_array_->get(ix2) ); + + sgCopyVec3( normal_array_->get(ix0), n ); + sgCopyVec3( normal_array_->get(ix1), n ); + sgCopyVec3( normal_array_->get(ix2), n ); + } +} + +//=========================================================================== + +static void readPoint(FILE* fp, sgVec3 p) +{ + short x_int, y_int, z_int; + y_int = ulEndianReadLittle16(fp); + z_int = ulEndianReadLittle16(fp); + x_int = ulEndianReadLittle16(fp); + + // Convert from .MDL units (ca 2mm) to meters + p[0] = -x_int/512.0f; + p[1] = y_int/512.0f; + p[2] = z_int/512.0f; +} + +//=========================================================================== + +// MtkPoint3D readPoint(FILE* fp) +// { +// short x_int, y_int, z_int; +// fread(&x_int, 2, 1, fp); +// fread(&y_int, 2, 1, fp); +// fread(&z_int, 2, 1, fp); + +// // Convert from .MDL units (ca 2mm) to meters +// MtkPoint3D p; +// p.x() = (double)x_int/512.0; +// p.y() = (double)y_int/512.0; +// p.z() = (double)z_int/512.0; + +// /* +// for(list::iterator i = matrix_stack_.begin(); +// i != matrix_stack_.end(); i++) +// { +// p = p*(*i); +// p += (*i).getCol(3); +// } +// */ + +// if(matrix_stack_.size() > 0) +// { +// MtkTransMatrix3D m = matrix_stack_.front(); +// p -= m.getCol(3); +// p = p*m; +// } + +// MtkPoint3D r; +// r.y() = p.x(); +// r.z() = -p.y(); +// r.x() = p.z(); + +// return r; +// } + +//=========================================================================== + +static void readVector(FILE* fp, sgVec3 v) +{ + short x_int, y_int, z_int; + y_int = ulEndianReadLittle16(fp); + z_int = ulEndianReadLittle16(fp); + x_int = ulEndianReadLittle16(fp); + + v[0] = -(float)x_int; + v[1] = (float)y_int; + v[2] = (float)z_int; + + sgNormaliseVec3( v ); +} + +//=========================================================================== +// for new lists, especially vertex list: + +struct oneVertex { + sgVec3 p, n; // point position, normal + sgVec2 tc; // texture coords +}; + +struct oneVertex *TheVertexList; // array. Kludge: only one allowed + +struct oneTexture { + char fname[64]; +}; + +struct oneTexture *TheTextureList; // array. Kludge: only one allowed +//=========================================================================== + +static void createTriangIndices(ssgIndexArray *ixarr, + int numverts, const sgVec3 s_norm, long dist) +{ + sgVec3 v1, v2, cross; + + if ( numverts > ixarr->getNum() ) { + ulSetError( UL_WARNING, "ssgLoadMDL: Index array with too few entries." ); + return; + } + + // triangulate polygons + if(numverts == 1) + { + unsigned short ix0 = *ixarr->get(0); + if ( ix0 >= vertex_array_->getNum() ) { + ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds (%d/%d).", + ix0, vertex_array_->getNum() ); + return; + } + + curr_index_->add(ix0); + curr_index_->add(ix0); + curr_index_->add(ix0); + } + + else if(numverts == 2) + { + unsigned short ix0 = *ixarr->get(0); + unsigned short ix1 = *ixarr->get(1); + if ( ix0 >= vertex_array_->getNum() || + ix1 >= vertex_array_->getNum() ) { + ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. (%d,%d / %d", + ix0, ix1, vertex_array_->getNum() ); + return; + } + + curr_index_->add(ix0); + curr_index_->add(ix1); + curr_index_->add(ix0); + } + + else if(numverts == 3) + { + unsigned short ix0 = *ixarr->get(0); + unsigned short ix1 = *ixarr->get(1); + unsigned short ix2 = *ixarr->get(2); + if ( ix0 >= vertex_array_->getNum() || + ix1 >= vertex_array_->getNum() || + ix2 >= vertex_array_->getNum() ) { + ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. " \ + "(%d,%d,%d / %d)", ix0, ix1, ix2, vertex_array_->getNum()); + return; + } + + sgSubVec3(v1, + vertex_array_->get(ix1), + vertex_array_->get(ix0)); + sgSubVec3(v2, + vertex_array_->get(ix2), + vertex_array_->get(ix0)); + + sgVectorProductVec3(cross, v1, v2); + + if(sgScalarProductVec3(cross, s_norm) >= 0.0f) + { + curr_index_->add(ix0); + curr_index_->add(ix1); + curr_index_->add(ix2); + } + else + { + curr_index_->add(ix0); + curr_index_->add(ix2); + curr_index_->add(ix1); + } + } + + else + { + unsigned short ix0 = *ixarr->get(0); + unsigned short ix1 = *ixarr->get(1); + unsigned short ix2 = *ixarr->get(2); + if ( ix0 >= vertex_array_->getNum() || + ix1 >= vertex_array_->getNum() || + ix2 >= vertex_array_->getNum() ) { + ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. " \ + "(%d,%d,%d / %d)", ix0, ix1, ix2, vertex_array_->getNum()); + return; + } + + // Ensure counter-clockwise ordering + sgMakeNormal(cross, + vertex_array_->get(ix0), + vertex_array_->get(ix1), + vertex_array_->get(ix2)); + bool flip = (sgScalarProductVec3(cross, s_norm) < 0.0); + + curr_index_->add(ix0); + char tsA[99999]; + int bWrong=FALSE; + // a lot of debug output follows - sorry! + sprintf(tsA, "------- %ld %f\n%f, %f, %f\n", + dist, ((float)dist)/(512.0f*32767.0f), s_norm[0], s_norm[1], s_norm[2]); + for(int i = 1; i < numverts; i++) + { + ix1 = *ixarr->get( flip ? numverts-i : i); +// + SGfloat f = sgScalarProductVec3(s_norm, vertex_array_->get(ix1)); + sprintf(tsA, "%s%f, ", tsA, f); + f = f - ((float)dist)/(512.0f*32767.0f); + if((f<-0.5f) || (f>0.5f)) + bWrong = TRUE; +// + if ( ix1 >= vertex_array_->getNum() ) { + ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. (%d/%d)", + ix1, vertex_array_->getNum()); + continue; + } + + curr_index_->add(ix1); + } + if (bWrong) + printf("%s\n-------\n", tsA); + + } +} + +//=========================================================================== + +static bool readTexIndices(FILE *fp, int numverts, const sgVec3 s_norm, bool flip_y, long dist) +{ + ssgIndexArray temp_index_; + + if(numverts <= 0) + return false; + + if(tex_coords_->getNum() < + vertex_array_->getNum()) + { + sgVec2 dummy_pt; + sgSetVec2(dummy_pt, FLT_MAX, FLT_MAX); + for(int i = tex_coords_->getNum(); + i < vertex_array_->getNum(); i++) + tex_coords_->add(dummy_pt); + } + + // Read index values and texture coordinates + for(int v = 0; v < numverts; v++) + { + unsigned short ix; + short tx_int, ty_int; + + ix = ulEndianReadLittle16(fp); + tx_int = ulEndianReadLittle16(fp); + ty_int = ulEndianReadLittle16(fp); + + if (flip_y) { + ty_int = 255 - ty_int; + } + + int tex_idx = ix - start_idx_ + last_idx_; + + sgVec2 tc; + sgSetVec2(tc, tx_int/255.0f, ty_int/255.0f); + + sgVec2 curr_tc; + + if ( tex_idx >= 0 && tex_idx < tex_coords_->getNum() ) { + sgCopyVec2(curr_tc, tex_coords_->get(tex_idx)); + } else { + ulSetError( UL_WARNING, "ssgLoadMDL: Texture coord out of range (%d).", + tex_idx ); + continue; + } + + double dist = sgDistanceVec2(curr_tc, tc); + + if((curr_tc[0] >= FLT_MAX - 1 && curr_tc[1] >= FLT_MAX - 1)) + { + //DEBUGPRINT( "." ); + sgCopyVec2(tex_coords_->get(tex_idx), tc); + } + + else if(dist > 0.0001) + { + // We have a different texture coordinate for an existing vertex, + // so we have to copy this vertex and create a new index for it + // to get the correct texture mapping. + + //DEBUGPRINT( "duplicating texture coordinate!\n"); + + int idx = ix - start_idx_ + last_idx_; + tex_idx = vertex_array_->getNum(); + + ssgVertexArray* vtx_arr = vertex_array_; //curr_vtx_; + ssgNormalArray* norm_arr = normal_array_; //curr_norm_; + + sgVec3 vtx, nrm; + sgCopyVec3( vtx, vtx_arr ->get(idx) ); + sgCopyVec3( nrm, norm_arr->get(idx) ); + vtx_arr ->add(vtx); + norm_arr->add(nrm); + + tex_coords_->add(tc); + } + + temp_index_.add(tex_idx); + +#ifdef DEBUG + int check_index = *temp_index_.get(v); + float *check_tc = tex_coords_->get(check_index); + DEBUGPRINT( "ix[" << v << "] = " << check_index << + " (u=" << check_tc[0] << ", v=" << + check_tc[1] << ")" << std::endl); +#endif + + } + + createTriangIndices(&temp_index_, numverts, s_norm, dist); + + return true; +} + +//=========================================================================== + +static bool readIndices(FILE* fp, int numverts, const sgVec3 s_norm, long dist) +{ + ssgIndexArray temp_index_; + + if(numverts <= 0) + return false; + + // Read index values + for(int v = 0; v < numverts; v++) + { + unsigned short ix; + ix = ulEndianReadLittle16(fp); + temp_index_.add(ix - start_idx_ + last_idx_); + DEBUGPRINT( "ix[" << v << "] = " << *temp_index_.get(v) << std::endl); + } + + createTriangIndices(&temp_index_, numverts, s_norm, dist); + + return true; +} + +//=========================================================================== + +static void setColor(int color, int pal_id) +{ + if(pal_id == 0x68) + { + curr_col_[0] = fsAltPalette[color].r / 255.0f; + curr_col_[1] = fsAltPalette[color].g / 255.0f; + curr_col_[2] = fsAltPalette[color].b / 255.0f; + curr_col_[3] = 0.2f; + } + else + { + curr_col_[0] = fsAcPalette[color].r / 255.0f; + curr_col_[1] = fsAcPalette[color].g / 255.0f; + curr_col_[2] = fsAcPalette[color].b / 255.0f; + curr_col_[3] = 1.0f; + } +} + +//=========================================================================== + +static void setColor(int r, int g, int b, int attr) +{ + curr_col_[0] = r / 255.0f; + curr_col_[1] = g / 255.0f; + curr_col_[2] = b / 255.0f; + if(attr < 239) + curr_col_[3] = 0.2f; +} + +//=========================================================================== + +static bool setTexture(char* name) +{ + curr_tex_name_ = name; + + return true; +} + +//=========================================================================== + +static ssgSimpleState *createState(bool use_texture) +{ + DEBUGPRINT("new State: col = " << curr_col_[0] << ", " << curr_col_[1] << + ", " << curr_col_[2] << ", " << curr_col_[3]); + if ( curr_tex_name_ == NULL ) + DEBUGPRINT(", tex = " << std::endl); + else + DEBUGPRINT(", tex = " << curr_tex_name_ << std::endl); + + ssgSimpleState *state = new ssgSimpleState(); + + state->setShininess(DEF_SHININESS); + state->setShadeModel(GL_SMOOTH); + + state->enable (GL_LIGHTING); + state->enable (GL_CULL_FACE); + state->disable (GL_COLOR_MATERIAL); + + if(curr_col_[3] < 0.99f) + { + state->setTranslucent(); + state->enable(GL_BLEND); + state->enable(GL_ALPHA_TEST); + } + else + { + state->setOpaque(); + state->disable(GL_BLEND); + state->disable(GL_ALPHA_TEST); + } + + if(curr_tex_name_ != NULL && use_texture) + { + state->setMaterial( GL_AMBIENT, 1.0f, 1.0f, 1.0f, curr_col_[3]); + state->setMaterial( GL_DIFFUSE, 1.0f, 1.0f, 1.0f, curr_col_[3]); + state->enable(GL_TEXTURE_2D); + state->setTexture( current_options -> + createTexture(curr_tex_name_, FALSE, FALSE) ) ; + } + else + { + state->setMaterial( GL_AMBIENT, curr_col_); + state->setMaterial( GL_DIFFUSE, curr_col_); + state->disable(GL_TEXTURE_2D); + } + + state->setMaterial( GL_SPECULAR, 1.0f, 1.0f, 1.0f, curr_col_[3] ); + state->setMaterial( GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f ); + + return state; +} + +static ssgBranch *getCurrGroup() { + //Find the correct parent for the new group + if(curr_xfm_) + return curr_xfm_; + else + { + return model_; + } +} + +//=========================================================================== + +// wk : It's not completely clear to me whether the following two functions can not be merged; Oh well. +// CreateAndAddLeaf1 is called then faces are created and then CreateAndAddLeaf2 is called + + +static void CreateAndAddLeaf1(GLenum ty, ssgTexCoordArray *tex_coords_P, bool use_texture) +{ + curr_index_ = new ssgIndexArray(); + curr_part_ = new ssgVtxArray( ty, vertex_array_, + normal_array_, + tex_coords_P, + NULL, + curr_index_ ); + curr_part_->setState( createState(true) ); + char sName[10]; + sprintf(sName, "lod %d", (int)curr_lod); + curr_part_->setName(sName); +} + +static void CreateAndAddLeaf2() +{ + ssgBranch* grp = getCurrGroup(); + ((ssgVtxArray *)curr_part_)->removeUnusedVertices(); + grp->addKid( current_options -> createLeaf(curr_part_, NULL) ); +} + + + +//=========================================================================== + +static ssgBranch *getMPGroup(int var) +{ + + switch(var) + { + case 0x4c: // Rudder + if(!rudder_grp_) + { + rudder_grp_ = new ssgBranch(); + rudder_grp_->setName("rudder"); + model_->addKid(rudder_grp_); + } + return rudder_grp_; + break; + + case 0x4e: // Elevator + if(!elevator_grp_) + { + elevator_grp_ = new ssgBranch(); + elevator_grp_->setName("elevator"); + model_->addKid(elevator_grp_); + } + return elevator_grp_; + break; + + case 0x6a: // Ailerons + if(!ailerons_grp_) + { + ailerons_grp_ = new ssgBranch(); + ailerons_grp_->setName("ailerons"); + model_->addKid(ailerons_grp_); + } + return ailerons_grp_; + break; + + case 0x6c: // Flaps + if(!flaps_grp_) + { + flaps_grp_ = new ssgBranch(); + flaps_grp_->setName("flaps"); + model_->addKid(flaps_grp_); + } + return flaps_grp_; + break; + + case 0x6e: // Gear + if(!gear_grp_) + { + gear_grp_ = new ssgBranch(); + gear_grp_->setName("gear"); + model_->addKid(gear_grp_); + } + return gear_grp_; + break; + + case 0x7c: // Spoilers + if(!spoilers_grp_) + { + spoilers_grp_ = new ssgBranch(); + spoilers_grp_->setName("spoilers"); + model_->addKid(spoilers_grp_); + } + return spoilers_grp_; + break; + + case 0x58: + case 0x7a: // Propeller + if(!prop_grp_) + { + prop_grp_ = new ssgBranch(); + prop_grp_->setName("propeller"); + model_->addKid(prop_grp_); + } + return prop_grp_; + break; + + default: + return model_; + } + return NULL; +} + +//=========================================================================== + +static void getMPLimits(int var, float *min, float *max) +{ + switch(var) + { + case 0x4c: // Rudder + *min = -30.0; + *max = 30.0; + break; + + case 0x4e: // Elevator + *min = -30.0; + *max = 30.0; + break; + + case 0x6a: // Ailerons + *min = -30.0; + *max = 30.0; + break; + + case 0x6c: // Flaps + *min = 0.0; + *max = 70.0; + break; + + case 0x6e: // Gear + *min = 0.0; + *max = -90.0; + break; + + case 0x7c: // Spoilers + *min = 0.0; + *max = 90.0; + break; + + case 0x58: + case 0x7a: // Propeller + *min = 0.0; + *max = 360.0; + break; + } +} + + + +void ParseBGL(FILE *fp) // "traversing" through the file +{ + bool done = false; + while(!feof(fp) && !done) + { + unsigned int skip_offset = 0; + + PRINT_STRUCTURE( "offset %lx\n", (long)ftell(fp)) + unsigned short opcode = ulEndianReadLittle16(fp); + + DEBUGPRINT( "opcode = " << std::hex << opcode << std::dec << " at address" << ftell(fp)-2 // -2 since opcode has already been read + << std::endl ); + + switch(opcode) + { + case 0x23: // BGL_CALL + { + short offset; + offset = ulEndianReadLittle16(fp); + long addr = ftell(fp); + DEBUGPRINT( "BGL_CALL(" << offset << ")\n" ); + push_stack(addr, curr_lod); + long dst = addr + offset - 4; + fseek(fp, dst, SEEK_SET); + + + PRINT_STRUCTURE( "call %lx\n", (long)offset) + } + break; + + case 0x8a: // BGL_CALL32 + { + int offset; + offset = ulEndianReadLittle32(fp); + long addr = ftell(fp); + DEBUGPRINT( "BGL_CALL32(" << offset << ")\n" ); + PRINT_STRUCTURE( "call32 %lx\n", (long)offset) + push_stack(addr, curr_lod); + long dst = addr + offset - 6; + fseek(fp, dst, SEEK_SET); + } + break; + + case 0x0d: // BGL_JUMP + { + short offset; + offset = ulEndianReadLittle16(fp); + PRINT_STRUCTURE( "jump %lx\n", (long)offset) + long addr = ftell(fp); + long dst = addr + offset - 4; + fseek(fp, dst, SEEK_SET); + DEBUGPRINT( "BGL_JUMP(" << offset << ")\n" ); + } + break; + + case 0x88: // BGL_JUMP32 + { + int offset; + offset = ulEndianReadLittle32(fp); + long addr = ftell(fp); + DEBUGPRINT( "BGL_JUMP32(" << offset << ")\n" ); + PRINT_STRUCTURE( "jump32 %lx\n", (long)offset) + long dst = addr + offset - 6; + fseek(fp, dst, SEEK_SET); + } + break; + + case 0x8e: // BGL_VFILE_MARKER + { + short offset; + offset = ulEndianReadLittle16(fp); + DEBUGPRINT( "vars: " << offset << std::endl); + break; + } + + case 0x39: // BGL_IFMSK + { + short offset, var, mask; + offset = ulEndianReadLittle16(fp); + var = ulEndianReadLittle16(fp); + mask = ulEndianReadLittle16(fp); + long addr = ftell(fp); + long dst = addr + offset - 8; + DEBUGPRINT( "BGL_IFMSK(" << offset << ", 0x" << std::hex << var << + ", 0x" << mask << std::dec << ")\n" ); + // if(var & mask == 0) + PRINT_STRUCTURE( "if msk %lx\n", (long)offset) + switch(var) + { + case 0x7e: + fseek(fp, dst, SEEK_SET); + break; + + default: + break; + } + } + break; + + case 0x24: // BGL_IFIN1 + { + short offset, lo, hi; + unsigned short var; + offset = ulEndianReadLittle16(fp); + var = ulEndianReadLittle16(fp); + lo = ulEndianReadLittle16(fp); + hi = ulEndianReadLittle16(fp); + DEBUGPRINT( "BGL_IFIN1(" << offset << ", 0x" << std::hex << var << + ", " << std::dec << lo << ", " << hi << ")\n" ); + PRINT_STRUCTURE( "ifin1 %lx\n", (long)offset) + curr_var_ = var; + } + break; + + case 0x46: // BGL_POINT_VICALL + { + short offset, var_rot_x, var_rot_y, var_rot_z; + unsigned short int_rot_x, int_rot_y, int_rot_z; + offset = ulEndianReadLittle16(fp); + sgVec3 ctr; + readPoint(fp, ctr); + + int_rot_y = ulEndianReadLittle16(fp); + var_rot_y = ulEndianReadLittle16(fp); + + int_rot_x = ulEndianReadLittle16(fp); + var_rot_x = ulEndianReadLittle16(fp); + + int_rot_z = ulEndianReadLittle16(fp); + var_rot_z = ulEndianReadLittle16(fp); + + float rx = 360.0f*(float)int_rot_x/0xffff; + float ry = 360.0f*(float)int_rot_y/0xffff; + float rz = 360.0f*(float)int_rot_z/0xffff; + + // We build a rotation matrix by adding all constant + // rotations (int_rot_*) to current_matrix_. As soon as we reach + // the actual variable rotation, we multiply + // the axis of the variable rotation with our current matrix. + // This will be the axis of rotation in the original coordinate + // system. This can now be inserted into a GngLinearControl + // transform. + if(var_rot_x > 0 || var_rot_y > 0 || var_rot_z > 0) + { + ssgAxisTransform* tmp = NULL; + if(curr_xfm_) + tmp = curr_xfm_; + curr_xfm_ = new ssgAxisTransform(); + curr_xfm_->setCenter(curr_rot_pt_); + + int var = 0; + if(var_rot_x > 0) + var = var_rot_x; + else if(var_rot_y > 0) + var = var_rot_y; + else if(var_rot_z > 0) + var = var_rot_z; + + float min_limit, max_limit; + getMPLimits(var, & min_limit, & max_limit); + + sgVec3 axis = { (float)var_rot_y, (float)var_rot_z, + (float)var_rot_x }; + sgNormaliseVec3( axis ) ; + sgXformVec3( axis, curr_matrix_ ) ; + sgNegateVec3(axis); + curr_xfm_->setAxis(axis); + curr_xfm_->setRotationLimits(min_limit, max_limit); + + char name[256]; + sprintf(name, "ssgAxisRotation(%x)", var); + curr_xfm_->setName(name); + if(tmp) + tmp->addKid(curr_xfm_); + else + { + ssgBranch* grp = getMPGroup(var); + grp->addKid(curr_xfm_); + } + } + + // Build up the constant rotations + sgMat4 rot_mat; + sgMakeRotMat4( rot_mat, ry, rz, rx ); + sgPostMultMat4( curr_matrix_, rot_mat ); + sgAddVec3( curr_rot_pt_, ctr ); + + long addr = ftell(fp); + long dst = addr + offset - 22; + fseek(fp, dst, SEEK_SET); + push_stack(addr, curr_lod); + + break; + } + + case 0x5f: // BGL_IFSIZEV + { + short offset; + unsigned short real_size, pixels_ref; + offset = ulEndianReadLittle16(fp); + real_size = ulEndianReadLittle16(fp); + pixels_ref = ulEndianReadLittle16(fp); + DEBUGPRINT("BGL_IFSIZEV: jmp = " << offset << ", sz = " << + real_size << ", px = " << pixels_ref << std::endl); + PRINT_STRUCTURE( "ifsizev!! %lx\n", (long)offset) + long addr = ftell(fp); + long dst = addr + offset - 8; + fseek(fp, dst, SEEK_SET); + push_stack(addr, ++noLoDs); + break; + } + + case 0x3b: // BGL_VINSTANCE + { + short offset, var; + offset = ulEndianReadLittle16(fp); + var = ulEndianReadLittle16(fp); + long addr = ftell(fp); + long var_abs = addr + var - 6; + fseek(fp, var_abs, SEEK_SET); + float p = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff; + float r = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff; + float h = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff; + sgMat4 rot_mat; + sgMakeRotMat4(rot_mat, h, p, r); + sgPostMultMat4(curr_matrix_, rot_mat); + DEBUGPRINT( "BGL_VINSTANCE(" << offset << ", h=" << h << ", p=" << + p << ", r=" << r << ")\n"); + long dst = addr + offset - 6; + fseek(fp, dst, SEEK_SET); + } + break; + + case 0x0: // EOF + case 0x22: // BGL return + { + curr_xfm_ = NULL; + sgMakeIdentMat4( curr_matrix_ ); + sgZeroVec3( curr_rot_pt_ ); + curr_var_ = 0; + DEBUGPRINT( "BGL return\n\n"); + PRINT_STRUCTURE1("return\n"); + if(stack_depth_ == 0) + done = true; + else + { + long addr = pop_stack(curr_lod); + fseek(fp, addr, SEEK_SET); + } + } + break; + + case 0x1a: // RESLIST (point list with no normals) + { + newPart(); + has_normals_ = false; + + start_idx_ = ulEndianReadLittle16(fp); + unsigned short numpoints = ulEndianReadLittle16(fp); + + DEBUGPRINT( "New group (unlit): start_idx = " << start_idx_ + << ", num vertices = " << numpoints << std::endl); + + sgVec3 null_normal; + sgZeroVec3( null_normal ); + + for(int i = 0; i < numpoints; i++) + { + sgVec3 p; + readPoint(fp, p); + curr_vtx_->add(p); + curr_norm_->add(null_normal); + } + } + break; + + case 0x29: // GORAUD RESLIST (point list with normals) + { + newPart(); + has_normals_ = true; + + start_idx_ = ulEndianReadLittle16(fp); + unsigned short numpoints = ulEndianReadLittle16(fp); + + DEBUGPRINT( "New group (goraud): start_idx = " << start_idx_ + << ", num vertices = " << numpoints << std::endl); + + for(int i = 0; i < numpoints; i++) + { + sgVec3 p; + readPoint(fp, p); + sgVec3 v; + readVector(fp, v); + curr_vtx_->add(p); + curr_norm_->add(v); + } + } + break; + + case 0x0f: // STRRES: Start line definition + { + unsigned short idx = ulEndianReadLittle16(fp); + DEBUGPRINT( "Start line: idx = " << idx << std::endl); + if(vtx_dirty_) + { + last_idx_ = vertex_array_->getNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_ ->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + vtx_dirty_ = false; + } + CreateAndAddLeaf1(GL_LINES, NULL, false); +#ifdef EXPERIMENTAL_CULL_FACE_CODE + curr_part_->setCullFace ( curr_cull_face_ ) ; +#endif + + curr_index_->add(idx - start_idx_ + last_idx_); + ssgBranch *grp = getCurrGroup(); + grp->addKid(curr_part_); + + //assert(curr_part_->getState()->getTexture() == NULL); + } + break; + + case 0x10: // CNTRES: Continue line definition + { + unsigned short idx = ulEndianReadLittle16(fp); + DEBUGPRINT( "Cont. line: idx = " << idx << std::endl); + curr_index_->add(idx - start_idx_ + last_idx_); + } + break; + + case 0x20: + case 0x7a: // Goraud shaded Texture-mapped ABCD Facet + { + if(tex_vtx_dirty_) + { + last_idx_ = vertex_array_->getNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_ ->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + tex_vtx_dirty_ = false; + } + CreateAndAddLeaf1(GL_TRIANGLE_FAN, tex_coords_, true); + + //assert(curr_part_->getState()->getTexture() != NULL); + + unsigned short numverts = ulEndianReadLittle16(fp); + DEBUGPRINT( "New part: (goraud/texture), num indices = " << + numverts << std::endl); + + // Normal vector + sgVec3 v; + readVector(fp, v); + + // Dot product reference +#ifdef EXPERIMENTAL_CULL_FACE_CODE + long l = ulEndianReadLittle32(fp) ; + if(l>0) + noGT++; + else if (l<0) + noLT++; + if((v[0]==0) && (v[1]==0) && (v[2]==0)) + no0++; + //rr_part_->setCullFace ( curr_cull_face_ ) ; + +#else + ulEndianReadLittle32(fp); +#endif + // Read vertex indices and texture coordinates + bool flip_y = FALSE; + if(curr_tex_name_!=NULL) + { char *texture_extension = + curr_tex_name_ + strlen(curr_tex_name_) - 3; + flip_y = ulStrEqual( texture_extension, "BMP" ) != 0 ; + } + /*old: + char *texture_extension = + curr_tex_name_ + strlen(curr_tex_name_) - 3; + bool flip_y = ulStrEqual( texture_extension, "BMP" ); + */ + readTexIndices(fp, numverts, v, flip_y, l); // adds stuff to curr_index_ + + if(!has_normals_) + { + while (normal_array_->getNum() < vertex_array_->getNum()) + normal_array_->add(v); + recalcNormals(); + } + CreateAndAddLeaf2(); + } + break; + + case 0x60: // BGL_FACE_TMAP + { + if(tex_vtx_dirty_) + { + last_idx_ = vertex_array_->getNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_ ->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + tex_vtx_dirty_ = false; + } + + CreateAndAddLeaf1(GL_TRIANGLE_FAN, tex_coords_, true); + +#ifdef EXPERIMENTAL_CULL_FACE_CODE + curr_part_->setCullFace ( curr_cull_face_ ) ; +#endif + //assert(curr_part_->getState()->getTexture() != NULL); + + unsigned short numverts = ulEndianReadLittle16(fp); + DEBUGPRINT( "New part: (goraud/texture), num indices = " << + numverts << std::endl); + + // Point in polygon + sgVec3 p; + readPoint(fp, p); + + // Normal vector + sgVec3 v; + readVector(fp, v); + + // Read vertex inidices and texture coordinates + bool flip_y = FALSE; + if(curr_tex_name_!=NULL) + { char *texture_extension = + curr_tex_name_ + strlen(curr_tex_name_) - 3; + flip_y = ulStrEqual( texture_extension, "BMP" ) != 0 ; + } + /* + char *texture_extension = + curr_tex_name_ + strlen(curr_tex_name_) - 3; + bool flip_y = ulStrEqual( texture_extension, "BMP" ); + */ + readTexIndices(fp, numverts, v, flip_y, -11); + + if(!has_normals_) + { + while (normal_array_->getNum() < vertex_array_->getNum()) + normal_array_->add(v); + recalcNormals(); + } + + CreateAndAddLeaf2(); + } + break; + + case 0x1d: // BGL_FACE + { + if(vtx_dirty_) + { + last_idx_ = vertex_array_->getNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + vtx_dirty_ = false; + } + + CreateAndAddLeaf1(GL_TRIANGLE_FAN, NULL, false); + +#ifdef EXPERIMENTAL_CULL_FACE_CODE + curr_part_->setCullFace ( curr_cull_face_ ) ; +#endif + //assert(curr_part_->getState()->getTexture() == NULL); + + unsigned short numverts = ulEndianReadLittle16(fp); + DEBUGPRINT( "BGL_FACE: num indices = " << numverts << std::endl); + + sgVec3 p; + readPoint(fp, p); + // Surface normal + sgVec3 v; + readVector(fp, v); + + // Read vertex indices + readIndices(fp, numverts, v, -11); + + if(!has_normals_) + { + while (normal_array_->getNum() < vertex_array_->getNum()) + normal_array_->add(v); + recalcNormals(); + } + + CreateAndAddLeaf2(); + } + break; + + case 0x3e: // FACETN (no texture) + case 0x2a: // Goraud shaded ABCD Facet + { + if(vtx_dirty_) + { + last_idx_ = vertex_array_->getNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + vtx_dirty_ = false; + } + + CreateAndAddLeaf1(GL_TRIANGLE_FAN, NULL, false); + + //assert(curr_part_->getState()->getTexture() == NULL); + + unsigned short numverts = ulEndianReadLittle16(fp); + DEBUGPRINT( "BGL_FACETN: num indices = " << numverts << std::endl); + + // Surface normal + sgVec3 v; + readVector(fp, v); + + // dot-ref +#ifdef EXPERIMENTAL_CULL_FACE_CODE + long l = ulEndianReadLittle32(fp) ; + if(l>0) + noGT++; + else if (l<0) + noLT++; + if((v[0]==0) && (v[1]==0) && (v[2]==0)) + no0++; + //rr_part_->setCullFace ( curr_cull_face_ ) ; + +#else + ulEndianReadLittle32(fp); +#endif + // Read vertex indices + readIndices(fp, numverts, v, l); + + if(!has_normals_) + { + while (normal_array_->getNum() < vertex_array_->getNum()) + normal_array_->add(v); + recalcNormals(); + } + + CreateAndAddLeaf2(); + } + break; + + case 0x18: // Set texture + { + unsigned short id, dx, scale, dy; + id = ulEndianReadLittle16(fp); + dx = ulEndianReadLittle16(fp); + scale = ulEndianReadLittle16(fp); + dy = ulEndianReadLittle16(fp); + char tex_name[14]; + fread(tex_name, 1, 14, fp); + static char tex_filename[14]; + int j = 0; + for(int i = 0; i < 14; i++) + { + if(!isspace(tex_name[i])) + tex_filename[j++] = tolower(tex_name[i]); + } + tex_filename[j] = '\0'; + DEBUGPRINT( "Set texture: name = " << tex_filename << ", id = " << id + << ", dx = " << dx << ", dy = " << dy << ", scale = " << + scale << std::endl); + setTexture(tex_filename); + } + break; + + case 0x43: // TEXTURE2 + { + unsigned short length, idx; + unsigned char flags, chksum; + unsigned int color; + static char tex_filename[128]; + length = ulEndianReadLittle16(fp); + idx = ulEndianReadLittle16(fp); + fread(&flags, 1, 1, fp); + fread(&chksum, 1, 1, fp); + color = ulEndianReadLittle32(fp); + if(chksum != 0) + { + DEBUGPRINT( "warning: TEXTURE2 Checksum != 0\n"); + } + + char c; + int i = 0; + while((c = getc(fp)) != 0) + { + if(!isspace(c)) + tex_filename[i++] = tolower(c); + } + tex_filename[i] = '\0'; + + // Padding byte + if((strlen(tex_filename) + 1) % 2) + c = getc(fp); + + DEBUGPRINT( "TEXTURE2: Set texture: name = " << tex_filename << + std::endl); + setTexture(tex_filename); + break; + } + + case 0x50: // GCOLOR (Goraud shaded color) + case 0x51: // LCOLOR (Line color) + case 0x52: // SCOLOR (Light source shaded surface color) + { + unsigned char color, param; + fread(&color, 1, 1, fp); + fread(¶m, 1, 1, fp); + DEBUGPRINT( "Set color = " << (int)color << " (" << std::hex << + (int)param << std::dec << ")\n"); + setColor((int)color, (int)param); + } + break; + + case 0x2D: // BGL_SCOLOR24 + { + unsigned char col[4]; + fread(col, 1, 4, fp); + DEBUGPRINT( "color = " << (int)col[0] << ", " << (int)col[2] << + ", " << (int)col[3] << ", " << (int)col[1] << std::endl); + setColor(col[0], col[2], col[3], col[1]); + break; + } + + + //------------------------------------------- + // The next code is either ignored + // or for experimental use.. + //------------------------------------------- + + case 0x03: + { + //DEBUGPRINT( "BGL_CASE\n" ); + unsigned short number_cases = ulEndianReadLittle16(fp); + skip_offset = 6 + 2 * number_cases; + PRINT_STRUCTURE1("case :-( !!\n"); + } + break; + + case 0xB0: //BGLOP_CRASH_OCTTREE + { +/* from scdis code by Takuya Murakami: + + 2 Jump Offset + 2 Crash Type (See CRASH_FLAG_xxx in makemdl.exe) + 2 # of nodes + 4*3 box x/y/z + 4*3 box w/h/d + 1*8 ? + 1*8*(# of nodes) ? +*/ + + ulEndianReadLittle32(fp); // ignore jump offset / crash type + int count = ulEndianReadLittle16(fp); + //fseek(fp, 4*3+4*3+1*8+1*8*count, SEEK_CUR); + fseek(fp, 5*8+8*count, SEEK_CUR); // completely experimental + } + break; + + case 0xB2: //light + { +/*from scdis code by Takuya Murakami: + Format: + 2 type + 4*3 offset (x,y,z, real4) + 4 intensity + 4 linear attenuation factor (real4) + 4 squared attenuation factor (real4) + 4 color (BGRA : strange byte order...) + 4*3 direction(x,y,z real4) +*/ + fseek(fp, 2+4*10, SEEK_CUR); + } + break; + + case 0xB3: //ifinf(1) + { + // long offset = ulEndianReadLittle32(fp); + // unsigned short v = ulEndianReadLittle16(fp); + // float low = ulEndianReadLittleFloat(fp); + // float high = ulEndianReadLittleFloat(fp); + ulEndianReadLittle32(fp); + ulEndianReadLittle16(fp); + ulEndianReadLittleFloat(fp); + ulEndianReadLittleFloat(fp); + + } + break; + //------------------------------------------- + // The next codes are CFS2 specific + //------------------------------------------- + + case 0xB5: //, VertexList, anaVertexList, 0 ) // VERTEX_LIST_BEGIN + { + + newPart(); + int count = ulEndianReadLittle16(fp); + ulEndianReadLittle32(fp); // dummy +#ifdef UL_WIN32 + if(TheVertexList) + ::MessageBox(0, "More than one vertexlist", "Warning:", 0); +#endif + TheVertexList = new oneVertex[count]; // kludge: TheVertexList is unused?? +// 4*8 x,y,z,nx,ny,nz,tu,tv (real4) + + for (int i = 0; i < count; i++) + { + TheVertexList[i].p[0] = ulEndianReadLittleFloat(fp); // / 1E6; + TheVertexList[i].p[1] = ulEndianReadLittleFloat(fp); // / 1E6; + TheVertexList[i].p[2] = ulEndianReadLittleFloat(fp); // / 1E6; + TheVertexList[i].n[0] = ulEndianReadLittleFloat(fp); + TheVertexList[i].n[1] = ulEndianReadLittleFloat(fp); + TheVertexList[i].n[2] = ulEndianReadLittleFloat(fp); + TheVertexList[i].tc[0]= ulEndianReadLittleFloat(fp); + TheVertexList[i].tc[1]= ulEndianReadLittleFloat(fp); + + curr_vtx_->add(TheVertexList[i].p); + curr_norm_->add(TheVertexList[i].n); + tex_coords_->add(TheVertexList[i].tc); + + } + } + break; + + case 0xB6: //, MaterialList, anaMaterialList, 0 ) // MATERIAL_LIST_BEGIN + { + + int count = ulEndianReadLittle16(fp); + ulEndianReadLittle32(fp); // dummy + fseek(fp, count*4*17, SEEK_CUR); + } + + break; + + case 0xB7: //, TextureList, anaTextureList, 0 ) // TEXTURE_LIST_BEGIN/TEXTURE_DEF/TEXTURE_LIST_END + { + + int count = ulEndianReadLittle16(fp); + ulEndianReadLittle32(fp); // dummy +#ifdef UL_WIN32 + if(TheTextureList) + ::MessageBox(0, "More than one texturelist", "Warning:", 0); +#endif + TheTextureList = new oneTexture[count]; // kludge: TheVertexList is unused?? + for(int i=0;igetNum(); + for(int i = 0; i < curr_vtx_->getNum(); i++) + { + vertex_array_->add(curr_vtx_ ->get(i)); + normal_array_->add(curr_norm_->get(i)); + } + tex_vtx_dirty_ = false; + } + + + + ssgVertexArray *vertex_array_bu = (ssgVertexArray *)vertex_array_->clone(); + + for(int i=0;igetNum();i++) + sgXformPnt3(vertex_array_->get(i), curr_matrix_); + + CreateAndAddLeaf1(GL_TRIANGLES, tex_coords_, true); + //delete vertex_array_; kludge!! + vertex_array_ = vertex_array_bu; // to undo the transformation + + //assert(curr_part_->getState()->getTexture() != NULL); + + int base = ulEndianReadLittle16(fp); + // int vertexcount = ulEndianReadLittle16(fp); + ulEndianReadLittle16(fp); + int wkcount = ulEndianReadLittle16(fp); + wkcount = wkcount / 3; // tri + assert(wkcount>0); + + DEBUGPRINT( "New part: (DrawTriList), num tris = " << wkcount << std::endl); + if(tex_coords_->getNum() != vertex_array_->getNum()) + { + printf("tex_coords_->getNum() = %d, vertex_array_->getNum() = %d\n", tex_coords_->getNum(), vertex_array_->getNum()); + assert(FALSE); + } + assert(tex_coords_->getNum() == normal_array_->getNum()); + for(int j=0;j=tex_coords_->getNum()) + { + printf("i1+base = %d, i1 = %d, base = %d, tex_coords_->getNum() = %d\n", i1+base, i1, base, tex_coords_->getNum()); + assert(FALSE); + } + assert(i2+basegetNum()); + assert(i3+basegetNum()); + + curr_index_->add(i1+base); + curr_index_->add(i2+base); + curr_index_->add(i3+base); + } + CreateAndAddLeaf2(); + } + break; + + case 0xBA: //, DrawLineList, anaDrawLineList, 0 ) + { + // int base = ulEndianReadLittle16(fp); + // int dummycount = ulEndianReadLittle16(fp); + ulEndianReadLittle16(fp); + ulEndianReadLittle16(fp); + + int wkcount = ulEndianReadLittle16(fp); + wkcount = wkcount / 2; + fseek(fp, 2*2*wkcount, SEEK_CUR); + } + break; + + case 0xBB: //, DrawPointList, anaDrawPointList, 0 ) + { + // int base = ulEndianReadLittle16(fp); + // int dummycount = ulEndianReadLittle16(fp); + ulEndianReadLittle16(fp); + ulEndianReadLittle16(fp); + + int wkcount = ulEndianReadLittle16(fp); + wkcount = wkcount / 1; + fseek(fp, 2*1*wkcount, SEEK_CUR); + } + break; + + case 0xBC: // BGL_BEGIN / BGLVersion + { + // long v = ulEndianReadLittle32(fp); + ulEndianReadLittle32(fp); + + PRINT_STRUCTURE("BGLVersion %lx\n", v) + } + break; + case 0xB8: // SetMaterial + { + ulEndianReadLittle16(fp); // word + short TheTextureIndex = ulEndianReadLittle16(fp); // word + if (TheTextureIndex != -1) + { + char *s = TheTextureList[TheTextureIndex].fname; + int j = 0; + static char tex_filename[64]; + + for(int i = 0; i < 64; i++) + { + if(!isspace(s[i])) + tex_filename[j++] = tolower(s[i]); + } + tex_filename[j] = '\0'; + //DEBUGPRINT( "Set texture: name = " << tex_filename << std::endl); + setTexture(tex_filename); + } + else + curr_tex_name_ = NULL; + } + break; + case 0xB4: // TextureSize + ulEndianReadLittle32(fp); // float + break; + case 0xBD: // BGL_END / EndVersion + break; + case 0xAE: // BGL_TRANSFORM_END + sgMakeIdentMat4( curr_matrix_ ); + + break; + case 0xAF: // BGL_TRANSFORM_MATRIX + { + sgMat4 this_mat; + this_mat[3][0] = ulEndianReadLittleFloat(fp); + this_mat[3][1] = ulEndianReadLittleFloat(fp); + this_mat[3][2] = ulEndianReadLittleFloat(fp); + this_mat[0][0] = ulEndianReadLittleFloat(fp); + this_mat[0][1] = ulEndianReadLittleFloat(fp); + this_mat[0][2] = ulEndianReadLittleFloat(fp); + this_mat[1][0] = ulEndianReadLittleFloat(fp); + this_mat[1][1] = ulEndianReadLittleFloat(fp); + this_mat[1][2] = ulEndianReadLittleFloat(fp); + this_mat[2][0] = ulEndianReadLittleFloat(fp); + this_mat[2][1] = ulEndianReadLittleFloat(fp); + this_mat[2][2] = ulEndianReadLittleFloat(fp); + DEBUGPRINT( "***** Matrix: " << std::endl << "x, y, z = " << + this_mat[3][0] << ", " << this_mat[3][1] << ", " << this_mat[3][2] << std::endl << "3 x 3 matrix:" << std::endl << + this_mat[0][0] << ", " << this_mat[0][1] << ", " << this_mat[0][2] << std::endl << + this_mat[1][0] << ", " << this_mat[1][1] << ", " << this_mat[1][2] << std::endl << + this_mat[2][0] << ", " << this_mat[2][1] << ", " << this_mat[2][2] << std::endl); + this_mat[0][3] = SG_ZERO ; + this_mat[1][3] = SG_ZERO ; + this_mat[2][3] = SG_ZERO ; + this_mat[3][3] = SG_ONE ; + sgPostMultMat4( curr_matrix_, this_mat); + + } + break; + + default: // Unknown opcode + { + if (opcode < 256) + { + if ( opcodes[opcode].size != -1) + { + DEBUGPRINT( "** " << opcodes[opcode].name << " (size " << + opcodes[opcode].size << ")" << std::endl ); + skip_offset = opcodes[opcode].size - 2; // opcode already read + } + else + { + DEBUGPRINT( "Unhandled opcode " << opcodes[opcode].name + << " (" << std::hex << opcode << std::dec << ")" << + std::endl ); + } + } + else + { + DEBUGPRINT( "Op-code out of range: " << std::hex << opcode << + std::dec << std::endl ); + } + } // default + break; + } // switch + + if (skip_offset > 0) + fseek( fp, skip_offset, SEEK_CUR ); + + } // while !feof... +} +//=========================================================================== + +#define MYMAKEFOURCC(a, b, c, d) \ + ((unsigned long)(a) | ((unsigned long)(b) << 8) | \ + ((unsigned long)(c) << 16) | ((unsigned long)(d) << 24 )) + +static unsigned long lRIFF = MYMAKEFOURCC('R', 'I', 'F', 'F'); +static unsigned long lMDL8 = MYMAKEFOURCC('M', 'D', 'L', '8'); + +void FindBGLBeginRIFF(FILE *fp) +// place file cursor on the first BGL command. +// if none found, places it on the file end +// This function is for RIFF format used in MSFS2k2 and 2k4 and CFS2 (and other MS sims?) +{ + unsigned int l; + while ((lRIFF != (l = ulEndianReadLittle32(fp))) && (!feof(fp))) + ; + if (lRIFF != l) // RIFF not found + { + assert(feof(fp)); + return; + } + ulEndianReadLittle32(fp); // ignore file length + l = ulEndianReadLittle32(fp); + if (l != MYMAKEFOURCC('M', 'D', 'L', '8')) + printf("Warning: Not a 'MDL8' RIFF file\n"); + else + printf("RIFF file, subtype 'MDL8' recognised\n"); + while(!feof(fp)) + { + char buffer[5]; + buffer[4] = 0; + fread(buffer, 4, 1, fp); + unsigned long offset = ulEndianReadLittle32(fp); + if (offset & 1L) + offset++; // if offset is odd, add one pad byte + printf("RIFF Chunk '%s' found, data length = %ld\n", buffer, offset); + if (0==strcmp(buffer, "BGL ")) + { + // Great!! + return; + } + fseek(fp, offset, SEEK_CUR); + } +} + +void FindBGLBeginOldVersion(FILE *fp) +// place file cursor on the first BGL command. +// if none found, places it on the file end +// This function is for old MDL files (for FS98, for example) +{ + unsigned short op1, op2; + op1 = ulEndianReadLittle16(fp); + + while(!feof(fp)) + { + op2 = ulEndianReadLittle16(fp); + if(op1 == 0x76 && op2 == 0x3a) + { + fseek(fp, -4, SEEK_CUR); + break; + } + op1 = op2; + } +} + + + +ssgEntity *ssgLoadMDL(const char *fname, const ssgLoaderOptions *options) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + ailerons_grp_ = NULL; + elevator_grp_ = NULL; + rudder_grp_ = NULL; + gear_grp_ = NULL; + spoilers_grp_ = NULL; + flaps_grp_ = NULL; + prop_grp_ = NULL; + TheVertexList = NULL; + TheTextureList = NULL; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE *fp = fopen(filename, "rb"); + if(!fp) + { + ulSetError( UL_WARNING, "ssgLoadMDL: Couldn't open MDL file '%s'!", + filename ); + return NULL; + } + + // Find beginning of BGL Code segment + unsigned long l = ulEndianReadLittle32(fp); + fseek(fp, 0, SEEK_SET); + if (l == lRIFF) // This is somewhat of a kludge, since ther "RIFF" is mostly at the beginning of the file, but not always. + FindBGLBeginRIFF(fp); + else + { FindBGLBeginOldVersion(fp); + if(feof(fp)) + { // Ok - so it is not "RIFF" at the beginning of the file and it is not an old file format - + // so search for RIFF anywhere + // I know of at least one file (the Wellesly V1.4, where the V1.4 is important) + // that had two "RIFF"s and ionyl the second one is the one we want. + // So we have to search for a place with "RIFF" + fseek(fp, 0, SEEK_SET); + l = ulEndianReadLittle32(fp); + while(!feof(fp)) + { + unsigned char c = fgetc(fp); + l = (l >> 8) | (c << 24); + if ( l == lRIFF ) + { // check whether it is a red herring... + ulEndianReadLittle32(fp); // ignore length + unsigned long ll = ulEndianReadLittle32(fp); + if (ll == lMDL8) + { // found it !! + fseek(fp, -12, SEEK_CUR); + unsigned long addr = ftell(fp); + if(addr&1L) + printf("strange... found RIFF, but on an odd adress %lx\n", addr); + else + printf("found a good RIFF header at address %lx\n", addr); + FindBGLBeginRIFF(fp); + break; // breaks the while(!feof(fp)) + } + } + } + } + } + + if(feof(fp)) + { + ulSetError( UL_WARNING, "ssgLoadMDL: No BGL Code found in file '%s'!", + filename ); + fclose(fp); + return NULL; + } + // end find begin + + + // Initialize object graph + model_ = new ssgBranch(); + char* model_name = new char[128]; + char *ptr = (char*)&fname[strlen(fname) - 1]; + while(ptr != &fname[0] && *ptr != '/') ptr--; + if(*ptr == '/') ptr++; + strcpy(model_name, ptr); + ptr = &model_name[strlen(model_name)]; + while(*ptr != '.' && ptr != &model_name[0]) ptr--; + *ptr = '\0'; + model_->setName(model_name); + + // Create group nodes for textured and non-textured objects + curr_vtx_ = new ssgVertexArray(); + curr_norm_ = new ssgNormalArray(); + + vertex_array_ = new ssgVertexArray(); + normal_array_ = new ssgNormalArray(); + + tex_coords_ = new ssgTexCoordArray(); + + start_idx_ = 0; + last_idx_ = 0; + curr_var_ = 0; + stack_depth_ = 0; + noLoDs = 1; // if there is no "branch" and no "++noLoDs" is called, we have 1 LoD. + // If there is one branch, we have 2, etc. + curr_lod = 0; + sgMakeIdentMat4(curr_matrix_); + + // Parse opcodes +#ifdef DEBUG +#ifdef _MSC_VER + wkfp=fopen("c:\\mdl_file_structure.txt", "wt"); +#else + wkfp=fopen("mdl_file_structure.txt", "wt"); +#endif +#endif + + ParseBGL(fp); // "traversing" through the file + + + fclose(fp); +#ifdef DEBUG + fclose(wkfp); +#endif +// :-((( delete curr_vtx_; + delete curr_norm_; + + DEBUGPRINT("\n" << vertex_array_->getNum() << " vertices\n"); + printf("NoLoDs = %d\n", (int)noLoDs); + printf("noGT=%d, noLT=%d, no0=%d\n", noGT, noLT, no0); + g_noLoDs = noLoDs; + + return model_; +} + +#else + +bool ssgLoadMDLTexture ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - MDL support not configured", fname ) ; + return false ; +} + + +#endif + diff --git a/src/ssg/ssgLoadMDL.h b/src/ssg/ssgLoadMDL.h new file mode 100644 index 0000000..b623f00 --- /dev/null +++ b/src/ssg/ssgLoadMDL.h @@ -0,0 +1,504 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadMDL.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +//=========================================================================== +// +// File: fsPalettes.h +// +// Created: Fri Oct 1 15:34:29 1999 +// +// Author: Thomas Engh Sevaldrud +// +// Revision: $Id: ssgLoadMDL.h 1568 2002-09-02 06:05:49Z sjbaker $ +// +// Description: +// +//=========================================================================== +// Copyright (c) 1999 Thomas E. Sevaldrud +//=========================================================================== + +#ifndef _SSGLOADMDL_H +#define _SSGLOADMDL_H + + + +//=========================================================================== +// Aircraft color palette. The aircraft in MSFS has +// a different palette than the default fs5.pal file. +// Entries with (?) means that I am not sure of the +// values. Entries with (L), means that the color is +// emissive (bright at night). + +struct acColor +{ + unsigned char r, g, b, emissive; +}; + +static const acColor fsAcPalette[53] = { + { 0, 0, 0 , 0 }, // Black + { 89, 89, 89 , 0 }, // Dark gray + { 169, 169, 169 , 0 }, // Gray + { 211, 211, 211 , 0 }, // Light gray + { 255, 255, 255 , 0 }, // White + { 255, 0, 0 , 0 }, // Red + { 0, 255, 0 , 0 }, // Green + { 0, 0, 255 , 0 }, // Blue + { 255, 165, 0 , 0 }, // Orange + { 255, 255, 0 , 0 }, // Yellow + { 165, 42, 42 , 0 }, // Brown + { 210, 180, 140 , 0 }, // Tan + { 255, 69, 0 , 0 }, // Rust (?) + { 107, 142, 35 , 0 }, // Olive (?) + { 0, 87, 112 , 0 }, // Water + { 255, 0, 0 , 1 }, // Red (L) + { 0, 255, 0 , 1 }, // Green (L) + { 0, 0, 255 , 1 }, // Blue (L) + { 57, 57, 112 , 1 }, // Aqua (L) (?) + { 255, 165, 0 , 1 }, // Orange (L) + { 255, 255, 0 , 1 }, // Yellow (L) + { 255, 255, 255 , 1 }, // White (L) + { 255, 255, 255 , 1 }, // White (L) + { 139, 0, 0 , 0 }, // Dark red + { 0, 100, 0 , 0 }, // Dark green + { 0, 0, 139 , 0 }, // Dark blue + { 205, 120, 0 , 0 }, // Dark orange + { 139, 100, 0 , 0 }, // Dark yellow (?) + { 82, 21, 21 , 0 }, // Dark brown (?) + { 105, 90, 70 , 0 }, // Dark tan + { 128, 35, 0 , 0 }, // Dark rust (?) + { 53, 71, 17 , 0 }, // Dark olive (?) + { 205, 0, 0 , 0 }, // Medium red + { 0, 179, 0 , 0 }, // Medium green + { 0, 0, 205 , 0 }, // Medium blue + { 255, 140, 0 , 0 }, // Medium orange (?) + { 205, 179, 0 , 0 }, // Medium yellow (?) + { 120, 32, 32 , 0 }, // Medium brown (?) + { 155, 135, 105 , 0 }, // Medium tan (?) + { 186, 52, 0 , 0 }, // Medium rust (?) + { 80, 106, 25 , 0 }, // Medium olive (?) + { 238, 173, 173 , 0 }, // Light red (?) + { 144, 238, 144 , 0 }, // Light green + { 173, 216, 230 , 0 }, // Light Blue + { 255, 162, 68 , 0 }, // Light Orange (?) + { 255, 255, 224 , 0 }, // Light Yellow + { 255, 130, 58 , 0 }, // Light Brown (?) + { 255, 220, 180 , 0 }, // Light Tan (?) + { 255, 137, 0 , 0 }, // Light rust (?) + { 157, 200, 54 , 0 }, // Light olive (?) + { 89, 89, 89 , 1 }, // Dark gray (?) (L) + { 169, 169, 169 , 1 }, // Gray (L) + { 211, 211, 211 , 1 } // Light Gray (L) +}; + +//=========================================================================== +// Alternative palette for transparent colors (?) +// (same as in fs5.pal?) + +static const acColor fsAltPalette[113] = { + { 0, 0, 0}, + { 8, 8, 8}, + { 16, 16, 16}, + { 24, 24, 24}, + { 32, 32, 32}, + { 40, 40, 40}, + { 48, 48, 48}, + { 56, 56, 56}, + { 65, 65, 65}, + { 73, 73, 73}, + { 81, 81, 81}, + { 89, 89, 89}, + { 97, 97, 97}, + { 105, 105, 105}, + { 113, 113, 113}, + { 121, 121, 121}, + { 130, 130, 130}, + { 138, 138, 138}, + { 146, 146, 146}, + { 154, 154, 154}, + { 162, 162, 162}, + { 170, 170, 170}, + { 178, 178, 178}, + { 186, 186, 186}, + { 195, 195, 195}, + { 203, 203, 203}, + { 211, 211, 211}, + { 219, 219, 219}, + { 227, 227, 227}, + { 235, 235, 235}, + { 247, 247, 247}, + { 255, 255, 255}, + { 21, 5, 5}, + { 42, 10, 10}, + { 63, 15, 15}, + { 84, 20, 20}, + { 105, 25, 25}, + { 126, 30, 30}, + { 147, 35, 35}, + { 168, 40, 40}, + { 189, 45, 45}, + { 210, 50, 50}, + { 231, 55, 55}, + { 252, 60, 60}, + { 5, 21, 5}, + { 10, 42, 10}, + { 15, 63, 15}, + { 20, 84, 20}, + { 25, 105, 25}, + { 30, 126, 30}, + { 35, 147, 35}, + { 40, 168, 40}, + { 45, 189, 45}, + { 50, 210, 50}, + { 55, 231, 55}, + { 60, 252, 60}, + { 0, 7, 23}, + { 0, 15, 40}, + { 0, 23, 58}, + { 0, 40, 84}, + { 0, 64, 104}, + { 0, 71, 122}, + { 0, 87, 143}, + { 0, 99, 156}, + { 0, 112, 179}, + { 0, 128, 199}, + { 0, 143, 215}, + { 0, 153, 230}, + { 28, 14, 0}, + { 56, 28, 0}, + { 84, 42, 0}, + { 112, 56, 0}, + { 140, 70, 0}, + { 168, 84, 0}, + { 196, 98, 0}, + { 224, 112, 0}, + { 252, 126, 0}, + { 28, 28, 0}, + { 56, 56, 0}, + { 84, 84, 0}, + { 112, 112, 0}, + { 140, 140, 0}, + { 168, 168, 0}, + { 196, 196, 0}, + { 224, 224, 0}, + { 252, 252, 0}, + { 25, 21, 16}, + { 50, 42, 32}, + { 75, 63, 48}, + { 100, 84, 64}, + { 125, 105, 80}, + { 150, 126, 96}, + { 175, 147, 112}, + { 200, 168, 128}, + { 225, 189, 144}, + { 28, 11, 7}, + { 56, 22, 14}, + { 84, 33, 21}, + { 112, 44, 28}, + { 140, 55, 35}, + { 168, 66, 42}, + { 196, 77, 49}, + { 224, 88, 56}, + { 252, 99, 63}, + { 17, 22, 9}, + { 34, 44, 18}, + { 51, 66, 27}, + { 68, 88, 36}, + { 85, 110, 45}, + { 102, 132, 54}, + { 119, 154, 63}, + { 136, 176, 72}, + { 153, 198, 81} +}; + +struct _ssgBGLOpCode { + unsigned short opcode; + const char *name; + int size; /* size includes opcode (2 bytes) + -1 indicates that special treatment is needed + to find the size */ + +}; + +static const _ssgBGLOpCode opcodes[] = { + { 0x00, "BGL_EOF" , 2 }, + { 0x01, "OBSOLETE 0x01" , 2 }, + { 0x02, "BGL_NOOP" , 2 }, + { 0x03, "BGL_CASE" , -1 }, + { 0x04, "RESERVED 0x04" , 2 }, + { 0x05, "BGL_SURFACE - Area" , 2 }, + { 0x06, "BGL_SPNT" , 8 }, + { 0x07, "BGL_CPNT" , 8 }, + { 0x08, "BLG_CLOSURE" , 2 }, + { 0x09, "OBSOLETE 0x09" , 2 }, + { 0x0a, "OBSOLETE 0x0a" , 10 }, + { 0x0b, "OBSOLETE 0x0b" , 10 }, + { 0x0c, "OBSOLETE 0x0c" , 2 }, + { 0x0d, "BGL_JUMP" , 4 }, + { 0x0e, "BGL_DEFRES" , 8 }, + { 0x0f, "BGL_STRRES" , 4 }, + { 0x10, "BGL_CNTRES" , 4 }, + { 0x11, "OBSOLETE 0x11" , 10 }, + { 0x12, "OBSOLETE 0x12" , 4 }, + { 0x13, "OBSOLETE 0x13" , 4 }, + { 0x14, "BGL_SCOLOR" , 4 }, + { 0x15, "BGL_ELEVATION_MAP" , -1 }, + { 0x16, "RESERVED 0x16" , -1 }, + { 0x17, "BGL_TEXTURE_ENABLE" , 4 }, + { 0x18, "BGL_TEXTURE" , 24 }, + { 0x19, "BGL_PALETTE" , 16 }, + { 0x1a, "BGL_RESLIST" , -1 }, + { 0x1b, "BGL_IFIN_BOX_RAW_PLANE" , 22 }, + { 0x1c, "BGL_IFIN2" , 16 }, + { 0x1d, "BGL_FACE" , -1 }, + { 0x1e, "BGL_HAZE" , 4 }, + { 0x1f, "RESERVED 0x1f" , 4 }, + { 0x20, "BGL_FACET_TMAP" , -1 }, + { 0x21, "BGL_IFIN3" , 22 }, + { 0x22, "BGL_RETURN - Return" , 2 }, + { 0x23, "BGL_CALL" , 4 }, + { 0x24, "BGL_IFIN1" , 10 }, + { 0x25, "BGL_SEPARATION_PLANE" , 14 }, + { 0x26, "BGL_SETWRD" , 6 }, + { 0x27, "OBSOLETE 0x27" , -1 }, + { 0x28, "OBSOLETE 0x28" , 10 }, + { 0x29, "BGL_GRESLIST" , -1 }, + { 0x2a, "BGL_GFACET" , -1 }, + { 0x2b, "BGL_ADDOBJ32" , 6 }, + { 0x2c, "BGL_REJECT" , 12 }, + { 0x2d, "BGL_SCOLOR24" , 6 }, + { 0x2e, "BGL_LCOLOR24" , 6 }, + { 0x2f, "BGL_SCALE" , 32 }, + { 0x30, "OBSOLETE 0x30" , 4 }, + { 0x31, "BGL_RESROW" , 18 }, + { 0x32, "BGL_ADDOBJ - PerspectiveCall" , 4 }, + { 0x33, "BGL_INSTANCE - RotatedCall" , 10 }, + { 0x34, "BGL_SUPER_SCALE" , 10 }, + { 0x35, "BGL_PNTROW" , 16 }, + { 0x36, "OBSOLETE 0x36" , -1 }, + { 0x37, "BGL_POINT" , 8 }, + { 0x38, "BGL_CONCAVE" , 2 }, + { 0x39, "BGL_IFMSK" , 8 }, + { 0x3a, "BGL_VPOSITION" , 12 }, + { 0x3b, "BGL_VINSTANCE" , 6 }, + { 0x3c, "BGL_POSITION" , 28 }, + { 0x3d, "BGL_SEED" , 24 }, + { 0x3e, "BGL_FACET" , -1 }, + { 0x3f, "BGL_SHADOW_CALL" , 4 }, + { 0x40, "BGL_SHADOW_VPOSITION" , 12 }, + { 0x41, "BGL_SHADOW_VICALL" , 6 }, + { 0x42, "BGL_POLYGON_RUNWAY" , 40 }, + { 0x43, "BGL_TEXTURE2" , -1 }, + { 0x44, "BGL_TEXTURE_RUNWAY" , 64 }, // 40 + { 0x45, "OBSOLETE 0x45" , 16 }, + { 0x46, "BGL_POINT_VICALL" , 22 }, + { 0x47, "RESERVED" , 4 }, + { 0x48, "BGL_VAR_SEG" , 4 }, + { 0x49, "BGL_BUILDING" , 18 }, + { 0x4a, "OBSOLETE 0x4A" , 22 }, + { 0x4b, "OBSOLETE 0x4B" , -1 }, + { 0x4c, "BGL_VSCALE" , 16 }, + { 0x4d, "BGL_MOVEL2G" , 6 }, + { 0x4e, "BGL_MOVEG2L" , 6 }, + { 0x4f, "BGL_MOVEWORD" , 6 }, + { 0x50, "BGL_GCOLOR" , 4 }, + { 0x51, "BGL_NEW_LCOLOR" , 4 }, + { 0x52, "BGL_NEW_SCOLOR" , 4 }, + { 0x53, "OBSOLETE 0x53" , 4 }, + { 0x54, "OBSOLETE 0x54" , 6 }, + { 0x55, "BGL_SURFACE_TYPE" , 10 }, + { 0x56, "BGL_SET_WEATHER" , 4 }, + { 0x57, "BGL_SET_WEATHER" , 10 }, + { 0x58, "BLG_TEXTURE_BOUNDS" , 10 }, + { 0x59, "OBSOLETE 0x59" , 4 }, + { 0x5a, "OBSOLETE 0x5a" , 4 }, + { 0x5b, "OBSOLETE 0x5b" , 4 }, + { 0x5c, "OBSOLETE 0x5c" , 6 }, + { 0x5d, "BGL_TEXTURE_REPEAT" , 8 }, + { 0x5e, "OBSOLETE 0x5e" , 4 }, + { 0x5f, "BGL_IFSIZEV" , 8 }, + { 0x60, "BGL_FACE_TMAP" , -1 }, + { 0x61, "RESERVED 0x53" , 6 }, + { 0x62, "BGL_IFVIS" , -1 }, + { 0x63, "BGL_LIBRARY_CALL" , 20 }, + { 0x64, "BGL_LIST" , -1 }, + { 0x65, "BGL_VSCOLOR" , 4 }, + { 0x66, "BGL_VGCOLOR" , 4 }, + { 0x67, "BGL_VLCOLOR" , 4 }, + { 0x68, "OBSOLETE" , 8 }, + { 0x69, "BGL_ROAD_START" , 10 }, + { 0x6a, "BGL_ROAD_CONT" , 8 }, + { 0x6b, "BGL_RIVER_START" , 10 }, + { 0x6c, "BGL_RIVER_CONT" , 8 }, + { 0x6d, "BGL_IFSIZEH" , 8 }, + { 0x6e, "BGL_TAXIWAY_START" , 10 }, + { 0x6f, "BGL_TAXIWAY_CONT" , 8 }, + { 0x70, "BGL_AREA_SENSE" , -1 }, + { 0x71, "BGL_ALTITUDE_SET" , 4 }, + { 0x72, "OBSOLETE 0x72" , 20 }, + { 0x73, "BGL_IFINBOXP" , 16 }, + { 0x74, "BGL_ADDCAT" , 6 }, + { 0x75, "BGL_ADDMNT" , 4 }, + { 0x76, "BGL_BGL - Perspective" , 2 }, + { 0x77, "BGL_SCALE_AGL - RefPoint" , 32 }, + { 0x78, "BGL_ROAD_CONTW" , 10 }, + { 0x79, "BGL_RIVER_CONTW" , 10 }, + { 0x7a, "BGL_GFACET_TMAP" , -1 }, + { 0x7b, "OBSOLETE 0x7b" , -1 }, + { 0x7c, "BGL_SELECT" , 10 }, + { 0x7d, "BGL_PERSPECTIVE" , 2 }, + { 0x7e, "BGL_SETWRD_GLOBAL" , 6 }, + { 0x7f, "OBSOLETE 0x7f" , 2 }, + { 0x80, "BGL_RESPNT" , 4 }, + { 0x81, "OBSOLETE 0x81" , 4 }, + { 0x82, "OBSOLETE 0x82" , 28 }, + { 0x83, "BGL_RESCALE" , 32 }, + { 0x84, "OBSOLETE 0x84" , 8 }, + { 0x85, "OBSOLETE 0x85" , 2 }, + { 0x86, "OBSOLETE 0x86" , 3 }, + { 0x87, "BGL_FIXED_COLORS" , 4 }, + { 0x88, "BGL_JUMP32" , 6 }, + { 0x89, "BGL_VAR_BASE32" , 6 }, + { 0x8a, "BGL_CALL32" , 6 }, + { 0x8b, "BGL_ADDCAT32" , 8 }, + { 0x8c, "RESERVED 0x8c" , 6 }, + { 0x8d, "RESERVED 0x8d" , 6 }, + { 0x8e, "BGL_VFILE_MARKER" , 4 }, + { 0x8f, "BGL_ALPHA" , 6 }, + { 0x90, "RESERVED 0x90" , -1 }, + { 0x91, "BGL_TEXT" , 12 }, + { 0x92, "OBSOLETE 0x92" , 4 }, + { 0x93, "RESERVED 0x93" , 4 }, + { 0x94, "BGL_CRASH" , 12 }, + { 0x95, "BGL_CRASH_INDIRECT" , 10 }, + { 0x96, "BGL_CRASH_START" , 6 }, + { 0x97, "BGL_CRASH_SPHERE" , 6 }, + { 0x98, "BGL_CRASH_BOX" , 22 }, + { 0x99, "BGL_SET_CRASH" , 4 }, + { 0x9a, "OBSOLETE 0x9a" , -1 }, + { 0x9b, "RESERVED 0x9b" , 8 }, + { 0x9c, "RESERVED 0x9c" , 6 }, + { 0x9d, "RESERVED 0x9d" , 14 }, + { 0x9e, "BGL_INTERPOLATE" , 20 }, + { 0x9f, "BGL_OVERRIDE" , 6 }, + { 0xa0, "BGL_OBJECT" , -1 }, + { 0xa1, "OBSOLETE 0xA1" , 10 }, + { 0xa2, "OBSOLETE 0xA2" , 8 }, + { 0xa3, "OBSOLETE 0xA3" , -1 }, + { 0xa4, "BGL_VALPHA" , 4 }, + { 0xa5, "OBSOLETE 0xA5" , 4 }, + { 0xa6, "OBSOLETE 0xA6" , 8 }, + { 0xa7, "BGL_SPRITE_VCALL" , 22 }, + { 0xa8, "BGL_TEXTURE_ROAD_START" , 12}, + { 0xa9, "BGL_IFIN_INSTANCE_BOX_PLANE" , 22}, + { 0xaa, "BGL_NEW_RUNWAY" , -1 }, + { 0xab, "RESERVED 0xAB" , 38 }, + { 0xac, "BGL_ZBIAS" , 4 }, + { 0xad, "BGL_ANIMATE" , 30 }, + { 0xae, "BGL_TRANSFORM_END" , 2 }, + { 0xaf, "BGL_TRANSFORM_MATRIX" , 50 }, + { 0xb0, "UNKNOWN 0xB0" , -1 }, + { 0xb1, "UNKNOWN 0xB1" , -1 }, + { 0xb2, "BGL_LIGHT" , 44 }, + { 0xb3, "BGL_IFINF1" , 14 }, + { 0xb4, "UNKNOWN 0xB4" , -1 }, + { 0xb5, "UNKNOWN 0xB5" , -1 }, + { 0xb6, "UNKNOWN 0xB6" , -1 }, + { 0xb7, "UNKNOWN 0xB7" , -1 }, + { 0xb8, "UNKNOWN 0xB8" , -1 }, + { 0xb9, "UNKNOWN 0xB9" , -1 }, + { 0xba, "UNKNOWN 0xBA" , -1 }, + { 0xbb, "UNKNOWN 0xBB" , -1 }, + { 0xbc, "UNKNOWN 0xBC" , -1 }, + { 0xbd, "UNKNOWN 0xBD" , -1 }, + { 0xbe, "UNKNOWN 0xBE" , -1 }, + { 0xbf, "UNKNOWN 0xBF" , -1 }, + { 0xc0, "UNKNOWN 0xC0" , -1 }, + { 0xc1, "UNKNOWN 0xC1" , -1 }, + { 0xc2, "UNKNOWN 0xC2" , -1 }, + { 0xc3, "UNKNOWN 0xC3" , -1 }, + { 0xc4, "UNKNOWN 0xC4" , -1 }, + { 0xc5, "UNKNOWN 0xC5" , -1 }, + { 0xc6, "UNKNOWN 0xC6" , -1 }, + { 0xc7, "UNKNOWN 0xC7" , -1 }, + { 0xc8, "UNKNOWN 0xC8" , -1 }, + { 0xc9, "UNKNOWN 0xC9" , -1 }, + { 0xca, "UNKNOWN 0xCA" , -1 }, + { 0xcb, "UNKNOWN 0xCB" , -1 }, + { 0xcc, "UNKNOWN 0xCC" , -1 }, + { 0xcd, "UNKNOWN 0xCD" , -1 }, + { 0xce, "UNKNOWN 0xCE" , -1 }, + { 0xcf, "UNKNOWN 0xCF" , -1 }, + { 0xd0, "UNKNOWN 0xD0" , -1 }, + { 0xd1, "UNKNOWN 0xD1" , -1 }, + { 0xd2, "UNKNOWN 0xD2" , -1 }, + { 0xd3, "UNKNOWN 0xD3" , -1 }, + { 0xd4, "UNKNOWN 0xD4" , -1 }, + { 0xd5, "UNKNOWN 0xD5" , -1 }, + { 0xd6, "UNKNOWN 0xD6" , -1 }, + { 0xd7, "UNKNOWN 0xD7" , -1 }, + { 0xd8, "UNKNOWN 0xD8" , -1 }, + { 0xd9, "UNKNOWN 0xD9" , -1 }, + { 0xda, "UNKNOWN 0xDA" , -1 }, + { 0xdb, "UNKNOWN 0xDB" , -1 }, + { 0xdc, "UNKNOWN 0xDC" , -1 }, + { 0xdd, "UNKNOWN 0xDD" , -1 }, + { 0xde, "UNKNOWN 0xDE" , -1 }, + { 0xdf, "UNKNOWN 0xDF" , -1 }, + { 0xe0, "UNKNOWN 0xE0" , -1 }, + { 0xe1, "UNKNOWN 0xE1" , -1 }, + { 0xe2, "UNKNOWN 0xE2" , -1 }, + { 0xe3, "UNKNOWN 0xE3" , -1 }, + { 0xe4, "UNKNOWN 0xE4" , -1 }, + { 0xe5, "UNKNOWN 0xE5" , -1 }, + { 0xe6, "UNKNOWN 0xE6" , -1 }, + { 0xe7, "UNKNOWN 0xE7" , -1 }, + { 0xe8, "UNKNOWN 0xE8" , -1 }, + { 0xe9, "UNKNOWN 0xE9" , -1 }, + { 0xea, "UNKNOWN 0xEA" , -1 }, + { 0xeb, "UNKNOWN 0xEB" , -1 }, + { 0xec, "UNKNOWN 0xEC" , -1 }, + { 0xed, "UNKNOWN 0xED" , -1 }, + { 0xee, "UNKNOWN 0xEE" , -1 }, + { 0xef, "UNKNOWN 0xEF" , -1 }, + { 0xf0, "UNKNOWN 0xF0" , -1 }, + { 0xf1, "UNKNOWN 0xF1" , -1 }, + { 0xf2, "UNKNOWN 0xF2" , -1 }, + { 0xf3, "UNKNOWN 0xF3" , -1 }, + { 0xf4, "UNKNOWN 0xF4" , -1 }, + { 0xf5, "UNKNOWN 0xF5" , -1 }, + { 0xf6, "UNKNOWN 0xF6" , -1 }, + { 0xf7, "UNKNOWN 0xF7" , -1 }, + { 0xf8, "UNKNOWN 0xF8" , -1 }, + { 0xf9, "UNKNOWN 0xF9" , -1 }, + { 0xfa, "UNKNOWN 0xFA" , -1 }, + { 0xfb, "UNKNOWN 0xFB" , -1 }, + { 0xfc, "UNKNOWN 0xFC" , -1 }, + { 0xfd, "UNKNOWN 0xFD" , -1 }, + { 0xfe, "UNKNOWN 0xFE" , -1 }, + { 0xff, "UNKNOWN 0xFF" , -1 } +}; + +//=========================================================================== + +#endif // _SSGLOADMDL_H + diff --git a/src/ssg/ssgLoadMDL_BGLTexture.cxx b/src/ssg/ssgLoadMDL_BGLTexture.cxx new file mode 100644 index 0000000..eddd6f5 --- /dev/null +++ b/src/ssg/ssgLoadMDL_BGLTexture.cxx @@ -0,0 +1,384 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadMDL_BGLTexture.cxx 2117 2007-09-13 23:21:09Z fayjf $ +*/ + +//=========================================================================== +// +// File: ssgLoadMDL_BGLTexture.cxx +// +// Created: Jun 1 22:20:31 2002 +// +// Author: Thomas Engh Sevaldrud +// Juergen Marquardt +// +// Revision: $Id: ssgLoadMDL_BGLTexture.cxx 2117 2007-09-13 23:21:09Z fayjf $ +// +// Description: +// +// Changes: by Juergen Marquardt +// - separated from ssgLoadMDL +// it's now used by ssgLoadMDL and ssgLoadBGL +// - added support for translucatant textures +// - rindex replaced by strrchr +// +//=========================================================================== + +#include "ssgLocal.h" +#include "ssgMSFSPalette.h" + +//=========================================================================== +// Texture palette values (from fs5.pal) +// Alpha channel added for more efficient +// rendering on 3dfx hardware. + +unsigned const char ssgFsTexPalette[1024] = { + 0x00, 0x00, 0x00, 0xff, + 0x08, 0x08, 0x08, 0xff, + 0x10, 0x10, 0x10, 0xff, + 0x18, 0x18, 0x18, 0xff, + 0x20, 0x20, 0x20, 0xff, + 0x28, 0x28, 0x28, 0xff, + 0x30, 0x30, 0x30, 0xff, + 0x38, 0x38, 0x38, 0xff, + 0x41, 0x41, 0x41, 0xff, + 0x49, 0x49, 0x49, 0xff, + 0x51, 0x51, 0x51, 0xff, + 0x59, 0x59, 0x59, 0xff, + 0x61, 0x61, 0x61, 0xff, + 0x69, 0x69, 0x69, 0xff, + 0x71, 0x71, 0x71, 0xff, + 0x79, 0x79, 0x79, 0xff, + 0x82, 0x82, 0x82, 0xff, + 0x8a, 0x8a, 0x8a, 0xff, + 0x92, 0x92, 0x92, 0xff, + 0x9a, 0x9a, 0x9a, 0xff, + 0xa2, 0xa2, 0xa2, 0xff, + 0xaa, 0xaa, 0xaa, 0xff, + 0xb2, 0xb2, 0xb2, 0xff, + 0xba, 0xba, 0xba, 0xff, + 0xc3, 0xc3, 0xc3, 0xff, + 0xcb, 0xcb, 0xcb, 0xff, + 0xd3, 0xd3, 0xd3, 0xff, + 0xdb, 0xdb, 0xdb, 0xff, + 0xe3, 0xe3, 0xe3, 0xff, + 0xeb, 0xeb, 0xeb, 0xff, + 0xf7, 0xf7, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x15, 0x05, 0x05, 0xff, + 0x2a, 0x0a, 0x0a, 0xff, + 0x3f, 0x0f, 0x0f, 0xff, + 0x54, 0x14, 0x14, 0xff, + 0x69, 0x19, 0x19, 0xff, + 0x7e, 0x1e, 0x1e, 0xff, + 0x93, 0x23, 0x23, 0xff, + 0xa8, 0x28, 0x28, 0xff, + 0xbd, 0x2d, 0x2d, 0xff, + 0xd2, 0x32, 0x32, 0xff, + 0xe7, 0x37, 0x37, 0xff, + 0xfc, 0x3c, 0x3c, 0xff, + 0x05, 0x15, 0x05, 0xff, + 0x0a, 0x2a, 0x0a, 0xff, + 0x0f, 0x3f, 0x0f, 0xff, + 0x14, 0x54, 0x14, 0xff, + 0x19, 0x69, 0x19, 0xff, + 0x1e, 0x7e, 0x1e, 0xff, + 0x23, 0x93, 0x23, 0xff, + 0x28, 0xa8, 0x28, 0xff, + 0x2d, 0xbd, 0x2d, 0xff, + 0x32, 0xd2, 0x32, 0xff, + 0x37, 0xe7, 0x37, 0xff, + 0x3c, 0xfc, 0x3c, 0xff, + 0x00, 0x07, 0x17, 0xff, + 0x00, 0x0f, 0x28, 0xff, + 0x00, 0x17, 0x3a, 0xff, + 0x00, 0x28, 0x54, 0xff, + 0x00, 0x40, 0x68, 0xff, + 0x00, 0x47, 0x7a, 0xff, + 0x00, 0x57, 0x8f, 0xff, + 0x00, 0x63, 0x9c, 0xff, + 0x00, 0x70, 0xb3, 0xff, + 0x00, 0x80, 0xc7, 0xff, + 0x00, 0x8f, 0xd7, 0xff, + 0x00, 0x99, 0xe6, 0xff, + 0x1c, 0x0e, 0x00, 0xff, + 0x38, 0x1c, 0x00, 0xff, + 0x54, 0x2a, 0x00, 0xff, + 0x70, 0x38, 0x00, 0xff, + 0x8c, 0x46, 0x00, 0xff, + 0xa8, 0x54, 0x00, 0xff, + 0xc4, 0x62, 0x00, 0xff, + 0xe0, 0x70, 0x00, 0xff, + 0xfc, 0x7e, 0x00, 0xff, + 0x1c, 0x1c, 0x00, 0xff, + 0x38, 0x38, 0x00, 0xff, + 0x54, 0x54, 0x00, 0xff, + 0x70, 0x70, 0x00, 0xff, + 0x8c, 0x8c, 0x00, 0xff, + 0xa8, 0xa8, 0x00, 0xff, + 0xc4, 0xc4, 0x00, 0xff, + 0xe0, 0xe0, 0x00, 0xff, + 0xfc, 0xfc, 0x00, 0xff, + 0x19, 0x15, 0x10, 0xff, + 0x32, 0x2a, 0x20, 0xff, + 0x4b, 0x3f, 0x30, 0xff, + 0x64, 0x54, 0x40, 0xff, + 0x7d, 0x69, 0x50, 0xff, + 0x96, 0x7e, 0x60, 0xff, + 0xaf, 0x93, 0x70, 0xff, + 0xc8, 0xa8, 0x80, 0xff, + 0xe1, 0xbd, 0x90, 0xff, + 0x1c, 0x0b, 0x07, 0xff, + 0x38, 0x16, 0x0e, 0xff, + 0x54, 0x21, 0x15, 0xff, + 0x70, 0x2c, 0x1c, 0xff, + 0x8c, 0x37, 0x23, 0xff, + 0xa8, 0x42, 0x2a, 0xff, + 0xc4, 0x4d, 0x31, 0xff, + 0xe0, 0x58, 0x38, 0xff, + 0xfc, 0x63, 0x3f, 0xff, + 0x11, 0x16, 0x09, 0xff, + 0x22, 0x2c, 0x12, 0xff, + 0x33, 0x42, 0x1b, 0xff, + 0x44, 0x58, 0x24, 0xff, + 0x55, 0x6e, 0x2d, 0xff, + 0x66, 0x84, 0x36, 0xff, + 0x77, 0x9a, 0x3f, 0xff, + 0x88, 0xb0, 0x48, 0xff, + 0x99, 0xc6, 0x51, 0xff, + 0x00, 0x3a, 0x68, 0xff, + 0x00, 0x57, 0x70, 0xff, + 0x2b, 0x70, 0x80, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0x40, 0xff, 0x40, 0xff, + 0x00, 0x00, 0xc0, 0xff, + 0x00, 0x69, 0x69, 0xff, + 0xff, 0x80, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, + 0x51, 0x51, 0x51, 0xff, + 0x79, 0x79, 0x79, 0xff, + 0x92, 0x92, 0x92, 0xff, + 0xaa, 0xaa, 0xaa, 0xff, + 0xc3, 0xc3, 0xc3, 0xff, + 0xe3, 0xe3, 0xe3, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0xc0, 0xc0, 0xff, 0xff, + 0xc8, 0xc8, 0xff, 0xff, + 0xd0, 0xd0, 0xff, 0xff, + 0xd8, 0xd8, 0xff, 0xff, + 0xe0, 0xe0, 0xff, 0xff, + 0xe8, 0xe8, 0xff, 0xff, + 0xf0, 0xf0, 0xff, 0xff, + 0xf8, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x10, 0x48, 0x10, 0xff, + 0x20, 0x50, 0x20, 0xff, + 0x30, 0x58, 0x30, 0xff, + 0x40, 0x60, 0x40, 0xff, + 0x50, 0x68, 0x50, 0xff, + 0x60, 0x70, 0x60, 0xff, + 0x70, 0x78, 0x70, 0xff, + 0x78, 0x7c, 0x78, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, + 0x21, 0x8c, 0xbd, 0xff, + 0x39, 0x84, 0xa5, 0xff, + 0xbd, 0x42, 0x42, 0xff, + 0x9c, 0x42, 0x42, 0xff, + 0x84, 0x4a, 0x4a, 0xff, + 0x21, 0x52, 0x6b, 0xff, + 0xd6, 0x5a, 0x52, 0xff, + 0xbd, 0x5a, 0x52, 0xff, + 0xa5, 0x5a, 0x52, 0xff, + 0x7b, 0x39, 0x31, 0xff, + 0x63, 0x39, 0x31, 0xff, + 0x6b, 0x4a, 0x42, 0xff, + 0x7b, 0x5a, 0x52, 0xff, + 0xb5, 0x5a, 0x42, 0xff, + 0x4a, 0x31, 0x29, 0xff, + 0xbd, 0x73, 0x5a, 0xff, + 0x8c, 0x5a, 0x31, 0xff, + 0x21, 0x31, 0x4a, 0xff, + 0xb5, 0x73, 0x31, 0xff, + 0x63, 0x42, 0x21, 0xff, + 0xa5, 0x73, 0x42, 0xff, + 0x31, 0x29, 0x21, 0xff, + 0xa5, 0x8c, 0x73, 0xff, + 0xbd, 0xa5, 0x8c, 0xff, + 0x39, 0x63, 0x7b, 0xff, + 0xb5, 0x6b, 0x18, 0xff, + 0xce, 0x7b, 0x21, 0xff, + 0x9c, 0x63, 0x21, 0xff, + 0x94, 0x6b, 0x31, 0xff, + 0x6b, 0x52, 0x31, 0xff, + 0x21, 0x21, 0x39, 0xff, + 0x21, 0x73, 0xa5, 0xff, + 0xd6, 0xd6, 0x21, 0xff, + 0xad, 0xad, 0x21, 0xff, + 0xc6, 0xc6, 0x29, 0xff, + 0x8c, 0x8c, 0x21, 0xff, + 0x73, 0x73, 0x21, 0xff, + 0xbd, 0xbd, 0x39, 0xff, + 0x9c, 0x9c, 0x31, 0xff, + 0xad, 0xad, 0x39, 0xff, + 0x7b, 0x7b, 0x31, 0xff, + 0x7b, 0x7b, 0x42, 0xff, + 0x4a, 0x4a, 0x31, 0xff, + 0x7b, 0x7b, 0x5a, 0xff, + 0x29, 0x29, 0x21, 0xff, + 0x5a, 0x63, 0x39, 0xff, + 0x6b, 0x73, 0x4a, 0xff, + 0x7b, 0x94, 0x52, 0xff, + 0x8c, 0xad, 0x63, 0xff, + 0x84, 0x9c, 0x63, 0xff, + 0x31, 0x42, 0x29, 0xff, + 0x63, 0xa5, 0x5a, 0xff, + 0x4a, 0xd6, 0x4a, 0xff, + 0x39, 0x8c, 0x39, 0xff, + 0x4a, 0xb5, 0x4a, 0xff, + 0x5a, 0xc6, 0x5a, 0xff, + 0x39, 0x7b, 0x39, 0xff, + 0x31, 0x63, 0x31, 0xff, + 0x5a, 0xa5, 0x5a, 0xff, + 0x52, 0x94, 0x52, 0xff, + 0x4a, 0x63, 0x4a, 0xff, + 0x39, 0x73, 0x84, 0xff, + 0x21, 0x63, 0x7b, 0xff, + 0x4a, 0x73, 0x84, 0xff +}; + +#ifdef SSG_LOAD_MDL_BGL_TEXTURE_SUPPORTED +// This really simple (raw paletted) format is used by older MSFS for textures +bool ssgLoadMDLTexture ( const char *fname, ssgTextureInfo* info ) +{ + FILE *tfile; + int index = 0; + if ( (tfile = fopen(fname, "rb")) == NULL) { + char *dupfname = strdup( fname); + char *p = strrchr( dupfname,'_'); + if (p != 0) { + *p = '\0'; + p++; + index = atoi (p); + if ( (tfile = fopen(dupfname, "rb")) == NULL) { + ulSetError( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", dupfname ); + free( dupfname); + return false ; + } + free( dupfname); + } + else { + ulSetError( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname ); + free( dupfname); + return false ; + } + } + + fseek(tfile, 0, SEEK_END); + unsigned long file_length = ftell(tfile); + + if (file_length != 65536) { + // this is not a MSFS-formatted texture, so it's probably a BMP + fclose(tfile); + return ssgLoadBMP( fname, info ); + } else { + fseek(tfile, 0, SEEK_SET); + + unsigned char *texels = new unsigned char[256 * 256 * 4]; + int c = 0; + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + unsigned char b; + fread(&b, 1, 1, tfile); + texels[c++] = ssgFsTexPalette[b*4 ]; + texels[c++] = ssgFsTexPalette[b*4 + 1]; + texels[c++] = ssgFsTexPalette[b*4 + 2]; + texels[c++] = (b width = 256 ; + info -> height = 256 ; + info -> depth = 4 ; + info -> alpha = TRUE ; //?? + } + + return ssgMakeMipMaps ( texels, 256, 256, 4 ) ; + } +} +#else + +bool ssgLoadMDLTexture ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - MDL/BGL Texture support not configured", fname ) ; + return false ; +} + +#endif diff --git a/src/ssg/ssgLoadOBJ.cxx b/src/ssg/ssgLoadOBJ.cxx new file mode 100644 index 0000000..10848a9 --- /dev/null +++ b/src/ssg/ssgLoadOBJ.cxx @@ -0,0 +1,761 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadOBJ.cxx 2020 2005-03-25 01:04:04Z wolfram_kuss $ +*/ + +// +// Wavefront OBJ import for SSG/PLIB +// Ported from IVCON by Dave McClurg (dpm@efn.org) in March-2000 +// Updated by Dave McClurg in April-2000 (added textures) +// +/* + Purpose: + + OBJ_READ reads a Wavefront OBJ file. + + Example: + + # magnolia.obj + + mtllib ./vp.mtl + + g + v -3.269770 -39.572201 0.876128 + v -3.263720 -39.507999 2.160890 + ... + v 0.000000 -9.988540 0.000000 + g stem + s 1 + usemtl brownskn + f 8 9 11 10 + f 12 13 15 14 + ... + f 788 806 774 + + Modified: + + 20 October 1998 + + Author: + + John Burkardt +*/ + +#include "ssgLocal.h" + +#define MAX_LINE_LEN 1024 +#define MAX_LINE 100000 +#define MAX_FACE 100000 +#define MAX_VERT 100000 +#define MAX_MATERIALS 1000 + + +struct faceData +{ + int vlist [3] ; + int tlist [3] ; + int nlist [3] ; + int mat_index ; +} ; + + +struct matData +{ + char* name ; + sgVec4 amb ; + sgVec4 diff ; + sgVec4 spec ; + float shine ; + float trans ; + char* tfname ; +} ; + + +static int num_line ; +static int* line_dex ; + +static int num_face ; +static faceData* face ; + +static int num_vert ; +static int num_vert_tex ; +static int num_vert_normal ; +static sgVec3* vert ; +static sgVec3* vert_tex ; +static sgVec3* vert_normal ; + +static int num_mat ; +static matData* materials ; + +static ssgBranch *current_branch = NULL ; + +static ssgState* get_state( matData* mat ) +{ + if (mat->tfname != NULL) + { + ssgState *st = ssgGetCurrentOptions () -> createState ( mat->tfname ) ; + if ( st != NULL ) + return st ; + } + + ssgSimpleState *st = new ssgSimpleState () ; + + st -> setMaterial ( GL_AMBIENT, mat -> amb ) ; + st -> setMaterial ( GL_DIFFUSE, mat -> diff ) ; + st -> setMaterial ( GL_SPECULAR, mat -> spec ) ; + st -> setShininess ( mat -> shine ) ; + + st -> enable ( GL_COLOR_MATERIAL ) ; + st -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + st -> enable ( GL_LIGHTING ) ; + st -> setShadeModel ( GL_SMOOTH ) ; + + if ( mat -> trans < 0.99f ) + { + st -> disable ( GL_ALPHA_TEST ) ; + st -> enable ( GL_BLEND ) ; + st -> setTranslucent () ; + } + else + { + st -> disable ( GL_BLEND ) ; + st -> setOpaque () ; + } + + if (mat->tfname != NULL) + { + st -> setTexture( ssgGetCurrentOptions () -> createTexture(mat->tfname) ) ; + st -> enable( GL_TEXTURE_2D ) ; + } + else + { + st -> disable( GL_TEXTURE_2D ) ; + } + + return st ; +} + +static int leqi ( const char* string1, const char* string2 ) +//LEQI compares two strings for equality, disregarding case. +{ + int i; + int nchar; + int nchar1; + int nchar2; + + nchar1 = strlen ( string1 ); + nchar2 = strlen ( string2 ); + + if ( nchar1 < nchar2 ) { + nchar = nchar1; + } + else { + nchar = nchar2; + } +/* + The strings are not equal if they differ over their common length. +*/ + for ( i = 0; i < nchar; i++ ) { + + if ( toupper ( string1[i] ) != toupper ( string2[i] ) ) { + return FALSE; + } + } +/* + The strings are not equal if the longer one includes nonblanks + in the tail. +*/ + if ( nchar1 > nchar ) { + for ( i = nchar; i < nchar1; i++ ) { + if ( string1[i] != ' ' ) { + return FALSE; + } + } + } + else if ( nchar2 > nchar ) { + for ( i = nchar; i < nchar2; i++ ) { + if ( string2[i] != ' ' ) { + return FALSE; + } + } + } + return TRUE; +} + +static char filename [ 1024 ] ; + + +static void load_materials ( const char* fname ) +{ + num_mat = 0 ; + + char path [ 1024 ] ; + ssgGetCurrentOptions () -> makeModelPath ( path, fname ) ; + + FILE* filein = fopen (path,"r") ; + if ( filein == 0 ) + { + char mtl_filename[ 1024 ]; + strcpy(mtl_filename, filename); + if(NULL!=strrchr(mtl_filename, '/')) + *strrchr(mtl_filename, '/') = 0; + if(NULL!=strrchr(mtl_filename, '\\')) + *strrchr(mtl_filename, '\\') = 0; + strncat(mtl_filename, "\\", 1024); + strncat(mtl_filename, fname, 1024); + + filein = fopen (mtl_filename,"r") ; + if ( filein == 0 ) + return ; // give up finding the material file + } + + int index = -1 ; + + char input[MAX_LINE_LEN]; + while ( fgets ( input, MAX_LINE_LEN, filein ) != NULL ) + { + char *next; + for ( next = input; *next != '\0' && isspace(*next); next++ ) + ; + if ( *next == '\0' ) + continue; + if ( *next == '#' || *next == '$' ) + continue; + + char token[MAX_LINE_LEN]; + int width; + int count; + float r1,r2,r3; + + sscanf ( next, "%s%n", token, &width ) ; + next = next + width ; + + if ( leqi ( token, "NEWMTL" ) == TRUE ) { + + char name[MAX_LINE_LEN]; + count = sscanf ( next, "%s%n", name, &width ) ; + + if ( count == 1 ) { + index ++ ; + memset( &materials[ index ], 0, sizeof(matData) ) ; + materials[ index ].name = ulStrDup ( name ) ; + materials[ index ].amb[3] = 1.0f ; + materials[ index ].diff[3] = 1.0f ; + materials[ index ].spec[3] = 1.0f ; + materials[ index ].shine = 0.0f ; + materials[ index ].trans = 1.0f ; + } + } + else if ( leqi ( token, "Ka" ) == TRUE ) { + + count = sscanf ( next, "%e %e %e", &r1, &r2, &r3 ) ; + + if ( count == 3 && index >= 0 ) { + materials[ index ].amb[0] = r1 ; + materials[ index ].amb[1] = r2 ; + materials[ index ].amb[2] = r3 ; + } + } + else if ( leqi ( token, "Kd" ) == TRUE ) { + + count = sscanf ( next, "%e %e %e", &r1, &r2, &r3 ) ; + + if ( count == 3 && index >= 0 ) { + materials[ index ].diff[0] = r1 ; + materials[ index ].diff[1] = r2 ; + materials[ index ].diff[2] = r3 ; + } + } + else if ( leqi ( token, "Ks" ) == TRUE ) { + + count = sscanf ( next, "%e %e %e", &r1, &r2, &r3 ) ; + + if ( count == 3 && index >= 0 ) { + materials[ index ].spec[0] = r1 ; + materials[ index ].spec[1] = r2 ; + materials[ index ].spec[2] = r3 ; + } + } + else if ( leqi ( token, "map_Kd" ) == TRUE ) { + + char tfname[MAX_LINE_LEN]; + count = sscanf ( next, "%s%n", tfname, &width ) ; + + if ( count == 1 && index >= 0 ) + materials[ index ].tfname = ulStrDup ( tfname ) ; + } + } + + fclose (filein) ; + + num_mat = (index+1) ; +} + + +static void add_mesh ( int mat_index ) +{ + int i ; + + //count faces with same material + int num = 0 ; + for ( i=0; i add ( vert[ face[i].vlist[j] ] ) ; + if ( num_vert_tex ) + tlist -> add ( vert_tex[ face[i].tlist[j] ] ) ; + if ( num_vert_normal ) + nlist -> add ( vert_normal[ face[i].nlist[j] ] ) ; + } + } + } + + ssgState *st = NULL ; + if ( mat_index < num_mat ) { + matData* mat = &materials[ mat_index ]; + if ( mat->tfname != 0 ) + st = ssgGetCurrentOptions() -> createState ( mat->tfname ) ; + if ( st == NULL ) + st = get_state ( mat ) ; + } + + ssgVtxTable *vtab = new ssgVtxTable ( GL_TRIANGLES, + vlist, nlist, tlist, 0 ) ; + + vtab -> setCullFace ( TRUE ); + if ( st ) + vtab -> setState ( st ) ; + + current_branch -> addKid ( vtab ) ; +} + +static int obj_read ( FILE *filein ) +{ + int count; + char *next; + char *next2; + char *next3; + int node; + float r1; + float r2; + float r3; + char input[MAX_LINE_LEN]; + char token[MAX_LINE_LEN]; + char token2[MAX_LINE_LEN]; + int width; + int mat_current = 0 ; +/* + Initialize. +*/ + num_line = 0 ; + line_dex = new int[ MAX_LINE ] ; + + num_face = 0 ; + face = new faceData[ MAX_FACE ] ; + + num_vert = 0 ; + num_vert_tex = 0 ; + num_vert_normal = 0 ; + vert = new sgVec3[ MAX_VERT ] ; + vert_tex = new sgVec3[ MAX_VERT ] ; + vert_normal = new sgVec3[ MAX_VERT ] ; + + num_mat = 0 ; + materials = new matData [ MAX_MATERIALS ] ; + +/* + Read the next line of the file into INPUT. +*/ + while ( fgets ( input, MAX_LINE_LEN, filein ) != NULL ) { + + for ( next = input; *next != '\0' && isspace(*next); next++ ) + ; + if ( *next == '\0' ) + continue; + if ( *next == '#' || *next == '$' ) + continue; + + sscanf ( next, "%s%n", token, &width ); + next = next + width; + +/* + F V1 V2 V3 + or + F V1/VT1/VN1 V2/VT2/VN2 ... + or + F V1//VN1 V2//VN2 ... + + Face. + A face is defined by the vertices. + Optionally, slashes may be used to include the texture vertex + and vertex normal indices. + + OBJ line node indices are 1 based rather than 0 based. + So we have to decrement them before loading them into FACE. +*/ + + if ( leqi ( token, "F" ) == TRUE ) { + + int vlist [4] ; + int tlist [4] ; + int nlist [4] ; + int ivert = 0 ; +/* + Read each item in the F definition as a token, and then + take it apart. +*/ + for ( ;; ) { + + count = sscanf ( next, "%s%n", token2, &width ); + next = next + width; + + if ( count != 1 ) { + break; + } + + count = sscanf ( token2, "%d%n", &node, &width ); + next2 = token2 + width; + + if ( count != 1 ) { + break; + } + + if ( ivert >= 4 ) { + break; + } + + vlist[ivert] = 0 ; + tlist[ivert] = 0 ; + nlist[ivert] = 0 ; + + if ( 0 < node && node <= num_vert ) { + vlist[ivert] = node-1; + } +/* + If there's a slash, extract the index of the texture vector. + If there's another slash, extract the index of the normal vector. +*/ + if ( *next2 == '/' ) { + + next3 = next2 + 1; + count = sscanf ( next3, "%d%n", &node, &width ); + + if ( 0 < node && node <= num_vert_tex ) { + tlist[ivert] = node-1; + } + + for ( ; next3 < token2 + MAX_LINE_LEN; next3++ ) { + + if ( *next3 == '/' ) { + next3 = next3 + 1; + count = sscanf ( next3, "%d%n", &node, &width ); + + if ( 0 < node && node <= num_vert_normal ) { + nlist[ivert] = node-1; + } + break; + } + } + } + ivert = ivert + 1; + } + + if ( ivert >= 3 ) { + + //quad? + if ( ivert == 4 ) { + if ( num_face+1 < MAX_FACE ) { + //0,1,3 + face[num_face].vlist[0] = vlist[0] ; + face[num_face].tlist[0] = tlist[0] ; + face[num_face].nlist[0] = nlist[0] ; + face[num_face].vlist[1] = vlist[1] ; + face[num_face].tlist[1] = tlist[1] ; + face[num_face].nlist[1] = nlist[1] ; + face[num_face].vlist[2] = vlist[3] ; + face[num_face].tlist[2] = tlist[3] ; + face[num_face].nlist[2] = nlist[3] ; + face[num_face].mat_index = mat_current ; + num_face = num_face + 1; + //3,1,2 + face[num_face].vlist[0] = vlist[3] ; + face[num_face].tlist[0] = tlist[3] ; + face[num_face].nlist[0] = nlist[3] ; + face[num_face].vlist[1] = vlist[1] ; + face[num_face].tlist[1] = tlist[1] ; + face[num_face].nlist[1] = nlist[1] ; + face[num_face].vlist[2] = vlist[2] ; + face[num_face].tlist[2] = tlist[2] ; + face[num_face].nlist[2] = nlist[2] ; + face[num_face].mat_index = mat_current ; + num_face = num_face + 1; + } + } + else { + if ( num_face < MAX_FACE ) { + for ( int i = 0 ; i < 3 ; i ++ ) { + face[num_face].vlist[i] = vlist[i] ; + face[num_face].tlist[i] = tlist[i] ; + face[num_face].nlist[i] = nlist[i] ; + } + face[num_face].mat_index = mat_current ; + num_face = num_face + 1; + } + } + } + } + +/* + G + Group name. +*/ + + else if ( leqi ( token, "G" ) == TRUE ) { + continue; + } +/* + L + I believe OBJ line node indices are 1 based rather than 0 based. + So we have to decrement them before loading them into LINE_DEX. +*/ + + else if ( leqi ( token, "L" ) == TRUE ) { + + int lastnode = -1 ; + + for ( ;; ) { + + count = sscanf ( next, "%d%n", &node, &width ); + next = next + width; + + if ( count != 1 ) { + break; + } + + if ( lastnode != -1 && num_line + 2 <= MAX_LINE ) { + + line_dex[num_line] = lastnode-1; + num_line = num_line + 1; + line_dex[num_line] = node-1; + num_line = num_line + 1; + } + + lastnode = node ; + } + } + +/* + LOD + Level of detail. +*/ + else if ( leqi ( token, "LOD" ) == TRUE ) { + continue; + } +/* + MG + Merging group. +*/ + else if ( leqi ( token, "MG" ) == TRUE ) { + continue; + } +/* + MTLLIB + Material library. +*/ + + else if ( leqi ( token, "MTLLIB" ) == TRUE ) { + + count = sscanf ( next, "%s%n", token2, &width ); + next = next + width; + + if ( count != 1 ) { + continue; + } + + load_materials ( token2 ) ; + mat_current = 0 ; + } +/* + USEMTL + Material name. +*/ + else if ( leqi ( token, "USEMTL" ) == TRUE ) { + + mat_current = 0 ; + + count = sscanf ( next, "%s%n", token2, &width ); + next = next + width; + + if ( count != 1 ) { + continue; + } + + for ( int i = 0 ; i < num_mat ; i ++ ) + if ( strcmp ( token2, materials[ i ].name ) == 0 ) + { + mat_current = i ; + break ; + } + } +/* + O + Object name. +*/ + else if ( leqi ( token, "O" ) == TRUE ) { + continue; + } +/* + P + Point. +*/ + else if ( leqi ( token, "P" ) == TRUE ) { + continue; + } +/* + SURF + Surface. +*/ + else if ( leqi ( token, "SURF" ) == TRUE ) { + continue; + } +/* + V X Y Z W + Geometric vertex. + W is optional, a weight for rational curves and surfaces. + The default for W is 1. +*/ + else if ( leqi ( token, "V" ) == TRUE ) { + + sscanf ( next, "%e %e %e", &r1, &r2, &r3 ); + + if ( num_vert < MAX_VERT ) { + vert[num_vert][0] = r1; + vert[num_vert][1] = r2; + vert[num_vert][2] = r3; + + num_vert = num_vert + 1; + } + } +/* + VT + Vertex texture. +*/ + else if ( leqi ( token, "VT" ) == TRUE ) { + + sscanf ( next, "%e %e %e", &r1, &r2, &r3 ); + + if ( num_vert_tex < MAX_VERT ) { + vert_tex[num_vert_tex][0] = r1; + vert_tex[num_vert_tex][1] = r2; + vert_tex[num_vert_tex][2] = r3; + + num_vert_tex = num_vert_tex + 1; + } + } +/* + VN + Vertex normals. +*/ + else if ( leqi ( token, "VN" ) == TRUE ) { + + sscanf ( next, "%e %e %e", &r1, &r2, &r3 ); + + if ( num_vert_normal < MAX_VERT ) { + vert_normal[num_vert_normal][0] = r1; + vert_normal[num_vert_normal][1] = r2; + vert_normal[num_vert_normal][2] = r3; + + num_vert_normal = num_vert_normal + 1; + } + } + } + + //create ssg nodes + if ( num_face ) + { + if ( num_mat ) { + for ( int i=0; i add ( vert[ line_dex[i] ] ) ; + ssgVtxTable *vtab = new ssgVtxTable ( GL_LINES, vlist, 0, 0, 0 ); + current_branch -> addKid ( vtab ) ; + } + + delete[] materials ; + delete[] vert ; + delete[] vert_tex ; + delete[] vert_normal ; + delete[] face ; + delete[] line_dex ; + + return TRUE; +} + +ssgEntity *ssgLoadOBJ ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + const ssgLoaderOptions* current_options = ssgGetCurrentOptions () ; + + current_branch = NULL ; + + current_options -> makeModelPath ( filename, fname ) ; + + FILE *loader_fd = fopen ( filename, "ra" ) ; + + if ( loader_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadOBJ: Failed to open '%s' for reading", filename ) ; + return NULL ; + } + + current_branch = new ssgTransform () ; + + obj_read ( loader_fd ) ; + + fclose ( loader_fd ) ; + + return current_branch ; +} diff --git a/src/ssg/ssgLoadOFF.cxx b/src/ssg/ssgLoadOFF.cxx new file mode 100644 index 0000000..deab960 --- /dev/null +++ b/src/ssg/ssgLoadOFF.cxx @@ -0,0 +1,344 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadOFF.cxx 1970 2004-10-04 08:11:26Z wolfram_kuss $ +*/ + +// +// .off loader for SSG/PLIB +// Warning: There are two formats called OFF +// +// There is Geomview off, see http://www.neuro.sfc.keio.ac.jp/~aly/polygon/format/off.html +// and http://www.graphics.cornell.edu/~gordon/peek/old/off.html +// This is the format implmented in this file. +// +// And there is off (Object File Format) by Digital Equipment Corporation, Workstation Systems Engineering +// (WSE), see for ex. http://www.dcs.ed.ac.uk/home/mxr/gfx/3d/OFF.spec. +// and http://www.landfield.com/faqs/graphics/fileformats-faq/part3/section-95.html +// We don't support this format. + +// Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in February 2001 +// +// We only support 2D and 3D data + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" +#include "ssgParser.h" + +#define u32 unsigned int + +static int _ssgLoadTranslucent=TRUE; + +extern sgVec4 currentDiffuse; + +void ssgSetLoadOFFTranslucent ( int i ) +// this is a kludge. +// it might be removed/replaced later on. +{ + _ssgLoadTranslucent = i; +} + + +static /* const */ ssgLoaderOptions* current_options = NULL ; + +static _ssgParserSpec parser_spec = +{ + "\r\n\t ", // delim_chars_skipable + ",;", // delim_chars_non_skipable + NULL, // pre_processor + "", // open_brace_chars + "", // close_brace_chars + '"', // quote_char. not used for OFF + '#', // comment_char + "%" // comment_string +} ; + + +static _ssgParser parser; +static ssgBranch* top_branch; + +/*static int Ascii2Int(int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +}*/ + +static int Ascii2UInt(unsigned int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = (unsigned int)(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int Ascii2Float(SGfloat &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = SGfloat(strtod( token, &endptr)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + + + + +#define MAX_NO_VERTICES_PER_FACE 1000 + +static class ssgLoaderWriterMesh theMesh; + + + + + +static int _ssgNoFacesToRead=-1, _ssgNoVerticesToRead; + +static int thereIs_ST=FALSE, thereIs_C=FALSE, thereIs_N=FALSE, + thereIs_4=FALSE, thereIs_n=TRUE; +static unsigned int dimension; // may be 2 or 3 + +static int parse() +{ + char* token; + + token = parser.getLine( 0 ); + if ( token == NULL ) + { parser.error("The file seems to be empty"); + return FALSE; + } + // If first line is "off", swallow it + if ( strlen(token) >= 3 ) + { char *p = token; + p += strlen(token)-3; + if (0==strcmp("OFF", p) ) + { if ( strlen(token) > 3 ) + // parse line + { thereIs_ST = ( NULL != strstr( token, "ST" )); + thereIs_C = ( NULL != strstr( token, "C" )); + thereIs_N = ( NULL != strstr( token, "N" )); + thereIs_4 = ( NULL != strstr( token, "4" )); + thereIs_n = ( NULL != strstr( token, "n" )); + if ( thereIs_ST || thereIs_C || thereIs_N ) + { parser.error("This is a \"ST\"- \"C\"- or \"N\"-OFF. These are not supoorted, sorry."); + return FALSE; + } + if ( thereIs_4 && !thereIs_n ) + { parser.error("This is a 4D-OFF file. We only support 3D data, sorry."); + return FALSE; + } + } + token = parser.getLine( 0 ); + assert( token != NULL ); + } + } + if ( thereIs_n ) + { + if ( ! Ascii2UInt(dimension, token, "Dimension" )) + return FALSE; + if ( thereIs_4 && thereIs_n ) + dimension++; + if ( (dimension != 3) && (dimension != 2)) + { parser.error("This is a %udD-OFF file. We only support 2D and 3D data, sorry.", dimension); + return FALSE; + } + token = parser.getLine( 0 ); // read space dimension + assert( token != NULL ); + } + // ****** read NVertices, NFaces and NEdges ************ + + unsigned int uInt; + int i; + if ( ! Ascii2UInt(uInt, token, "NVertices")) + return FALSE; + _ssgNoVerticesToRead = uInt; + + token = parser.parseToken( "NFaces" ); + if ( ! Ascii2UInt(uInt, token, "NFaces")) + return FALSE; + _ssgNoFacesToRead = uInt; + + token = parser.parseToken( "NEdges" ); + if ( ! Ascii2UInt(uInt, token, "NEdges")) + return FALSE; + + // **** init theMesh *** + theMesh.reInit (); + theMesh.createVertices( _ssgNoVerticesToRead ); + theMesh.createFaces ( _ssgNoFacesToRead ); + + + // ***** read Vertex Coords + for ( i = 0; i < _ssgNoVerticesToRead ; i++ ) + { + sgVec3 vert; + token = parser.getLine( ); // may return NULL? + assert(token!=NULL); + + if (!Ascii2Float(vert[0], token, "x")) + return FALSE; + if (!parser.parseFloat(vert[1], "y")) + return FALSE; + if ( dimension == 2 ) + vert[2] = 0.0; + else + if (!parser.parseFloat(vert[2], "z")) + return FALSE; + theMesh.addVertex(vert); + } + // ********* Read Faces ********** + int iVertex, aiVertices[MAX_NO_VERTICES_PER_FACE]; + unsigned int nNoOfVerticesForThisFace, j; + + for(i=0;i<_ssgNoFacesToRead ;i++) + { token = parser.getLine( ); // may return NULL? + assert(token!=NULL); + if (!Ascii2UInt(nNoOfVerticesForThisFace , token, "number of vertices for this face")) + return FALSE; + assert(nNoOfVerticesForThisFace setTranslucent () ; + ss -> disable ( GL_ALPHA_TEST ) ; + ss -> enable ( GL_BLEND ) ; + + //new + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ss->setAlphaClamp(0.1f); + } + float *ssf=ss->getMaterial ( GL_DIFFUSE ); + ssf[0]=0.8f; + ssf[1]=0.8f; + ssf[2]=1.0f; + if ( _ssgLoadTranslucent ) + ssf[3]=0.4f; + else + ssf[3]=1.0f; + sgCopyVec4 ( currentDiffuse, ssf ); + + ssf=ss->getMaterial ( GL_EMISSION ); + ssf[0]=0.0f; + ssf[1]=0.0f; + ssf[2]=0.0f; + ssf[3]=1.0f; + ssf=ss->getMaterial ( GL_SPECULAR ); + ssf[0]=1.0f; + ssf[1]=1.0f; + ssf[2]=1.0f; + ssf[3]=1.0f; + ssf=ss->getMaterial ( GL_AMBIENT ); + ssf[0]=0.3f; + ssf[1]=0.3f; + ssf[2]=0.3f; + ssf[3]=1.0f; + + + if ( !_ssgLoadTranslucent ) + // This code has simply been copyied over from ssgLoadM, where it works + { ss->setOpaque(); + ss->disable(GL_BLEND); + ss->disable(GL_ALPHA_TEST); + ss->disable(GL_TEXTURE_2D); + ss->enable(GL_COLOR_MATERIAL); + ss->enable(GL_LIGHTING); + ss->setShadeModel(GL_SMOOTH); + ss->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.0f, 1.0f); + ss->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.0f, 1.0f); + ss->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + ss->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + ss->setShininess(50); + } + + + theMesh.createMaterials( 1 ); + theMesh.addMaterial( &ss ); + theMesh.createMaterialIndices( _ssgNoFacesToRead ) ; + for(i=0;i<_ssgNoFacesToRead ;i++) + theMesh.addMaterialIndex ( 0 ) ; + + + theMesh.addToSSG( + ss, // kludge. NIV135 + current_options, + top_branch); + + return TRUE ; +} + + +ssgEntity *ssgLoadOFF ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + top_branch = new ssgBranch ; + if ( !parser.openFile( fname, &parser_spec )) + { + delete top_branch ; + return 0; + } + if ( !parse() ) + { + delete top_branch ; + top_branch = 0 ; + } +// parse_free(); + parser.closeFile(); + + return top_branch ; +} diff --git a/src/ssg/ssgLoadPCX.cxx b/src/ssg/ssgLoadPCX.cxx new file mode 100644 index 0000000..8ccd2a4 --- /dev/null +++ b/src/ssg/ssgLoadPCX.cxx @@ -0,0 +1,188 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 2001 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadPCX.cxx 1963 2004-09-26 09:27:28Z bram $ +*/ + +//=========================================================================== +// +// File: ssgLoadPCX.cxx +// +// Created: 12.12.2003 +// +// Author: Wolfram Kuss +// +// Revision: $Id: ssgLoadPCX.cxx 1963 2004-09-26 09:27:28Z bram $ +// +// Description: Loads PCX texture files. Only 8 bit indexed for now. +// +// +//=========================================================================== + +#include "ssgLocal.h" +typedef unsigned char UByte,*UByteP; +#include "pcx.h" + + +#ifdef SSG_LOAD_PCX_SUPPORTED + +int ReadFileIntoBuffer(const char *fname, UByte *&buffer, UByte *&bufferorig, unsigned long &file_length) +// Opens the file, allocates buffer of correct size to hold the file, reads it, closes it +{ + // **** open file **** + FILE *tfile; + if ( (tfile = fopen(fname, "rb")) == NULL) { + ulSetError( UL_WARNING, "ssgLoadTexture: Failed to open file '%s' for reading.", fname ); + return false ; + } + + // **** allocate buffer, read file into it and close file **** + fseek(tfile, 0, SEEK_END); + file_length = ftell(tfile); + fseek(tfile, 0, SEEK_SET); + + buffer = new UByte[file_length]; + bufferorig = buffer; + + fread(buffer, file_length, 1, tfile); + fclose(tfile); + return true; +} + +bool ssgLoadPCX ( const char *fname, ssgTextureInfo* info ) +{ + UByte *buffer, *bufferorig; + unsigned long file_length; + if(!ReadFileIntoBuffer(fname, buffer, bufferorig, file_length)) + return false ; + // **** "read" header and "analyse" it **** + pcxHeaderType *ppcxHeader = (pcxHeaderType *) buffer; + buffer += sizeof(pcxHeaderType); + + short width = ppcxHeader->xmax-ppcxHeader->x+1; + short height = ppcxHeader->ymax-ppcxHeader->y+1; + //p->isMasked = ((p->resOfRowanTexture & 0x200) != 0) ? 1 : 0; + + if ( info != NULL ) + { + info -> width = width ; + info -> height = height ; + info -> depth = 4 ; + info -> alpha = TRUE ; //I think we have to set it to true always, since we always generate 4 bytes per pixel + } + + // **** read body´; Do error checking **** + long size = ((long)width)*height; + UByte *pAlfa = NULL, * pBody = new UByte [size]; // 1 byte per texel + UByte * pBodyorig = pBody; + + if(!ReadPCXBody(buffer, ppcxHeader, pBody)) +// writes to pBody, which must have been allocated + { + delete [] buffer; + delete [] pBody; + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - unsupported or broken PCX texture file", fname ) ; + return false ; + } + if(*buffer++ != 12) + { ulSetError ( UL_WARNING, "ssgLoadTexture: '%s' - PCX files needs a '12' byte", fname ) ; + return false ; + } + assert(bufferorig + file_length - 768 == buffer); + // starting at "buffer", you can find the palette, 256 entrys with 3 bytes each + // only true for version 5 (and possible later versions)= + +// start alfa handling + // PCX does not allow alfa, so to enable alfa, you need two files :-(, + // one for the body, for example abc.pcx and one for the alfa component, for example abc_trans.pcx + if(fname[strlen(fname)-4]=='.') + { + char *t, *s = new char[strlen(fname)+15]; + strcpy(s, fname); + t=&(s[strlen(s)-4]); + strcpy(t, "_trans.pcx"); + if(ulFileExists(s)) + { + UByte *alfaBuffer, *alfaBufferorig; + if(!ReadFileIntoBuffer(s, alfaBuffer, alfaBufferorig, file_length)) + return false ; + // **** "read" header and "analyse" it **** + ppcxHeader = (pcxHeaderType *) alfaBuffer; + alfaBuffer += sizeof(pcxHeaderType); + + if(width != ppcxHeader->xmax-ppcxHeader->x+1) + ulSetError ( UL_WARNING, "ssgLoadTexture: '%s' - Width does not agree to 'body' width, so alfa is ignored", s ) ; + else + { + + if (height != ppcxHeader->ymax-ppcxHeader->y+1) + ulSetError ( UL_WARNING, "ssgLoadTexture: '%s' - Height does not agree to 'body' height, so alfa is ignored", s ) ; + else + { + // **** read body´; Do error checking **** + pAlfa = new UByte [size]; // 1 byte per texel + + if(!ReadPCXBody(alfaBuffer, ppcxHeader, pAlfa)) + // writes to pBody, which must have been allocated + { + delete [] buffer; + delete [] pAlfa; + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - unsupported or broken PCX texture file", fname ) ; + return false ; + } + } + } + } + } +// end alfa handling + + UByte *texels = new UByte [size * 4]; // 4 bytes per texel + int c = 0; + int iRunningIndex = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + UByte a = pAlfa?pAlfa[iRunningIndex]:255; + UByte b = pBody[iRunningIndex++]; + texels[c++] = buffer[b*3 ]; + texels[c++] = buffer[b*3 + 1]; + texels[c++] = buffer[b*3 + 2]; + texels[c++] = a; + } + } + + delete [] pBodyorig; + delete [] bufferorig; + if (pAlfa) + delete [] pAlfa; + return ssgMakeMipMaps ( texels, width, height, 4 ) ; +} +#else + +bool ssgLoadPCX ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - PCX texture support not configured", fname ) ; + return false ; +} + +#endif + diff --git a/src/ssg/ssgLoadPNG.cxx b/src/ssg/ssgLoadPNG.cxx new file mode 100644 index 0000000..774e2d0 --- /dev/null +++ b/src/ssg/ssgLoadPNG.cxx @@ -0,0 +1,57 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadPNG.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +#ifdef SSG_LOAD_PNG_SUPPORTED + +#include + +bool ssgLoadPNG ( const char *fname, ssgTextureInfo* info ) +{ + pngInfo png_info; + if (!pngLoad(fname, PNG_BUILDMIPMAP, PNG_ALPHA, &png_info)) { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname ) ; + return false ; + } + if ( info != NULL ) + { + info -> width = png_info.Width ; + info -> height = png_info.Height ; + info -> depth = png_info.Depth ; + info -> alpha = png_info.Alpha ; + } + return true ; +} + +#else + +bool ssgLoadPNG ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, "ssgLoadTexture: '%s' - you need glpng for PNG format support", + fname ) ; + return false ; +} + +#endif diff --git a/src/ssg/ssgLoadSGI.cxx b/src/ssg/ssgLoadSGI.cxx new file mode 100644 index 0000000..8727b3a --- /dev/null +++ b/src/ssg/ssgLoadSGI.cxx @@ -0,0 +1,538 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadSGI.cxx 2081 2006-04-16 17:45:33Z bram $ +*/ + + +#include "ssgLocal.h" + +/* Some magic constants in the file header. */ + +#define SGI_IMG_MAGIC 0x01DA +#define SGI_IMG_SWABBED_MAGIC 0xDA01 /* This is how it appears on a PC */ +#define SGI_IMG_VERBATIM 0 +#define SGI_IMG_RLE 1 + +class ssgSGIHeader +{ +public: /* Yuk! Need to hide some of this public stuff! */ + unsigned short magic ; + int max ; + int min ; + int colormap ; + char type ; + char bpp ; + unsigned int *start ; + int *leng ; + unsigned short dim ; + unsigned short xsize ; + unsigned short ysize ; + unsigned short zsize ; + int tablen ; + /* added by J. Nathan Matias */ + FILE *image_fd; + char image_fname [ 512 ]; + int isSwapped; + unsigned char *rle_temp; + bool loadSGI_bool; + + + ssgSGIHeader () ; + ssgSGIHeader(const char *fname, ssgTextureInfo* info ); + ~ssgSGIHeader () ; + + bool openFile(const char *fname); + void makeConsistant () ; + void getRow ( unsigned char *buf, int y, int z ) ; + void getPlane ( unsigned char *buf, int z ) ; + void getImage ( unsigned char *buf ) ; + void readHeader () ; + +protected: + void swab_short ( unsigned short *x ); + void swab_int ( unsigned int *x ); + void swab_int_array ( int *x, int leng ); + unsigned char readByte (); + unsigned short readShort (); + unsigned int readInt (); + +} ; + + +#ifdef SSG_LOAD_SGI_SUPPORTED + +void ssgSGIHeader::makeConsistant () +{ + /* + Sanity checks - and a workaround for buggy RGB files generated by + the MultiGen Paint program because it will sometimes get confused + about the way to represent maps with more than one component. + + eg Y > 1, Number of dimensions == 1 + Z > 1, Number of dimensions == 2 + */ + + if ( ysize > 1 && dim < 2 ) dim = 2 ; + if ( zsize > 1 && dim < 3 ) dim = 3 ; + if ( dim < 1 ) ysize = 1 ; + if ( dim < 2 ) zsize = 1 ; + if ( dim > 3 ) dim = 3 ; + if ( zsize < 1 && ysize == 1 ) dim = 1 ; + if ( zsize < 1 && ysize != 1 ) dim = 2 ; + if ( zsize >= 1 ) dim = 3 ; + + /* + A very few SGI image files have 2 bytes per component - this + library cannot deal with those kinds of files. + */ + + if ( bpp == 2 ) + { + ulSetError ( UL_FATAL, "ssgLoadTexture: Can't work with SGI images with %d bpp", bpp ) ; + } + + bpp = 1 ; + min = 0 ; + max = 255 ; + magic = SGI_IMG_MAGIC ; + colormap = 0 ; +} + + +void ssgSGIHeader::swab_short ( unsigned short *x ) +{ + if ( isSwapped ) + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + +void ssgSGIHeader::swab_int ( unsigned int *x ) +{ + if ( isSwapped ) + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + +void ssgSGIHeader::swab_int_array ( int *x, int leng ) +{ + if ( ! isSwapped ) + return ; + + for ( int i = 0 ; i < leng ; i++ ) + { + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; + x++ ; + } +} + + +unsigned char ssgSGIHeader::readByte () +{ + unsigned char x ; + fread ( & x, sizeof(unsigned char), 1, image_fd ) ; + return x ; +} + +unsigned short ssgSGIHeader::readShort () +{ + unsigned short x ; + fread ( & x, sizeof(unsigned short), 1, image_fd ) ; + swab_short ( & x ) ; + return x ; +} + +unsigned int ssgSGIHeader::readInt () +{ + unsigned int x ; + fread ( & x, sizeof(unsigned int), 1, image_fd ) ; + swab_int ( & x ) ; + return x ; +} + + +void ssgSGIHeader::getRow ( unsigned char *buf, int y, int z ) +{ + if ( y >= ysize ) y = ysize - 1 ; + if ( z >= zsize ) z = zsize - 1 ; + + fseek ( image_fd, start [ z * ysize + y ], SEEK_SET ) ; + + if ( type == SGI_IMG_RLE ) + { + unsigned char *tmpp = rle_temp ; + unsigned char *bufp = buf ; + int length = leng [ z * ysize + y ]; + + fread ( rle_temp, 1, length, image_fd ) ; + + unsigned char pixel, count ; + + while ( tmpp < rle_temp + length ) + { + pixel = *tmpp++ ; + + count = ( pixel & 0x7f ) ; + + if ( count == 0 ) + break ; + + if ( pixel & 0x80 ) + { + while ( count-- ) + *bufp++ = *tmpp++ ; + } + else + { + pixel = *tmpp++ ; + + while ( count-- ) + *bufp++ = pixel ; + } + } + } + else + fread ( buf, 1, xsize, image_fd ) ; +} + + +void ssgSGIHeader::getPlane ( unsigned char *buf, int z ) +{ + if ( image_fd == NULL ) + return ; + + if ( z >= zsize ) z = zsize - 1 ; + + for ( int y = 0 ; y < ysize ; y++ ) + getRow ( & buf [ y * xsize ], y, z ) ; +} + + + +void ssgSGIHeader::getImage ( unsigned char *buf ) +{ + if ( image_fd == NULL ) + return ; + + for ( int y = 0 ; y < ysize ; y++ ) + for ( int z = 0 ; z < zsize ; z++ ) + getRow ( & buf [ ( z * ysize + y ) * xsize ], y, z ) ; +} + + +ssgSGIHeader::ssgSGIHeader () +{ + dim = 0 ; + start = NULL ; + leng = NULL ; + rle_temp = NULL ; + image_fd = NULL ; +} + +ssgSGIHeader::ssgSGIHeader ( const char *fname, ssgTextureInfo* info ) +{ + ssgSGIHeader *sgihdr = this ; + + start = NULL ; + leng = NULL ; + rle_temp = NULL ; + + bool success=openFile(fname); + + if ( !success ) + { + loadSGI_bool = false ; + return ; + } + + GLubyte *image = new GLubyte [ sgihdr->xsize * + sgihdr->ysize * + sgihdr->zsize ] ; + + GLubyte *ptr = image ; + + unsigned char *rbuf = new unsigned char [ sgihdr->xsize ] ; + unsigned char *gbuf = (sgihdr->zsize>1) ? new unsigned char [ sgihdr->xsize ] : (unsigned char *) NULL ; + unsigned char *bbuf = (sgihdr->zsize>2) ? new unsigned char [ sgihdr->xsize ] : (unsigned char *) NULL ; + unsigned char *abuf = (sgihdr->zsize>3) ? new unsigned char [ sgihdr->xsize ] : (unsigned char *) NULL ; + + for ( int y = 0 ; y < sgihdr->ysize ; y++ ) + { + int x ; + + switch ( sgihdr->zsize ) + { + case 1 : + sgihdr->getRow ( rbuf, y, 0 ) ; + + for ( x = 0 ; x < sgihdr->xsize ; x++ ) + *ptr++ = rbuf [ x ] ; + + break ; + + case 2 : + sgihdr->getRow ( rbuf, y, 0 ) ; + sgihdr->getRow ( gbuf, y, 1 ) ; + + for ( x = 0 ; x < sgihdr->xsize ; x++ ) + { + *ptr++ = rbuf [ x ] ; + *ptr++ = gbuf [ x ] ; + } + break ; + + case 3 : + sgihdr->getRow ( rbuf, y, 0 ) ; + sgihdr->getRow ( gbuf, y, 1 ) ; + sgihdr->getRow ( bbuf, y, 2 ) ; + + for ( x = 0 ; x < sgihdr->xsize ; x++ ) + { + *ptr++ = rbuf [ x ] ; + *ptr++ = gbuf [ x ] ; + *ptr++ = bbuf [ x ] ; + } + break ; + + case 4 : + sgihdr->getRow ( rbuf, y, 0 ) ; + sgihdr->getRow ( gbuf, y, 1 ) ; + sgihdr->getRow ( bbuf, y, 2 ) ; + sgihdr->getRow ( abuf, y, 3 ) ; + + for ( x = 0 ; x < sgihdr->xsize ; x++ ) + { + *ptr++ = rbuf [ x ] ; + *ptr++ = gbuf [ x ] ; + *ptr++ = bbuf [ x ] ; + *ptr++ = abuf [ x ] ; + } + break ; + } + } + + fclose ( image_fd ) ; + image_fd = NULL ; + + delete [] rbuf ; + delete [] gbuf ; + delete [] bbuf ; + delete [] abuf ; + + if ( info != NULL ) + { + info -> width = sgihdr->xsize ; + info -> height = sgihdr->ysize ; + info -> depth = sgihdr->zsize ; + info -> alpha = ( sgihdr->zsize == 2 || sgihdr->zsize == 4 ) ; + } + + bool result = + ssgMakeMipMaps ( image, sgihdr->xsize, sgihdr->ysize, sgihdr->zsize ) ; + + + loadSGI_bool= result ; +} + + + +ssgSGIHeader::~ssgSGIHeader() +{ + if (start != NULL) + delete [] start; + + if (leng != NULL) + delete [] leng; + + if (image_fd != NULL) + fclose(image_fd); + + if (rle_temp != NULL) + delete [] rle_temp; +} + + +void ssgSGIHeader::readHeader () +{ + isSwapped = FALSE ; + + magic = readShort () ; + + if ( magic != SGI_IMG_MAGIC && magic != SGI_IMG_SWABBED_MAGIC ) + { + ulSetError ( UL_FATAL, "%s: Unrecognised magic number 0x%04x", + image_fname, magic ) ; + } + + if ( magic == SGI_IMG_SWABBED_MAGIC ) + { + isSwapped = TRUE ; + swab_short ( & magic ) ; + } + + type = readByte () ; + bpp = readByte () ; + dim = readShort () ; + + /* + This is a backstop test - if for some reason the magic number isn't swabbed, this + test will still catch a swabbed file. Of course images with more than 256 dimensions + are not catered for :-) + */ + + if ( dim > 255 ) + { + ulSetError ( UL_WARNING, "%s: Bad swabbing?!?", image_fname ) ; + isSwapped = ! isSwapped ; + swab_short ( & dim ) ; + magic = SGI_IMG_MAGIC ; + } + + xsize = readShort () ; + ysize = readShort () ; + zsize = readShort () ; + min = readInt () ; + max = readInt () ; + readInt () ; /* Dummy field */ + + int i ; + + for ( i = 0 ; i < 80 ; i++ ) + readByte () ; /* Name field */ + + colormap = readInt () ; + + for ( i = 0 ; i < 404 ; i++ ) + readByte () ; /* Dummy field */ + + makeConsistant () ; + + tablen = ysize * zsize ; + start = new unsigned int [ tablen ] ; + leng = new int [ tablen ] ; +} + +bool ssgSGIHeader::openFile(const char *fname) +{ + ssgSGIHeader *sgihdr=this; + + strcpy ( image_fname, fname ) ; + image_fd = fopen ( image_fname, "rb" ) ; + + if ( image_fd == NULL ) + { + /* perror ( "ssgLoadTexture" ) ;*/ + ulSetError ( UL_WARNING, "ssgSGIHeader::: Failed to open '%s' for reading.", image_fname ) ; + return false ; + } + + sgihdr -> readHeader () ; + + if ( sgihdr -> type == SGI_IMG_RLE ) + { + fread ( sgihdr->start, sizeof (unsigned int), sgihdr->tablen, image_fd ) ; + fread ( sgihdr->leng , sizeof (int), sgihdr->tablen, image_fd ) ; + swab_int_array ( (int *) sgihdr->start, sgihdr->tablen ) ; + swab_int_array ( (int *) sgihdr->leng , sgihdr->tablen ) ; + + int maxlen = 0 ; + + for ( int i = 0 ; i < sgihdr->tablen ; i++ ) + if ( sgihdr->leng [ i ] > maxlen ) + maxlen = sgihdr->leng [ i ] ; + + rle_temp = new unsigned char [ maxlen ] ; + } + else + { + rle_temp = NULL ; + + for ( int i = 0 ; i < sgihdr->zsize ; i++ ) + for ( int j = 0 ; j < sgihdr->ysize ; j++ ) + { + sgihdr->start [ i * sgihdr->ysize + j ] = sgihdr->xsize * ( i * sgihdr->ysize + j ) + 512 ; + sgihdr->leng [ i * sgihdr->ysize + j ] = sgihdr->xsize ; + } + } + + if ( sgihdr->zsize <= 0 || sgihdr->zsize > 4 ) + { + ulSetError ( UL_FATAL, "ssgLoadTexture: '%s' is corrupted.", image_fname ) ; + } + + return true ; +} + + +//wrapper function that does the same job as the previous one +//except we now have a better architecture + +bool ssgLoadSGI ( const char *fname, ssgTextureInfo* info ) +{ + ssgSGIHeader *sgihdr=new ssgSGIHeader( fname, info); + bool returnval=sgihdr->loadSGI_bool; + delete sgihdr; + return returnval; +} + + +#else + + /*if there's no SGI support, allow things to compile, but print an error*/ +ssgSGIHeader::ssgSGIHeader() +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - SGI support not configured", fname ) ; + return; +} + + +ssgSGIHeader::ssgSGIHeader(const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - SGI support not configured", fname ) ; + return; +} + +void SGIHeader::makeConsistant (){return;} +void SGIHeader::getRow ( unsigned char *buf, int y, int z ) {return;} +void SGIHeader::getPlane ( unsigned char *buf, int z ) {return;} +void SGIHeader::getImage ( unsigned char *buf ) {return;} +void SGIHeader::readHeader () {return;} +void SGIHeader::swab_short ( unsigned short *x ){return;} +void SGIHeader::swab_int ( unsigned int *x ){return;} +void SGIHeader::swab_int_array ( int *x, int leng ){return;} +unsigned char SGIHeader::readByte (){return;} +unsigned short SGIHeader::readShort (){return;} +unsigned int SGIHeader::readInt (){return;} + +bool ssgLoadSGI ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - SGI support not configured", fname ) ; + return false ; +} + +#endif diff --git a/src/ssg/ssgLoadSSG.cxx b/src/ssg/ssgLoadSSG.cxx new file mode 100644 index 0000000..b249848 --- /dev/null +++ b/src/ssg/ssgLoadSSG.cxx @@ -0,0 +1,277 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadSSG.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +// code for loading and writing .ssg files + +#include "ssgLocal.h" + +// used for reading only: +int _ssgFileVersionNumber = 0 ; + +// simple list of ssgBase derived objects: +class _ssgBaseList : public ssgSimpleList +{ +public: + + _ssgBaseList() : ssgSimpleList( sizeof(ssgBase *), 16 ) {} + + ~_ssgBaseList() {} + + ssgBase *get ( unsigned n ) + { + return n < total ? ((ssgBase **) list) [ n ] : NULL; + } + + void add ( ssgBase *obj ) + { + sizeChk ( 1 ) ; + ((ssgBase **) list) [ total++ ] = obj ; + } + + int find ( ssgBase *obj ) + { + for ( unsigned i = 0 ; i < total ; i++ ) + if ( ((ssgBase **) list) [ i ] == obj ) + return i ; + return -1 ; + } + +}; + +// list of ssgBase objects for instance referencing: +static _ssgBaseList *_ssgInstanceList ; + + +int _ssgLoadObject ( FILE *f, ssgBase **objp, int type_mask ) +{ + int type = 0, key = 0; + ssgBase *obj; + + _ssgReadInt ( f, &type ) ; + + if ( type == _SSG_BACKWARDS_REFERENCE ) + { + _ssgReadInt ( f, &key ) ; + + obj = _ssgInstanceList -> get ( key ) ; + if ( obj == NULL ) + { + if ( key != 0 ) + { + ulSetError ( UL_WARNING, + "ssgLoadObject: Unexpected null object for key %d.", key ) ; + return FALSE ; + } + } + else if ( ! obj -> isAKindOf ( type_mask ) ) + { + ulSetError ( UL_WARNING, "ssgLoadObject: Bad type %#x (%s), expected %#x.", + obj -> getType (), obj -> getTypeName (), type_mask ) ; + return FALSE ; + } + } + else + { + if ( ( type & type_mask ) != type_mask ) + { + ulSetError ( UL_WARNING, "ssgLoadObject: Bad type %#x, expected %#x.", + type, type_mask ) ; + return FALSE ; + } + + obj = ssgCreateOfType ( type ) ; + if ( obj == NULL ) + return FALSE ; + + _ssgInstanceList -> add ( obj ) ; + + if ( ! obj -> load ( f ) ) + { + ulSetError ( UL_DEBUG, "ssgLoadObject: Failed to load object of type %s.", + obj -> getTypeName () ) ; + return FALSE ; + } + + if ( obj -> isAKindOf ( ssgTypeEntity () ) ) + { + ((ssgEntity *) obj) -> recalcBSphere () ; + } + } + + if ( _ssgReadError () ) + { + ulSetError ( UL_WARNING, "ssgLoadObject: Read error." ) ; + return FALSE ; + } + + *objp = obj ; + + return TRUE ; +} + + +int _ssgSaveObject ( FILE *f, ssgBase *obj ) +{ + int key = _ssgInstanceList -> find ( obj ) ; + + if ( key >= 0 ) + { + _ssgWriteInt ( f, _SSG_BACKWARDS_REFERENCE ) ; + _ssgWriteInt ( f, key ) ; + } + else + { + _ssgWriteInt ( f, obj -> getType () ) ; + + _ssgInstanceList -> add ( obj ) ; + + if ( ! obj -> save ( f ) ) + { + ulSetError ( UL_DEBUG, "ssgSaveObject: Failed to save object of type %s.", + obj -> getTypeName () ) ; + return FALSE ; + } + } + + if ( _ssgWriteError () ) + { + ulSetError ( UL_WARNING, "ssgSaveObject: Write error." ) ; + return FALSE ; + } + + return TRUE ; +} + + +ssgEntity *ssgLoadSSG ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + const ssgLoaderOptions* current_options = ssgGetCurrentOptions () ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE *fd = fopen ( filename, "rb" ) ; + + if ( fd == NULL ) + { + perror ( filename ) ; + ulSetError ( UL_WARNING, + "ssgLoadSSG: Failed to open '%s' for reading.", filename ) ; + return NULL ; + } + + int magic ; + ssgEntity *kid ; + + _ssgReadInt ( fd, & magic ) ; + + if ( ( magic & 0xFFFFFF00 ) != ( SSG_FILE_MAGIC_NUMBER & 0xFFFFFF00 ) ) + { + if (((magic & 0x0000FF)>> 0)==((SSG_FILE_MAGIC_NUMBER & 0xFF000000)>>24) && + ((magic & 0x00FF00)>> 8)==((SSG_FILE_MAGIC_NUMBER & 0x00FF0000)>>16) && + ((magic & 0xFF0000)>>16)==((SSG_FILE_MAGIC_NUMBER & 0x0000FF00)>> 8) ) + ulSetError ( UL_WARNING, "ssgLoadSSG: File appears to be byte swapped!" ) ; + else + ulSetError ( UL_WARNING, "ssgLoadSSG: File has incorrect magic number!" ) ; + + return NULL ; + } + + /* + Save the old version number so we can do recursive loads + */ + + int oldFileVersion = _ssgFileVersionNumber ; + _ssgFileVersionNumber = ( magic & 0xFF ) ; + + if ( _ssgFileVersionNumber == 0 ) + { + ulSetError ( UL_WARNING, + "ssgLoadSSG: SSG file format version zero is no longer supported, sorry! For more, see the docs." ) ; + _ssgFileVersionNumber = oldFileVersion ; + return NULL ; + } + + if ( _ssgFileVersionNumber > SSG_FILE_VERSION ) + { + ulSetError ( UL_WARNING, + "ssgLoadSSG: This version of SSG is too old to load this file!" ) ; + _ssgFileVersionNumber = oldFileVersion ; + return NULL ; + } + + _ssgBaseList *oldInstanceList = _ssgInstanceList ; // in case of recursive loads + _ssgInstanceList = new _ssgBaseList ; + _ssgInstanceList -> add ( NULL ) ; // index 0 --> NULL + + int success = _ssgLoadObject ( fd, (ssgBase **) &kid, ssgTypeEntity () ) ; + + if ( ! success ) + { + ulSetError ( UL_WARNING, "ssgLoadSSG: Failed to load object." ) ; + kid = NULL ; + } + + delete _ssgInstanceList ; + _ssgInstanceList = oldInstanceList ; + _ssgFileVersionNumber = oldFileVersion ; + + fclose ( fd ) ; + + return kid ; +} + + +int ssgSaveSSG ( const char *filename, ssgEntity *ent ) +{ + FILE *fd = fopen ( filename, "wb" ) ; + + if ( fd == NULL ) + { + perror ( filename ) ; + ulSetError ( UL_WARNING, + "ssgSaveSSG: Failed to open '%s' for writing.", filename ) ; + return FALSE ; + } + + _ssgBaseList *oldInstanceList = _ssgInstanceList ; // for recursive saves + _ssgInstanceList = new _ssgBaseList ; + _ssgInstanceList -> add ( NULL ) ; // index 0 --> NULL + + _ssgWriteInt ( fd, SSG_FILE_MAGIC_NUMBER ) ; + + int success = _ssgSaveObject ( fd, ent ) ; + + if ( ! success ) + ulSetError ( UL_WARNING, "ssgSaveSSG: Failed to write object." ) ; + + delete _ssgInstanceList ; + _ssgInstanceList = oldInstanceList ; + + fclose ( fd ) ; + + return success ; +} + + diff --git a/src/ssg/ssgLoadStrip.cxx b/src/ssg/ssgLoadStrip.cxx new file mode 100644 index 0000000..3b91a48 --- /dev/null +++ b/src/ssg/ssgLoadStrip.cxx @@ -0,0 +1,119 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadStrip.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include "ssgLocal.h" + +static ssgLoaderOptions* current_options; + +ssgEntity* ssgLoadStrip( const char* fname, const ssgLoaderOptions* options ) { + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE* model_file = fopen(filename, "r"); + + if (model_file == NULL) { + ulSetError(UL_WARNING, "ssgLoadStrip: Couldn't open file '%s'.", filename); + return NULL; + } + + ssgSimpleState* state = new ssgSimpleState(); + state->setOpaque(); + state->disable(GL_BLEND); + state->disable(GL_ALPHA_TEST); + state->disable(GL_TEXTURE_2D); + state->enable(GL_COLOR_MATERIAL); + state->enable(GL_LIGHTING); + state->setShadeModel(GL_SMOOTH); + state->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.0f, 1.0f); + state->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.0f, 1.0f); + state->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + state->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + state->setShininess(50); + + int i; + int num_vertices = ulEndianReadLittle32(model_file); + ssgBranch* model = new ssgBranch(); + ssgVertexArray* vertices = new ssgVertexArray(); + ssgNormalArray* normals = new ssgNormalArray(); + + for (i = 0; i < num_vertices; i++) { + sgVec3 new_vec; + int j; + + for (j = 0; j < 3; j++) { + new_vec[j] = ulEndianReadLittleFloat(model_file); + } + vertices->add(new_vec); + + for (j = 0; j < 3; j++) { + new_vec[j] = ulEndianReadLittleFloat(model_file); + } + normals->add(new_vec); + } + + int num_strips = ulEndianReadLittle32(model_file); + for (i = 0; i < num_strips; i++) { + int num_indices = ulEndianReadLittle32(model_file); + + ssgIndexArray* strip_indices = new ssgIndexArray(num_indices); + + for (int j = 0; j < num_indices; j++) { + strip_indices->add( ulEndianReadLittle16(model_file) ); + } + + ssgVtxArray* varr = new ssgVtxArray( GL_TRIANGLE_STRIP, + vertices, + normals, + NULL, + NULL, + strip_indices ); + varr->setState( state ); + varr->setCullFace( TRUE ); + + ssgLeaf* leaf = current_options->createLeaf(varr, NULL); + model->addKid(leaf); + } + + int num_indices = ulEndianReadLittle32(model_file); + ssgIndexArray* indices = new ssgIndexArray(num_indices); + for (i = 0; i < num_indices; i++) { + indices->add( ulEndianReadLittle16(model_file) ); + } + + ssgVtxArray* varr = new ssgVtxArray( GL_TRIANGLES, + vertices, + normals, + NULL, + NULL, + indices ); + varr->setState( state ); + varr->setCullFace( TRUE ); + + ssgLeaf* leaf = current_options->createLeaf(varr, NULL); + model->addKid(leaf); + + return model; +} diff --git a/src/ssg/ssgLoadTGA.cxx b/src/ssg/ssgLoadTGA.cxx new file mode 100644 index 0000000..825eab9 --- /dev/null +++ b/src/ssg/ssgLoadTGA.cxx @@ -0,0 +1,452 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadTGA.cxx 1749 2003-01-30 00:51:59Z stromberg $ +*/ + + +#include "ssgLocal.h" + +#ifdef SSG_LOAD_TGA_SUPPORTED + +//#include + +/* + * Submitted by Sam Stickland : sam@spacething.org + * Targe loading code based on code written Dave Gay : f00Dave@bigfoot.com, http://personal.nbnet.nb.ca/daveg/ + * + * Jan 29, 2003: + * Added support for other image types, as well as run-length encoding. + * /marten (stromberg@users.sf.net) + */ + + +inline int get16u(const GLubyte *ptr) // little endian +{ + return (ptr[0] | (ptr[1] << 8)); +} + + +bool ssgLoadTGA ( const char *fname, ssgTextureInfo* info ) +{ + +#if 1 // new loader: + + + /* TGA file layout: + * + * ============================= + * Header: + * Ofs Sz Desc + * ---------------- + * 0 1 Image ID Length + * 1 1 Colormap Type + * 2 1 Image Type + * 3 2 Colormap Origin + * 5 2 Colormap Length + * 7 1 Colormap Entry Size + * 8 2 X Origin of Image + * 10 2 Y Origin of Image + * 12 2 Image Width + * 14 2 Image Height + * 16 1 Image Pixel Size + * 17 1 Image Descriptor + * ============================= + * Image Identification + * ============================= + * Colormap Data + * ============================= + * Image Data + * ============================= + */ + + + GLubyte header[18]; + + FILE *f = fopen(fname, "rb"); + + if ( f == NULL ) + { + ulSetError( UL_WARNING, "ssgLoadTGA: Failed to open '%s' for reading.", fname ); + return false; + } + + if ( fread(header, 18, 1, f) != 1 ) + { + ulSetError( UL_WARNING, "ssgLoadTGA: Failed to read header of '%s'.", fname ); + fclose(f); + return false; + } + + // colormap info + int cm_type = header[1]; + int cm_first = get16u(header + 3); + int cm_count = get16u(header + 5); + int cm_bits = header[7]; + + // image info + int type = header[2]; + int xsize = get16u(header + 12); + int ysize = get16u(header + 14); + int bits = header[16]; + + /* image types: + * + * 0 - no image + * 1 - colormap + * 2 - RGB + * 3 - grayscale + * 9 - colormap, RLE + * 10 - RGB, RLE + * 11 - grayscale, RLE + * + */ + + if ( cm_type > 1 || (type & ~(8+3)) != 0 || (type & 3) == 0 || ((type & 3) == 1 && cm_type == 0) ) + { + ulSetError( UL_WARNING, "ssgLoadTGA: '%s' is not a TGA image.", fname ); + fclose(f); + return false; + } + + if ( (type & 3) == 1 && cm_bits != 8 && cm_bits != 16 && cm_bits != 24 && cm_bits != 32 ) + { + ulSetError( UL_WARNING, "ssgLoadTGA: Unsupported colormap depth %d.", cm_bits ); + fclose(f); + return false; + } + + if ( ((type & 3) != 2 && bits != 8) || // colormap and grayscale must be 8-bit + ((type & 3) == 2 && bits != 16 && bits != 24 && bits != 32) ) // RGB may not be 8-bit + { + ulSetError( UL_WARNING, "ssgLoadTGA: Unsupported depth %d for image type %d.", bits, type ); + fclose(f); + return false; + } + + ulSetError( UL_DEBUG, "ssgLoadTGA: Loading '%s', %s %dx%d-%d%s.", + fname, + (type & 3) == 1 ? "colormap" : (type & 3) == 2 ? "RGB" : "grayscale", + xsize, ysize, bits, + (type & 8) != 0 ? " RLE" : ""); + + + bool eof = false; + + + // skip image identification + + if (fseek(f, header[0], SEEK_CUR) != 0) + eof = true; + + + // read colormap + + GLubyte *cm_data = 0; + + if (cm_type != 0) + { + cm_data = new GLubyte [ (cm_bits / 8) * cm_count ]; + if (fread(cm_data, (cm_bits / 8) * cm_count, 1, f) != 1) + eof = true; + } + + + // read image data + + GLubyte *image = new GLubyte [ (bits / 8) * xsize * ysize ]; + + if ((type & 8) != 0) + { + // unpack RLE data + + int comp = (bits / 8); + int current = 0; + + for (;;) + { + int code = getc(f); + + if (code == EOF) { + eof = true; + break; + } + + int length = (code & 0x7f) + 1; + + if (current + length > xsize * ysize) + { + ulSetError( UL_WARNING, "ssgLoadTGA: RLE unpack problems." ); + fclose(f); + delete [] cm_data; + delete [] image; + return false; + } + + if ((code & 0x80) == 0) + { + // raw packet + if (fread(image + current * comp, length * comp, 1, f) != 1) { + eof = true; + break; + } + current += length; + } + else + { + // run-length packet + GLubyte data[4]; + if (fread(data, comp, 1, f) != 1) { + eof = true; + break; + } + for (int i = 0; i < length; i++) + memcpy(image + (current++) * comp, data, comp); + } + + if (current == xsize * ysize) + break; + } + } + else + { + if (fread(image, (bits / 8) * xsize * ysize, 1, f) != 1) + eof = true; + } + + if (eof || ferror(f)) + { + ulSetError( UL_WARNING, "ssgLoadTGA: %s.", + ferror(f) ? "Read error" : "Unexpected end of file" ); + fclose(f); + delete [] cm_data; + delete [] image; + return false; + } + + fclose(f); + + + // apply colormap + + if ((type & 3) == 1) + { + int comp = (cm_bits / 8); + + GLubyte *source = image; + GLubyte *target = new GLubyte [ comp * xsize * ysize ]; + + for (int i = 0; i < xsize * ysize; i++) + { + int index = source[i] - cm_first; + if (index < 0 || index > cm_count) + memset(target + i * comp, 0, comp); + else + memcpy(target + i * comp, cm_data + index * comp, comp); + } + + delete [] source; + + image = target; + bits = cm_bits; + } + + delete [] cm_data; + + + // convert image to plain GL_LUMINANCE, GL_RGB or GL_RGBA + + int zsize = (bits == 8) ? 1 : (bits == 24) ? 3 : 4; + + GLubyte *pixels = new GLubyte [ xsize * ysize * zsize ]; + + for (int i = 0; i < xsize * ysize; i++) + { + switch (bits) { + + case 8: + pixels[i] = image[i]; + break; + + case 16: + { + int temp = get16u(image + 2*i); // ARRR RRGG GGGB BBBB + pixels[4*i + 0] = (temp & 0x7c00) >> 7; + pixels[4*i + 1] = (temp & 0x03e0) >> 2; + pixels[4*i + 2] = (temp & 0x001f) << 3; + pixels[4*i + 3] = (temp & 0x8000) ? 255 : 0; + break; + } + + case 24: + pixels[3*i + 0] = image[3*i + 2]; + pixels[3*i + 1] = image[3*i + 1]; + pixels[3*i + 2] = image[3*i + 0]; + break; + + case 32: + pixels[4*i + 0] = image[4*i + 2]; + pixels[4*i + 1] = image[4*i + 1]; + pixels[4*i + 2] = image[4*i + 0]; + pixels[4*i + 3] = image[4*i + 3]; + break; + } + } + + delete [] image; + +#if 0 + if (zsize == 3) + { + printf("writing 'out.ppm'...\n"); + FILE *f = fopen("out.ppm", "wb"); + fprintf(f, "P6\n%d %d\n255\n", xsize, ysize); + fwrite(pixels, 3 * xsize * ysize, 1, f); + fclose(f); + } +#endif + + if ( info != NULL ) + { + info -> width = xsize; + info -> height = ysize; + info -> depth = zsize; + info -> alpha = (zsize == 4); + } + + return ssgMakeMipMaps ( pixels, xsize, ysize, zsize ); + + +#else // old loader: + + +#define DEF_targaHeaderLength 12 +#define DEF_targaHeaderContent "\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + struct stat fileinfo; + int bytesRead, width, height, maxLen; + char *pData = NULL; + + if ( stat(fname, &fileinfo) == -1 ) { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname); + return false ; + } + + FILE *tfile; + if( (tfile = fopen(fname, "rb")) == NULL) { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname); + return false ; + } + + maxLen = fileinfo.st_size; + pData = new char [maxLen]; + fread (pData, maxLen, 1, tfile); + fclose (tfile); + pData[0] = 0x00; + + if( memcmp( pData, DEF_targaHeaderContent, DEF_targaHeaderLength ) != 0 ) { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Not a targa (apparently).", fname); + delete[] pData; + return false ; + } + + unsigned char smallArray[ 2 ]; + + memcpy( smallArray, pData + DEF_targaHeaderLength + 0, 2 ); + width = smallArray[ 0 ] + smallArray[ 1 ] * 0x0100; + + memcpy( smallArray, pData + DEF_targaHeaderLength + 2, 2 ); + height = smallArray[ 0 ] + smallArray[ 1 ] * 0x0100; + + memcpy( smallArray, pData + DEF_targaHeaderLength + 4, 2 ); + int depth = smallArray[ 0 ]; + // + smallArray[ 1 ] * 0x0100; + + if( ( width <= 0 ) || ( height <= 0 ) ) + { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Width and height < 0.", fname); + delete[] pData; + return false ; + } + + // Only allow 24-bit and 32-bit! + bool is24Bit( depth == 24 ); + bool is32Bit( depth == 32 ); + + if( !( is24Bit || is32Bit ) ) + { + ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Not 24 or 32 bit.", fname); + delete[] pData; + return false ; + } + + // Make it a BGRA array for now. + int bodySize( width * height * 4 ); + unsigned char * texels = new unsigned char[ bodySize ]; + if( is32Bit ) + { + // Texture is 32 bit + // Easy, just copy it. + memcpy( texels, pData + DEF_targaHeaderLength + 6, bodySize ); + } + else if( is24Bit ) + { + // Texture is 24 bit + bytesRead = DEF_targaHeaderLength + 6; + for( int loop = 0; loop < bodySize; loop += 4, bytesRead += 3 ) + { + memcpy( texels + loop, pData + bytesRead, 3 ); + texels[ loop + 3 ] = 255; // Force alpha to max. + } + } + + // Swap R & B (convert to RGBA). + for( int loop = 0; loop < bodySize; loop += 4 ) + { + unsigned char tempC = texels[ loop + 0 ]; + texels[ loop + 0 ] = texels[ loop + 2 ]; + texels[ loop + 2 ] = tempC; + } + + delete[] pData; + + if ( info != NULL ) + { + info -> width = width ; + info -> height = height ; + info -> depth = 4 ; + info -> alpha = is32Bit? 1: 0 ; + } + return ssgMakeMipMaps ( texels, width, height, 4) ; + + +#endif +} + +#else + +bool ssgLoadTGA ( const char *fname, ssgTextureInfo* info ) +{ + ulSetError ( UL_WARNING, + "ssgLoadTexture: '%s' - TGA support not configured", fname ) ; + return false ; +} + +#endif diff --git a/src/ssg/ssgLoadTRI.cxx b/src/ssg/ssgLoadTRI.cxx new file mode 100644 index 0000000..3b4bbe6 --- /dev/null +++ b/src/ssg/ssgLoadTRI.cxx @@ -0,0 +1,128 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadTRI.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +// +// TRI ( AC3D triangle file ) import for SSG/PLIB +// Written by Dave McClurg (dpm@efn.org) in March-2000 +// + +#include "ssgLocal.h" + +#define MAX_TRI 100000 + +struct triData +{ + sgVec3 v[3] ; + int color ; +} ; + +/******************************************************************************/ + +ssgEntity *ssgLoadTRI ( const char *fname, const ssgLoaderOptions* options ) + +/******************************************************************************/ + +/* + Purpose: + + reads an AC3D triangle file. + + Example: + + Each line contains 9 floating point values and a 1 hex value for color. + the 9 floating point values represent 3 vertices of a triangle + the color format is 0xRRGGBB (eg 0xffffff is white) + + 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0xffffff + +*/ +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + const ssgLoaderOptions* current_options = ssgGetCurrentOptions () ; + + //open the file + char filename [ 1024 ] ; + current_options -> makeModelPath ( filename, fname ) ; + + FILE *loader_fd = fopen ( filename, "ra" ) ; + + if ( loader_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadTRI: Failed to open '%s' for reading", filename ) ; + return NULL ; + } + + //read the data + triData* tri = new triData [ MAX_TRI ] ; + int num_tri = 0 ; + + char buffer [ 1024 ] ; + + while ( fgets ( buffer, 1024, loader_fd ) != NULL ) + { + float coord [9] ; + int color ; + + if ( sscanf ( buffer, "%e %e %e %e %e %e %e %e %e %d", + &coord[0], &coord[1], &coord[2], + &coord[3], &coord[4], &coord[5], + &coord[6], &coord[7], &coord[8], + &color ) != 10 ) + { + ulSetError ( UL_WARNING, "ssgLoadTRI: Can't parse triangle: %s", buffer ) ; + } + else if ( num_tri < MAX_TRI ) + { + float* cp = coord; + for ( int i=0; i<3; i++ ) + for ( int j=0; j<3; j++ ) + tri[ num_tri ].v[ i ][ j ] = *cp++; + + tri[ num_tri ].color = color ; + num_tri ++ ; + } + else + { + break; + } + } + + fclose ( loader_fd ) ; + + ssgBranch* current_branch = NULL ; + + if ( num_tri ) + { + ssgVertexArray* vlist = new ssgVertexArray ( num_tri * 3 ) ; + for ( int i=0; i add ( tri[ i ].v[ j ] ) ; + ssgVtxTable *vtab = new ssgVtxTable ( GL_TRIANGLES, vlist, 0, 0, 0 ); + current_branch = new ssgTransform () ; + current_branch -> addKid ( vtab ) ; + } + + delete[] tri ; + + return current_branch ; +} diff --git a/src/ssg/ssgLoadTexture.cxx b/src/ssg/ssgLoadTexture.cxx new file mode 100644 index 0000000..785a435 --- /dev/null +++ b/src/ssg/ssgLoadTexture.cxx @@ -0,0 +1,364 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadTexture.cxx 2137 2009-01-01 19:10:32Z fayjf $ +*/ + + +#include "ssgLocal.h" +#include + + +struct _ssgTextureFormat +{ + const char *extension ; + bool (*loadfunc) ( const char *, ssgTextureInfo* info ) ; +} ; + + +enum { MAX_FORMATS = 100 } ; + +static _ssgTextureFormat formats [ MAX_FORMATS ] ; +static int num_formats = 0 ; + +static int total_texels_loaded = 0 ; + + +int ssgGetNumTexelsLoaded () +{ + return total_texels_loaded ; +} + + +bool ssgMakeMipMaps ( GLubyte *image, int xsize, int ysize, int zsize, bool freeData ) +{ + bool non_power_of_two_tex_supported = ssgIsExtensionSupported( "GL_ARB_texture_non_power_of_two" ); + + if ( !non_power_of_two_tex_supported && + ( ! ((xsize & (xsize-1))==0) || + ! ((ysize & (ysize-1))==0) ) ) + { + ulSetError ( UL_WARNING, "Map is not a power-of-two in size!" ) ; + return false ; + } + + GLubyte *texels [ 20 ] ; /* One element per level of MIPmap */ + + for ( int l = 0 ; l < 20 ; l++ ) + texels [ l ] = NULL ; + + texels [ 0 ] = image ; + + int lev ; + + for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 || + ( ysize >> (lev+1) ) != 0 ) ; lev++ ) + { + /* Suffix '1' is the higher level map, suffix '2' is the lower level. */ + + int l1 = lev ; + int l2 = lev+1 ; + int w1 = xsize >> l1 ; + int h1 = ysize >> l1 ; + int w2 = xsize >> l2 ; + int h2 = ysize >> l2 ; + + if ( w1 <= 0 ) w1 = 1 ; + if ( h1 <= 0 ) h1 = 1 ; + if ( w2 <= 0 ) w2 = 1 ; + if ( h2 <= 0 ) h2 = 1 ; + + texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ; + + for ( int x2 = 0 ; x2 < w2 ; x2++ ) + for ( int y2 = 0 ; y2 < h2 ; y2++ ) + for ( int c = 0 ; c < zsize ; c++ ) + { + int x1 = x2 + x2 ; + int x1_1 = ( x1 + 1 ) % w1 ; + int y1 = y2 + y2 ; + int y1_1 = ( y1 + 1 ) % h1 ; + + int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * zsize + c ] ; + int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * zsize + c ] ; + int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * zsize + c ] ; + int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ; + +if ( c == 3 ) /* Alpha */ +{ + int a = t1 ; + if ( t2 > a ) a = t2 ; + if ( t3 > a ) a = t3 ; + if ( t4 > a ) a = t4 ; + texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] = a ; +} +else + texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] = + ( t1 + t2 + t3 + t4 ) / 4 ; + } + } + + texels [ lev+1 ] = NULL ; + + glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ; + + int map_level = 0 ; + +#ifndef PROXY_TEXTURES_ARE_BROKEN + GLint ww ; + + do + { + glTexImage2D ( GL_PROXY_TEXTURE_2D, + map_level, zsize, xsize, ysize, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, NULL ) ; + + glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ; + + if ( ww == 0 ) + { + if ( freeData ) + delete [] texels [ 0 ] ; + else + delete [] texels [ 1 ] ; /* User does not want input image freed, but we need to free all the rest of them */ + + xsize >>= 1 ; + ysize >>= 1 ; + + for ( int l = 0 ; texels [ l ] != NULL ; l++ ) + texels [ l ] = texels [ l+1 ] ; + + if ( xsize < 64 && ysize < 64 ) + { + ulSetError ( UL_FATAL, + "SSG: OpenGL will not accept a downsized version ?!?" ) ; + } + } + } while ( ww == 0 ) ; +#endif + + for ( int i = 0 ; texels [ i ] != NULL ; i++ ) + { + int w = xsize>>i ; + int h = ysize>>i ; + + if ( w <= 0 ) w = 1 ; + if ( h <= 0 ) h = 1 ; + + total_texels_loaded += w * h ; + + glTexImage2D ( GL_TEXTURE_2D, + map_level, zsize, w, h, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ; + map_level++ ; + if ( freeData || ( i > 0 ) ) + delete [] texels [ i ] ; + } + + return true ; +} + + +static void ssgLoadDummyTexture ( ssgTextureInfo* info ) +{ + GLubyte *image = new GLubyte [ 2 * 2 * 3 ] ; + + /* Red and white chequerboard */ + + image [ 0 ] = 255 ; image [ 1 ] = 0 ; image [ 2 ] = 0 ; + image [ 3 ] = 255 ; image [ 4 ] = 255 ; image [ 5 ] = 255 ; + image [ 6 ] = 255 ; image [ 7 ] = 255 ; image [ 8 ] = 255 ; + image [ 9 ] = 255 ; image [ 10] = 0 ; image [ 11] = 0 ; + + if ( info != NULL ) + { + info -> width = 2 ; + info -> height = 2 ; + info -> depth = 3 ; + info -> alpha = 0 ; + } + + ssgMakeMipMaps ( image, 2, 2, 3 ) ; +} + + +void ssgAddTextureFormat ( const char* extension, + bool (*loadfunc) (const char*, ssgTextureInfo* info) ) +{ + for ( int i = 0 ; i < num_formats ; i++ ) + { + if ( ulStrEqual ( formats [ i ] . extension, extension ) ) + { + formats [ i ] . extension = extension ; + formats [ i ] . loadfunc = loadfunc ; + return ; + } + } + + if ( num_formats < MAX_FORMATS ) + { + formats [ num_formats ] . extension = extension ; + formats [ num_formats ] . loadfunc = loadfunc ; + num_formats ++ ; + } + else + { + ulSetError ( UL_WARNING, "ssgAddTextureFormat: too many formats" ); + } +} + + +static int fileMTimeCmp( const char *fname_input, const char *fname_output ) +// Compare the two input file names against their modification time +// -1: fname_input newer than fname_output +// 1: fname_input older than fname_output +// 0: stat error +{ +#if defined(UL_WIN32) && !defined(UL_CYGWIN) +#define stat _stat +#endif + struct stat buffer_in, buffer_out; + + if ( stat( fname_input, &buffer_in ) == 0 && stat( fname_output, &buffer_out ) == 0 ) + return buffer_in.st_mtime > buffer_out.st_mtime ? -1 : 1; + else + return 0; +} + + +bool ssgConvertTexture( char * fname_output, const char * fname_input ) +// converts file to .rgb (Silicon Graphics) format +// returns true if the file has been converted to rgb, or already exists as rgb +// if it returns false, then it has already output an error message +{ + char *extension ; + + strcpy( fname_output, fname_input); // copy so that a) there is enough room for .rgb and b) we don't change the buffer of fname_input + extension = strrchr(fname_output, '.'); + if ( extension == NULL ) + { + ulSetError(UL_WARNING, "There is no extension in the texture '%s'.", fname_input); + return false; // no extension -> give up + } + extension[1] = 'r'; + extension[2] = 'g'; + extension[3] = 'b'; + extension[4] = 0; + + if ( ulFileExists ( fname_output ) ) + return true; // found *.rgb-file + + // look for original, non-rgb - file + if ( !ulFileExists ( fname_input ) ) + { + ulSetError(UL_WARNING, "Can't find the texture file '%s'.", fname_input); + return false; // no input file => we can't convert it + } + + if ( ulFileExists ( fname_output ) && fileMTimeCmp( fname_input, fname_output ) != -1 ) + return true; // found *.rgb-file, and it's newer than fname_input, so no conversion is needed + + // ****** found original file. convert it. ****** + char command [ 1024 ] ; + char *conv_cmd = +#ifdef UL_WIN32 + "imconvert"; +#else + "convert"; +#endif + sprintf(command, "%s -verbose %s sgi:%s", conv_cmd, fname_input, fname_output); + + if ( system( command ) < 0 || !ulFileExists ( fname_output ) ) + { + ulSetError(UL_WARNING, "Couldn't convert texture '%s'. Did you install ImageMagick?" + " You may also convert it manually to '%s' and reload the model.", + fname_input, fname_output); + return false; + } + + return true; +} + + + +bool ssgLoadTexture ( const char * filename, ssgTextureInfo* info ) +{ + if ( info != NULL ) + { + info -> width = 0 ; + info -> height = 0 ; + info -> depth = 0 ; + info -> alpha = 0 ; + } + + if ( filename == NULL || *filename == '\0' ) + return false ; + + //find extension + const char *extn = & ( filename [ strlen(filename) ] ) ; + while ( extn != filename && *extn != '/' && *extn != '.' ) + extn-- ; + + if ( *extn != '.' ) + { + ulSetError ( UL_WARNING, "ssgLoadTexture: Cannot determine file type for '%s'", filename ); + ssgLoadDummyTexture ( info ) ; + return false ; + } + + _ssgTextureFormat *f = formats ; + for ( int i=0; iloadfunc != NULL && + ulStrNEqual ( extn, f->extension, strlen(f->extension) ) ) + { + if ( f->loadfunc( filename, info ) ) + return true ; + + ssgLoadDummyTexture ( info ) ; /* fail */ + return false ; + } + } +#ifdef SSG_LOAD_SGI_SUPPORTED + char * fname_output = new char [ strlen(filename) + 4 ]; // +4 as reserve for .rgb + if ( ssgConvertTexture( fname_output, filename) ) + if ( ssgLoadSGI ( fname_output, info ) ) + { delete [] fname_output; + return true; + } + delete [] fname_output; + +#else + ulSetError ( UL_WARNING, "ssgLoadTexture: Unrecognised file type '%s'", extn ) ; +#endif + + ssgLoadDummyTexture ( info ) ; + return false ; +} + + diff --git a/src/ssg/ssgLoadVRML.h b/src/ssg/ssgLoadVRML.h new file mode 100644 index 0000000..275dbcb --- /dev/null +++ b/src/ssg/ssgLoadVRML.h @@ -0,0 +1,150 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002, 2002 William Lachance, Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net +*/ + +class ssgListOfNodes : public ssgSimpleList +// list of POINTERs to ssgBase +// used for storing/querying DEF'd info +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) { return NULL; }; // Fixme NIV14: 2do + ssgListOfNodes ( int init = 3 ) : ssgSimpleList ( sizeof(ssgBase*), init ) {} + class ssgBase *get ( unsigned int n ) { return *( (class ssgBase **) raw_get ( n ) ) ; } + void add ( class ssgBase *thing ) { raw_add ( (char *) &thing ) ; } ; + void replace( class ssgBase *thing, unsigned int n ) { raw_set( (char *) &thing, n); } + + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) {}; // Fixme NIV14: 2do +}; + + +class _nodeIndex +{ + private: + ssgListOfNodes *nodeList; + public: + _nodeIndex() + { + nodeList = new ssgListOfNodes(); + } + ~_nodeIndex() + { + for( int i=0; igetNum(); i++ ) + { + ssgBase *extractedThing = nodeList->get( i ); + if( extractedThing->getRef() == 0 ) + delete( extractedThing ); + } + } + + void insert( ssgBase *thing ) + { + // replace the node if a node with an identical tag already exists + for( int i=0; igetNum(); i++ ) + { + ssgBase *tempThing = nodeList->get( i ); + if( !strcmp( tempThing->getName(), thing->getName() ) ) + { + nodeList->replace( thing, i ); + ulSetError(UL_DEBUG, "Replaced element %i.", i); + return; + } + } + // otherwise add it to end of list + nodeList->add( thing ); + } + + ssgBase * extract( char *defName ) + { + for( int i=0; igetNum(); i++ ) + { + ssgBase *extractedThing = nodeList->get( i ); + if( !strcmp( extractedThing->getName(), defName ) ) + return extractedThing; + } + + return NULL; + } +}; + +// the current properties for a certain point in scene traversal +class _traversalState +{ + private: + ssgVertexArray *vertices; + ssgTexCoordArray *textureCoordinates; + ssgTransform *transform; + ssgTexture *texture; + bool textureCoordinatesArePerFaceAndVertex; + GLenum frontFace; + bool enableCullFace; + + public: + + bool getEnableCullFace() { return enableCullFace; } + void setEnableCullFace( bool newEnableCullFace ) { enableCullFace = newEnableCullFace; } + + GLenum getFrontFace( void ) { return frontFace; } + void setFrontFace( GLenum newFrontFace ) { frontFace = newFrontFace; } + + ssgTransform * getTransform( void ) { return transform; } + void setTransform( ssgTransform *newTransform ) { transform = newTransform; } + + ssgVertexArray * getVertices( void ) { return vertices; } + void setVertices( ssgVertexArray *newVertices ) { vertices = newVertices; } + + ssgTexCoordArray *getTextureCoordinates( void ) { return textureCoordinates; } + void setTextureCoordinates( ssgTexCoordArray *newTextureCoordinates ) { textureCoordinates = newTextureCoordinates; } + + ssgTexture * getTexture( void ) { return texture; } + void setTexture( ssgTexture *newTexture ) { texture = newTexture; } + + bool areTextureCoordinatesArePerFaceAndVertex( void ) { return textureCoordinatesArePerFaceAndVertex; } + + _traversalState *clone() { return new _traversalState(*this); } + _traversalState() { vertices = NULL; textureCoordinates = NULL; transform = NULL; texture = NULL; textureCoordinatesArePerFaceAndVertex = TRUE; enableCullFace = FALSE; } +}; + +// tags for functions which may actually modify the scene graph +struct _parseTag +{ + const char *token ; + bool (*func) ( ssgBranch *parentBranch, _traversalState *parentData, char *defName ) ; +} ; + +// the vrml1 common subset that is shared with inventor +bool vrml1_parseCoordinate3( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseTextureCoordinate2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseShapeHints( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseMatrixTransform( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseScale( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseRotation( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseTranslation( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +bool vrml1_parseUseDirective( ssgBranch *parentBranch, _traversalState *currentData, char *useName, char *defName ); +bool vrml1_parseCoordIndex( ssgLoaderWriterMesh *loaderMesh, _traversalState *currentData ); +bool vrml1_parseTextureCoordIndex( ssgLoaderWriterMesh *loaderMesh, _traversalState *currentData ); +bool parseUnidentified(); + +void applyTransform( ssgTransform *currentTransform, _traversalState *currentData ); +void mergeTransformNodes( ssgTransform *newTransform, ssgTransform *oldTransform1, ssgTransform *oldTransform2 ); +bool parseVec( SGfloat *v, int vSize ); +ssgIndexArray * parseIndexArray( _traversalState *currentData ); + +extern _ssgParser vrmlParser; diff --git a/src/ssg/ssgLoadVRML1.cxx b/src/ssg/ssgLoadVRML1.cxx new file mode 100644 index 0000000..4f96f2a --- /dev/null +++ b/src/ssg/ssgLoadVRML1.cxx @@ -0,0 +1,932 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 William Lachance, Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net +*/ + +// ssgLoadVRML1: loads vrml1 files into the scenegraph +// Written by William Lachance (wlach@interlog.com) +// known bugs/limitations: +// - implicit texture mapping is not supported +// - explicit normal definitions not supported (they are calculated automatically) +// - only a very small subset of the inventor spec is supported +// - there is no support for primitives (cubes, spheres, cylinders, or cones) +// - no support for materials +// - no support for weblinks in vrml(well, this isn't much of a tragedy) :-) +// .. +// this loader borrows, to some extent, from the previous vrml file loader: +// - array based token identification +// - flipping the axises so that z is up + +#include "ssgLocal.h" +#include "ssgParser.h" +#include "ssgLoaderWriterStuff.h" + +#include "ssgLoadVRML.h" + +static _ssgParserSpec parser_spec = +{ + "\r\n\t, ", // delim_chars_skipable + 0, // delim_chars_non_skipable + NULL, // pre_processor + "{[", // open_brace_chars + "}]", // close_brace_chars + '"', // quote_char + '#', // comment_char + 0 // comment_string +}; + +_ssgParser vrmlParser; +static ssgLoaderOptions* currentOptions = NULL ; +static _nodeIndex *definedNodes = NULL; + +static bool vrml1_parseSeparator( ssgBranch *parentBranch, _traversalState *parentData, char *defName ); +static bool vrml1_parseSwitch( ssgBranch *parentBranch, _traversalState *parentData, char *defName ); +static bool vrml1_parseIndexedFaceSet( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); +static bool vrml1_parseTexture2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ); + +static _parseTag vrmlTags [] = +{ + { "Separator", vrml1_parseSeparator }, + { "Switch", vrml1_parseSwitch }, + { "IndexedFaceSet", vrml1_parseIndexedFaceSet }, + { "Coordinate3", vrml1_parseCoordinate3 }, + { "TextureCoordinate2", vrml1_parseTextureCoordinate2 }, + { "Texture2", vrml1_parseTexture2 }, + { "ShapeHints", vrml1_parseShapeHints }, + { "MatrixTransform", vrml1_parseMatrixTransform }, + { "Scale", vrml1_parseScale }, + { "Rotation", vrml1_parseRotation }, + { "Translation", vrml1_parseTranslation }, + { NULL, NULL }, +}; + +ssgEntity *ssgLoadVRML1( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + currentOptions = ssgGetCurrentOptions () ; + + if ( !vrmlParser.openFile( fname, &parser_spec ) ) { + ulSetError ( UL_WARNING, "ssgLoadVRML1: Failed to open '%s' for reading", fname ) ; + return 0; + } + + definedNodes = new _nodeIndex(); + + // check for a valid header header + char *token; + if( !(token = vrmlParser.getRawLine()) ) + return 0; + if( strstr( token, "#VRML V1.0 ascii" ) == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML1: valid vrml1 header not found" ); + return 0; + } + + // creating a root node.. (a transform that changes Zup with Yup) + ssgBranch *rootTransform = new ssgTransform(); + sgCoord *tmpCoord = new sgCoord(); + sgSetCoord( tmpCoord, 0.0f, 0.0f, 0.0f, 0.0f, 90.0f, 0.0f ); + ((ssgTransform *)rootTransform)->setTransform( tmpCoord ); + + vrmlParser.expectNextToken( "Separator" ); + + if( !vrml1_parseSeparator( (ssgBranch *)rootTransform, NULL, NULL ) ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML: Failed to extract valid object(s) from %s", fname ) ; + delete( definedNodes ); + delete( rootTransform ); + return NULL ; + } + + vrmlParser.closeFile(); + delete( definedNodes ); + + return (ssgBranch *)rootTransform; +} + +static bool vrml1_parseSeparator( ssgBranch *parentBranch, _traversalState *parentData, char *defName ) +{ + char *childDefName = NULL; + + char *token; + + vrmlParser.expectNextToken( "{" ); + + // create a branch for this node + ssgBranch *currentBranch = new ssgBranch(); + + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + _traversalState *currentData; + if( parentData == NULL ) + currentData = new _traversalState(); + else + currentData = parentData->clone(); + + token = vrmlParser.getNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "DEF" ) ) + { + token = vrmlParser.getNextToken( NULL ); + ulSetError(UL_DEBUG, "DEF: Found an object definition %s.", token); + childDefName = new char[50]; + strncpy( childDefName, token, 50); + } + else if( !strcmp( token, "USE" ) ) + { + token = vrmlParser.getNextToken( NULL ); + ulSetError(UL_DEBUG, "USE: Found a use directive %s.", token); + if( !vrml1_parseUseDirective( currentBranch, currentData, token, childDefName ) ) + { + delete( currentBranch ); + delete( currentData ); + if( childDefName != NULL ) + delete [] childDefName; + return FALSE; + } + } + else + { + int i=0; bool tokenFound = FALSE; + while( vrmlTags[i].token != NULL && !tokenFound ) + { + if( !strcmp( token, vrmlTags[i].token ) ) + { + if( !(vrmlTags[i].func( currentBranch, currentData, childDefName ) ) ) + { + delete( currentBranch ); + delete( currentData ); + if( childDefName != NULL ) + delete [] childDefName; + return FALSE; + } + + tokenFound = TRUE; + } + i++; + } + if( !tokenFound ) + parseUnidentified(); + } + token = vrmlParser.getNextToken( NULL ); + } + + parentBranch->addKid( currentBranch ); + + // delete the currentData structure (we may use its content, but not its form) + delete( currentData ); + + return TRUE; +} + +static bool vrml1_parseSwitch( ssgBranch *parentBranch, _traversalState *parentData, char *defName ) +// UNSUPPORTED BEHAVIOUR: does not do a check for a whichChild parameter. Assumes that a switch +// "hides" all of its children. +{ + char *childDefName = NULL; + + char *token; + + vrmlParser.expectNextToken( "{" ); + + // create a branch for this node + ssgBranch *currentBranch = new ssgSelector(); + ((ssgSelector *)currentBranch)->select( 0 ); // fixme: allow for children to be traversed + + + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + _traversalState *currentData; + if( parentData == NULL ) + currentData = new _traversalState(); + else + currentData = parentData->clone(); + + token = vrmlParser.getNextToken( NULL ); + + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "DEF" ) ) + { + token = vrmlParser.getNextToken( NULL ); + ulSetError(UL_DEBUG, "DEF: Found an object definition %s.", token); + if( childDefName != NULL ) + delete [] childDefName; + childDefName = ulStrDup ( token ) ; + } + else if( !strcmp( token, "USE" ) ) + { + token = vrmlParser.getNextToken( NULL ); + ulSetError(UL_DEBUG, "USE: Found a use directive %s.", token); + if( !vrml1_parseUseDirective( currentBranch, currentData, token, childDefName ) ) + { + delete( currentBranch ); + delete( currentData ); + if( childDefName != NULL ) + delete [] childDefName; + return FALSE; + } + } + else + { + int i=0; bool tokenFound = FALSE; + while( vrmlTags[i].token != NULL && !tokenFound ) + { + if( !strcmp( token, vrmlTags[i].token ) ) + { + if( !(vrmlTags[i].func( currentBranch, currentData, childDefName ) ) ) + { + delete( currentBranch ); + delete( currentData ); + if( childDefName != NULL ) + delete [] childDefName; + return FALSE; + } + + tokenFound = TRUE; + } + i++; + } + if( !tokenFound ) + parseUnidentified(); + } + + token = vrmlParser.getNextToken( NULL ); + } + + parentBranch->addKid( currentBranch ); + + delete( currentData ); // delete the currentData structure (we may use its content, but not its form) + + return TRUE; +} + + +// parseVec: tries to parse a vec (of vSize), returns true if successful, false otherwise +bool parseVec( SGfloat *v, int vSize ) +{ + for( int i=0; iadd( index ); + else + return NULL; + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken( "-1" ); + + // we have to reverse vertex ordering if vertices are in clockwise order + if( currentData->getFrontFace() == GL_CW ) + { + // so return something else that goes in reverse order + ssgIndexArray *reversedIndexArray = new ssgIndexArray( indexArray->getNum() ); + for( int i=(indexArray->getNum()-1); i>=0; i-- ) + { + int newIndex = (int)*indexArray->get( i ); + reversedIndexArray->add( newIndex ); + } + delete( indexArray ); + return reversedIndexArray; + } + + return indexArray; +} + +// parseCoordinate3: parses a list of 3d coordinates, adds them to the current +// vertice array +bool vrml1_parseCoordinate3( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + int numVertices = 0; + + // Ok, now we can allocate a new vertex table + //ssgVertexArray *currentVertices = new ssgVertexArray(); + ssgVertexArray *currentVertices = new ssgVertexArray(); + if( defName != NULL ) + { + currentVertices->setName( defName ); + definedNodes->insert( currentVertices ); + } + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("point"); + + // an array? most likely.. + token = vrmlParser.peekAtNextToken( NULL ); + if( !strcmp( token, "[" ) ) + { + vrmlParser.expectNextToken("["); + // begin parsing vertices + token = vrmlParser.peekAtNextToken( NULL ); + + while( strcmp( token, "]" ) ) + { + sgVec3 v; + if( ! parseVec( v, 3 ) ) + return FALSE; + numVertices++; + currentVertices->add( v ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken("]"); + } + + // otherwise it must be a singular value + else + { + sgVec3 v; + if( ! parseVec( v, 3 ) ) + return FALSE; + numVertices++; + currentVertices->add( v ); + } + + ulSetError(UL_DEBUG, "Level: %i. Found %i vertices here.", vrmlParser.level, numVertices); + + vrmlParser.expectNextToken("}"); + + currentData->setVertices( currentVertices ); + + return TRUE; +} + +bool vrml1_parseTextureCoordinate2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + int numTextureCoordinates = 0; + + ssgTexCoordArray *currentTextureCoordinates = new ssgTexCoordArray(); + if( defName != NULL ) + { + currentTextureCoordinates->setName( defName ); + definedNodes->insert( currentTextureCoordinates ); + } + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("point"); + + // an array? most likely.. + token = vrmlParser.peekAtNextToken( NULL ); + if( !strcmp( token, "[" ) ) + { + vrmlParser.expectNextToken("["); + // begin parsing TexCoords + token = vrmlParser.peekAtNextToken( NULL ); + + while( strcmp( token, "]" ) ) + { + sgVec2 v; + if( ! parseVec( v, 2 ) ) + return FALSE; + numTextureCoordinates++; + currentTextureCoordinates->add( v ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken("]"); + } + + // otherwise it must be a singular value + else + { + sgVec2 v; + if( ! parseVec( v, 2 ) ) + return FALSE; + numTextureCoordinates++; + currentTextureCoordinates->add( v ); + } + + ulSetError(UL_DEBUG, "Level: %i. Found %i TexCoords here.", vrmlParser.level, numTextureCoordinates); + + vrmlParser.expectNextToken("}"); + + currentData->setTextureCoordinates( currentTextureCoordinates ); + + return TRUE; +} + +static bool vrml1_parseIndexedFaceSet( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + bool texCoordIndexGiven = FALSE; + + ssgBranch *currentBranch = new ssgBranch(); + if( defName != NULL ) + { + currentBranch->setName( defName ); + definedNodes->insert( currentBranch ); + } + + ssgLoaderWriterMesh *loaderMesh = new ssgLoaderWriterMesh(); + loaderMesh->createFaces(); + loaderMesh->setVertices( currentData->getVertices() ); + if( currentData->getTexture() != NULL && currentData->getTextureCoordinates() != NULL ) + loaderMesh->createPerFaceAndVertexTextureCoordinates2(); + + vrmlParser.expectNextToken("{"); + + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "coordIndex" ) ) + { + vrmlParser.expectNextToken("coordIndex"); + if( !vrml1_parseCoordIndex( loaderMesh, currentData ) ) + { + delete( currentBranch ); + delete( loaderMesh ); + return FALSE; + } + + } + + else if( !strcmp( token, "textureCoordIndex" ) ) + { + texCoordIndexGiven = TRUE; + vrmlParser.expectNextToken("textureCoordIndex"); + if( !vrml1_parseTextureCoordIndex( loaderMesh, currentData ) ) + { + delete( currentBranch ); + delete( loaderMesh ); + return FALSE; + } + + } + else + token = vrmlParser.getNextToken( NULL ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + + //ulSetError(UL_DEBUG, "Level: %i. Found %i faces here.", vrmlParser.level, numFaces); + + vrmlParser.expectNextToken( "}" ); + + // ------------------------------------------------------- + // add the face set to ssg + // ------------------------------------------------------- + + // kludge. We need a state for addToSSG: + ssgSimpleState * ss = new ssgSimpleState () ; // (0) ? + ss -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5, 1.0); + ss -> setMaterial ( GL_DIFFUSE, 1.0, 1.0, 1.0, 1.0) ; // 0.8, 0.8, 1.0, 1.0f + ss -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + ss -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0, 1.0); + ss -> setShininess ( 20 ) ; // Fixme, NIV14: Is that correct? + + // ------------------------------------------------------- + // texturing stuff + // ------------------------------------------------------- + // todo: give an implicit mapping if texture coordinates are not given + // todo: add support for per-vertex texturing + if( currentData->getTexture() != NULL && currentData->getTextureCoordinates() != NULL && texCoordIndexGiven ) + { + ss -> setTexture ( currentData->getTexture() ); + ss -> enable( GL_TEXTURE_2D ); + } + else + ss -> disable( GL_TEXTURE_2D ); + + ss -> disable ( GL_COLOR_MATERIAL ) ; + //ss -> enable ( GL_COLOR_MATERIAL ) ; + //ss -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + ss -> enable ( GL_LIGHTING ) ; + ss -> setShadeModel ( GL_SMOOTH ) ; + + ss ->disable(GL_ALPHA_TEST); //needed? + + ss -> disable ( GL_BLEND ) ; + + ss -> setOpaque () ; + + if( !currentData->getEnableCullFace() ) + ss->disable( GL_CULL_FACE ); + + if( !loaderMesh->checkMe() ) + { + delete( currentBranch ); + delete( loaderMesh ); + return FALSE; + } + + if( currentData->getTransform() != NULL ) + { + currentBranch->addKid( currentData->getTransform() ); // FIXME: in case we're reusing transforms, perhaps they should be reinstanced? (currently we don't allow transforms to be defed) + loaderMesh->addToSSG( ss, currentOptions, currentData->getTransform() ); + } + else + loaderMesh->addToSSG( ss, currentOptions, currentBranch ); + + parentBranch->addKid( currentBranch ); + + delete( loaderMesh ); + + return TRUE; +} + +static bool vrml1_parseTexture2( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + char *fileName = NULL; bool wrapU = FALSE, wrapV = FALSE; + + vrmlParser.expectNextToken("{"); + + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "filename") ) + { + vrmlParser.expectNextToken("filename"); + token = vrmlParser.getNextToken( NULL ); + fileName = new char[ strlen( token ) + 1]; + strcpy( fileName, token ); + } + else if( !strcmp( token, "wrapS") ) + { + vrmlParser.expectNextToken("wrapS"); + token = vrmlParser.getNextToken( NULL ); + if( !strcmp( token, "REPEAT") ) + wrapU = TRUE; + } + else if( !strcmp( token, "wrapT") ) + { + vrmlParser.expectNextToken("wrapT"); + token = vrmlParser.getNextToken( NULL ); + if( !strcmp( token, "REPEAT") ) + wrapV = TRUE; + } + else + token = vrmlParser.getNextToken( NULL ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + + + if( fileName == NULL ) + return FALSE; + + //ssgTexture *currentTexture = new ssgTexture( fileName, wrapU, wrapV ); + ssgTexture *currentTexture = currentOptions -> createTexture ( fileName, wrapU, wrapV ); + currentData->setTexture( currentTexture ); + vrmlParser.expectNextToken("}"); + + delete [] fileName; + + return TRUE; +} + +bool vrml1_parseShapeHints( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + char *token; + vrmlParser.expectNextToken("{"); + + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "}" ) ) + { + if( !strcmp( token, "vertexOrdering") ) + { + vrmlParser.expectNextToken("vertexOrdering"); + token = vrmlParser.getNextToken( NULL ); + if( !strcmp( token, "CLOCKWISE") ) + { + currentData->setEnableCullFace( TRUE ); + currentData->setFrontFace( GL_CW ); + } + else if( !strcmp( token, "COUNTERCLOCKWISE") ) + { + currentData->setEnableCullFace( TRUE ); + currentData->setFrontFace( GL_CCW ); + } + else if( !strcmp( token, "UNKNOWN_ORDERING") ) + currentData->setEnableCullFace( FALSE ); + else + { + ulSetError ( UL_WARNING, "ssgLoadVRML: invalid vertex ordering directive" ) ; + return FALSE; + } + + } + else + token = vrmlParser.getNextToken( NULL ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken("}"); + + return TRUE; +} + +bool vrml1_parseMatrixTransform( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + ssgTransform *currentTransform = new ssgTransform(); + sgMat4 transformMat; + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("matrix"); + for( unsigned int i=0; i<4; i++ ) + for( unsigned int j=0; j<4; j++ ) + { + if( !vrmlParser.getNextFloat( transformMat[i][j], NULL ) ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML: Expected a float for a matrix, didn't get it." ) ; + return FALSE; + } + } + vrmlParser.expectNextToken("}"); + + currentTransform->setTransform( transformMat ); + + applyTransform( currentTransform, currentData ); + + //ulSetError(UL_DEBUG, "Found a Matrix Transform (%f, %f, %f %f), (%f, %f, %f %f), (%f, %f, %f %f), (%f, %f, %f %f)", xForm[0][0], xForm[1][0], xForm[2][0], xForm[3][0], + // xForm[0][1], xForm[1][1], xForm[2][1], xForm[3][1], + // xForm[0][2], xForm[1][2], xForm[2][2], xForm[3][2], + // xForm[0][3], xForm[1][3], xForm[2][3], xForm[3][3] ); + + return TRUE; +} + +bool vrml1_parseScale( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + ssgTransform *currentTransform = new ssgTransform(); + sgVec3 scaleFactor; + + sgCoord moveFactor; sgZeroCoord( &moveFactor ); + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("scaleFactor"); + if( !parseVec( scaleFactor, 3 ) ) + return FALSE; + vrmlParser.expectNextToken("}"); + + currentTransform->setTransform( &moveFactor, scaleFactor[0], scaleFactor[1], scaleFactor[2] ); + + applyTransform( currentTransform, currentData ); + + ulSetError(UL_DEBUG, "Found a scale transform: %f %f %f", scaleFactor[0], scaleFactor[1], scaleFactor[2] ); + + return TRUE; +} + +bool vrml1_parseRotation( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + ssgTransform *currentTransform = new ssgTransform(); + sgVec3 axis; + SGfloat angle; + sgMat4 rotation; + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("rotation"); + if( !parseVec( axis, 3 ) ) + return FALSE; + if( !vrmlParser.getNextFloat( angle, NULL ) ) + return FALSE; + vrmlParser.expectNextToken("}"); + + angle *= SG_RADIANS_TO_DEGREES; + + sgMakeRotMat4( rotation, angle, axis ) ; + currentTransform->setTransform( rotation ); + + applyTransform( currentTransform, currentData ); + + ulSetError(UL_DEBUG, "Found a rotation: %f %f %f %f", axis[0], axis[1], axis[2], angle ); + + return TRUE; +} + +bool vrml1_parseTranslation( ssgBranch *parentBranch, _traversalState *currentData, char *defName ) +{ + ssgTransform *currentTransform = new ssgTransform(); + sgVec3 transform; + + vrmlParser.expectNextToken("{"); + vrmlParser.expectNextToken("translation"); + if( !parseVec( transform, 3 ) ) + return FALSE; + vrmlParser.expectNextToken("}"); + + currentTransform->setTransform( transform ); + + applyTransform( currentTransform, currentData ); + + ulSetError(UL_DEBUG, "Found a translation: %f %f %f", transform[0], transform[1], transform[2] ); + + return TRUE; +} + + +bool vrml1_parseUseDirective( ssgBranch *parentBranch, _traversalState *currentData, char *useName, char *defName ) +{ + // find the node within the list of defined nodes + ssgBase *node = definedNodes->extract( useName ); + + if( node==NULL ) + return TRUE; + + if( node->isA( ssgTypeBranch() ) ) + { + ssgBranch *currentBranch = NULL; + if( currentData->getTransform() != NULL ) + { + currentBranch = currentData->getTransform(); + currentBranch->addKid( (ssgEntity *)node ); + } + else + currentBranch = (ssgBranch *)node; + + parentBranch->addKid( currentBranch ); + + return TRUE; + } + + return TRUE; +} + + +// parseUnidentified: A node that we either don't support or isn't part of the +// VRML/IV spec. Just skip it. +bool parseUnidentified() +{ + char *token; + + int startLevel = vrmlParser.level; + int currentLevel = startLevel + 1; + + vrmlParser.expectNextToken("{"); + + while( currentLevel != startLevel ) + { + token = vrmlParser.getNextToken( NULL ); + + if( !strcmp( token, "{" ) ) + currentLevel++; + else if( !strcmp( token, "}" ) ) + currentLevel--; + } + + return TRUE; +} + +void applyTransform( ssgTransform *currentTransform, _traversalState *currentData ) +{ + if( currentData->getTransform() == NULL ) + currentData->setTransform( currentTransform ); + else + { + ssgTransform *newTransform = new ssgTransform(); + mergeTransformNodes( newTransform, currentTransform, currentData->getTransform() ); + // this will have to be changed when we allow use declarations on transforms + delete( currentTransform ); + currentData->setTransform( newTransform ); + } +} + +bool vrml1_parseCoordIndex( ssgLoaderWriterMesh *loaderMesh, _traversalState *currentData ) +{ + char *token = vrmlParser.peekAtNextToken( NULL ); + int numFaces = 0; + + // an array? most likely.. + if( !strcmp( token, "[" ) ) + { + vrmlParser.expectNextToken("["); + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "]" ) ) + { + ssgIndexArray *currentFaceIndices = parseIndexArray( currentData ); + if( currentFaceIndices == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML1: invalid index list" ) ; + return FALSE; + } + loaderMesh->addFace( (ssgIndexArray **) ¤tFaceIndices ); + //ulSetError(UL_DEBUG, "Level: %i. Added a face with %i vertices", vrmlParser.level, numVerticesInFace ); + numFaces++; + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken( "]" ); + } + + // otherwise a single point + else + { + ssgIndexArray *currentFaceIndices = parseIndexArray( currentData ); + if( currentFaceIndices == NULL ) + { + ulSetError ( UL_WARNING, "ssgLoadVRML1: invalid index list" ) ; + return FALSE; + } + loaderMesh->addFace( (ssgIndexArray **) ¤tFaceIndices ); + numFaces++; + + vrmlParser.expectNextToken( "-1" ); + } + + return TRUE; +} + +bool vrml1_parseTextureCoordIndex( ssgLoaderWriterMesh *loaderMesh, _traversalState *currentData ) +{ + char *token = vrmlParser.peekAtNextToken( NULL ); + + // an array? most likely.. + if( !strcmp( token, "[" ) ) + { + vrmlParser.expectNextToken("["); + token = vrmlParser.peekAtNextToken( NULL ); + while( strcmp( token, "]" ) ) + { + ssgIndexArray *currentTextureCoordinateIndices = parseIndexArray( currentData ); + if( currentTextureCoordinateIndices == NULL ) + return FALSE; + ssgTexCoordArray *currentPerFaceAndVertexTextureCoordinateList = new ssgTexCoordArray( currentTextureCoordinateIndices->getNum() ); + for( int i=0; igetNum(); i++ ) + currentPerFaceAndVertexTextureCoordinateList->add( (currentData->getTextureCoordinates())->get( (unsigned int)*currentTextureCoordinateIndices->get( i ) ) ); + loaderMesh->addPerFaceAndVertexTextureCoordinate2( (ssgTexCoordArray **) ¤tPerFaceAndVertexTextureCoordinateList ); + + delete( currentTextureCoordinateIndices ); + //ulSetError(UL_DEBUG, "Level: %i. Added a face with %i vertices", vrmlParser.level, numVerticesInFace ); + + token = vrmlParser.peekAtNextToken( NULL ); + } + vrmlParser.expectNextToken( "]" ); + } + // otherwise a single point + else + { + ssgIndexArray *currentTextureCoordinateIndices = parseIndexArray( currentData ); + if( currentTextureCoordinateIndices == NULL ) + return FALSE; + ssgTexCoordArray *currentPerFaceAndVertexTextureCoordinateList = new ssgTexCoordArray( currentTextureCoordinateIndices->getNum() ); + for( int i=0; igetNum(); i++ ) + currentPerFaceAndVertexTextureCoordinateList->add( (currentData->getTextureCoordinates())->get( (unsigned int)*currentTextureCoordinateIndices->get( i ) ) ); + loaderMesh->addPerFaceAndVertexTextureCoordinate2( (ssgTexCoordArray **) ¤tPerFaceAndVertexTextureCoordinateList ); + + delete( currentTextureCoordinateIndices ); + //ulSetError(UL_DEBUG, "Level: %i. Added a face with %i vertices", vrmlParser.level, numVerticesInFace ); + } + + return TRUE; +} + + +void mergeTransformNodes( ssgTransform *newTransform, ssgTransform *oldTransform1, ssgTransform *oldTransform2 ) +{ + sgMat4 oldTransformMat1; + sgMat4 oldTransformMat2; + sgMat4 newTransformMat; + + oldTransform1->getTransform( oldTransformMat1 ); + oldTransform2->getTransform( oldTransformMat2 ); + sgMultMat4( newTransformMat, oldTransformMat1, oldTransformMat2 ) ; + newTransform->setTransform( newTransformMat ); +} diff --git a/src/ssg/ssgLoadX.cxx b/src/ssg/ssgLoadX.cxx new file mode 100644 index 0000000..3df5ea1 --- /dev/null +++ b/src/ssg/ssgLoadX.cxx @@ -0,0 +1,757 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadX.cxx 2172 2012-08-14 17:56:51Z wolfram_kuss $ +*/ + +// +// .X loader for SSG/PLIB +// .X is the 3D file format for Micro$ofts directX. +// Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in Oct/Nov of 2000 +// +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" +#include "ssgParser.h" + +#define u32 unsigned int + +// These functions return TRUE on success +typedef int HandlerFunctionType(const char *sName, const char *firstToken); + + +static char *globEmpty=""; + +static ssgBranch *curr_branch_; + +struct EntityType +{ + const char * sName; + HandlerFunctionType *HandleEntity; + int bMayBeIgnored; +} ; + + + +static /* const */ ssgLoaderOptions* current_options = NULL ; + +static _ssgParserSpec parser_spec = +{ + "\r\n\t ", // delim_chars_skipable + ",;", // delim_chars_non_skipable + NULL, // pre_processor + "{", // open_brace_chars + "}", // close_brace_chars + '"', // quote_char + '#', // comment_char + "//" // comment_string +} ; + + +static _ssgParser parser; +static ssgBranch* top_branch; + +static int Ascii2Int(int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int Ascii2UInt(unsigned int &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = (unsigned int)(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int Ascii2Float(SGfloat &retVal, const char *token, const char* name ) +// returns TRUE on success +{ + char *endptr; + retVal = SGfloat(strtod( token, &endptr)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { parser.error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +static int HandleHeader(const char * /* sName */, const char *firstToken) +{ + //parser.expectNextToken("{"); + int Dummy; + if (! Ascii2Int(Dummy, firstToken, "Header.major")) + return FALSE; + + parser.expectNextToken(";"); + if (!parser.getNextInt(Dummy, "Header.minor")) + return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextInt(Dummy, "Header.flags")) + return FALSE; + parser.expectNextToken(";"); + parser.expectNextToken("}"); + return TRUE; +} + +static void IgnoreEntity(int startLevel) +// startLevel should be 0 when you are "in front of the {" (normally) +// or 1 when you have already parsed the "{" +{ + int Level = startLevel; + char *token; + + while ( TRUE) + { token = parser.getNextToken(0); + if ( parser.eof ) + { parser.error("unexpected end of file\n"); + return ; //FALSE; + } + + assert(token!=NULL); + if ( ulStrEqual (token,"{") ) + Level++; + else if ( ulStrEqual (token,"}") ) + { assert(Level>0); // Fixme, NIV14: Handle this gracefully. + if (Level==1) + return; // found THE closing brace of entitiy + Level--; // Found A closing brace + } + } +} + +static int HandleMesh(const char *sName, const char *firstToken); +static int HandleMeshMaterialList(const char *sName, const char *firstToken); +static int HandleTextureCoords(const char *sName, const char *firstToken); +static int HandleMaterial(const char *sName, const char *firstToken); +static int HandleTextureFileName(const char *sName, const char *firstToken); + + +static EntityType aEntities[] = +{ + { "Header", HandleHeader, FALSE}, + { "Vector", NULL, FALSE}, + { "Coords2d", NULL, FALSE}, + { "Quaternion", NULL, FALSE}, + { "Matrix4x4", NULL, FALSE}, + { "ColorRGBA", NULL, FALSE}, + { "ColorRGB", NULL, FALSE}, + { "Indexed Color", NULL, FALSE}, + { "Boolean", NULL, FALSE}, + { "Boolean2d", NULL, FALSE}, + { "Material", HandleMaterial, FALSE}, + { "TextureFilename", HandleTextureFileName, FALSE}, + { "MeshFace", NULL, FALSE}, + { "MeshFaceWraps", NULL, FALSE}, + { "MeshTextureCoords", HandleTextureCoords, FALSE}, + { "MeshNormals", NULL, TRUE}, + { "MeshVertexColors", NULL, FALSE}, + { "MeshMaterialList", HandleMeshMaterialList, FALSE}, + { "Mesh", HandleMesh, FALSE}, + { "FrameTransformMatrix", NULL, FALSE}, + { "Frame", NULL, FALSE}, + { "FloatKeys", NULL, FALSE}, + { "TimedFloatKeys", NULL, FALSE}, + { "AnimationKey", NULL, FALSE}, + { "AnimationOptions", NULL, FALSE}, + { "Animation", NULL, FALSE}, + { "AnimationSet", NULL, FALSE}, + { "template", NULL, TRUE}, + { NULL, NULL, FALSE} +}; + + +static int ParseEntity(char *token) +// called recursively +{ int i=0; + + while(aEntities[i].sName!=NULL) + { if (ulStrEqual (token,aEntities[i].sName)) + { if (aEntities[i].HandleEntity) + { char *sNextToken, *sName=globEmpty; + sNextToken=parser.getNextToken(0); + if ( parser.eof ) + { parser.error("unexpected end of file\n"); + return FALSE; + } + // if entity is named, read the name. skip "{": + sName = NULL; + if (0 != strcmp(sNextToken, "{")) + { sName = new char[ strlen(sNextToken) + 1 ]; + assert ( sName ); + strcpy(sName, sNextToken); + sNextToken = parser.getNextToken(0); + if (0 != strcmp(sNextToken, "{")) + parser.error("\"{\" expected\n"); + } + sNextToken = parser.getNextToken(0); + + if(sNextToken[0] == '<') // UUID + sNextToken = parser.getNextToken(0); + if ( parser.eof ) + { parser.error("unexpected end of file\n"); + return FALSE; + } + + if (!aEntities[i].HandleEntity(sName, sNextToken)) + return FALSE; + if ( sName ) + delete [] sName; + } + else + if (aEntities[i].bMayBeIgnored) + IgnoreEntity ( 0 ); + else + { + parser.error("I am sorry, but Entity-type '%s' is not yet implemented.", aEntities[i].sName); + return FALSE ; + } + + break; + } + i++; + } + if (aEntities[i].sName==NULL) + { + parser.error("unexpected token %s", token); + return FALSE ; + } + return TRUE; +} + + + +#define MAX_NO_VERTICES_PER_FACE 1000 + +static class ssgLoaderWriterMesh currentMesh; + +static ssgSimpleState *currentState; +extern sgVec4 currentDiffuse; + +static int HandleTextureFileName(const char * /*sName*/, const char *firstToken) +{/* + TextureFilename { + "../image/box_top.gif"; + } #TextureFilename + */ + char *filename_ptr, *filename = new char [ strlen(firstToken)+1 ] ; + assert(filename!=NULL); + strcpy ( filename, firstToken ) ; + filename_ptr = filename ; + + if ( filename_ptr[0] == '"' ) + filename_ptr++; + if (filename_ptr[strlen(filename_ptr)-1] == '"') + filename_ptr[strlen(filename_ptr)-1] = 0; + currentState -> setTexture( current_options -> createTexture( filename_ptr ) ); + currentState -> enable( GL_TEXTURE_2D ); + + + parser.expectNextToken(";"); + parser.expectNextToken("}"); + delete [] filename; + return TRUE; +} + +static class ssgSimpleStateList *globalMaterialList; +static int HandleMaterial(const char * sName, const char *firstToken) +// return TRUE on success +{ SGfloat power; + int bFoundTextureFileName = FALSE; + sgVec4 specularColour, EmissiveColour; + + // read body + if (! Ascii2Float(currentDiffuse[0], firstToken, "Facecolour R")) + return FALSE; + + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[1], "Facecolour G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[2], "Facecolour B")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(currentDiffuse[3], "Facecolour A")) return FALSE; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + if (!parser.getNextFloat(power, "power")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[0], "Specular R")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[1], "Specular G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(specularColour[2], "Specular B")) return FALSE; + specularColour[3] = 0.0; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[0], "Emissive R")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[1], "Emissive G")) return FALSE; + parser.expectNextToken(";"); + if (!parser.getNextFloat(EmissiveColour[2], "Emissive B")) return FALSE; + EmissiveColour[3] = 0.0; + parser.expectNextToken(";"); + parser.expectNextToken(";"); + + // create SimpleState + + currentState = new ssgSimpleState () ; + +// currentState -> setMaterial ( GL_AMBIENT, mat -> amb ) ; + currentState -> setMaterial ( GL_DIFFUSE, currentDiffuse) ; + currentState -> setMaterial ( GL_SPECULAR, specularColour) ; + currentState -> setMaterial ( GL_SPECULAR, specularColour[0], + specularColour[1], specularColour[2], currentDiffuse[3] ) ; + currentState -> setMaterial ( GL_EMISSION, EmissiveColour[0], + EmissiveColour[1], EmissiveColour[2], currentDiffuse[3] ) ; + + currentState -> setShininess ( power ) ; // Fixme, NIV14: Is that correct? + + currentState -> enable ( GL_COLOR_MATERIAL ) ; + currentState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + currentState -> enable ( GL_LIGHTING ) ; + currentState -> setShadeModel ( GL_SMOOTH ) ; + + if ( currentDiffuse[3] > 0.0f ) + { + currentState -> disable ( GL_ALPHA_TEST ) ; + currentState -> enable ( GL_BLEND ) ; + currentState -> setTranslucent () ; + } + else + { + currentState -> disable ( GL_BLEND ) ; + currentState -> setOpaque () ; + } + currentState -> disable( GL_TEXTURE_2D ); + + currentState -> setName ( sName ) ; + + while(TRUE) + { char *nextToken =parser.getNextToken(0); + if (0==strcmp("}", nextToken)) + { + + /*if(currentMesh.theMaterialsExists()) + currentMesh.addMaterial( ¤tState ); + else*/ + { + if(!globalMaterialList) + globalMaterialList = new ssgSimpleStateList(3); + + globalMaterialList->add(¤tState); + } + return TRUE; // Material is finished. success + } + + if (! ulStrEqual ( "TextureFilename", nextToken) ) + { parser.error("TextureFilename expected!\n"); + return FALSE; + } + if ( bFoundTextureFileName ) + { parser.error("Only one TextureFileName per Material please!\n"); + return FALSE; + } + if (!ParseEntity(nextToken)) // read "TextureFileName" + return FALSE; + bFoundTextureFileName = TRUE; + } + return TRUE; //lint !e527 +} + +static int HandleTextureCoords(const char * /* sName */, const char *firstToken) +{ + u32 nNoOfVertices, i; + + + if (! Ascii2UInt(nNoOfVertices, firstToken, "nNoOfVertices")) + return FALSE; + + if ( nNoOfVertices != currentMesh.getNumVertices()) + { parser.error("No of vertices of mesh (%d) and no " + "of texture coordinates (%d) do not match!\n" + "Therefore the texture coordinates are ignored!", + ( int ) currentMesh.getNumVertices(), ( int ) nNoOfVertices ); + IgnoreEntity ( 1 ); // ignores TC. + return FALSE; + } + currentMesh.createPerVertexTextureCoordinates2( nNoOfVertices ) ; + + parser.expectNextToken(";"); + for(i=0;i currentMesh.getNumFaces()) + { parser.error("No of face indexes of materiallist (%d) is greater than then no " + "of faces (%d)!\n" + "Therefore the material list is ignored!", + ( int ) nFaceIndexes, ( int ) currentMesh.getNumFaces()); + IgnoreEntity ( 1 ); // ignores TC. + return TRUE; // go on parsing + } + if ( nFaceIndexes > currentMesh.getNumFaces()) + parser.message("Informational: No of face indexes of materiallist (%d) is less than then no " + "of faces (%d)\n" , + ( int ) nFaceIndexes, ( int ) currentMesh.getNumFaces()); + for ( i=0 ; igetNum(); i++) + { + currentMesh.addMaterial(globalMaterialList->get(i)); + } + return TRUE; // Mesh is finished. success + } + if(ulStrEqual ("{", nextToken) ) + { // reference to global material + nextToken =parser.getNextToken(0); // name of material + parser.expectNextToken("}"); + // search for "name" in global list + if(globalMaterialList == NULL) + { + parser.error("No global materials defined, but used!\n"); + return FALSE; + } + for(int i=0; igetNum(); i++) + { + ssgSimpleState * ss = *(globalMaterialList->get(i)); + if(ulStrEqual(nextToken, ss->getName())) + break; + } + } + else + { + if ( !ulStrEqual ("Material", nextToken) ) + { parser.error("Material expected!\n"); + return FALSE; + } + if ( nMaterialsRead >= nMaterials ) + { parser.error("Too many Materials!\n"); + return FALSE; + } + if (!ParseEntity(nextToken)) // read "Material" + return FALSE; + } + nMaterialsRead++; + } + return TRUE; //lint !e527 +} + + +static int HandleMesh(const char * sName, const char *firstToken) +{ u32 i, j, nNoOfVertices, nNoOfVerticesForThisFace, nNoOfFaces; + int iVertex, aiVertices[MAX_NO_VERTICES_PER_FACE]; + + //char *sMeshName = parser.getNextToken("Mesh name"); + //parser.expectNextToken("{"); + if (! Ascii2UInt(nNoOfVertices, firstToken, "nNoOfVertices")) + return FALSE; + + //parser.getNextInt("number of vertices"); + + currentMesh.reInit (); + currentMesh.setName( sName ); + currentMesh.createVertices( nNoOfVertices ); + + parser.expectNextToken(";"); + for(i=0;isetOpaque(); + currentState->disable(GL_BLEND); + currentState->disable(GL_ALPHA_TEST); + currentState->disable(GL_TEXTURE_2D); + currentState->enable(GL_COLOR_MATERIAL); + currentState->enable(GL_LIGHTING); + currentState->setShadeModel(GL_SMOOTH); + currentState->setMaterial(GL_AMBIENT , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_DIFFUSE , 0.7f, 0.7f, 0.7f, 1.0f); + currentState->setMaterial(GL_SPECULAR, 1.0f, 1.0f, 1.0f, 1.0f); + currentState->setMaterial(GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f); + currentState->setShininess(50); +/* + currentState -> setMaterial ( GL_AMBIENT, 0.5, 0.5, 0.5); + currentState -> setMaterial ( GL_DIFFUSE, 0.7, 0.7, 0.7); // light grey + currentState -> setMaterial ( GL_SPECULAR, 1.0, 1.0, 1.0); + currentState -> setMaterial ( GL_EMISSION, 0.0, 0.0, 0.0); + + currentState -> setShininess ( 3 ) ; + + //currentState -> enable ( GL_COLOR_MATERIAL ) ; + //currentState -> setColourMaterial ( GL_AMBIENT_AND_DIFFUSE ) ; + + currentState -> enable ( GL_LIGHTING ) ; + currentState -> setShadeModel ( GL_SMOOTH ) ; + + currentState -> disable ( GL_BLEND ) ; + currentState -> setOpaque () ; + currentState -> disable( GL_TEXTURE_2D ); + */ + } + + currentMesh.addToSSG( + currentState // Pfusch, kludge. NIV135 + , + current_options, + curr_branch_); + return TRUE; +} + +inline int TwoCharsToInt(char char1, char char2) +{ + return ((int)(char1-'0'))*256+char2-'0'; +} + +static int HeaderIsValid(char *firstToken) +{ // xof 0302txt 0064 + if (!ulStrEqual (firstToken,"xof")) + { + parser.error("not X format, invalid Header"); + return FALSE ; + } + char* token = parser.getNextToken("2nd Header field"); + if (strlen(token)!=7) + { + parser.error("not X format, invalid Header"); + return FALSE ; + } + char *sp=&(token[4]); + if (!ulStrEqual (sp,"txt")) + { + if (!ulStrEqual (sp,"bin")) + parser.error("not X format, invalid Header"); + else + parser.error("Binary X format files are not supported. If you have access to Windows, " + "please use Microsofts conversion-utility convx from the directX-SDK " + "to convert to ascii."); + return FALSE ; + } + if (strncmp(token, "0302", 4)) + parser.message("This loader is written for X-file-format version 3.2.\n" + "AFAIK this is the only documented version.\n" + "Your file has version %d.%d\n" + "Use the file at your own risk\n", + TwoCharsToInt(token[0], token[1]), + TwoCharsToInt(token[2], token[3])); + token = parser.getNextToken("3rd Header field"); + if (!ulStrEqual(token,"0032") && !ulStrEqual (token,"0064")) + { + parser.error("not X format, invalid Header"); + return FALSE ; + } + return TRUE; +} + +static int parse() +{ + int firsttime = TRUE; + char* token; + //int startLevel = parser.level; + token = parser.getNextToken(0); + while (! parser.eof ) + { + if (firsttime) + { + if(!HeaderIsValid(token)) + return FALSE; + firsttime = FALSE; + } + else + { if (!ParseEntity(token)) + return FALSE; + } + token = parser.getNextToken(0); + } + return TRUE ; +} + + +ssgEntity *ssgLoadX ( const char *fname, const ssgLoaderOptions* options ) +{ + ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ; + current_options = ssgGetCurrentOptions () ; + + currentState = NULL; + globalMaterialList = NULL; + top_branch = new ssgBranch ; + curr_branch_ = top_branch; + if ( !parser.openFile( fname, &parser_spec )) + { + delete top_branch ; + return 0; + } + if ( !parse() ) + { + delete top_branch ; + top_branch = 0 ; + } +// parse_free(); + parser.closeFile(); + + delete globalMaterialList; + return top_branch ; +} diff --git a/src/ssg/ssgLoadXPlaneObj.cxx b/src/ssg/ssgLoadXPlaneObj.cxx new file mode 100644 index 0000000..e327d81 --- /dev/null +++ b/src/ssg/ssgLoadXPlaneObj.cxx @@ -0,0 +1,463 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoadXPlaneObj.cxx 1728 2002-11-09 21:38:28Z sjbaker $ +*/ + +/* + * XPlane object loader for PLIB. + * Written by Bernie Bright , Sept 2002. + * + * KNOWN ISSUES + * + * Only version 1 objects are handled. + * + * The .obj XPlane custom object file extension clashes with Wavefront objects. + * For the moment, XPlane objects must be renamed with a .xpl extension. + * + * Point objects are not handled. + */ + +#include +#include + +#include "ssgLocal.h" + +/** + * XPlane .obj loader. + */ +class ObjLoader +{ +public: + ObjLoader( const ssgLoaderOptions* options ); + + ~ObjLoader(); + + ssgEntity* load( const char* fname ); + +private: + ssgState* get_state( const char* tfname ); + char* fgets( char* s, int size, FILE* fp ); + char* getline( char* line, size_t n, FILE* fp ); + char* getTextureName( const char* p, char* tname, size_t n ); + const char* file_extension( const char* fname ); + +private: + ssgLoaderOptions* options; + ssgBranch* root; +}; + +ObjLoader::ObjLoader( const ssgLoaderOptions* options_ ) + : options(0) + , root(0) +{ + ssgSetCurrentOptions( const_cast(options_) ); + this->options = ssgGetCurrentOptions(); +} + +ObjLoader::~ObjLoader() +{ +} + +ssgState* +ObjLoader::get_state( const char* tfname ) +{ + if (tfname != 0) + { + ssgState* st = options->createState( const_cast(tfname) ); + if (st != 0) + return st; + } + + ssgSimpleState* st = new ssgSimpleState; + st->setName( tfname ); + st->setMaterial( GL_SPECULAR, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_EMISSION, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_AMBIENT, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_DIFFUSE, 0.0f, 0.0f, 0.0f ); + st->enable( GL_LIGHTING ); + st->setShadeModel( GL_SMOOTH ); + + bool has_alpha = false; + if (tfname != 0) + { + ssgTexture* tex = options->createTexture( const_cast(tfname) ); + has_alpha = tex->hasAlpha(); + st->setTexture(tex); + st->enable( GL_TEXTURE_2D ); + } + else + { + st->disable( GL_TEXTURE_2D ); + } + + if (has_alpha) + { + st->disable( GL_ALPHA_TEST ); + st->enable( GL_BLEND ); + st->setTranslucent(); + } + else + { + st->disable( GL_ALPHA_TEST ); + st->disable( GL_BLEND ); + st->setOpaque(); + } + + return st; +} + +/** + * XPlane object files have either IBM (\r\n) or Apple (\r) line endings. + * Handle both here. + */ +char* +ObjLoader::fgets( char* s, int size, FILE* fp ) +{ + char* p = s; + int n = 0; + int c = 0; + + for (--size; n < size; ++n) + { + c = getc(fp); + if (c == EOF) + return 0; + + if (c == '\r') + break; + *p++ = c; + } + + *p = 0; + + c = getc(fp); + if (c != '\n') + ungetc( c, fp ); + + return s; +} + +/** + * Read the next non-blank line. + * Returns pointer to first non-whitespace character or NULL on error or EOF. + */ +char* +ObjLoader::getline( char* line, size_t n, FILE* fp ) +{ + for (;;) + { + char* p = fgets( line, n, fp ); + if (p == 0) + return 0; + + // Skip leading whitespace. + while (*p != 0 && isspace(*p)) + ++p; + + if (*p != 0) + return p; + } +} + +/** + * + */ +char* +ObjLoader::getTextureName( const char* p, char* buf, size_t bufsize ) +{ + if (p == 0 || buf == 0) + return 0; + + while (*p != 0 && isspace(*p)) + ++p; + + size_t n = 0; + char* bp = buf; + for ( ; *p != 0 && n < bufsize && !isspace(*p); ++n, ++bp, ++p) + { + // Replace ':' path separator with '/'. + *bp = *p == ':' ? '/' : *p; + } + + if (n < bufsize) + *bp = 0; + + return const_cast(p); +} + +const char* +ObjLoader::file_extension( const char *fname ) +{ + const char* p = fname + strlen(fname); + + while (p != fname && *p != '/' && *p != '.' ) + --p; + + return p; +} + +ssgEntity* +ObjLoader::load( const char* fname ) +{ + char mname[1024]; + options->makeModelPath( mname, fname ); + + FILE* fp = fopen( fname, "r" ); + if (fp == 0) + { + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: Failed to open '%s' for reading.", + fname ); + return 0; + } + + char line[ 1024 ]; + + // Read obj header. + char* p = getline( line, sizeof(line), fp ); + if (p == 0) + { + ulSetError( UL_WARNING, "ssgLoadXPlaneObj: expected header." ); + fclose(fp); + return 0; + } + + if (*p != 'I' && *p != 'A') + { + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: invalid header, expected 'I' or 'A'" ); + fclose(fp); + return 0; + } + + p = getline( line, sizeof(line), fp ); + if (p == 0) + { + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: unexpected EOF while reading header." ); + fclose(fp); + return 0; + } + + root = new ssgTransform(); + root->setName( fname ); + + if (*p == '2') + { + // Version 2 obj file. + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: Version 2 not yet implemented" ); + + fclose( fp ); + delete root; + return 0; + + // TODO: implement V2. + } + else + { + // Version 1 obj file. + + // Version number is optional. + if (*p == '1') + { + // get TextureDef + p = getline( line, sizeof(line), fp ); + if (p == 0) + { + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: unexpected EOF " + "while reading data." ); + fclose(fp); + return 0; + } + } + + int type = 0; + char* endp; + do + { + type = strtol( p, &endp, 10 ); + switch (type) + { + case 4: + { + // Get texture filename. + // Allow room for extension and terminator. + char tname[ 251 + 4 + 1 ]; + endp = getTextureName( endp, tname, 251 ); + if (endp == 0) + { + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: Error reading TextureDef" ); + delete root; + fclose( fp ); + return 0; + } + + strcat( tname, ".bmp" ); + + // TODO: check for comment following texture name. + + sgVec3* vc = new sgVec3[4]; + sgVec2* tc = new sgVec2[4]; + sgVec4* cc = new sgVec4[1]; + + for (int i = 0; i < 4; ++i) + { + p = getline( line, sizeof(line), fp ); + // Read vertices, swapping y and z. + sscanf( p, "%f%f%f", &vc[i][0], &vc[i][2], &vc[i][1] ); + } + + sgSetVec2( tc[0], 1.0f, 1.0f ); + sgSetVec2( tc[1], 1.0f, 0.0f ); + sgSetVec2( tc[2], 0.0f, 0.0f ); + sgSetVec2( tc[3], 0.0f, 1.0f ); + + sgSetVec4( cc[0], 1.0f, 1.0f, 1.0f, 1.0f ); + + ssgVertexArray* vl = new ssgVertexArray( 4, vc ); + ssgTexCoordArray* tl = new ssgTexCoordArray( 4, tc ); + ssgNormalArray* nl = new ssgNormalArray(); + ssgColourArray* cl = new ssgColourArray( 1, cc ); + + ssgVtxTable* vtab = new ssgVtxTable( GL_POLYGON, + vl, nl, tl, cl ); + vtab->setState( get_state( tname ) ); + root->addKid( vtab ); + } + break; + + case 3: + { + // Get texture filename. + // Allow room for extension and terminator. + char tname[ 251 + 4 + 1 ]; + endp = getTextureName( endp, tname, 251 ); + strcat( tname, ".bmp" ); + + sgVec3* vc = new sgVec3[3]; + sgVec2* tc = new sgVec2[3]; + sgVec4* cc = new sgVec4[1]; + + for (int i = 0; i < 3; ++i) + { + p = getline( line, sizeof(line), fp ); + // Read vertices, swapping y and z. + sscanf( p, "%f%f%f", &vc[i][0], &vc[i][2], &vc[i][1] ); + } + + sgSetVec2( tc[0], 1.0f, 1.0f ); + sgSetVec2( tc[1], 1.0f, 0.0f ); + sgSetVec2( tc[2], 0.0f, 0.0f ); + + sgSetVec4( cc[0], 1.0f, 1.0f, 1.0f, 1.0f ); + + ssgVertexArray* vl = new ssgVertexArray( 3, vc ); + ssgTexCoordArray* tl = new ssgTexCoordArray( 3, tc ); + ssgNormalArray* nl = new ssgNormalArray(); + ssgColourArray* cl = new ssgColourArray( 1, cc ); + + ssgVtxTable* vtab = new ssgVtxTable( GL_POLYGON, + vl, nl, tl, cl ); + vtab->setState( get_state( tname ) ); + root->addKid( vtab ); + } + break; + + case 2: + { + float r, g, b; + sscanf( endp, "%f%f%f", &r, &g, &b ); + + sgVec3* vc = new sgVec3[2]; + sgVec4* cc = new sgVec4[1]; + for (int i = 0; i < 2; ++i) + { + p = getline( line, sizeof(line), fp ); + // Read vertices, swapping y and z. + sscanf( p, "%f%f%f", &vc[i][0], &vc[i][2], &vc[i][1] ); + } + + // Scale rgb values to range 0..1 + sgSetVec4( cc[0], r/10.f, g/10.f, b/10.f, 1.0f ); + + ssgVertexArray* vl = new ssgVertexArray( 2, vc ); + ssgTexCoordArray* tl = new ssgTexCoordArray(); + ssgNormalArray* nl = new ssgNormalArray(); + ssgColourArray* cl = new ssgColourArray( 1, cc ); + + ssgVtxTable* vtab = new ssgVtxTable( GL_LINES, + vl, nl, tl, cl ); + ssgSimpleState* st = new ssgSimpleState; + st->setMaterial( GL_SPECULAR, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_EMISSION, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_AMBIENT, 0.0f, 0.0f, 0.0f ); + st->setMaterial( GL_DIFFUSE, 0.0f, 0.0f, 0.0f ); + st->enable( GL_LIGHTING ); + st->setShadeModel( GL_SMOOTH ); + st->disable( GL_TEXTURE_2D ); + st->disable( GL_ALPHA_TEST ); + st->disable( GL_BLEND ); + st->setOpaque(); + vtab->setState( st ); + root->addKid( vtab ); + } + break; + + case 1: + { + int r, g, b; + sscanf( endp, "%d%d%d", &r, &g, &b ); + p = getline( line, sizeof(line), fp ); + float x, y, z; + sscanf( p, "%f%f%f", &x, &z, &y ); + + // TODO: create appropriate ssgEntity. + } + break; + + case 99: + break; + + default: + ulSetError( UL_WARNING, + "ssgLoadXPlaneObj: invalid line, '%s'", line ); + fclose(fp); + delete root; + return 0; + } + } + while ((p = getline( line, sizeof(line), fp )) != 0 && type != 99); + } + + fclose( fp ); + return root; +} + +ssgEntity* +ssgLoadXPlaneOBJ( const char* fname, const ssgLoaderOptions* options ) +{ + ObjLoader loader( options ); + return loader.load( fname ); +} + diff --git a/src/ssg/ssgLoaderWriterStuff.cxx b/src/ssg/ssgLoaderWriterStuff.cxx new file mode 100644 index 0000000..691ed98 --- /dev/null +++ b/src/ssg/ssgLoaderWriterStuff.cxx @@ -0,0 +1,914 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoaderWriterStuff.cxx 2060 2006-01-05 20:54:15Z fayjf $ +*/ + +// ssgLoaderWriterStuff.cxx +// Here you will find classes and functions you can use to +// implement loaders and writers for ssg +// Also, there is the parser for loading ASCII files, which +// has its own file ssgParser.cxx and there are functions like +// the stripifier that are usefull not only for loaders/writers. +// +// 1. Version written by Wolfram Kuss (Wolfram.Kuss@t-online.de) +// in Nov of 2000 +// Distributed with Steve Bakers plib under the LGPL licence + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + + +#undef ABS +#undef MIN +#undef MAX +#define ABS(x) ((x) >= 0 ? (x) : -(x)) +#define MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define MIN3(a,b,c) ((a) <= (b) ? MIN(a,c) : MIN(b,c)) +#define MAX3(a,b,c) ((a) >= (b) ? MAX(a,c) : MAX(b,c)) + +// texture coord epsilon +#define TC_EPSILON 0.01 + + +sgVec4 currentDiffuse; + +// *********************************************************************** +// ******************** small utility functions ************************ +// *********************************************************************** + +void ssgAccumVerticesAndFaces( ssgEntity* node, sgMat4 transform, ssgVertexArray* vertices, + ssgIndexArray* indices, SGfloat epsilon, ssgSimpleStateArray* ssa, + ssgIndexArray* materialIndices, ssgTexCoordArray *texCoordArray) +// Accumulates all vertices and Faces (indexes of vertices making up faces) +// from node and any node below. +// Calls itself recursively. +// If indices is NULL, no face info is accumulated +// if epsilon is < 0.0, it is ignored. Else vertices are only accumulated if +// there is no vertex inside epsilon yet +{ + + assert( vertices != NULL ); + assert( (epsilon < 0.0) || (indices == NULL) ); // sorry: using epsilon AND using indices not implemented + if ( ssa != NULL ) { assert( indices != NULL ); } + assert ( ((ssa==NULL) && (materialIndices==NULL)) || ((ssa!=NULL) && (materialIndices!=NULL))); + if ( node->isAKindOf( ssgTypeTransform() ) ) { + sgMat4 local_transform; + ssgTransform *t_node = (ssgTransform*)node; + + t_node->getTransform(local_transform); + sgPostMultMat4( local_transform, transform ); + + for (ssgEntity* kid = t_node->getKid(0); kid != NULL; + kid = t_node->getNextKid()) { + ssgAccumVerticesAndFaces( kid, local_transform, vertices, indices, epsilon, ssa, materialIndices, texCoordArray); + } + } + else if ( node->isAKindOf( ssgTypeBranch() ) ) { + ssgBranch *b_node = (ssgBranch*)node; + for (ssgEntity* kid = b_node->getKid(0); kid != NULL; + kid = b_node->getNextKid()) { + ssgAccumVerticesAndFaces( kid, transform, vertices, indices, epsilon, ssa, materialIndices, texCoordArray); + } + } + else if ( node->isAKindOf( ssgTypeLeaf() ) ) { + ssgLeaf* l_node = (ssgLeaf*)node; + int i, vert_low = vertices->getNum(); + + int useTexture = FALSE; + if ( texCoordArray ) + if ( l_node->getState() ) + if (l_node->getState()->isAKindOf(ssgTypeSimpleState())) + { + ssgSimpleState * ss = (ssgSimpleState *) l_node->getState(); + if ( ss->isEnabled ( GL_TEXTURE_2D ) ) +// wk kludge!!! if ( l_node -> getNumTexCoords () == l_node -> getNumVertices() ) + useTexture = TRUE; + } + + for (i = 0; i < l_node->getNumVertices(); i++) { + sgVec3 new_vertex; + sgXformVec3(new_vertex, l_node->getVertex(i), transform); + + if ( epsilon < 0.0 ) + { + vertices->add(new_vertex); + if ( useTexture ) + texCoordArray ->add ( l_node->getTexCoord(i) ); + else if ( texCoordArray ) + texCoordArray ->add ( _ssgTexCoord00 ); + } + else + { + int j, bFound = FALSE, nv1 = vertices -> getNum (); + for ( j = 0; j < nv1; j++) + { + float *oldvertex = vertices -> get(j); + if (( new_vertex[0] - oldvertex[0] > -epsilon) && + ( new_vertex[0] - oldvertex[0] < epsilon) && + ( new_vertex[1] - oldvertex[1] > -epsilon) && + ( new_vertex[1] - oldvertex[1] < epsilon) && + ( new_vertex[2] - oldvertex[2] > -epsilon) && + ( new_vertex[2] - oldvertex[2] < epsilon)) + { + float *f = 0; + if ( useTexture ) + { + assert( texCoordArray ); // if texCoordArray would be NULL, useTexture would not be set. + f = texCoordArray -> get(j); + } + if ( !useTexture || ((l_node->getTexCoord(i)[0] == f[0]) && + (l_node->getTexCoord(i)[1] == f[1]))) + { + bFound = TRUE; + break; + } + } + } + if ( ! bFound ) + { + vertices -> add ( new_vertex ); + if ( useTexture ) + texCoordArray ->add ( l_node->getTexCoord(i) ); + else if ( texCoordArray ) + texCoordArray ->add ( _ssgTexCoord00 ); + } + } + } + + if ( indices != NULL ) + { + int index=-1; + if ( ssa != NULL ) + { + ssgState *s = l_node->getState(); + if ( s != NULL ) + { index = ssa->findIndex (reinterpret_cast (s) ); + if ( index < 0 ) + { ssa -> add(reinterpret_cast (s) ); + index = ssa->getNum()-1; + } + } + } + for (i = 0; i < l_node->getNumTriangles(); i++) { + short v1, v2, v3; + l_node->getTriangle(i, &v1, &v2, &v3); + indices->add( vert_low + v1 ); + indices->add( vert_low + v2 ); + indices->add( vert_low + v3 ); + if ( materialIndices != NULL ) + materialIndices->add(index); // index is -1 for leafs without state + } + } + } + if ( texCoordArray ) + { + assert( vertices->getNum() == texCoordArray->getNum() ); + } +} ; + + +/* + ssgTriangulate - triangulate a simple polygon. +*/ + +static int triangulateConcave(sgVec3 *coords, int *w, int n, int x, int y, int *tris) +{ + struct Vtx { + int index; + float x, y; + Vtx *next; + }; + + Vtx buf[16], *arr, *p0, *p1, *p2, *m0, *m1, *m2, *t; + int i, chk, num_tris; + float a0, a1, a2, b0, b1, b2, c0, c1, c2; + + /* construct a circular linked list of the vertices */ + arr = (n > 16) ? new Vtx [ n ] : buf; + p0 = &arr[0]; + p0->index = w ? w[0] : 0; + p0->x = coords[p0->index][x]; + p0->y = coords[p0->index][y]; + p1 = p0; + p2 = 0; + for (i = 1; i < n; i++) { + p2 = &arr[i]; + p2->index = w ? w[i] : i; + p2->x = coords[p2->index][x]; + p2->y = coords[p2->index][y]; + p1->next = p2; + p1 = p2; + } + p2->next = p0; + + m0 = p0; + m1 = p1 = p0->next; + m2 = p2 = p1->next; + chk = 0; + num_tris = 0; + + while (p0 != p2->next) { + if (chk && m0 == p0 && m1 == p1 && m2 == p2) { + /* no suitable vertex found.. */ + ulSetError(UL_WARNING, "ssgTriangulate: Self-intersecting polygon."); + if (arr != buf) + delete [] arr; + return 0; + } + chk = 1; + + a0 = p1->y - p2->y; + a1 = p2->y - p0->y; + a2 = p0->y - p1->y; + b0 = p2->x - p1->x; + b1 = p0->x - p2->x; + b2 = p1->x - p0->x; + + if (b0 * a2 - b2 * a0 < 0) { + /* current angle is concave */ + p0 = p1; + p1 = p2; + p2 = p2->next; + } + else { + /* current angle is convex */ + float xmin = MIN3(p0->x, p1->x, p2->x); + float xmax = MAX3(p0->x, p1->x, p2->x); + float ymin = MIN3(p0->y, p1->y, p2->y); + float ymax = MAX3(p0->y, p1->y, p2->y); + + c0 = p1->x * p2->y - p2->x * p1->y; + c1 = p2->x * p0->y - p0->x * p2->y; + c2 = p0->x * p1->y - p1->x * p0->y; + + for (t = p2->next; t != p0; t = t->next) { + /* see if the triangle contains this vertex */ + if (xmin <= t->x && t->x <= xmax && + ymin <= t->y && t->y <= ymax && + a0 * t->x + b0 * t->y + c0 > 0 && + a1 * t->x + b1 * t->y + c1 > 0 && + a2 * t->x + b2 * t->y + c2 > 0) + break; + } + + if (t != p0) { + p0 = p1; + p1 = p2; + p2 = p2->next; + } + else { + /* extract this triangle */ + tris[3 * num_tris + 0] = p0->index; + tris[3 * num_tris + 1] = p1->index; + tris[3 * num_tris + 2] = p2->index; + num_tris++; + + p0->next = p1 = p2; + p2 = p2->next; + + m0 = p0; + m1 = p1; + m2 = p2; + chk = 0; + } + } + } + + tris[3 * num_tris + 0] = p0->index; + tris[3 * num_tris + 1] = p1->index; + tris[3 * num_tris + 2] = p2->index; + num_tris++; + + if (arr != buf) + delete [] arr; + + return num_tris; +} + +int _ssgTriangulate( sgVec3 *coords, int *w, int n, int *tris ) +{ + float *a, *b; + int i, x, y; + + /* trivial case */ + if (n <= 3) { + if (n == 3) { + tris[0] = w ? w[0] : 0; + tris[1] = w ? w[1] : 1; + tris[2] = w ? w[2] : 2; + return 1; + } + ulSetError(UL_WARNING, "ssgTriangulate: Invalid number of vertices (%d).", n); + return 0; + } + + /* compute areas */ + { + float s[3], t[3]; + int swap; + + s[0] = s[1] = s[2] = 0; + b = coords[w ? w[n - 1] : n - 1]; + + for (i = 0; i < n; i++) { + a = b; + b = coords[w ? w[i] : i]; + s[0] += a[1] * b[2] - a[2] * b[1]; + s[1] += a[2] * b[0] - a[0] * b[2]; + s[2] += a[0] * b[1] - a[1] * b[0]; + } + + /* select largest area */ + t[0] = ABS(s[0]); + t[1] = ABS(s[1]); + t[2] = ABS(s[2]); + i = t[0] > t[1] ? t[0] > t[2] ? 0 : 2 : t[1] > t[2] ? 1 : 2; + swap = (s[i] < 0); /* swap coordinates if clockwise */ + x = (i + 1 + swap) % 3; + y = (i + 2 - swap) % 3; + } + + /* concave check */ + { + float x0, y0, x1, y1; + + a = coords[w ? w[n - 2] : n - 2]; + b = coords[w ? w[n - 1] : n - 1]; + x1 = b[x] - a[x]; + y1 = b[y] - a[y]; + + for (i = 0; i < n; i++) { + a = b; + b = coords[w ? w[i] : i]; + x0 = x1; + y0 = y1; + x1 = b[x] - a[x]; + y1 = b[y] - a[y]; + if (x0 * y1 - x1 * y0 < 0) + return triangulateConcave(coords, w, n, x, y, tris); + } + } + + /* convert to triangles */ + { + int v0 = 0, v1 = 1, v = n - 1; + int even = 1; + for (i = 0; i < n - 2; i++) { + if (even) { + tris[3 * i + 0] = w ? w[v0] : v0; + tris[3 * i + 1] = w ? w[v1] : v1; + tris[3 * i + 2] = w ? w[v] : v; + v0 = v1; + v1 = v; + v = v0 + 1; + } + else { + tris[3 * i + 0] = w ? w[v1] : v1; + tris[3 * i + 1] = w ? w[v0] : v0; + tris[3 * i + 2] = w ? w[v] : v; + v0 = v1; + v1 = v; + v = v0 - 1; + } + even = !even; + } + } + return n - 2; +} + + + +// *********************************************************************** +// ******************** class ssgLoaderWriterMesh ************************ +// *********************************************************************** + +void ssgLoaderWriterMesh::reInit(void) // was: ReInit +{ + theVertices = NULL ; + materialIndices = NULL ; + theFaces = NULL ; + perFaceAndVertexTextureCoordinate2Lists = NULL ; + theMaterials = NULL ; + perVertexTextureCoordinates2 = NULL ; + name = NULL ; + textureCoordinatesArePerVertex = TRUE ; +} + +ssgLoaderWriterMesh::ssgLoaderWriterMesh() +{ + reInit(); +} + +ssgLoaderWriterMesh::~ssgLoaderWriterMesh() +{} + +void ssgLoaderWriterMesh::deletePerFaceAndVertexTextureCoordinates2() // was: deleteTCPFAV +{} + +// creation stuff: + +void ssgLoaderWriterMesh::createVertices( int numReservedVertices ) // was: ThereAreNVertices +{ + assert( theVertices == NULL ); + theVertices = new ssgVertexArray ( numReservedVertices ); +} + +void ssgLoaderWriterMesh::addVertex( sgVec3 v ) +{ + assert( theVertices!=NULL ); + theVertices->add ( v ); +} + +void ssgLoaderWriterMesh::setVertices( class ssgVertexArray *vertexArray ) +{ + assert( theVertices == NULL ); + theVertices = vertexArray; +} + +void ssgLoaderWriterMesh::setPerVertexTextureCoordinates2( class ssgTexCoordArray *texCoordArray ) +{ + assert( perVertexTextureCoordinates2 == NULL ); + perVertexTextureCoordinates2 = texCoordArray; +} + +void ssgLoaderWriterMesh::createFaces( int numReservedFaces ) // was: ThereAreNFaces +{ + assert( theFaces == NULL ); + theFaces = new ssgListOfLists ( numReservedFaces ); +} + +void ssgLoaderWriterMesh::addFace( ssgIndexArray **indexArray ) +{ + assert( theFaces!=NULL ); + theFaces->add ( (ssgSimpleList **)indexArray ); +} + +void ssgLoaderWriterMesh::addFaceFromIntegerArray( int numVertices, int *vertices ) +{ + int j; + class ssgIndexArray *oneFace = new ssgIndexArray( numVertices ); + oneFace->ref(); + for( j=0; jadd( vertices[j] ); + + addFace( (ssgIndexArray **) &oneFace ); +} + +void ssgLoaderWriterMesh::createPerFaceAndVertexTextureCoordinates2( int numReservedTextureCoordinateLists ) // was: ThereAreNTCPFAV +{ + assert( perFaceAndVertexTextureCoordinate2Lists == NULL ); + perFaceAndVertexTextureCoordinate2Lists = new ssgListOfLists( numReservedTextureCoordinateLists ); +} + +void ssgLoaderWriterMesh::addPerFaceAndVertexTextureCoordinate2( ssgTexCoordArray **textureCoordinates2 ) // was: addTCPFAV +{ + assert( perFaceAndVertexTextureCoordinate2Lists != NULL ); + perFaceAndVertexTextureCoordinate2Lists->add( (ssgSimpleList **)textureCoordinates2 ); +} + +void ssgLoaderWriterMesh::createPerVertexTextureCoordinates2( int numReservedTextureCoordinates ) +{ + assert( perVertexTextureCoordinates2 == NULL ); + perVertexTextureCoordinates2 = new ssgTexCoordArray ( numReservedTextureCoordinates ); +} + +void ssgLoaderWriterMesh::addPerVertexTextureCoordinate2( sgVec2 textureCoordinate ) +{ + assert( perVertexTextureCoordinates2 != NULL ); + perVertexTextureCoordinates2->add ( textureCoordinate ); +} + +void ssgLoaderWriterMesh::createMaterialIndices( int numReservedMaterialIndices ) // ThereAreNMaterialIndexes +{ + assert( materialIndices == NULL ); + materialIndices = new ssgIndexArray ( numReservedMaterialIndices ); +} + +void ssgLoaderWriterMesh::addMaterialIndex( short materialIndex ) +{ + assert( materialIndices != NULL ); + materialIndices->add ( materialIndex ); +} + +void ssgLoaderWriterMesh::createMaterials( int numReservedMaterials ) // ThereAreNMaterials( int n ) +{ + assert( theMaterials == NULL ); + theMaterials = new ssgSimpleStateList( numReservedMaterials ); +} + +void ssgLoaderWriterMesh::addMaterial ( class ssgSimpleState **simpleState ) +{ + assert( theMaterials != NULL ); + theMaterials->add( simpleState ); +} + +static void recalcNormals( ssgIndexArray* indexList, ssgVertexArray* vertexList, ssgNormalArray *normalList ) +// wl: modified to use more code from sg +{ + sgVec3 v1, v2, n; + + for (int i = 0; i < indexList->getNum() / 3; i++) { + short indices[3] = { *indexList->get( i*3 ), *indexList->get( i*3 + 1), *indexList->get( i*3 + 2) }; + + sgSubVec3(v1, vertexList->get(indices[1]), vertexList->get(indices[0])); + sgSubVec3(v2, vertexList->get(indices[2]), vertexList->get(indices[0])); + + sgVectorProductVec3( n, v1, v2 ); + SGfloat normalLength = sgLengthVec3( n ); + if( normalLength > 0.00001 ) + sgNormaliseVec3( n ); + + sgCopyVec3( normalList->get( indices[0] ), n ); + sgCopyVec3( normalList->get( indices[1] ), n ); + sgCopyVec3( normalList->get( indices[2] ), n ); + } + } + +// addOneNodeToSSGFromPerFaceAndVertexTextureCoordinates: this function replicates each vertex (based on face usage) and +// assigns the appropriate texture coordinates to them (based on the per-face texture indices) +void ssgLoaderWriterMesh::addOneNodeToSSGFromPerFaceAndVertexTextureCoordinates2( class ssgVertexArray *theVertices, + class ssgListOfLists *thePerFaceAndVertexTextureCoordinates2, + class ssgListOfLists *theFaces, + class ssgSimpleState *currentState,// Pfusch, kludge. NIV135 + class ssgLoaderOptions* current_options, + class ssgBranch *curr_branch_) // was: AddOneNode2SSGFromCPFAV + +{ + int i, j; + + assert( theVertices!=NULL ); + assert( theFaces!=NULL ); + + // note: I am changing theVertices here, but that is allowed. + class ssgTexCoordArray *perVertexTextureCoordinates2 = new ssgTexCoordArray( theVertices->getNum() ); + sgVec2 unUsed; + unUsed[0]=-99999; // FixMe: It would be nicer to have an extra array of booleans + unUsed[1]=-99999; + for( i=0; igetNum(); i++) + perVertexTextureCoordinates2->add( unUsed ); + for( i=0; igetNum(); i++) + { + class ssgIndexArray *oneFace = *((class ssgIndexArray **) theFaces->get( i )); + class ssgTexCoordArray *textureCoordsForOneFace = *( (ssgTexCoordArray **) thePerFaceAndVertexTextureCoordinates2->get( i ) ); + if ( textureCoordsForOneFace != NULL ) // It is allowed that some or even all faces are untextured. + { + for( j=0; jgetNum(); j++ ) + { + short *ps = oneFace->get(j); + float *newTextureCoordinate2 = textureCoordsForOneFace->get( j ); + float *oldTextureCoordinate2 = perVertexTextureCoordinates2->get( *ps ); + + assert( oldTextureCoordinate2 != NULL ); + if ((oldTextureCoordinate2[0]==-99999) && (oldTextureCoordinate2[1]==-99999)) // tc unused until now. Use it + { + sgVec2 pv; // FixMe: mem leak? + pv[0]=newTextureCoordinate2[0]; + pv[1]=newTextureCoordinate2[1]; + perVertexTextureCoordinates2->set( pv, *ps ); + } + else + { // can we simply use the "old" value? + if ( TC_EPSILON < ABS ( newTextureCoordinate2[0]-oldTextureCoordinate2[0] ) + + ABS ( newTextureCoordinate2[1]-oldTextureCoordinate2[1] )) + { // NO, we can't. Duplicate vertex + // not allowed: theVertices->add(theVertices->get(*ps)); + // create duplicate 3D. FixMe: clone needed? + + float * f = theVertices->get(*ps); + sgVec3 v; + v[0] = f[0]; v[1] = f[1]; v[2] = f[2]; + theVertices->add( v ); + sgVec2 pv; + pv[0]=newTextureCoordinate2[0]; + pv[1]=newTextureCoordinate2[1]; + perVertexTextureCoordinates2->add( pv ); // create duplicate 2D + *ps=theVertices->getNum()-1; // use duplicate + assert ( *oneFace->get(j) == theVertices->getNum()-1); + } + } + } + } + } + addOneNodeToSSGFromPerVertexTextureCoordinates2(theVertices, perVertexTextureCoordinates2, theFaces, currentState, + current_options, curr_branch_); +} + +void ssgLoaderWriterMesh::addOneNodeToSSGFromPerVertexTextureCoordinates2( class ssgVertexArray *theVertices, + class ssgTexCoordArray *theTextureCoordinates2, + class ssgListOfLists *theFaces, + class ssgSimpleState *currentState,// kludge NIV135 + class ssgLoaderOptions* current_options, + class ssgBranch *curr_branch_) // was: AddOneNode2SSGFromCPV +{ + int i, j; + //start Normals, FixMe, kludge NIV135 + + ssgNormalArray *normalList = new ssgNormalArray( theVertices->getNum() ); + sgVec3 kludge; + for( i=0; igetNum(); i++ ) + normalList->add(kludge); //currentMesh.vl->get(i)); + + class ssgIndexArray* indexList = new ssgIndexArray ( theFaces->getNum() * 3 ) ; // there are MINIMAL n * 3 indexes + + for( i=0; igetNum(); i++ ) + { + class ssgIndexArray *oneFace = *((class ssgIndexArray **) theFaces->get( i )); + if ( oneFace->getNum() >= 3 ) + { + for(j=0;jgetNum();j++) + { + if (j<3) + indexList->add(*oneFace->get(j)); + else // add a complete triangle + { + indexList->add(*oneFace->get(0)); + indexList->add(*oneFace->get(j-1)); + indexList->add(*oneFace->get(j)); + } + } + } + } + recalcNormals( indexList, theVertices, normalList ); // Fixme, NIV14: only do this if there are no normals in the file + + ssgColourArray* colours = NULL ; + + if ( currentState -> isEnabled ( GL_LIGHTING ) ) + { + if ( colours == NULL ) + { + colours = new ssgColourArray ( 1 ) ; + sgVec4 currentDiffuseColour; + sgCopyVec4(currentDiffuseColour, currentState->getMaterial(GL_DIFFUSE)); + colours -> add ( currentDiffuseColour ) ; + } + } + ssgVtxArray* leaf = new ssgVtxArray ( GL_TRIANGLES, theVertices, normalList, theTextureCoordinates2, colours, indexList ) ; + leaf -> setCullFace ( TRUE ) ; + leaf -> setState ( currentState ) ; + + ssgEntity *model = current_options -> createLeaf ( leaf, NULL) ; + assert( model != NULL ); + model->setName(name); + curr_branch_->addKid(model); +} + +void ssgLoaderWriterMesh::setName( const char *meshName ) +{ + delete [] name; + if ( !meshName ) + name = NULL; + else + name = ulStrDup(meshName); +} + + +void ssgLoaderWriterMesh::addToSSG( + class ssgSimpleState *currentState,// FixMe, kludge. NIV135 + class ssgLoaderOptions* current_options, + class ssgBranch *curr_branch_ ) +{ + int i, j, k; + unsigned short oldVertexIndex, newVertexIndex; + class ssgIndexArray *thisFace; + +#ifdef WRITE_MESH_TO_STDOUT + if ( theMaterials == NULL ) + ulSetError(UL_DEBUG, "( theMaterials == NULL )"); + else + { + ulSetError(UL_DEBUG, "%d Materials:", theMaterials->getNum()); + for(i=0;igetNum();i++) + { ulSetError(UL_DEBUG, "%ld", (long)theMaterials->get(i)); + } + } + if ( materialIndices == NULL ) + ulSetError(UL_DEBUG, "( materialIndices == NULL )"); + else + { + ulSetError(UL_DEBUG, "%d Material Indexes:", materialIndices->getNum()); + for(i=0;igetNum();i++) + { short s=*(materialIndices->get(i)); + ulSetError(UL_DEBUG, "%ld", (long)s); + } + } +#endif + if ( theMaterials == NULL ) + { + if ( perFaceAndVertexTextureCoordinate2Lists == NULL ) + addOneNodeToSSGFromPerVertexTextureCoordinates2( theVertices, perVertexTextureCoordinates2 /* may be NULL */, theFaces, currentState, current_options, curr_branch_); + else + addOneNodeToSSGFromPerFaceAndVertexTextureCoordinates2(theVertices, perFaceAndVertexTextureCoordinate2Lists, theFaces, currentState, + current_options, curr_branch_); + } + else + { + assert( theVertices != NULL ); + assert( theFaces != NULL ); + // FixMe: What about faces without state? They should have material -1 + for( i=0; i < theMaterials->getNum(); i++ ) + { + // I often allocate too much; This is wastefull on memory, but fast since it never "resizes": + class ssgVertexArray *newVertices = new ssgVertexArray ( theVertices->getNum() ); + class ssgListOfLists *newFaces = new ssgListOfLists ( theFaces->getNum() ); + class ssgIndexArray *oldVertexIndexToNewVertexIndex = new ssgIndexArray ( theVertices->getNum() ); + class ssgListOfLists *newPerFaceAndVertexTextureCoordinate2Lists = NULL; + class ssgTexCoordArray *newPerVertexTextureCoordinates2 = NULL; + + if( perFaceAndVertexTextureCoordinate2Lists != NULL ) + newPerFaceAndVertexTextureCoordinate2Lists = new ssgListOfLists(); + if ( perVertexTextureCoordinates2 != NULL ) + newPerVertexTextureCoordinates2 = new ssgTexCoordArray(); + + for (j=0; jgetNum(); j++) + oldVertexIndexToNewVertexIndex->add ( short(0xFFFF) ); // 0xFFFF stands for "unused in new Mesh" + + // Go through all the old Faces, look for the correct material and copy those + // faces and indexes into the new + // FixMe, 2do, NIV135: if the Materials just differ through the colour, one would not need + // several meshes, but could use the colour array. However, this is not possible, + // if they differ by for example the texture + assert( materialIndices != NULL ); + for ( j=0; jgetNum(); j++ ) + if ( i == *(materialIndices->get( + // for *.x-files, there may be less materialIndices than faces. I then simply repeat + // the last index all the time: + jgetNum() ? j : materialIndices->getNum()-1 )) + ) + { + // take this face + thisFace = *((class ssgIndexArray **) theFaces->get( j )); + newFaces->add( (class ssgSimpleList **)&thisFace); + //thisFace = *((class ssgIndexArray **) newFaces->get( newFaces->getNum()-1 )); + + if( perFaceAndVertexTextureCoordinate2Lists != NULL ) + newPerFaceAndVertexTextureCoordinate2Lists ->add( perFaceAndVertexTextureCoordinate2Lists -> get( j ) ); + + for( k=0; k < thisFace->getNum(); k++ ) + { + oldVertexIndex = * thisFace->get(k); + newVertexIndex = *oldVertexIndexToNewVertexIndex->get( oldVertexIndex ); + + if ( 0xFFFF == newVertexIndex ) + { + newVertexIndex = newVertices->getNum(); + newVertices->add( theVertices->get( oldVertexIndex ) ); + oldVertexIndexToNewVertexIndex->set( newVertexIndex, oldVertexIndex ); + + if ( perVertexTextureCoordinates2 != NULL ) + newPerVertexTextureCoordinates2 -> add( perVertexTextureCoordinates2->get( oldVertexIndex ) ); + } + // From here on the indexes in thisFace are only valid in relation to + // newVertices and newtextureCoordinatePerVertex. Since this face will not be used for any + // further material, this doesn't lead to problems. + thisFace->set( newVertexIndex, k ); + } + } +#ifdef WRITE_MESH_TO_STDOUT + ulSetError(UL_DEBUG, "NumVert: %d", newVertices->getNum()); + for(j=0;jgetNum();j++) + { float *f=newVertices->get(j); + ulSetError(UL_DEBUG, "%f, %f, %f",f[0], f[1], f[2]); + } + for(j=0;jgetNum();j++) + { + thisFace = *((class ssgIndexArray **) newFaces->get( j )); + fprintf(stderr, "%d EP:", thisFace->getNum()); + for(k=0;kgetNum();k++) + { + oldVertexIndex = * thisFace->get(k); + fprintf(stderr, "%d, ", oldVertexIndex); + } + putc('\n', stderr); + } +#endif + if ( newFaces->getNum() > 0 ) + { + currentState = *theMaterials->get(i); + if ( perFaceAndVertexTextureCoordinate2Lists == NULL ) + // FixMe: textureCoordinatePerVertex-indices are not compatible to newVertices-indices?!? + addOneNodeToSSGFromPerVertexTextureCoordinates2( newVertices, newPerVertexTextureCoordinates2 /* may be NULL */, newFaces, currentState, current_options, curr_branch_); + else + addOneNodeToSSGFromPerFaceAndVertexTextureCoordinates2(newVertices, newPerFaceAndVertexTextureCoordinate2Lists, newFaces, currentState, + current_options, curr_branch_); + } + } + } +} + +int ssgLoaderWriterMesh::checkMe() +// returns TRUE; if ok. +// Writes out errors by calling ulSetError with severity UL_WARNING, +// and a bit of debug info as UL_DEBUG +// May stop on first error. + +// FixMe; todo: textureCoordinatePerVertex and tCPFAV. NIV135 +{ + int i, oneIndex; + class ssgIndexArray * vertexIndsForOneFace; + class ssgTexCoordArray * textureCoordsForOneFace; + + // **** check theVertices ***** + if ( theVertices == NULL ) + { if (( materialIndices == NULL ) && + (theFaces == NULL ) && + ( perFaceAndVertexTextureCoordinate2Lists == NULL )) + { ulSetError( UL_DEBUG, "LoaderWriterMesh::checkMe(): The mesh is empty\n"); + return TRUE; + } + else + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): No theVertices is NULL; but not the rest!\n"); + return FALSE; + } + + } + // **** check materialIndices and theMaterials ***** + /* FixMe; kludge: 2do. NIV135 + // one index per face: + class ssgIndexArray *materialIndices; + + theMaterials + */ + if ((( theMaterials == NULL ) && ( materialIndices != NULL )) || + (( theMaterials != NULL ) && ( materialIndices == NULL ))) + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): " + "One of theMaterials and materialIndices was NULL and the other != NULL!\n"); + return FALSE; + } + if ( materialIndices != NULL ) + { for (i=0;igetNum();i++) + { oneIndex = *materialIndices->get(i); + assert(theMaterials!=NULL); + if (( oneIndex < 0 ) || ( oneIndex >= theMaterials->getNum())) + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): " + "Material index out of range. Index = %d, " + "theMaterials->getNum() = %d.\n", + oneIndex, theMaterials->getNum()); + return FALSE; + } + } + } + + + // **** check theFaces ***** + // Each sublist is of type ssgIndexArray and contains the indexes of the vertices: + if ( theFaces == NULL ) + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): There are vertices but no faces.\n"); + return FALSE; + } + for(i=0;igetNum();i++) + { + vertexIndsForOneFace = *((ssgIndexArray **) theFaces->get ( i )); + if ( vertexIndsForOneFace == NULL ) + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): the vertexindexes for one face are NULL!\n"); + return FALSE; + } + } + // **** check textureCoordinates ***** + // Each sublist is of type ssgTexCoordArray and contains the texture coordinates + if ( perFaceAndVertexTextureCoordinate2Lists != NULL ) // may be NULL + { if ( theFaces->getNum() != perFaceAndVertexTextureCoordinate2Lists->getNum()) + { ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): " + "There must be as many faces in theFaces as in textureCoordinates. But " + "theFaces->getNum() =%d, tCPFAV->getNum() = %d!\n", + theFaces->getNum(), perFaceAndVertexTextureCoordinate2Lists->getNum()); + return FALSE; + } + for(i=0;igetNum();i++) + { + textureCoordsForOneFace = *((ssgTexCoordArray **) perFaceAndVertexTextureCoordinate2Lists->get ( i )); + if ( textureCoordsForOneFace != NULL ) // It is allowed that some or even all faces are untextured. + { + vertexIndsForOneFace = *((ssgIndexArray **) theFaces->get ( i )); + if ( textureCoordsForOneFace->getNum() != vertexIndsForOneFace ->getNum()) + { + ulSetError( UL_WARNING, "LoaderWriterMesh::checkMe(): Face %d: " + "Each face must have as many texture corrdinates (or none) as vertices. But " + "textureCoordsForOneFace->getNum() =%d, vertexIndsForOneFace ->getNum() = %d!\n", + i, textureCoordsForOneFace->getNum(), vertexIndsForOneFace ->getNum()); + return FALSE; + } + } + } + } + return TRUE; // success +} diff --git a/src/ssg/ssgLoaderWriterStuff.h b/src/ssg/ssgLoaderWriterStuff.h new file mode 100644 index 0000000..2a42979 --- /dev/null +++ b/src/ssg/ssgLoaderWriterStuff.h @@ -0,0 +1,161 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLoaderWriterStuff.h 1935 2004-05-04 12:37:33Z wolfram_kuss $ +*/ + +//#include "ssgLocal.h" + +// ******************** small utility functions ************************ + +void ssgAccumVerticesAndFaces( ssgEntity* node, sgMat4 transform, ssgVertexArray* vertices, + ssgIndexArray* indices, SGfloat epsilon, + ssgSimpleStateArray* ssa = NULL, + ssgIndexArray* materialIndices = NULL, + ssgTexCoordArray *texCoordArray = NULL); + +/* + ssgTriangulate - + Triangulate a simple polygon (possibly concave, but not self-intersecting). + The number of triangles written to 'triangles' is returned, which is always + less than or equal to num - 2. The polygon index array 'indices' may be null, + in which case an identity mapping is assumed. + Note that the implementation is optimized for small polygons, and since + the algorithm is O(num^2) it is not efficient on large polygons. +*/ +int _ssgTriangulate( sgVec3 *vertices, int *indices, int num, int *triangles ); + + +// ******************** class ssgLoaderWriterMesh ************************ + +class ssgListOfLists : public ssgSimpleList +// list of POINTERs to ssgSimpleLists +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) { return NULL; }; // Fixme NIV14: 2do + ssgListOfLists ( int init = 3 ) : ssgSimpleList ( sizeof(ssgSimpleList*), init ) {} + class ssgSimpleList **get ( unsigned int n ) { return (class ssgSimpleList **) raw_get ( n ) ; } + void add ( class ssgSimpleList **thing ) { raw_add ( (char *) thing ) ; } ; + void set ( class ssgSimpleList **thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) {}; // Fixme NIV14: 2do +} ; + +//ssgSimpleState* + +class ssgSimpleStateList : public ssgSimpleList +// list of POINTERs to ssgSimpleStates +{ +public: + + virtual ssgBase *clone ( int clone_flags = 0 ) { return NULL; }; // Fixme NIV14: 2do + ssgSimpleStateList( int init = 3 ) : ssgSimpleList ( sizeof(ssgSimpleState*), init ) {} + class ssgSimpleState **get ( unsigned int n ) { return (class ssgSimpleState **) raw_get ( n ) ; } + void add ( class ssgSimpleState **thing ) { raw_add ( (char *) thing ) ; } ; + void set ( class ssgSimpleState **thing, unsigned int n ) { raw_set ( (char *) thing, n ) ; } ; + virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) {}; // Fixme NIV14: 2do +} ; + +class ssgLoaderWriterMesh +{ + // ***** general **** + char *name; + // array of Vec3s: + class ssgVertexArray *theVertices; + // one index per face: + class ssgIndexArray *materialIndices; + + // Each sublist is of type ssgIndexArray and contains the indexes of the vertices: + class ssgListOfLists *theFaces; + // material list: + class ssgSimpleStateList *theMaterials; + + // ***** mode switches ***** + int textureCoordinatesArePerVertex; // and not per vertex and face (bTCs_are_per_vertex) + // ***** complicated (texture coordinates are per face and vertex) mode ***** + // Each sublist is of type ssgTexCoordArray and contains the texture coordinates + class ssgListOfLists *perFaceAndVertexTextureCoordinate2Lists; // was: tCPFAV = TextureCoordinatesPerFaceAndVertex + + // ***** easy (texture coordinates are per vertex) mode ********* + class ssgTexCoordArray *perVertexTextureCoordinates2; // was: tCPV = TextureCoordinatesPerVertex + + void addOneNodeToSSGFromPerVertexTextureCoordinates2( class ssgVertexArray *theVertices, + class ssgTexCoordArray *theTextureCoordinates2, + class ssgListOfLists *theFaces, + class ssgSimpleState *currentState,// Pfusch, kludge. NIV135 + class ssgLoaderOptions* current_options, + class ssgBranch *curr_branch_ ); // was: AddOneNode2SSGFromCPV + void addOneNodeToSSGFromPerFaceAndVertexTextureCoordinates2( class ssgVertexArray *theVertices, + class ssgListOfLists *theTextureCoordinate2Lists, + class ssgListOfLists *theFaces, + class ssgSimpleState *currentState,// Pfusch, kludge. NIV135 + class ssgLoaderOptions* current_options, + class ssgBranch *curr_branch_ ); // was: AddOneNodeToSSGFromCPFAV + + public: + + class ssgVertexArray *getVertices(void) { return theVertices; } + void setVertices( class ssgVertexArray *vertexArray ); + class ssgTexCoordArray *getPerVertexTextureCoordinates2(void) { return perVertexTextureCoordinates2; } + void setPerVertexTextureCoordinates2( class ssgTexCoordArray *texCoordArray ); + void setName( const char *meshName ); + + void addToSSG( + class ssgSimpleState *currentstate, + class ssgLoaderOptions* currentOptions, + class ssgBranch *curr_branch_ ); + + // construction/destruction: + ssgLoaderWriterMesh(); + ~ssgLoaderWriterMesh(); + void reInit(void); + void deletePerFaceAndVertexTextureCoordinates2(); // was: deleteTCPFAV + + // creation: + void createVertices( int numReservedVertices = 8 ) ; // was: ThereAreNVertices + void addVertex( sgVec3 v ) ; + + void createFaces( int numReservedFaces = 3 ) ; // was: ThereAreNFaces + void addFace( ssgIndexArray **indexArray ) ; + void addFaceFromIntegerArray( int numVertices, int *vertices );// AddFaceFromCArray + + void createPerFaceAndVertexTextureCoordinates2( int numReservedTextureCoordinate2Lists = 3 ) ; // ThereAreNTCPFAV + void addPerFaceAndVertexTextureCoordinate2( ssgTexCoordArray **textureCoordinateArray ) ; // addTCPFAV + + void createPerVertexTextureCoordinates2( int numReservedTextureCoordinates2 = 3 ); // was: ThereAreNTCPV + void addPerVertexTextureCoordinate2( sgVec2 textureCoordinate ); // was; addTCPV + + void createMaterialIndices( int numReservedMaterialIndices = 3 ); // ThereAreNMaterialIndexes + void addMaterialIndex( short materialIndex ) ; + + void createMaterials( int numReservedMaterials = 3 ); // was: ThereAreNMaterials + void addMaterial( class ssgSimpleState **simpleState ) ; + + unsigned int getNumVertices(void) { return theVertices->getNum(); } ; + unsigned int getNumFaces (void) { return theFaces->getNum(); } ; + unsigned int getNumMaterials(void) { return theMaterials->getNum(); } ; + + + // tools: + int checkMe(); +}; + + diff --git a/src/ssg/ssgLocal.h b/src/ssg/ssgLocal.h new file mode 100644 index 0000000..17d3e8e --- /dev/null +++ b/src/ssg/ssgLocal.h @@ -0,0 +1,136 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgLocal.h 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#define _SSG_PUBLIC public + +#include "ssg.h" + +void _ssgStartOfFrameInit () ; +void _ssgEndOfFrameCleanup () ; + + +extern void (*__ssgEnableTable[64])() ; +extern void (*__ssgDisableTable[64])() ; + +extern sgMat4 _ssgOpenGLAxisSwapMatrix ; +extern int _ssgIsHotTest ; +extern int _ssgIsLosTest ; +extern int _ssgFileVersionNumber ; + +void _ssgForceLineState () ; + +void _ssgDrawDList () ; +void _ssgSetTweenState ( float state, int mode ) ; +void _ssgPushMatrix ( sgMat4 m ) ; +void _ssgPopMatrix () ; +void _ssgLoadMatrix ( sgMat4 m ) ; +void _ssgLoadTexMatrix ( sgMat4 m ) ; +void _ssgUnloadTexMatrix () ; +void _ssgDrawLeaf ( ssgLeaf *l ) ; + +void _ssgAddHit ( ssgLeaf *l, int trinum, sgMat4 mat, sgVec4 pl ) ; +void _ssgPushPath ( ssgEntity *l ) ; +void _ssgPopPath () ; + +extern int stats_num_vertices ; +extern int stats_num_leaves ; +extern int stats_isect_triangles ; +extern int stats_cull_test ; +extern int stats_isect_test ; +extern int stats_bind_textures ; + +extern int stats_hot_triangles ; +extern int stats_hot_test ; +extern int stats_hot_no_trav ; +extern int stats_hot_radius_reject ; +extern int stats_hot_triv_accept ; +extern int stats_hot_straddle ; + +extern int stats_los_triangles ; +extern int stats_los_test ; +extern int stats_los_no_trav ; +extern int stats_los_radius_reject ; +extern int stats_los_triv_accept ; +extern int stats_los_straddle ; + +extern ssgState *( *_ssgGetAppState)( char *) ; + +void _ssgReadFloat ( FILE *fd, float *var ) ; +void _ssgWriteFloat ( FILE *fd, const float var ) ; +void _ssgReadUInt ( FILE *fd, unsigned int *var ) ; +void _ssgWriteUInt ( FILE *fd, const unsigned int var ) ; +void _ssgReadInt ( FILE *fd, int *var ) ; +void _ssgWriteInt ( FILE *fd, const int var ) ; +void _ssgReadUShort ( FILE *fd, unsigned short *var ) ; +void _ssgWriteUShort ( FILE *fd, const unsigned short var ) ; +void _ssgReadShort ( FILE *fd, short *var ) ; +void _ssgWriteShort ( FILE *fd, const short var ) ; + +void _ssgReadFloat ( FILE *fd, const unsigned int n, float *var ) ; +void _ssgWriteFloat ( FILE *fd, const unsigned int n, const float *var ) ; +void _ssgReadUInt ( FILE *fd, const unsigned int n, unsigned int *var ) ; +void _ssgWriteUInt ( FILE *fd, const unsigned int n, const unsigned int *var ) ; +void _ssgReadInt ( FILE *fd, const unsigned int n, int *var ) ; +void _ssgWriteInt ( FILE *fd, const unsigned int n, const int *var ) ; +void _ssgReadUShort ( FILE *fd, const unsigned int n, unsigned short *var ) ; +void _ssgWriteUShort ( FILE *fd, const unsigned int n, const unsigned short *var ) ; +void _ssgReadShort ( FILE *fd, const unsigned int n, short *var ) ; +void _ssgWriteShort ( FILE *fd, const unsigned int n, const short *var ) ; +void _ssgReadBytes ( FILE *fd, const unsigned int n, void *var ) ; +void _ssgWriteBytes ( FILE *fd, const unsigned int n, const void *var ) ; + +void _ssgReadString ( FILE *fd, char **var ) ; +void _ssgWriteString ( FILE *fd, const char *var ) ; + +void _ssgReadVec2 ( FILE *fd, sgVec2 var ) ; +void _ssgWriteVec2 ( FILE *fd, const sgVec2 var ) ; +void _ssgReadVec3 ( FILE *fd, sgVec3 var ) ; +void _ssgWriteVec3 ( FILE *fd, const sgVec3 var ) ; +void _ssgReadVec4 ( FILE *fd, sgVec4 var ) ; +void _ssgWriteVec4 ( FILE *fd, const sgVec4 var ) ; + +void _ssgReadMat4 ( FILE *fd, sgMat4 var ) ; +void _ssgWriteMat4 ( FILE *fd, const sgMat4 var ) ; + +int _ssgReadError ( void ) ; +int _ssgWriteError ( void ) ; + +float _ssgGetCurrentTweenState () ; +int _ssgGetCurrentTweenMode () ; +void _ssgSetCurrentTweenSettings ( float tweenstate, int tweenmode ) ; +void _ssgSetRealCurrentTweenSettings ( float tweenstate, int tweenmode ) ; + +/* + Routines for storing arbitrary ssgBase derived objects within SSG files. + Both functions return 1 on success, and 0 on failure. + If an object is encountered that is not derived from type_mask, then + the loading is aborted and 0 returned. +*/ +int _ssgSaveObject ( FILE * , ssgBase * ) ; +int _ssgLoadObject ( FILE * , ssgBase ** , int type_mask = 0 ) ; + +extern class ssgStatistics _ssgCurrStatistics ; +extern bool _ssgBackFaceCollisions ; + + diff --git a/src/ssg/ssgMSFSPalette.h b/src/ssg/ssgMSFSPalette.h new file mode 100644 index 0000000..449e15c --- /dev/null +++ b/src/ssg/ssgMSFSPalette.h @@ -0,0 +1,29 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgMSFSPalette.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#ifndef __SSGMSFSPALETTE +#define __SSGMSFSPALETTE + +extern unsigned const char ssgFsTexPalette[] ; + +#endif // __SSGMSFSPALETTE diff --git a/src/ssg/ssgOptimiser.cxx b/src/ssg/ssgOptimiser.cxx new file mode 100644 index 0000000..eb0fd23 --- /dev/null +++ b/src/ssg/ssgOptimiser.cxx @@ -0,0 +1,1108 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgOptimiser.cxx 1999 2005-01-09 13:10:08Z bram $ +*/ + + +#include "ssgLocal.h" + +#include "ssgVertSplitter.h" + +static float optimise_vtol [3] = +{ + 0.001f, /* DISTANCE_SLOP = One millimeter */ + 0.04f, /* COLOUR_SLOP = Four percent */ + 0.004f, /* TEXCOORD_SLOP = One texel on a 256 map */ +} ; + +static float* current_vtol = 0 ; + +#define DISTANCE_SLOP current_vtol[0] +#define COLOUR_SLOP current_vtol[1] +#define TEXCOORD_SLOP current_vtol[2] + +inline float frac ( float x ) +{ + return x - (float) floor(x) ; +} + +struct OptVertex +{ + sgVec3 vertex ; + sgVec3 normal ; + sgVec2 texcoord ; + sgVec4 colour ; + int counter ; + + void print () { ulSetError ( UL_DEBUG, "%d:(%g,%g,%g):(%g,%g):(%g,%g,%g,%g):(%g,%g,%g)", + counter, + vertex[0],vertex[1],vertex[2], + texcoord[0],texcoord[1], + colour[0],colour[1],colour[2],colour[3], + normal[0],normal[1],normal[2] ) ; } + + OptVertex ( sgVec3 v, sgVec2 t, sgVec4 c ) + { + sgCopyVec3 ( vertex , v ) ; + sgCopyVec2 ( texcoord, t ) ; + sgCopyVec4 ( colour , c ) ; + sgSetVec3 ( normal , 0.0f, 0.0f, 0.0f ) ; + counter = 1 ; + } + + OptVertex ( OptVertex* cloneFrom ) + { + sgCopyVec3 ( vertex , cloneFrom -> vertex ) ; + sgCopyVec2 ( texcoord, cloneFrom -> texcoord ) ; + sgCopyVec4 ( colour , cloneFrom -> colour ) ; + sgSetVec3 ( normal , 0.0f, 0.0f, 0.0f ) ; + counter = 1 ; + } + + int equal ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_frac ) + { + if ( ! sgCompareVec3 ( vertex , v, DISTANCE_SLOP ) || + ! sgCompareVec4 ( colour , c, COLOUR_SLOP ) ) + return FALSE ; + + if ( ! tex_frac ) + return sgCompareVec2 ( texcoord, t, TEXCOORD_SLOP ) ; + + return ( fabs ( frac ( texcoord[0] ) - frac ( t[0] ) ) <= TEXCOORD_SLOP && + fabs ( frac ( texcoord[1] ) - frac ( t[1] ) ) <= TEXCOORD_SLOP ) ; + } + + void bump () { counter++ ; } + void dent () { counter-- ; } + int getCount () { return counter ; } +} ; + + +#define MAX_OPT_VERTEX_LIST 10000 + +class OptVertexList +{ +public: + short vnum, tnum ; + OptVertex **vlist ; + short *tlist ; + ssgState *state ; + int cullface ; + + OptVertexList ( ssgState *s, int cf ) + { + /* + Have to dynamically allocate these to get + around Mac's CodeWarrior restriction on + 32Kb as max structure size. + */ + + vlist = new OptVertex* [ MAX_OPT_VERTEX_LIST ] ; + tlist = new short [ MAX_OPT_VERTEX_LIST * 3 ] ; + state = s ; + if (state != NULL) state -> ref(); //~T.G. + cullface = cf ; + vnum = tnum = 0 ; + } + + ~OptVertexList () + { + for ( int i = 0 ; i < vnum ; i++ ) + delete vlist [ i ] ; + + delete [] vlist ; + delete [] tlist ; + + if (state != NULL) ssgDeRefDelete(state); //~T.G. + } + + short find ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_fraction_only = FALSE ) ; + + short add ( sgVec3 v1, sgVec2 t1, sgVec4 c1, + sgVec3 v2, sgVec2 t2, sgVec4 c2, + sgVec3 v3, sgVec2 t3, sgVec4 c3 ) ; + short add ( sgVec3 v, sgVec2 t, sgVec4 c ) ; + short add ( short v1, short v2, short v3 ) ; + void add ( ssgLeaf *l ) ; + + void makeNormals () ; + + void print () + { + ulSetError ( UL_DEBUG, "LIST: %d unique vertices and %d triangles", + vnum, tnum ) ; + } + + void follow ( int tri, int v1, int v2, int backwards, int *len, short *list, short *next ) ; + + int getLeastConnected ( short *t, short *v ) + { + int least = 32767 ; + *v = 0 ; + + /* Find the least connected vertex that *is* connected */ + + int i ; + + for ( i = 0 ; i < vnum ; i++ ) + { + int c = vlist [ i ] -> getCount () ; + + if ( c > 0 && c < least ) + { + least = c ; + *v = i ; + } + } + + if ( least == 32767 ) /* Didn't find an unused vertex - so punt. */ + return FALSE ; + + least = 32767 ; + *t = 32767 ; + + for ( i = 0 ; i < tnum ; i++ ) + if ( tlist[i*3+0] == *v || tlist[i*3+1] == *v || tlist[i*3+2] == *v ) + { + int c = vlist [ tlist[i*3+0] ] -> getCount () + + vlist [ tlist[i*3+1] ] -> getCount () + + vlist [ tlist[i*3+2] ] -> getCount () ; + + if ( c < least ) + { + least = c ; + *t = i ; + } + } + + if ( least == 32767 ) /* Didn't find an unused vertex - so punt. */ + return FALSE ; + + return TRUE ; /* Got it! */ + } + + void sub ( short t ) + { + vlist [ tlist[t*3+0] ] -> dent () ; + vlist [ tlist[t*3+1] ] -> dent () ; + vlist [ tlist[t*3+2] ] -> dent () ; + + tlist[t*3+0] = -1 ; + tlist[t*3+1] = -1 ; + tlist[t*3+2] = -1 ; + } +} ; + + +short OptVertexList::add ( sgVec3 v1, sgVec2 t1, sgVec4 c1, + sgVec3 v2, sgVec2 t2, sgVec4 c2, + sgVec3 v3, sgVec2 t3, sgVec4 c3 ) +{ + // If possible, this routine moves the texturecoordinates of + // all three vertices of a Tria so that one needs less vertices. + // This doesn't affect looks, but enhances the speed a bit. + // This is not possible, if warpu or wrapv is FALSE + + int bWrapsInBothDirections = FALSE; + short vi1, vi2, vi3; +/* + if ( state->isAKindOf( ssgTypeSimpleState() ) ) + bWrapsInBothDirections = + ( (((ssgSimpleState *)state)->getWrapU()) && + (((ssgSimpleState *)state)->getWrapV()) ); + */ + if (!bWrapsInBothDirections) + { + /* Find which (if any) of the vertices are a match for one in the list */ + + vi1 = add ( v1, t1, c1 ) ; + vi2 = add ( v2, t2, c2 ) ; + vi3 = add ( v3, t3, c3 ) ; + } + else + { + /* + Sharing vertices is tricky because of texture coordinates + that have the same all-important fractional part - but + differ in their integer parts. + */ + + sgVec2 adjust ; + + /* Find which (if any) of the vertices are a match for one in the list */ + + vi1 = find ( v1, t1, c1, TRUE ) ; + vi2 = find ( v2, t2, c2, TRUE ) ; + vi3 = find ( v3, t3, c3, TRUE ) ; + + /* Compute texture offset coordinates (if needed) to make everything match */ + + if ( vi1 >= 0 ) + sgSubVec2 ( adjust, t1, vlist[vi1]->texcoord ) ; + else + if ( vi2 >= 0 ) + sgSubVec2 ( adjust, t2, vlist[vi2]->texcoord ) ; + else + if ( vi3 >= 0 ) + sgSubVec2 ( adjust, t3, vlist[vi3]->texcoord ) ; + else + { + /* + OK, there was no match - so just remove + any large numbers from the texture coords + */ + + adjust [ 0 ] = (float) floor ( t1[0] ) ; + adjust [ 1 ] = (float) floor ( t1[1] ) ; + } + + /* + Now adjust the texture coordinates and add them into the list + */ + sgVec2 tmp ; + sgSubVec2 ( tmp, t1, adjust ) ; vi1 = add ( v1, tmp, c1 ) ; + sgSubVec2 ( tmp, t2, adjust ) ; vi2 = add ( v2, tmp, c2 ) ; + sgSubVec2 ( tmp, t3, adjust ) ; vi3 = add ( v3, tmp, c3 ) ; + } + return add ( vi1, vi2, vi3 ) ; +} + + +short OptVertexList::add ( short v1, short v2, short v3 ) +{ + if ( v1 == v2 || v2 == v3 || v3 == v1 ) /* Toss degenerate triangles */ + { + vlist [ v1 ] -> dent () ; /* Un-reference their vertices */ + vlist [ v2 ] -> dent () ; + vlist [ v3 ] -> dent () ; + return -1 ; + } + + tlist [ tnum*3+ 0 ] = v1 ; + tlist [ tnum*3+ 1 ] = v2 ; + tlist [ tnum*3+ 2 ] = v3 ; + + return tnum++ ; +} + +// +// Splits vertices across "sharp" edges to improve the normal +// direction. +// +void OptVertexList::makeNormals () +{ + int i, j ; + ssgVertSplitter vs ( vnum, tnum ) ; + + // Copy in the vertex and triangle data + for ( i = 0 ; i < vnum ; i ++ ) + sgCopyVec3 ( vs.vert ( i ), vlist[ i ] -> vertex ) ; + + for ( i = 0 ; i < tnum ; i ++ ) { + short* t = tlist + 3 * i ; + vs.setTri ( i , t[0], t[1], t[2] ); + } + + // Run the algorithm. Get the "sharp" angle from somewhere + // intelligent, instead of hardcoding it. + vs.splitAndCalcNormals () ; + + // Generate the new vertices by cloning their originals + int newVerts = vs.newVerts () ; + if ( vnum + newVerts > MAX_OPT_VERTEX_LIST ) + return; // Oh well. No harm done, at least. + + for ( i = 0 ; i < newVerts ; i++ ) { + OptVertex *ov = vlist[ vs.origVert ( i ) ] ; + vlist[ i + vnum ] = new OptVertex ( ov ); + } + vnum += newVerts; + + // Copy out the new, optimized normal data + for(i=0; inormal, vs.norm(i)); + + // Zero out the reference counts for the vertices; they will have + // changed during the split and will be recalculated below. This + // is an ugly hack -- a more elegant solution would be to do the + // bump()/dent() calls inside the ssgVertSplitter code as each vertex + // is duplicated. + for ( i = 0 ; i < vnum ; i++ ) + while ( vlist [ i ] -> getCount () ) + vlist [ i ] -> dent () ; + + // Copy out the possibly changed vertices for the triangles + for (i = 0 ; i < tnum ; i++ ) { + short* oldtri = tlist + 3 * i ; + int* newtri = vs.getTri ( i ) ; + for ( j = 0 ; j < 3 ; j++ ) { + oldtri [ j ] = newtri [ j ] ; + vlist[ newtri [ j ] ] -> bump (); + } + } +} + +short OptVertexList::find ( sgVec3 v, sgVec2 t, sgVec4 c, int tex_frac ) +{ + for ( short i = 0 ; i < vnum ; i++ ) + { + if ( vlist[i] -> equal ( v, t, c, tex_frac ) ) + return i ; + } + return -1 ; +} + +short OptVertexList::add ( sgVec3 v, sgVec2 t, sgVec4 c ) +{ + short i = find ( v, t, c, FALSE ) ; + + if ( i >= 0 ) + { + vlist [ i ] -> bump () ; + return i ; + } + + vlist [ vnum ] = new OptVertex ( v, t, c ) ; + return vnum++ ; +} + +void OptVertexList::add ( ssgLeaf *l ) +{ + int j ; + + for ( j = 0 ; j < l -> getNumTriangles () ; j ++ ) + { + short v1, v2, v3 ; + l -> getTriangle ( j, &v1, &v2, &v3 ) ; + add ( l->getVertex ( v1 ), l->getTexCoord ( v1 ), l->getColour ( v1 ), + l->getVertex ( v2 ), l->getTexCoord ( v2 ), l->getColour ( v2 ), + l->getVertex ( v3 ), l->getTexCoord ( v3 ), l->getColour ( v3 ) ) ; + } +} + + +void OptVertexList::follow ( int tri, int v1, int v2, int backwards, int *len, + short *new_vlist, short *new_vc ) +{ + /* WARNING - RECURSIVE !! */ + + v1 = tlist [ tri*3+ v1 ] ; + v2 = tlist [ tri*3+ v2 ] ; + + /* + This triangle's work is done - dump it. + */ + + (*len)++ ; + sub ( tri ) ; + + /* + If the exit edge vertices don't *both* have a reference + then we are done. + */ + + if ( vlist [ v1 ] -> getCount () <= 0 || + vlist [ v2 ] -> getCount () <= 0 ) + return ; + + /* + Search for a polygon that shares that edge in the correct + direction - and follow it. + */ + + for ( int i = 0 ; i < tnum ; i++ ) + { + if ( tlist [ i*3+ 0 ] < 0 ) /* Deleted triangle */ + continue ; + + if ( backwards ) + { + /* If the previous polygon was backwards */ + + if ( tlist [ i*3+ 0 ] == v1 && tlist [ i*3+ 2 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 1 ] ; + follow ( i, 0, 1, !backwards, len, new_vlist, new_vc ) ; + return ; + } + else + if ( tlist [ i*3+ 1 ] == v1 && tlist [ i*3+ 0 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 2 ] ; + follow ( i, 1, 2, !backwards, len, new_vlist, new_vc ) ; + return ; + } + else + if ( tlist [ i*3+ 2 ] == v1 && tlist [ i*3+ 1 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 0 ] ; + follow ( i, 2, 0, !backwards, len, new_vlist, new_vc ) ; + return ; + } + } + else + { + /* If the previous polygon was forwards... */ + + if ( tlist [ i*3+ 0 ] == v1 && tlist [ i*3+ 2 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 1 ] ; + follow ( i, 1, 2, !backwards, len, new_vlist, new_vc ) ; + return ; + } + else + if ( tlist [ i*3+ 1 ] == v1 && tlist [ i*3+ 0 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 2 ] ; + follow ( i, 2, 0, !backwards, len, new_vlist, new_vc ) ; + return ; + } + else + if ( tlist [ i*3+ 2 ] == v1 && tlist [ i*3+ 1 ] == v2 ) + { + new_vlist [ (*new_vc)++ ] = tlist [ i*3+ 0 ] ; + follow ( i, 0, 1, !backwards, len, new_vlist, new_vc ) ; + return ; + } + } + } +} + + +static ssgLeaf** build_leaf_list ( ssgEntity *ent, ssgLeaf** leaf_list=0 ) +{ + enum { MAX_LEAF_COUNT = 10000 } ; + static int leaf_count ; + + if ( leaf_list == NULL ) + { + leaf_list = new ssgLeaf* [ MAX_LEAF_COUNT+1 ] ; + leaf_count = 0 ; + leaf_list [ leaf_count ] = NULL ; + } + + if ( ent -> isAKindOf ( ssgTypeBranch () ) ) + { + ssgBranch *b_ent = (ssgBranch *) ent ; + for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ; + k = b_ent -> getNextKid () ) + { + build_leaf_list ( k, leaf_list ) ; + } + } + else if ( ent -> isAKindOf ( ssgTypeLeaf () ) ) + { + ssgLeaf *l = (ssgLeaf *) ent ; + + bool found = false ; + for ( int i = 0 ; leaf_list [ i ] != NULL ; i ++ ) + { + if ( leaf_list [ i ] == l ) + { + found = true ; + break ; + } + } + + if ( !found && leaf_count < MAX_LEAF_COUNT ) + { + leaf_list [ leaf_count ++ ] = l ; + leaf_list [ leaf_count ] = NULL ; + } + } + + return leaf_list ; +} + +/* +* NAME +* ssgArrayTool +* +* DESCRIPTION +* Process the graph and convert all leaf entities into vertex arrays. +* +* Each vertex is described by a single index (instead of different +* indices into the v, vc, vt-arrays). This may introduce new redundant +* vertex data into the data set, which may seem silly. However, when +* rendering through OpenGL, it is in most cases the optimal solution +* to use indexed vertex arrays, which only have ONE index for all +* the vertex data. +* +* INPUTS +* +* ent - the entity to process +* +* vtol - an array of 3 floats used to specify tolerances +* vtol[0] - tolerance value for vertices +* vtol[1] - tolerance for colours +* vtol[2] - tolerance for texture coordinates +* +* make_normals - if true then averaged vertex normals are computed +*/ +void ssgArrayTool ( ssgEntity *ent, float* vtol, bool make_normals ) +{ + current_vtol = vtol? vtol: optimise_vtol ; + + ssgLeaf** leaf_list = build_leaf_list ( ent ) ; + for ( int i = 0 ; leaf_list [ i ] != NULL ; i ++ ) + { + ssgLeaf *l = leaf_list [ i ] ; + ssgState *s = l -> getState() ; + int cf = l -> getCullFace() ; + + OptVertexList list ( s, cf ) ; + list . add ( l ) ; + + if ( list . tnum > 0 ) /* If all the triangles are degenerate maybe */ + { + ssgIndexArray *new_index = new ssgIndexArray ( list . tnum * 3 ) ; + ssgVertexArray *new_coords = new ssgVertexArray ( list . vnum ) ; + ssgTexCoordArray *new_texcoords = new ssgTexCoordArray ( list . vnum ) ; + ssgColourArray *new_colours = new ssgColourArray ( list . vnum ) ; + ssgNormalArray *new_normals = 0 ; + + if ( make_normals ) + { + list . makeNormals () ; + new_normals = new ssgNormalArray ( list . vnum ) ; + } + + for ( int t = 0 ; t < list . tnum ; t++ ) + { + new_index -> add ( list . tlist [ t*3+ 0 ] ) ; + new_index -> add ( list . tlist [ t*3+ 1 ] ) ; + new_index -> add ( list . tlist [ t*3+ 2 ] ) ; + } + + for ( int v = 0 ; v < list . vnum ; v++ ) + { + new_coords -> add ( list . vlist[ v ]->vertex ) ; + new_texcoords-> add ( list . vlist[ v ]->texcoord ) ; + new_colours -> add ( list . vlist[ v ]->colour ) ; + if ( make_normals ) + new_normals -> add ( list . vlist[ v ]->normal ) ; + } + + ssgVtxArray *new_varray = new ssgVtxArray ( GL_TRIANGLES, + new_coords, new_normals, new_texcoords, new_colours, new_index ) ; + new_varray -> setState ( list.state ) ; + new_varray -> setCullFace ( list.cullface ) ; + + ssgBranch *p ; + + /* + Add the new leaf + */ + for ( p = l -> getParent ( 0 ) ; p != NULL ; + p = l -> getNextParent () ) + p -> addKid ( new_varray ) ; + + /* + Remove the old leaf + */ + for ( p = new_varray -> getParent ( 0 ) ; p != NULL ; + p = new_varray -> getNextParent () ) + p -> removeKid ( l ) ; + } + } + delete[] leaf_list ; + + ent -> recalcBSphere () ; +} + +void ssgStripify ( ssgEntity *ent ) +{ + current_vtol = optimise_vtol ; + + /* + Walk down until we find a leaf node, then + back up one level, collect all the ssgVtxTables + into one big heap and triangulate them. + */ + + if ( ent -> isAKindOf ( ssgTypeLeaf () ) ) + return ; + + ssgBranch *b_ent = (ssgBranch *) ent ; + + /* + Count number of unique materials (and cull-facedness) + - make a list of them. Recursively stripify non-leaf nodes. + */ + + int stot = 0 ; + ssgState **slist = new ssgState *[ b_ent -> getNumKids () ] ; + int *cflist = new int [ b_ent -> getNumKids () ] ; + + for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ; + k = b_ent -> getNextKid () ) + { + if ( k -> isAKindOf ( ssgTypeVtxTable () ) ) + { + GLenum thisType = ((ssgVtxTable *)k)->getPrimitiveType (); + if ((thisType != GL_POINTS) && (thisType != GL_LINES) && + (thisType != GL_LINE_STRIP) && (thisType != GL_LINE_LOOP)) + { + int i ; + ssgState *s = ((ssgLeaf *) k ) -> getState() ; + int c = ((ssgLeaf *) k ) -> getCullFace() ; + + for ( i = 0 ; i < stot ; i++ ) + if ( s == slist [ i ] && c == cflist [ i ] ) + break ; + + if ( i >= stot ) + { + slist [ i ] = s ; + cflist [ i ] = c ; + stot++ ; + } + } + } + else + if ( k -> isAKindOf ( ssgTypeBranch () ) ) + ssgStripify ( k ) ; + } + + /* + Now, for each unique state, grab all the VtxTable leaf nodes + and smoosh them into one. + */ + + for ( int i = 0 ; i < stot ; i++ ) + { + /* + Put it into a triangle-oriented structure and + then do stripifying and average normal generation. + + Ick! + */ + + OptVertexList list ( slist [ i ], cflist [ i ] ) ; + + ssgEntity *k = b_ent -> getKid ( 0 ) ; + + while ( k != NULL ) + { + if ( k -> isAKindOf ( ssgTypeVtxTable () ) && + ((ssgLeaf *) k ) -> getState() == slist [ i ] && + ((ssgLeaf *) k ) -> getCullFace() == cflist [ i ] ) + { + GLenum thisType = ((ssgVtxTable *)k)->getPrimitiveType (); + if ((thisType != GL_POINTS) && (thisType != GL_LINES) && + (thisType != GL_LINE_STRIP) && (thisType != GL_LINE_LOOP)) + { list . add ( (ssgVtxTable *) k ) ; + b_ent -> removeKid ( k ) ; + k = b_ent -> getKid ( 0 ) ; + } + else + k = b_ent -> getNextKid () ; + } + else + k = b_ent -> getNextKid () ; + } + + if ( list . tnum == 0 ) /* If all the triangles are degenerate maybe */ + continue ; + + /* + So, now we have all the important information sucked out of + all those nodes and safely tucked away in the OptVertexList + + Let's take this opportunity to compute vertex normals. + */ + + list . makeNormals () ; + + /* + Find the least connected triangle. + Use it as the starting point. + */ + + short tleast, nleast ; + + while ( list . getLeastConnected ( & tleast, & nleast ) ) + { + /* OK, we have our starting point - follow where it + leads - but which way to start? We need two vertices + with at least two references - and not the least + referenced vertex please. */ + + short *new_vlist = new short [ list.tnum * 3 ] ; + short new_vc = 0 ; + + int striplength = 0 ; + + if ( nleast == list.tlist[tleast*3+0] ) + { + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ; + list . follow ( tleast, 1, 2, FALSE, &striplength, new_vlist, & new_vc ) ; + } + else + if ( nleast == list.tlist[tleast*3+1] ) + { + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ; + list . follow ( tleast, 2, 0, FALSE, &striplength, new_vlist, & new_vc ) ; + } + else + if ( nleast == list.tlist[tleast*3+2] ) + { + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 2 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 0 ] ; + new_vlist [ new_vc++ ] = list.tlist [ tleast*3+ 1 ] ; + list . follow ( tleast, 0, 1, FALSE, &striplength, new_vlist, & new_vc ) ; + } + else + ulSetError ( UL_WARNING, "Tleast doesn't contain nleast!" ) ; + + ssgVertexArray *new_coords = new ssgVertexArray ( new_vc ) ; + ssgNormalArray *new_normals = new ssgNormalArray ( new_vc ) ; + ssgTexCoordArray *new_texcoords = new ssgTexCoordArray ( new_vc ) ; + ssgColourArray *new_colours = new ssgColourArray ( new_vc ) ; + + for ( int m = 0 ; m < new_vc ; m++ ) + { + new_coords -> add ( list.vlist[new_vlist[m]]->vertex ) ; + new_normals -> add ( list.vlist[new_vlist[m]]->normal ) ; + new_texcoords-> add ( list.vlist[new_vlist[m]]->texcoord ) ; + new_colours -> add ( list.vlist[new_vlist[m]]->colour ) ; + } + + delete [] new_vlist ; + + ssgVtxTable *new_vtable = new ssgVtxTable ( GL_TRIANGLE_STRIP, + new_coords, new_normals, new_texcoords, new_colours ) ; + new_vtable -> setState ( list.state ) ; + new_vtable -> setCullFace ( list.cullface ) ; + + b_ent -> addKid ( new_vtable ) ; + } + } + + delete [] slist ; + delete [] cflist ; +} + + +/* + These routines are essentially non-realtime tree optimisations. +*/ + +static void safe_replace_kid ( ssgBranch *parent, ssgEntity *old_kid, ssgEntity *new_kid ) +{ + /* + Replace old_kid by new_kid in a "safe" manner. + new_kid may be null, in which case old_kid is removed. + If parent is null then loop over all parents of old_kid. + */ + + if ( old_kid == new_kid ) + return ; + + if ( parent == NULL ) + { + int n = old_kid -> getNumParents () ; + while ( n-- > 0 ) + safe_replace_kid ( old_kid -> getParent ( 0 ), old_kid, new_kid ) ; + return ; + } + + // assert ( parent -> searchForKid ( old_kid ) >= 0 ) ; + + if ( new_kid == NULL ) + { + if ( parent -> isAKindOf ( ssgTypeSelector () ) ) + { + /* cannot remove kids from selectors */ + parent -> replaceKid ( old_kid, new ssgInvisible ) ; + } + else + { + parent -> removeKid ( old_kid ) ; + } + } + else + { + parent -> replaceKid ( old_kid, new_kid ) ; + } +} + +static void strip ( ssgEntity *ent ) +{ + /* + Strip off all branches with no kids - and snip out all + simple branches with just one kid. + A node with user data is always left unchanged. + */ + + if ( ! ent -> isAKindOf ( ssgTypeBranch () ) ) + return ; + + ssgBranch *b_ent = (ssgBranch *) ent ; + + for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ; + k = b_ent -> getNextKid () ) + strip ( k ) ; + + switch ( b_ent -> getNumKids () ) + { + case 0: + if ( b_ent -> getUserData() == NULL && b_ent -> getName () == NULL ) + safe_replace_kid ( NULL, b_ent, NULL ) ; + break; + + case 1: + if ( b_ent -> isA ( ssgTypeBranch () ) && + b_ent -> getUserData () == NULL ) + { + ssgEntity *k = b_ent -> getKid ( 0 ) ; + if ( b_ent -> getName () != NULL && k -> getName () != NULL ) + break; + if ( b_ent -> getName () != NULL ) + k -> setName ( b_ent -> getName () ) ; + + safe_replace_kid ( NULL, b_ent, k ) ; + } + else if ( ! b_ent -> isAKindOf ( ssgTypeSelector () ) && + b_ent -> getKid ( 0 ) -> isA ( ssgTypeBranch () ) && + b_ent -> getKid ( 0 ) -> getUserData () == NULL ) + { + ssgBranch *b_kid = (ssgBranch *) b_ent -> getKid ( 0 ) ; + for ( ssgEntity *k = b_kid -> getKid ( 0 ) ; k != NULL ; + k = b_kid -> getNextKid () ) + b_ent -> addKid ( k ) ; + b_ent -> removeKid ( b_kid ) ; + b_ent -> recalcBSphere () ; + } + break; + + default: + if ( b_ent -> isDirtyBSphere () ) + b_ent -> recalcBSphere () ; + } +} + +void flatten ( ssgBranch *parent, ssgEntity *ent, sgMat4 mat ) +{ + /* + Move all transforms down to the leaf nodes and + then multiply them out. You need to strip() the + tree after calling this. + */ + + sgMat4 mat2 ; + + /* + The following nodes may (currently) not be flattened: + - ssgCutout, + - ssgRangeSelector, and + - ssgTransform with user data. + */ + if ( ent -> isAKindOf ( ssgTypeCutout () ) || + ent -> isAKindOf ( ssgTypeRangeSelector () ) || + ( ent -> isA ( ssgTypeTransform () ) && + ent -> getUserData () != NULL ) ) + { + /* Insert a transform node if needed. */ + if ( mat != NULL ) { + ssgTransform *tr = new ssgTransform ; + tr -> setTransform ( mat ) ; + tr -> addKid ( ent ) ; + safe_replace_kid ( parent, ent, tr ) ; + } + + /* Traverse as usual. */ + if ( ent -> isAKindOf ( ssgTypeBranch () ) ) + { + ssgBranch *b_ent = (ssgBranch *) ent ; + for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ; + k = b_ent -> getNextKid () ) + flatten ( b_ent, k, NULL ) ; + } + + return ; + } + + /* + Clone the node if needed (there is no need to clone it recursively, + especially not past unflattable nodes). + */ + if ( ent -> getRef () > 1 && mat != NULL ) + { + ssgEntity *clone = (ssgEntity *) ent -> clone ( SSG_CLONE_GEOMETRY | + SSG_CLONE_USERDATA ) ; + safe_replace_kid ( parent, ent, clone ) ; + ent = clone ; + } + + /* + Apply the transformation on leaf nodes. + */ + if ( ent -> isAKindOf ( ssgTypeLeaf () ) ) + { + if ( mat != NULL ) + ((ssgLeaf *) ent) -> transform ( mat ) ; + return ; + } + + /* + Replace transform nodes with simple branches. + */ + if ( ent -> isAKindOf ( ssgTypeTransform () ) ) + { + ssgTransform *t_ent = (ssgTransform *) ent ; + + t_ent -> getTransform ( mat2 ) ; + if ( mat != NULL ) + sgPostMultMat4 ( mat2, mat ) ; + + mat = sgClassifyMat4 ( mat2 ) != 0 ? mat2 : NULL ; + + ssgBranch *br = new ssgBranch ; + /* + FIXME! It would have been very neat to do: + br -> copy_from ( t_ent, 0 ) ; + */ + br -> setName ( t_ent -> getName () ) ; + for ( ssgEntity *k = t_ent -> getKid ( 0 ) ; k != NULL ; + k = t_ent -> getNextKid () ) + br -> addKid ( k ) ; + t_ent -> removeAllKids () ; + + safe_replace_kid ( NULL, ent, br ) ; + ent = br ; + } + + /* + Finally traverse the kids. + */ + if ( ent -> isAKindOf ( ssgTypeBranch () ) ) + { + ssgBranch *b_ent = (ssgBranch *) ent ; + for ( ssgEntity *k = b_ent -> getKid ( 0 ) ; k != NULL ; + k = b_ent -> getNextKid () ) + flatten ( b_ent, k, mat ) ; + } + +} + +void ssgFlatten ( ssgEntity *ent ) +{ + if ( ! ent -> isAKindOf ( ssgTypeBranch () ) ) + return ; + + ssgBranch *b_ent = (ssgBranch *) ent ; + sgVec4 *mat = NULL ; + sgMat4 xform, ident ; + + /* + If the top level node is a ssgTransform, then do not replace it; + instead load an identity transform and multiply out the matrix. + */ + + if ( b_ent -> isA ( ssgTypeTransform () ) && + b_ent -> getUserData () == NULL ) + { + sgMakeIdentMat4 ( ident ) ; + ((ssgTransform *) b_ent) -> getTransform ( xform ) ; + ((ssgTransform *) b_ent) -> setTransform ( ident ) ; + mat = xform ; + } + + /* + Since the top level node may not be removed, loop over the kids. + Done in two passes because *kid* may be removed. + */ + ssgEntity *kid; + + for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ; + kid = b_ent -> getNextKid () ) + flatten ( b_ent, kid, mat ) ; + + for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ; + kid = b_ent -> getNextKid () ) + strip ( kid ) ; + + if ( b_ent -> isDirtyBSphere () ) + b_ent -> recalcBSphere () ; +} + + +/* +* NAME +* ssgTransTool +* +* DESCRIPTION +* Apply a transform (translate, rotate, scale) to all verticies of a graph. +* +* INPUTS +* ent -- the entity to process +* trans -- transform +*/ +void ssgTransTool ( ssgEntity *ent, const sgMat4 trans ) +{ + if ( ent -> isAKindOf ( ssgTypeLeaf () ) ) + { + ((ssgLeaf *) ent) -> transform ( trans ) ; + return ; + } + + if ( ! ent -> isAKindOf ( ssgTypeBranch () ) ) + return ; + + ssgBranch *b_ent = (ssgBranch *) ent ; + sgMat4 mat, ident, xform ; + + sgCopyMat4 ( mat, trans ) ; + + if ( b_ent -> isA ( ssgTypeTransform () ) && + b_ent -> getUserData () == NULL ) + { + sgMakeIdentMat4 ( ident ) ; + ((ssgTransform *) b_ent) -> getTransform ( xform ) ; + ((ssgTransform *) b_ent) -> setTransform ( ident ) ; + sgPreMultMat4 ( mat, xform ) ; + } + + else if ( b_ent -> isAKindOf ( ssgTypeTransform () ) || + b_ent -> isAKindOf ( ssgTypeCutout () ) || + b_ent -> isAKindOf ( ssgTypeRangeSelector () ) ) + { + ulSetError ( UL_WARNING, + "ssgTransTool: Cannot handle this kind of node at top level." ) ; + return ; + } + + ssgEntity *kid; + for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ; + kid = b_ent -> getNextKid () ) + flatten ( b_ent, kid, mat ) ; + + for ( kid = b_ent -> getKid ( 0 ) ; kid != NULL ; + kid = b_ent -> getNextKid () ) + strip ( kid ) ; + + b_ent -> recalcBSphere () ; +} diff --git a/src/ssg/ssgParser.cxx b/src/ssg/ssgParser.cxx new file mode 100644 index 0000000..2557237 --- /dev/null +++ b/src/ssg/ssgParser.cxx @@ -0,0 +1,531 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgParser.cxx 2117 2007-09-13 23:21:09Z fayjf $ +*/ + +// +// File parser for SSG/PLIB +// Written by Dave McClurg (dpm@efn.org) in Feb-2000 +// extended by Wolfram Kuss (w_kuss@rz-online.de) in Nov-2000 + +// This is mainly an lexical analyzer that extracts tokens from ascii-files + +// Be sure to read the ssg-documentation, especially the chapter +// on loaders/writers + + +#define AM_IN_SSGPARSER_CXX 1 + +#include "ssgLocal.h" +#include "ssgParser.h" + + +static _ssgParserSpec default_spec = +{ + "\r\n\t ", // delim_chars_skipable + 0, // delim_chars_non_skipable + NULL, // pre_processor + 0, // open_brace_chars + 0, // close_brace_chars + '"', // quote_char + 0, // comment_char + "//" // comment_string +} ; + + +// Output an error +void _ssgParser::error( const char *format, ... ) +{ + char msgbuff[ 255 ]; + va_list argp; + + char* msgptr = msgbuff; + if (linenum) + { + msgptr += sprintf ( msgptr,"%s, line %d: ", + path, linenum ); + } + + va_start( argp, format ); + vsprintf( msgptr, format, argp ); + va_end( argp ); + + ulSetError ( UL_WARNING, "%s", msgbuff ) ; +} + + +// Output a message +void _ssgParser::message( const char *format, ... ) +{ + char msgbuff[ 255 ]; + va_list argp; + + char* msgptr = msgbuff; + if (linenum) + { + msgptr += sprintf ( msgptr,"%s, line %d: ", + path, linenum ); + } + + va_start( argp, format ); + vsprintf( msgptr, format, argp ); + va_end( argp ); + + ulSetError ( UL_DEBUG, "%s", msgbuff ) ; +} + +// Opens the file and does a few internal calculations based on the spec. +int _ssgParser::openFile( const char* fname, const _ssgParserSpec* _spec ) +// returns TRUE on success +{ + if ( !_spec ) _spec = &default_spec ; + + if ( _spec->comment_string != NULL ) + { assert ( _spec->comment_string [0] != 0 ); + } + + memset(this,0,sizeof(_ssgParser)); + memcpy( &spec, _spec, sizeof(spec) ); + ssgGetCurrentOptions () -> makeModelPath ( path, fname ) ; + fileptr = fopen( path, "rb" ); + if ( ! fileptr ) + { + error("cannot open file: %s",path); + return FALSE; + } + eof = FALSE; + // Calculate anyDelimiter and return. + anyDelimiter[0] = 0; + int length = 0; + if ( spec.delim_chars_skipable != NULL ) + { length +=strlen ( spec.delim_chars_skipable); + strcat(anyDelimiter, spec.delim_chars_skipable); + } + if ( spec.delim_chars_non_skipable != NULL ) + { length += strlen ( spec.delim_chars_non_skipable ) ; + strcat ( anyDelimiter, spec.delim_chars_non_skipable ) ; + } + if ( spec.open_brace_chars != NULL ) + { length +=strlen ( spec.open_brace_chars ); + strcat ( anyDelimiter, spec.open_brace_chars ); + } + if ( spec.close_brace_chars != NULL ) + { length +=strlen ( spec.close_brace_chars ) ; + strcat ( anyDelimiter, spec.close_brace_chars ) ; + } + assert ( length < MAX_DELIMITER_CHARS ); + return TRUE; +} + + +void _ssgParser::closeFile() +{ + fclose( fileptr ) ; + fileptr = 0 ; +} + +static char *EOF_string = "EOF reached"; +static char *EOL_string = "EOL reached"; + +char* _ssgParser::getNextToken( const char* name ) +// Fetches next token, even if it has to read over some empty or comment-only lines to get to it. +// Never returns NULL. Returns EOF_string on EOF. +{ + while(!( curtok < numtok )) + { //int startLevel = level; + //ulSetError(UL_DEBUG, "Forcing!"); + if(getLine( -999 ) == NULL) // -999 + { if ( name ) + error("missing %s",name) ; + return EOF_string; + } + assert(curtok==1); + curtok=0; // redo the get one token that getLine does + } + char* token = 0 ; + assert ( curtok < numtok ); + token = tokptr [ curtok++ ] ; + return(token) ; +} + +char *_ssgParser::peekAtNextToken( const char* name ) +// Like getNextToken, but doesn't remove the token from the input stream +{ + while(!( curtok < numtok )) + { //int startLevel = level; + //ulSetError(UL_DEBUG, "Forcing!"); + if(getLine( -999 ) == NULL) // -999 + { if ( name ) + error("missing %s",name) ; + return EOF_string; + } + assert(curtok==1); + curtok=0; // redo the get one token that getLine does + } + char* token = 0 ; + assert ( curtok < numtok ); + token = tokptr [ curtok ] ; + return(token) ; +} + + + +int _ssgParser::getNextFloat( SGfloat &retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = getNextToken(name); + retVal = SGfloat(strtod( token, &endptr)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain a floating point number but contains %s",name, token) ; + return FALSE; + } +} + +int _ssgParser::getNextInt( int & retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = getNextToken(name); + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +int _ssgParser::getNextString(char *&retVal, const char* name ) // returns TRUE on success +// wk: This is only for strings where we know they are inside spec.quote_chars, correct? +{ + char *token = getNextToken( NULL ); + + if ( spec.quote_char && *token == spec.quote_char ) + { + //knock off the quotes + token++ ; + int len = strlen( token ) ; + if (len > 0 && token[len-1] == spec.quote_char) + token[len-1] = 0; + } + + if( name != NULL && strcmp( token, name ) ) + { + error("Expected %s but got %s instead", name, token) ; + return FALSE; + } + + retVal = token; + return TRUE; +} + +int _ssgParser::getNextUInt( unsigned int & retVal, const char* name ) +// returns TRUE on success +{ char *endptr, *token = getNextToken(name); + retVal = (unsigned int)(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + + +void _ssgParser::expectNextToken( const char* name ) +// Swallows the next token. If it is not name, then there is an error message +{ + char* token = getNextToken(name); + if (strcmp(token,name)) + error("missing %s",name) ; +} + +// internal function. A token consisting of a single char has been found. +// This is copied to a new buffer, so that I have the space to add the 0. +void _ssgParser::addOneCharToken ( char *ptr ) +{ + assert( (long)onechartokenbuf_ptr- (long)onechartokenbuf < 4096 ) ; // Buffer overflow + + onechartokenbuf_ptr [ 0 ] = *ptr; + onechartokenbuf_ptr [ 1 ] = 0; + tokptr [ numtok++ ] = onechartokenbuf_ptr; + onechartokenbuf_ptr += 2; // prepare for nect onechartoken +} + +static const char *mystrchr( const char *string, int c ) +// like strchr, but string may be NULL +{ + if (string == NULL ) + return NULL; + else + return strchr( string, c ); +} + + +// gets the next line (no matter where it is), without tokenizing it +// useful for parsing text-formatted files which are identified by +// comments at the very beginning +char* _ssgParser::getRawLine() +// return NULL on eof +{ + tokbuf[0]=0; + + //get the next line with something on it + if ( fgets ( linebuf, sizeof(linebuf), fileptr ) == NULL ) + { + eol = TRUE; + eof = TRUE; + return(0) ; + } + + memcpy( tokbuf, linebuf, sizeof(linebuf) ) ; + + return tokbuf; +} + +// wk: This works and is IMHO robust. +// However, I feel it could be smaller, more elegant and readable. +char* _ssgParser::getLine( int startLevel ) +// return NULL on eof or if (level < startLevel) +{ + // throw away old tokens + tokbuf [ 0 ] = 0 ; + numtok = 0 ; + curtok = 0 ; + eol = FALSE; + onechartokenbuf_ptr = onechartokenbuf ; + + //get the next line with something on it + char* ptr = tokbuf , *tptr; + while ( *ptr == 0 ) + { + linenum++ ; + if ( fgets ( linebuf, sizeof(linebuf), fileptr ) == NULL ) + { eol = TRUE; + eof = TRUE; + return(0) ; + } + if(spec.pre_processor != NULL) + spec.pre_processor (linebuf); + memcpy( tokbuf, linebuf, sizeof(linebuf) ) ; + ptr = tokbuf ; + + // check for comments + tptr=strchr(tokbuf, spec.comment_char); + if ( tptr != NULL ) + *tptr = 0; + if ( spec.comment_string != NULL ) + { + tptr=strstr(tokbuf, spec.comment_string); + if ( tptr != NULL ) + *tptr = 0; + } + + //skip delim_chars + if ( spec.delim_chars_skipable != NULL ) + while ( *ptr && strchr(spec.delim_chars_skipable,*ptr) ) + ptr++ ; + } + + //tokenize the line + numtok = 0 ; + while ( *ptr ) + { + //skip delim_chars + if ( spec.delim_chars_skipable != NULL ) + while ( *ptr && strchr(spec.delim_chars_skipable,*ptr) ) + ptr++ ; + + if ( *ptr == 0 ) + break; // only skipable stuff left, dont create another token. + + // now unnessary?: + if ( *ptr == spec.comment_char ) + { + *ptr = 0 ; + break; + } + + //count the token + tokptr [ numtok++ ] = ptr ; + + //handle quoted string + if ( spec.quote_char && *ptr == spec.quote_char ) + { + ptr++ ; + while ( *ptr && *ptr != spec.quote_char ) + ptr++ ; + } + + //adjust level + if ( spec.open_brace_chars && *ptr && mystrchr(spec.open_brace_chars,*ptr) ) + level++ ; + else if ( spec.close_brace_chars && *ptr && mystrchr(spec.close_brace_chars,*ptr) ) + level-- ; + else + //find end of token + while ( *ptr && !strchr(anyDelimiter,*ptr) ) + ptr++ ; + + if ( *ptr != 0 ) + if ( ptr == tokptr [ numtok-1 ] ) + { // we dont want tokens of length zero + assert(NULL==mystrchr(spec.delim_chars_skipable,*ptr)); + // ptr is non-skipable, return it as token of length one + numtok--; // remove zero-length token + addOneCharToken ( ptr ) ; // and add new token instead + *ptr++ = 0; + continue; + } + + //mark end of token + if( *ptr && ( mystrchr(spec.delim_chars_non_skipable,*ptr) + || mystrchr(spec.open_brace_chars,*ptr) + || mystrchr(spec.close_brace_chars,*ptr) ) ) + { + // ptr is non-skipable, return it as token of length one + // additional to the one already in tokptr [ numtok-1 ]. + addOneCharToken ( ptr ) ; + *ptr++ = 0; + } + if ( spec.delim_chars_skipable != NULL ) + while ( *ptr && strchr(spec.delim_chars_skipable,*ptr) ) + *ptr++ = 0 ; + } + if (level >= startLevel) + return parseToken (0) ; + return 0 ; +} + + +char* _ssgParser::parseToken( const char* name ) +// returns the next token from the current line. +// Never returns NULL, but may return EOL_string +{ + char* token = EOL_string ; + if ( curtok < numtok ) + token = tokptr [ curtok++ ] ; + else + { eol = TRUE; + if ( name ) + error("missing %s",name) ; + } + return(token) ; +} + + +int _ssgParser::parseString(char *&retVal, const char* name ) // returns TRUE on success +// wk: This is only for strings where we know they are inside spec.quote_chars, correct? +{ + char* token = EOL_string ; + retVal = EOL_string ; + + if ( curtok >= numtok ) + { eol = TRUE; + if ( name ) + error("missing %s",name) ; + return FALSE; + } + + if ( numtok > 0 && spec.quote_char && *tokptr [ curtok ] == spec.quote_char ) + { + token = tokptr [ curtok++ ] ; + + //knock off the quotes + token++ ; + int len = strlen (token) ; + if (len > 0 && token[len-1] == spec.quote_char) + token[len-1] = 0 ; + } + else + { if ( name ) + error("missing %s",name) ; + return FALSE; + } + retVal = token; + return TRUE; +} + +int _ssgParser::parseDouble( double &retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = parseToken(name); + retVal = strtod( token, &endptr); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain a floating point number but contains %s",name, token) ; + return FALSE; + } +} + +int _ssgParser::parseFloat( SGfloat &retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = parseToken(name); + retVal = SGfloat(strtod( token, &endptr)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain a floating point number but contains %s",name, token) ; + return FALSE; + } +} + +int _ssgParser::parseInt(int &retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = parseToken(name); + retVal = int(strtol( token, &endptr, 10)); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + +int _ssgParser::parseUInt(unsigned int &retVal, const char* name ) +// returns TRUE on success +{ + char *endptr, *token = parseToken(name); + long l = strtol( token, &endptr, 10); + if (l<0) + message("The field %s should contain an UNSIGNED integer number but contains %s",name, token) ; + retVal = (unsigned int)(l); + if ( (endptr == NULL) || (*endptr == 0)) + return TRUE; + else + { error("The field %s should contain an integer number but contains %s",name, token) ; + return FALSE; + } +} + + + +void _ssgParser::expect( const char* name ) +// Swallows the next token. If it is not name, then there is an error message +{ + char* token = parseToken(name); + if (strcmp(token,name)) + error("missing %s",name) ; +} diff --git a/src/ssg/ssgParser.h b/src/ssg/ssgParser.h new file mode 100644 index 0000000..1f74c7a --- /dev/null +++ b/src/ssg/ssgParser.h @@ -0,0 +1,116 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgParser.h 1970 2004-10-04 08:11:26Z wolfram_kuss $ +*/ + + +#ifndef _INCLUDED_SSGPARSER_H_ +#define _INCLUDED_SSGPARSER_H_ + + +// Be sure to read the ssg-documentation, especially the chapter +// on loaders/writers + + +typedef void PreProcessorFunctionType(char *line); + +// The _ssgParserSpec contains the rules how to extract the tokens: + +struct _ssgParserSpec +{ // delimiters; Thats is, chars that delimit tokens: + const char* delim_chars_skipable ; // these are "swallowed" by the parser + const char* delim_chars_non_skipable ; // These are handed to the app. + PreProcessorFunctionType *pre_processor; // After reading a line, it is first given to the preprocessor to tweak it in place. + const char* open_brace_chars ; + const char* close_brace_chars ; + char quote_char ; + char comment_char ; // For ex. ';' or '#' + const char* comment_string; // For example "//" +} ; + + +class _ssgParser +{ + enum { MAX_TOKENS = 50000 } ; + enum { MAX_DELIMITER_CHARS = 5000 } ; + + char path[ 256 ] ; + _ssgParserSpec spec ; + FILE* fileptr ; + + int linenum ; + char linebuf[ 50000 ] ; + char tokbuf[ 50000 ] ; + char anyDelimiter [ MAX_DELIMITER_CHARS ] ; + char* tokptr[ MAX_TOKENS ] ; + int numtok ; + int curtok ; + + char onechartokenbuf [ 50000 ]; + char *onechartokenbuf_ptr; + void addOneCharToken ( char *ptr ) ; + +public : +// ************************** general ************************** + int openFile( const char* fname, const _ssgParserSpec* spec = 0 ); // TRUE = success + void closeFile(); + + void error( const char *format, ... ); + void message( const char *format, ... ); + + int eof ; // end of file reached yet? + int eol ; // end of line reached yet? +// ************************** line-by-line API ************************** + int level; + + char* getLine( int startLevel=0 ); // may return NULL + char* getRawLine(); + // All the name -parameters are only for error-messages + + // The parse... - functions get the next token from the current line + // that was fetched with getLine and return the token as + // char*, SGfloat, int, unsigned int or char* + + char* parseToken( const char* name ); + // These return TRUE on success: + int parseFloat( SGfloat &retVal, const char* name ); + int parseDouble( double &retVal, const char* name ); + int parseInt(int &retVal, const char* name ); + int parseUInt(unsigned int &retVal, const char* name ); + int parseString(char *&retVal, const char* name ); + + void expect( const char* name ); + +// ************************** line structure independant API ************************** + + // These six functions get the next token - regardless of what line it is on + char *getNextToken( const char* name ); + char *peekAtNextToken( const char* name ); + // These return TRUE on success: + int getNextFloat( SGfloat &retVal, const char* name ); + int getNextInt( int & retVal, const char* name ); + int getNextString(char *&retVal, const char* name ); + int getNextUInt( unsigned int & retVal, const char* name ); + void expectNextToken( const char* name ); + } ; + + +#endif diff --git a/src/ssg/ssgRangeSelector.cxx b/src/ssg/ssgRangeSelector.cxx new file mode 100644 index 0000000..3577dbe --- /dev/null +++ b/src/ssg/ssgRangeSelector.cxx @@ -0,0 +1,200 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgRangeSelector.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgRangeSelector::copy_from ( ssgRangeSelector *src, int clone_flags ) +{ + ssgSelector::copy_from ( src, clone_flags ) ; + + additive = src -> isAdditive () ; + + for ( unsigned int i = 0 ; i < 33 ; i++ ) + rng_list [ i ] = src -> getRange ( i ) ; +} + + +ssgBase *ssgRangeSelector::clone ( int clone_flags ) +{ + ssgRangeSelector *b = new ssgRangeSelector ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgRangeSelector::ssgRangeSelector (void) +{ + type = ssgTypeRangeSelector () ; + additive = FALSE ; + rng_list[0] = 0.0f ; + + for ( int i = 1 ; i < 33 ; i++ ) + rng_list[i] = SG_MAX ; +} + +ssgRangeSelector::~ssgRangeSelector (void) +{ +} + +void ssgRangeSelector::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + float range = sgLengthVec3 ( m [ 3 ] ) ; + + if ( range < rng_list [ 0 ] ) /* Too close */ + { + select ( 0 ) ; + return ; + } + + unsigned int sel = 0 ; + + for ( int i = 1 ; i < 33 ; i++ ) + { + ssgEntity *e = getKid ( i-1 ) ; + + if ( e == NULL || rng_list [ i ] == SG_MAX ) + { + select ( 0 ) ; + return ; + } + + if ( e != NULL && range < rng_list [ i ] ) + { + e -> cull ( f, m, cull_result != SSG_INSIDE ) ; + sel |= 1 << (i-1) ; + + if ( ! additive ) + { + selectStep ( i-1 ) ; + return ; + } + } + } + + select ( sel ) ; + + postTravTests ( SSGTRAV_CULL ) ; +} + + +void ssgRangeSelector::hot ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_HOT ) ) + return ; + + if ( additive ) + ssgBranch::hot ( sp, m, test_needed ) ; + else + { + /* No point in testing this node since it only has one child */ + + _ssgPushPath ( this ) ; + + ssgEntity *e = getKid ( 0 ) ; + + if ( e != NULL ) + e -> hot ( sp, m, test_needed ) ; + + _ssgPopPath () ; + } + + postTravTests ( SSGTRAV_HOT ) ; +} + + +void ssgRangeSelector::los ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_LOS ) ) + return ; + + if ( additive ) + ssgBranch::los ( sp, m, test_needed ) ; + else + { + /* No point in testing this node since it only has one child */ + + _ssgPushPath ( this ) ; + + ssgEntity *e = getKid ( 0 ) ; + + if ( e != NULL ) + e -> los ( sp, m, test_needed ) ; + + _ssgPopPath () ; + } + + postTravTests ( SSGTRAV_LOS ) ; +} + +void ssgRangeSelector::isect ( sgSphere *sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_ISECT ) ) + return ; + + if ( additive ) + ssgBranch::isect ( sp, m, test_needed ) ; + else + { + /* No point in testing this node since it only has one child */ + + _ssgPushPath ( this ) ; + + ssgEntity *e = getKid ( 0 ) ; + + if ( e != NULL ) + e -> isect ( sp, m, test_needed ) ; + + _ssgPopPath () ; + } + + postTravTests ( SSGTRAV_ISECT ) ; +} + + + +int ssgRangeSelector::load ( FILE *fd ) +{ + _ssgReadInt ( fd, & additive ) ; + _ssgReadFloat ( fd, 33, rng_list ) ; + + return ssgSelector::load(fd) ; +} + +int ssgRangeSelector::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, additive ) ; + _ssgWriteFloat ( fd, 33, rng_list ) ; + + return ssgSelector::save(fd) ; +} + + diff --git a/src/ssg/ssgRoot.cxx b/src/ssg/ssgRoot.cxx new file mode 100644 index 0000000..a94e594 --- /dev/null +++ b/src/ssg/ssgRoot.cxx @@ -0,0 +1,61 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgRoot.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgRoot::copy_from ( ssgRoot *src, int clone_flags ) +{ + ssgBranch::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgRoot::clone ( int clone_flags ) +{ + ssgRoot *b = new ssgRoot ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgRoot::ssgRoot (void) +{ + type = ssgTypeRoot () ; +} + +ssgRoot::~ssgRoot (void) +{ +} + + +int ssgRoot::load ( FILE *fd ) +{ + return ssgBranch::load(fd) ; +} + +int ssgRoot::save ( FILE *fd ) +{ + return ssgBranch::save(fd) ; +} + + + diff --git a/src/ssg/ssgSave3ds.cxx b/src/ssg/ssgSave3ds.cxx new file mode 100644 index 0000000..8db1dc3 --- /dev/null +++ b/src/ssg/ssgSave3ds.cxx @@ -0,0 +1,511 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSave3ds.cxx 2108 2006-12-11 20:42:05Z fayjf $ +*/ + +/******************************************************* + ** ssgSave3ds.cxx + ** + ** Written by Per Liedman (liedman@home.se) + ** Last updated: 2001-02-25 + ** + ** This was written to be a part of Stephen J Bakers + ** PLIB (http://plib.sourceforge.net) + *******************************************************/ + +/* This writer is highly experimental. It has *not* been tested with + 3D Studio. Please note that this writer does not save hierarchal + information */ + +#include +#include "ssgLocal.h" +#include "ssg3ds.h" + +static FILE* save_fd; + +static const int MAX_MATERIALS = 128; +static ssgSimpleState* mat[MAX_MATERIALS]; +static int mat_count; + +static void writeChunkHeader( unsigned short id, unsigned int length ) { + ulEndianWriteLittle16( save_fd, id ); + ulEndianWriteLittle32( save_fd, length ); +} + +class _ssgSave3dsData { +public: + _ssgSave3dsData( void* ptr, size_t memb_size, size_t nmemb ) : + swabbed(false), memb_size(memb_size), nmemb(nmemb), ptr(ptr), next(NULL) + { } + ~_ssgSave3dsData() { + if (next != NULL) { + delete next; + } + + free(ptr); + } + + unsigned int size() { + unsigned int s = memb_size*nmemb; + if (next != NULL) { + s += next->size(); + } + return s; + } + + void write() { + if (!swabbed) { + switch (memb_size) { + case 2: + ulEndianLittleArray16( (unsigned short*)ptr, nmemb ); + break; + case 4: + ulEndianLittleArray32( (unsigned int*)ptr, nmemb ); + break; + default: + break; + } + + swabbed = true; + } + + fwrite( ptr, memb_size, nmemb, save_fd ); + if (next != NULL) { + next->write(); + } + } + + void append( _ssgSave3dsData* next ) { + this->next = next; + } + +protected: + bool swabbed; + + size_t memb_size, nmemb; + void* ptr; + _ssgSave3dsData* next; +}; + +class _ssgSave3dsChunk { +public: + _ssgSave3dsChunk( unsigned short id ) : + id(id), first_data(NULL), last_data(NULL), next_sibling(NULL), + first_kid(NULL), last_kid(NULL) {} + + ~_ssgSave3dsChunk() { + if (first_data != NULL) + delete first_data; + if (next_sibling != NULL) + delete next_sibling; + if (first_kid != NULL) + delete first_kid; + } + + void addData( _ssgSave3dsData* data ) { + if (first_data == NULL) { + first_data = last_data = data; + } else { + last_data->append(data); + last_data = data; + } + } + + void addKid( _ssgSave3dsChunk* kid ) { + if (first_kid == NULL) { + first_kid = last_kid = kid; + } else { + last_kid->next_sibling = kid; + last_kid = kid; + } + } + + void addSibling( _ssgSave3dsChunk* sibling ) { + sibling->next_sibling = next_sibling; + next_sibling = sibling; + } + + unsigned int size() { + unsigned int s = 6; + if (first_data != NULL) + s += first_data->size(); + + _ssgSave3dsChunk* kid = first_kid; + while (kid != NULL) { + s += kid->size(); + kid = kid->next_sibling; + } + + return s; + } + + void write() { + unsigned int s = size(); + writeChunkHeader(id, s); + + if (first_data != NULL) { + first_data->write(); + } + + _ssgSave3dsChunk* kid = first_kid; + while (kid != NULL) { + kid->write(); + kid = kid->next_sibling; + } + } + +protected: + unsigned short id; + _ssgSave3dsData *first_data, *last_data; + _ssgSave3dsChunk *next_sibling; + _ssgSave3dsChunk *first_kid, *last_kid; +}; + +static char* get_material_name( ssgSimpleState *state ) { + static char matnamebuff[16]; + + if (state->getName() != NULL) { + return state->getName(); + } else { + for (int i = 0; i < mat_count; i++) { + if (state == mat[i]) { + sprintf(matnamebuff, "Material #%d", i+1); + return matnamebuff; + } + } + + ulSetError(UL_WARNING, "ssgSave3ds: Material not found."); + return NULL; + } +} + +static _ssgSave3dsChunk* create_vertex_chunk( ssgLeaf* leaf, + sgMat4 transform ) { + _ssgSave3dsChunk* vertexlist = new _ssgSave3dsChunk( CHUNK_VERTLIST ); + + unsigned short *num_verts; + _ssgSave3dsData *nverts; + float* vdata; + + num_verts = new unsigned short; + *num_verts = leaf->getNumVertices(); + nverts = new _ssgSave3dsData(num_verts, 2, 1); + + vdata = new float[ *num_verts * 3]; + for (int i = 0; i < *num_verts; i++) { + sgXformPnt3(&vdata[i*3], leaf->getVertex(i), transform); + } + _ssgSave3dsData *vertices = new _ssgSave3dsData(vdata, 4, + *num_verts * 3); + vertexlist->addData(nverts); + vertexlist->addData(vertices); + + return vertexlist; +} + +static _ssgSave3dsChunk* create_maplist_chunk( ssgLeaf* leaf ) { + _ssgSave3dsChunk* maplist = new _ssgSave3dsChunk( CHUNK_MAPLIST ); + + unsigned short *num_verts; + _ssgSave3dsData *nverts; + float* vdata; + + num_verts = new unsigned short; + *num_verts = leaf->getNumTexCoords(); + nverts = new _ssgSave3dsData(num_verts, 2, 1); + + vdata = new float[ *num_verts * 2]; + for (int i = 0; i < *num_verts; i++) { + sgCopyVec2(&vdata[i*2], leaf->getTexCoord(i)); + } + _ssgSave3dsData *mapdata = new _ssgSave3dsData(vdata, 4, + *num_verts * 2); + maplist->addData(nverts); + maplist->addData(mapdata); + + return maplist; +} + +static _ssgSave3dsChunk* create_facemat_chunk( ssgLeaf* leaf ) { + char* matname = get_material_name( (ssgSimpleState*)leaf->getState() ); + char* namecopy = ulStrDup ( matname ) ; + + _ssgSave3dsData* namedata = new _ssgSave3dsData(namecopy, 1, + strlen(matname) + 1); + + unsigned short *fmdata = new unsigned short[ leaf->getNumTriangles() + 1 ]; + fmdata[0] = leaf->getNumTriangles(); + for (int i = 0; i < leaf->getNumTriangles(); i++) { + fmdata[i+1] = i; + } + + _ssgSave3dsData* fmlist = new _ssgSave3dsData(fmdata, 2, + leaf->getNumTriangles() + 1); + + _ssgSave3dsChunk* facemat = new _ssgSave3dsChunk( CHUNK_FACEMAT ); + facemat->addData(namedata); + facemat->addData(fmlist ); + + return facemat; +} + +static _ssgSave3dsChunk* create_facelist_chunk( ssgLeaf* leaf ) { + _ssgSave3dsChunk* facelist = new _ssgSave3dsChunk( CHUNK_FACELIST ); + + short* fdata = new short[leaf->getNumTriangles() * 4 + 1]; + fdata[0] = leaf->getNumTriangles(); + + for (int i = 0; i < leaf->getNumTriangles(); i++) { + leaf->getTriangle(i, + &fdata[i * 4 + 1], + &fdata[i * 4 + 2], + &fdata[i * 4 + 3]); + fdata[i * 4 + 4] = 7; // flag indicating anti-clockwise ordering (?) + } + _ssgSave3dsData *facedata = new _ssgSave3dsData(fdata, 2, + leaf->getNumTriangles()*4 + 1); + facelist->addData(facedata); + + facelist->addKid( create_facemat_chunk(leaf) ); + + return facelist; +} + +static _ssgSave3dsChunk* create_transform_chunk() { + // creates an identity transform + _ssgSave3dsChunk* transform = new _ssgSave3dsChunk( CHUNK_TRMATRIX ); + + float* matrix = new float[12]; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + matrix[i * 3 + j] = (i == j) ? 1.0f : 0.0f; + } + } + + _ssgSave3dsData* matdata = new _ssgSave3dsData( matrix, 4, 12 ); + transform->addData(matdata); + + return transform; +} + +static void traverse_objects( ssgEntity* node, _ssgSave3dsChunk *parent, + sgMat4 transform ) { + if ( node->isAKindOf( ssgTypeTransform() ) ) { + sgMat4 local_transform; + ssgTransform *t_node = (ssgTransform*)node; + + t_node->getTransform(local_transform); + sgPostMultMat4( local_transform, transform ); + + for (ssgEntity* kid = t_node->getKid(0); kid != NULL; + kid = t_node->getNextKid()) { + traverse_objects( kid, parent, local_transform ); + } + } else if ( node->isAKindOf( ssgTypeBranch() ) ) { + ssgBranch *b_node = (ssgBranch*)node; + for (ssgEntity* kid = b_node->getKid(0); kid != NULL; + kid = b_node->getNextKid()) { + traverse_objects( kid, parent, transform ); + } + } else if ( node->isAKindOf( ssgTypeLeaf() ) ) { + ssgLeaf* l_node = (ssgLeaf*)node; + + _ssgSave3dsChunk *mesh = new _ssgSave3dsChunk(CHUNK_TRIMESH ); + + mesh -> addKid( create_vertex_chunk (l_node, transform) ); + mesh -> addKid( create_facelist_chunk(l_node) ); + mesh -> addKid( create_transform_chunk() ); + if (l_node->getNumTexCoords() > 0) { + mesh -> addKid( create_maplist_chunk(l_node) ); + } + + parent->addKid(mesh); + } +} + +static void create_objects_chunk( ssgEntity* ent, _ssgSave3dsChunk* parent ) { + _ssgSave3dsChunk* objs = new _ssgSave3dsChunk(CHUNK_OBJBLOCK); + static const char const_objname[] = "Object written by ssgSave3ds"; + + char *objname = ulStrDup ( const_objname ) ; + objs->addData( new _ssgSave3dsData(objname, 1, strlen(objname)+1) ); + parent->addKid(objs); + + sgMat4 ident; + sgMakeIdentMat4( ident ); + + traverse_objects( ent, objs, ident ); +} + +static _ssgSave3dsChunk *create_colour_chunk( unsigned short id, + float* colour ) { + _ssgSave3dsChunk *chunk = new _ssgSave3dsChunk(id); + + _ssgSave3dsChunk *rgbchunk = new _ssgSave3dsChunk(CHUNK_RGB1); + float *colvec = new sgVec3; + sgCopyVec3(colvec, colour); + _ssgSave3dsData *coldata = new _ssgSave3dsData(colvec, 4, 3); + rgbchunk->addData(coldata); + + chunk->addKid(rgbchunk); + return chunk; +} + +static _ssgSave3dsChunk *create_shininess_chunk( float shininess ) { + _ssgSave3dsChunk *chunk = new _ssgSave3dsChunk(CHUNK_SHINE_STRENGTH); + + _ssgSave3dsChunk *percent = new _ssgSave3dsChunk(CHUNK_AMOUNT); + unsigned short *amount = new unsigned short; + *amount = (unsigned short)(shininess * 100.0f / 128.0f); + _ssgSave3dsData *sdata = new _ssgSave3dsData(amount, 2, 1); + percent->addData(sdata); + + chunk->addKid(percent); + return chunk; +} + +static _ssgSave3dsChunk *create_mapparam_chunk( unsigned short id, float val ) { + _ssgSave3dsChunk *chunk = new _ssgSave3dsChunk( id ); + float *f = new float; + *f = val; + chunk -> addData( new _ssgSave3dsData(f, 4, 1) ); + return chunk; +} + +static _ssgSave3dsChunk *create_map_chunk( ssgSimpleState *state ) { + _ssgSave3dsChunk *map_chunk = new _ssgSave3dsChunk(CHUNK_TEXTURE); + + _ssgSave3dsChunk *mapname_chunk = new _ssgSave3dsChunk(CHUNK_MAPFILENAME); + char *mapname = ulStrDup(state->getTextureFilename()); + mapname_chunk -> addData( new _ssgSave3dsData(mapname, 1, + strlen(mapname) + 1) ); + map_chunk -> addKid(mapname_chunk); + + map_chunk -> addKid( create_mapparam_chunk( CHUNK_MAP_USCALE, 1.0f ) ); + map_chunk -> addKid( create_mapparam_chunk( CHUNK_MAP_VSCALE, 1.0f ) ); + map_chunk -> addKid( create_mapparam_chunk( CHUNK_MAP_UOFFST, 0.0f ) ); + map_chunk -> addKid( create_mapparam_chunk( CHUNK_MAP_VOFFST, 0.0f ) ); + + return map_chunk; +} + +static _ssgSave3dsChunk *create_material_chunk( ssgSimpleState *state ) { + _ssgSave3dsChunk *mat = new _ssgSave3dsChunk(CHUNK_MATERIAL); + + _ssgSave3dsChunk *matname_chunk = new _ssgSave3dsChunk(CHUNK_MATNAME); + char *matname; + if (state->getName() != NULL) + matname = ulStrDup(state->getName()); + else { + matname = new char[16]; + sprintf(matname, "Material #%d", mat_count); + } + _ssgSave3dsData *matname_data = new _ssgSave3dsData(matname, 1, + strlen(matname) + 1); + matname_chunk->addData(matname_data); + mat->addKid(matname_chunk); + + mat->addKid( create_colour_chunk( CHUNK_AMBIENT, + state->getMaterial(GL_AMBIENT) ) ); + mat->addKid( create_colour_chunk( CHUNK_DIFFUSE, + state->getMaterial(GL_DIFFUSE) ) ); + mat->addKid( create_colour_chunk( CHUNK_SPECULAR, + state->getMaterial(GL_SPECULAR) ) ); + mat->addKid( create_shininess_chunk( state->getShininess() ) ); + + if (state->isEnabled(GL_COLOR_MATERIAL)) { + ulSetError(UL_WARNING, + "State \"%s\" has GL_COLOR_MATERIAL enabled, which " \ + "is not supported by 3DS format. Data will be lost.", + matname); + } + + if (state->getTextureFilename() != NULL) { + mat->addKid( create_map_chunk(state) ); + } + + return mat; +} + +static void traverse_materials( ssgEntity* node, _ssgSave3dsChunk* parent ) { + if ( node->isAKindOf( ssgTypeBranch() ) ) { + ssgBranch *b_node = (ssgBranch*)node; + for (ssgEntity* kid = b_node->getKid(0); kid != NULL; + kid = b_node->getNextKid()) { + traverse_materials( kid, parent ); + } + } else if ( node->isAKindOf( ssgTypeLeaf() ) ) { + ssgLeaf* l_node = (ssgLeaf*)node; + ssgSimpleState *state = (ssgSimpleState*)l_node->getState(); + + for (int i = 0; i < mat_count; i++) { + if (state == mat[i]) + return; + } + + assert(mat_count < MAX_MATERIALS); + + mat[mat_count++] = state; + parent->addKid( create_material_chunk(state) ); + } +} + +static void create_materials_chunk( ssgEntity* ent, _ssgSave3dsChunk* parent ) { + mat_count = 0; + for (int i = 0; i < MAX_MATERIALS; i++) { + mat[i] = NULL; + } + + traverse_materials( ent, parent ); +} + +int ssgSave3ds( const char* filename, ssgEntity* ent ) { + save_fd = fopen( filename, "wb" ); + + if (save_fd == NULL) { + ulSetError( UL_WARNING, "ssgSave3ds: Failed to open '%s' for writing", + filename ); + return FALSE; + } + + _ssgSave3dsChunk *top_chunk = new _ssgSave3dsChunk( CHUNK_MAIN ); + _ssgSave3dsChunk *main_chunk = new _ssgSave3dsChunk( CHUNK_OBJMESH ); + + _ssgSave3dsChunk *version_chunk = new _ssgSave3dsChunk( CHUNK_VERSION ); + unsigned int *version = new unsigned int; + *version = 3; + _ssgSave3dsData *versiondata = new _ssgSave3dsData(version, 4, 1); + version_chunk -> addData(versiondata); + + top_chunk -> addKid( main_chunk ); + top_chunk -> addKid( version_chunk ); + + create_materials_chunk (ent, main_chunk); + create_objects_chunk (ent, main_chunk); + top_chunk->write(); + + delete top_chunk; + + fclose(save_fd); + + return TRUE; +} diff --git a/src/ssg/ssgSaveAC.cxx b/src/ssg/ssgSaveAC.cxx new file mode 100644 index 0000000..76ce9a3 --- /dev/null +++ b/src/ssg/ssgSaveAC.cxx @@ -0,0 +1,257 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveAC.cxx 2169 2012-08-14 17:28:50Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + +static ssgSimpleStateArray gSSL ; +static const int writeTextureWithoutPath = TRUE ; + +struct saveTriangle +{ + int v [ 3 ] ; + sgVec2 t [ 3 ] ; +} ; + +static FILE *save_fd ; +static sgVec3 *vlist ; +static saveTriangle *tlist ; + + +static int ssgSaveLeaf ( ssgEntity *ent ) +{ int i; + assert ( ent -> isAKindOf ( ssgTypeLeaf() ) ) ; + + ssgLeaf *vt = (ssgLeaf *) ent ; + + int num_verts = vt -> getNumVertices () ; + int num_tris = vt -> getNumTriangles () ; + + vlist = new sgVec3 [ num_verts ] ; + tlist = new saveTriangle [ num_tris ] ; + + for ( i = 0 ; i < num_verts; i++ ) + sgCopyVec3 ( vlist[i], vt->getVertex ( i ) ) ; + + for ( i = 0 ; i < num_tris ; i++ ) + { + short vv0, vv1, vv2 ; + + vt -> getTriangle ( i, &vv0, &vv1, &vv2 ) ; + + tlist[i].v[0] = vv0 ; + tlist[i].v[1] = vv1 ; + tlist[i].v[2] = vv2 ; + + sgCopyVec2 ( tlist[i].t[0], vt->getTexCoord ( vv0 ) ) ; + sgCopyVec2 ( tlist[i].t[1], vt->getTexCoord ( vv1 ) ) ; + sgCopyVec2 ( tlist[i].t[2], vt->getTexCoord ( vv2 ) ) ; + } + + fprintf ( save_fd, "OBJECT poly\n" ) ; + fprintf ( save_fd, "name \"%s\"\n", ent->getPrintableName() ) ; + + ssgState* st = vt -> getState () ; + + if ( st && st -> isAKindOf ( ssgTypeSimpleState() ) ) + { + ssgSimpleState* ss = (ssgSimpleState*) vt -> getState () ; + + if ( ss -> isEnabled ( GL_TEXTURE_2D ) ) + { + const char* tfname = ss -> getTextureFilename () ; + + if (( tfname != NULL ) && ( tfname[0] != 0 )) + { + if ( writeTextureWithoutPath ) + { + + const char *s = strrchr ( tfname, '\\' ) ; + if ( s == NULL ) + s = strrchr ( tfname, '/' ) ; + + if ( s == NULL ) + fprintf ( save_fd, "texture \"%s\"\n", tfname ) ; + else + fprintf ( save_fd, "texture \"%s\"\n", ++s ) ; + } + else + fprintf ( save_fd, "texture \"%s\"\n", tfname ) ; + } + } + } + + fprintf ( save_fd, "numvert %d\n", num_verts ) ; + + for ( i = 0 ; i < num_verts ; i++ ) + fprintf ( save_fd, "%g %g %g\n", vlist[i][0],vlist[i][2],-vlist[i][1] ) ; + + fprintf ( save_fd, "numsurf %d\n", num_tris ) ; + + for ( i = 0 ; i < num_tris ; i++ ) + { + fprintf ( save_fd, "SURF 0x0%x\n", (! vt -> getCullFace () ) << 5 ) ; + ssgState *s = vt->getState (); + + int istate = 0; + + if ( s != NULL ) + if ( s -> isAKindOf ( ssgTypeSimpleState() ) ) + { + istate = gSSL.findIndex ( (ssgSimpleState *) s ) ; + assert ( istate >= 0 ) ; + } + + fprintf ( save_fd, "mat %d\n", istate ) ; + fprintf ( save_fd, "refs 3\n" ) ; + fprintf ( save_fd, "%d %g %g\n", + tlist[i].v[0],tlist[i].t[0][0],tlist[i].t[0][1] ) ; + fprintf ( save_fd, "%d %g %g\n", + tlist[i].v[1],tlist[i].t[1][0],tlist[i].t[1][1] ) ; + fprintf ( save_fd, "%d %g %g\n", + tlist[i].v[2],tlist[i].t[2][0],tlist[i].t[2][1] ) ; + } + + fprintf ( save_fd, "kids 0\n" ) ; + + delete [] vlist ; + delete [] tlist ; + + return TRUE ; +} + + + +static int ssgSaveACInner ( ssgEntity *ent ) +{ + /* WARNING - RECURSIVE! */ + + if ( ent -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) ent ; + + fprintf ( save_fd, "OBJECT group\n" ) ; + fprintf ( save_fd, "kids %d\n", ent->getNumKids() ) ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + if ( ! ssgSaveACInner( br -> getKid ( i ) ) ) + return FALSE ; + + return TRUE ; + } + + return ssgSaveLeaf ( ent ) ; +} + + + + +int ssgSaveAC ( const char *filename, ssgEntity *ent ) +{ + int i ; + + save_fd = fopen ( filename, SSG_SAVE_TEXT_FILE ) ; + + if ( save_fd == NULL ) + { + ulSetError ( UL_WARNING, + "ssgSaveAC: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + + fprintf ( save_fd, "AC3Db\n" ) ; + + gSSL.collect( ent ); + + /* + There is a *huge* problem here. + + The AC3D format doesn't support per-vertex or even + per-polygon colours. + + You *must* use materials. + + Hence what we really should do is to collect together + all the per-vertex colours in the SSG model and make + a material from each one (possibly more if that colour + is on many polygons which have different ssgSimpleState's. + + For now, I'll cheat and set the GL_COLORMATERIAL colours + to white - which works *reasonably* well for most heavily + textured models...well, at least you don't get uninitialised + colours coming from SSG. + */ + + for (i = 0 ; i < gSSL.getNum(); i++) + { + sgVec4 white = { 1, 1, 1, 1 } ; + + ssgSimpleState * ss = gSSL.get(i); + + float *em = ss->getMaterial (GL_EMISSION ); + float *sp = ss->getMaterial (GL_SPECULAR ); + float *am = ss->getMaterial (GL_AMBIENT ); + float *di = ss->getMaterial (GL_DIFFUSE ); + + if ( ss->isEnabled ( GL_COLOR_MATERIAL ) ) + { + switch ( ss->getColourMaterial () ) + { + case GL_AMBIENT_AND_DIFFUSE : am = di = white ; break ; + case GL_EMISSION : em = white ; break ; + case GL_SPECULAR : sp = white ; break ; + case GL_AMBIENT : am = white ; break ; + case GL_DIFFUSE : di = white ; break ; + } + } + + int shiny = (int) ss->getShininess (); + + fprintf ( save_fd, "MATERIAL \"%s\"" + " rgb %f %f %f" + " amb %f %f %f" + " emis %f %f %f" + " spec %f %f %f" + " shi %d trans %f\n", + ss->getPrintableName(), + di[0], di[1], di[2], + am[0], am[1], am[2], + em[0], em[1], em[2], + sp[0], sp[1], sp[2], + shiny, 1.0-di[3] ) ; + } + + fprintf ( save_fd, "OBJECT world\n" ) ; + fprintf ( save_fd, "kids 1\n" ) ; + + int bReturn = ssgSaveACInner ( ent ) ; + + gSSL.removeAll () ; + + fclose ( save_fd ) ; + + return bReturn ; +} + + diff --git a/src/ssg/ssgSaveASC.cxx b/src/ssg/ssgSaveASC.cxx new file mode 100644 index 0000000..917d4d4 --- /dev/null +++ b/src/ssg/ssgSaveASC.cxx @@ -0,0 +1,234 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +// +// ASC (ascii files, used for example by 3DS R4 (the DOS version)) export for SSG/PLIB +// Written by Wolfram Kuss in Mar-2003 +// + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + +static FILE *save_fd ; +static ssgSimpleStateArray gSSL; + +float save_scale = 1.0f; +int calledByTheHuman = TRUE; + +static void save_vtx_table ( ssgVtxTable *vt ) +{ + GLenum mode = vt -> getPrimitiveType () ; + if ( mode != GL_TRIANGLES && + mode != GL_TRIANGLE_FAN && + mode != GL_TRIANGLE_STRIP ) + { + //only triangle export + return; + } + +/* + Named object: +*/ + int j ; + + + int istate = gSSL.findIndex ( (ssgSimpleState*)vt->getState () ) ; + int num_vert = vt -> getNumVertices () ; + int num_face = vt -> getNumTriangles () ; + + ssgSimpleState* st = ( istate != -1 )? gSSL.get( istate ): 0; + bool mapped = ( st && st -> isEnabled ( GL_TEXTURE_2D ) && + vt -> getNumTexCoords () == num_vert ); + + fprintf ( save_fd, "Named object: \"%s\"\n", vt->getPrintableName()); + fprintf ( save_fd, "Tri-mesh, Vertices: %d Faces: %d\n", num_vert, num_face ); + if(mapped) + fprintf ( save_fd, "Mapped\n"); + +/* + Vertex list: +*/ + fprintf ( save_fd, "Vertex list:\n"); + const char *material = "PALGREY27"; + ssgState *s = vt->getState(); + if (s) + if(s->isAKindOf(ssgTypeSimpleState())) + { + + ssgTexture * t = ((ssgSimpleState *)s)->getTexture(); + if(t) + { + char *fn = t->getFilename(); + if (fn) + { + char *tsA = new char [strlen(fn)+1]; // kludge: mem leak! + strcpy(tsA, fn); + + char *t = strrchr(tsA, '\\'); + if (!t) + t = tsA; + else + t++; + char *t1 = strrchr(t, '/'); + if (!t1) + t1 = t; + else + t1++; + + char *t2 = strrchr(t1, '.'); + if (t2) + { + // special handling for *.0af .. *.9af to be able to differntiate between them: + if((t2[2]=='a') && (t2[3]=='f')) + *t2='_'; + else + *t2=0; + } + + material = t1; + } + } + } + for ( j = 0; j < num_vert; j++ ) + { + sgVec3 v; + sgCopyVec3 ( v, vt->getVertex ( j ) ) ; + + fprintf ( save_fd, "Vertex %d: X:%f Y:%f Z:%f", j, save_scale*v[0], save_scale*v[1], save_scale*v[2] ); + if(mapped) + { + sgVec2 tv ; + sgCopyVec2 ( tv, vt->getTexCoord ( j ) ) ; + // *************************************************** + // WK: Originally I did not have the "U:" and "V:" !!! + // Strange !! Maybe there are two formats?? + // *************************************************** + fprintf ( save_fd, " U:%f V:%f\n", tv[0], tv[1]); // 1.0f - tv[1]); + } + else + fprintf ( save_fd, "\n"); + } + +/* + Face list: +*/ + if(num_face > 0) + fprintf ( save_fd, "Face list:\n"); + for ( j = 0; j < num_face; j++ ) + { + short i1,i2,i3; + vt -> getTriangle ( j, &i1, &i2, &i3 ) ; + + fprintf ( save_fd, "Face %d: A:%d B:%d C:%d AB:1 BC:1 CA:1\n", j, i1, i2, i3 ); + fprintf ( save_fd, "Material:\"%s\"\n", material ); + } + fprintf ( save_fd, "\n"); // empty line after the named object + +/* + Item MESH_NUMTVERTEX. +*/ + + fflush ( save_fd ) ; +/* + Item NUMCVERTEX. +*/ + //fprintf ( save_fd, " *MESH_NUMCVERTEX 0\n" ); + + //if ( st ) fprintf ( save_fd, " *MATERIAL_REF %d\n", gSSL.findIndex ( st ) ); + +} + +// wk: Kludge !!! +// if the "calling app" like PPE sets this, then only nodes with spare ==1 are saved !! +int bUseSpare=FALSE; + +static void save_geom ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + for (int i = 0 ; i < br -> getNumKids () ; i++ ) + save_geom ( br -> getKid ( i ) ) ; + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { int bSaveIt = TRUE; + if(bUseSpare) + if( 1 != e->getSpare() ) + bSaveIt = FALSE; + + if ( bSaveIt ) + { + ssgVtxTable *vt = (ssgVtxTable *) e ; + save_vtx_table ( vt ) ; + } + } +} + + +int ssgSaveASC ( FILE* fileout, ssgEntity *ent ) +{ + save_fd = fileout ; + +/* + Write the header. +*/ + fprintf ( save_fd, "Ambient light color: Red=0.039216 Green=0.039216 Blue=0.039216\n" ); + fprintf ( save_fd, "Solid background color: Red=0.372549 Green=0.454902 Blue=0.639216\n" ); + + + gSSL.collect ( ent ) ; + //save_states () ; + save_geom ( ent ) ; + gSSL.removeAll(); + + fflush ( save_fd ) ; + + return TRUE ; +} + + +int ssgSaveASC ( const char *filename, ssgEntity *ent ) +{ + save_fd = fopen ( filename, "wa" ) ; + + if ( save_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveASC: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + if(calledByTheHuman) + if(save_scale!=1.0f) + { +#ifdef WIN32 + char temp[999]; + sprintf(temp, "Applying a scale factor of %f", save_scale); + ::MessageBox(0, temp, "For your info:", 0); +#else + printf("Applying a scale factor of %f", save_scale); +#endif + } + int result = ssgSaveASC ( save_fd, ent ) ; + + fclose ( save_fd ) ; + return result ; +} diff --git a/src/ssg/ssgSaveASE.cxx b/src/ssg/ssgSaveASE.cxx new file mode 100644 index 0000000..533e07b --- /dev/null +++ b/src/ssg/ssgSaveASE.cxx @@ -0,0 +1,343 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveASE.cxx 2169 2012-08-14 17:28:50Z wolfram_kuss $ +*/ + +// +// ASE ( 3DSMAX ASCII EXPORT Version 2.00 ) export for SSG/PLIB +// Written by Dave McClurg (dpm@efn.org) in Feb-2000 +// + +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + +static FILE *save_fd ; +static ssgSimpleStateArray gSSL; + +static void save_state ( ssgSimpleState* st, int istate ) +{ + + float* amb = st -> getMaterial ( GL_AMBIENT ) ; + float* diff = st -> getMaterial ( GL_DIFFUSE ) ; + float* spec = st -> getMaterial ( GL_SPECULAR ) ; + float shine = st -> getShininess () ; + float trans = st -> isTranslucent () ? 1.0f : 0.0f ; + + fprintf ( save_fd, " *MATERIAL %d {\n", istate ); + fprintf ( save_fd, " *MATERIAL_NAME \"Material #%d\"\n", istate ); + fprintf ( save_fd, " *MATERIAL_CLASS \"Standard\"\n" ); + fprintf ( save_fd, " *MATERIAL_AMBIENT %f %f %f\n", amb[0], amb[1], amb[2] ) ; + fprintf ( save_fd, " *MATERIAL_DIFFUSE %f %f %f\n", diff[0], diff[1], diff[2] ) ; + fprintf ( save_fd, " *MATERIAL_SPECULAR %f %f %f\n", spec[0], spec[1], spec[2] ) ; + fprintf ( save_fd, " *MATERIAL_SHINE %f\n", shine ) ; + fprintf ( save_fd, " *MATERIAL_SHINESTRENGTH %f\n", shine ) ; + fprintf ( save_fd, " *MATERIAL_TRANSPARENCY %f\n", trans ) ; + fprintf ( save_fd, " *MATERIAL_WIRESIZE 1.0000\n" ); + fprintf ( save_fd, " *MATERIAL_SHADING Blinn\n" ); + fprintf ( save_fd, " *MATERIAL_XP_FALLOFF 0.0000\n" ); + fprintf ( save_fd, " *MATERIAL_SELFILLUM 0.0000\n" ); + fprintf ( save_fd, " *MATERIAL_TWOSIDED\n" ); + fprintf ( save_fd, " *MATERIAL_FALLOFF In\n" ); + fprintf ( save_fd, " *MATERIAL_SOFTEN\n" ); + fprintf ( save_fd, " *MATERIAL_XP_TYPE Filter\n" ) ; + + if ( st -> isEnabled ( GL_TEXTURE_2D ) ) + { + const char* tfname = st -> getTextureFilename() ; + + fprintf ( save_fd, " *MAP_DIFFUSE {\n" ); + fprintf ( save_fd, " *MAP_NAME \"Map #%d\"\n", istate ) ; + fprintf ( save_fd, " *MAP_CLASS \"Bitmap\"\n" ) ; + fprintf ( save_fd, " *MAP_SUBNO 1\n" ) ; + fprintf ( save_fd, " *MAP_AMOUNT 1.0000\n" ) ; + fprintf ( save_fd, " *BITMAP \"%s\"\n", tfname ) ; + fprintf ( save_fd, " *MAP_TYPE Spherical\n" ) ; + fprintf ( save_fd, " *UVW_U_OFFSET 0.0000\n" ) ; + fprintf ( save_fd, " *UVW_V_OFFSET 0.0000\n" ) ; + fprintf ( save_fd, " *UVW_U_TILING 1.0000\n" ) ; + fprintf ( save_fd, " *UVW_V_TILING 1.0000\n" ) ; + fprintf ( save_fd, " *UVW_ANGLE 0.0000\n" ) ; + fprintf ( save_fd, " *UVW_BLUR 1.0000\n" ) ; + fprintf ( save_fd, " *UVW_BLUR_OFFSET 0.0000\n" ) ; + fprintf ( save_fd, " *UVW_NOUSE_AMT 1.0000\n" ) ; + fprintf ( save_fd, " *UVW_NOISE_SIZE 1.0000\n" ) ; + fprintf ( save_fd, " *UVW_NOISE_LEVEL 1\n" ) ; + fprintf ( save_fd, " *UVW_NOISE_PHASE 0.0000\n" ) ; + fprintf ( save_fd, " *BITMAP_FILTER Pyramidal\n" ) ; + fprintf ( save_fd, " }\n" ); + } + + fprintf ( save_fd, " }\n" ); +} + + +static void save_states () +{ + fprintf ( save_fd, "*MATERIAL_LIST {\n" ) ; + fprintf ( save_fd, " *MATERIAL_COUNT %d\n", gSSL.getNum() ) ; + + for ( int i=0; i < gSSL.getNum(); i++ ) + save_state ( gSSL.get(i) , i ) ; + + fprintf ( save_fd, "}\n" ); +} + + +static void save_vtx_table ( ssgVtxTable *vt ) +{ + GLenum mode = vt -> getPrimitiveType () ; + if ( mode != GL_TRIANGLES && + mode != GL_TRIANGLE_FAN && + mode != GL_TRIANGLE_STRIP ) + { + //only triangle export + return; + } + + const char* name = vt->getPrintableName() ; + int j ; + + int istate = gSSL.findIndex ( (ssgSimpleState*)vt->getState () ) ; + ssgSimpleState* st = ( istate != -1 )? gSSL.get( istate ): 0; + +/* + Begin the big geometry block. +*/ + fprintf ( save_fd, "*GEOMOBJECT {\n" ); + fprintf ( save_fd, " *NODE_NAME \"%s\"\n", name ); + +/* + Sub block NODE_TM: +*/ + fprintf ( save_fd, " *NODE_TM {\n" ); + fprintf ( save_fd, " *NODE_NAME \"%s\"\n", name ); + fprintf ( save_fd, " *INHERIT_POS 0 0 0\n" ); + fprintf ( save_fd, " *INHERIT_ROT 0 0 0\n" ); + fprintf ( save_fd, " *INHERIT_SCL 0 0 0\n" ); + fprintf ( save_fd, " *TM_ROW0 1.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_ROW1 0.0000 1.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_ROW2 0.0000 0.0000 1.0000\n" ); + fprintf ( save_fd, " *TM_ROW3 0.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_POS 0.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_ROTAXIS 0.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_ROTANGLE 0.0000\n" ); + fprintf ( save_fd, " *TM_SCALE 1.0000 1.0000 1.0000\n" ); + fprintf ( save_fd, " *TM_SCALEAXIS 0.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *TM_SCALEAXISANG 0.0000\n" ); + fprintf ( save_fd, " }\n" ); + +/* + Sub block MESH: + Items +*/ + int num_vert = vt -> getNumVertices () ; + int num_face = vt -> getNumTriangles () ; + fprintf ( save_fd, " *MESH {\n" ); + fprintf ( save_fd, " *TIMEVALUE 0\n" ); + fprintf ( save_fd, " *MESH_NUMVERTEX %d\n", num_vert ); + fprintf ( save_fd, " *MESH_NUMFACES %d\n", num_face ); + +/* + Sub sub block MESH_VERTEX_LIST +*/ + fprintf ( save_fd, " *MESH_VERTEX_LIST {\n" ); + for ( j = 0; j < num_vert; j++ ) + { + sgVec3 v; + sgCopyVec3 ( v, vt->getVertex ( j ) ) ; + fprintf ( save_fd, " *MESH_VERTEX %d %f %f %f\n", + j, v[0], v[1], v[2] ); + } + fprintf ( save_fd, " }\n" ); + +/* + Sub sub block MESH_FACE_LIST + Items MESH_FACE +*/ + fprintf ( save_fd, " *MESH_FACE_LIST {\n" ); + for ( j = 0; j < num_face; j++ ) + { + short i1,i2,i3; + vt -> getTriangle ( j, &i1, &i2, &i3 ) ; + + fprintf ( save_fd, " *MESH_FACE %d: A: %d B: %d C: %d", j, i1, i2, i3 ); + fprintf ( save_fd, " AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING *MESH_MTLID 1\n" ); + } + fprintf ( save_fd, " }\n" ); + +/* + Item MESH_NUMTVERTEX. +*/ + if ( st && st -> isEnabled ( GL_TEXTURE_2D ) && + vt -> getNumTexCoords () == num_vert ) + { + fprintf ( save_fd, " *MESH_NUMTVERTEX %d\n", num_vert ); + fprintf ( save_fd, " *MESH_TVERTLIST {\n" ); + for ( j = 0; j < num_vert; j++ ) + { + sgVec2 tv ; + sgCopyVec2 ( tv, vt->getTexCoord ( j ) ) ; + fprintf ( save_fd, " *MESH_TVERT %d %f %f %f\n", + j, tv[0], 1.0f - tv[1], 1.0f ); + } + fprintf ( save_fd, " }\n" ); + fprintf ( save_fd, " *MESH_NUMTVFACES %d\n", num_face ); + fprintf ( save_fd, " *MESH_TFACELIST {\n" ); + for ( j = 0; j < num_face; j++ ) + { + short i1,i2,i3; + vt -> getTriangle ( j, &i1, &i2, &i3 ) ; + + fprintf ( save_fd, " *MESH_TFACE %d %d %d %d\n", j, i1, i2, i3 ); + } + fprintf ( save_fd, " }\n" ); + } + else + { + fprintf ( save_fd, " *MESH_NUMTVERTEX 0\n" ); + } + + fprintf ( save_fd, "done\n" ) ; + fflush ( save_fd ) ; +/* + Item NUMCVERTEX. +*/ + fprintf ( save_fd, " *MESH_NUMCVERTEX 0\n" ); + +#if 0 +/* + Sub block MESH_NORMALS + Items MESH_FACENORMAL, MESH_VERTEXNORMAL (repeated) +*/ + fprintf ( save_fd, " *MESH_NORMALS {\n" ); + for ( j = 0; j < num_face; j++ ) { + + fprintf ( save_fd, " *MESH_FACENORMAL %d %f %f %f\n", + iface, face_normal[0][j], face_normal[1][j], face_normal[2][j] ); + + for ( ivert = 0; ivert < face_order[j]; ivert++ ) { + fprintf ( save_fd, " *MESH_VERTEXNORMAL %d %f %f %f\n", + face[ivert][iface], vertex_normal[0][ivert][iface], + vertex_normal[1][ivert][j], vertex_normal[2][ivert][j] ); + } + } + fprintf ( save_fd, " }\n" ); +#endif + +/* + Close the MESH object. +*/ + fprintf ( save_fd, " }\n" ); + +/* + A few closing parameters. +*/ + fprintf ( save_fd, " *PROP_MOTIONBLUR 0\n" ); + fprintf ( save_fd, " *PROP_CASTSHADOW 1\n" ); + fprintf ( save_fd, " *PROP_RECVSHADOW 1\n" ); + + if ( st ) + fprintf ( save_fd, " *MATERIAL_REF %d\n", gSSL.findIndex ( st ) ); + +/* + Close the GEOM object. +*/ + fprintf ( save_fd, "}\n" ); +} + + +static void save_geom ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + if ( br -> isAKindOf ( ssgTypeSelector() ) ) + { + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + save_geom ( br -> getKid ( i ) ) ; + } + else + { + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + save_geom ( br -> getKid ( i ) ) ; + } + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { + ssgVtxTable *vt = (ssgVtxTable *) e ; + save_vtx_table ( vt ) ; + } +} + + +int ssgSaveASE ( FILE* fileout, ssgEntity *ent ) +{ + save_fd = fileout ; + +/* + Write the header. +*/ + fprintf ( save_fd, "*3DSMAX_ASCIIEXPORT 200\n" ); + fprintf ( save_fd, "*COMMENT \"created by SSG.\"\n" ); + +/* + Write the scene block. +*/ + fprintf ( save_fd, "*SCENE {\n" ); + fprintf ( save_fd, " *SCENE_FILENAME \"\"\n" ); + fprintf ( save_fd, " *SCENE_FIRSTFRAME 0\n" ); + fprintf ( save_fd, " *SCENE_LASTFRAME 100\n" ); + fprintf ( save_fd, " *SCENE_FRAMESPEED 30\n" ); + fprintf ( save_fd, " *SCENE_TICKSPERFRAME 160\n" ); + fprintf ( save_fd, " *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000\n" ); + fprintf ( save_fd, " *SCENE_AMBIENT_STATIC 0.0431 0.0431 0.0431\n" ); + fprintf ( save_fd, "}\n" ); + + + gSSL.collect ( ent ) ; + save_states () ; + save_geom ( ent ) ; + gSSL.removeAll(); + + fflush ( save_fd ) ; + + return TRUE ; +} + + +int ssgSaveASE ( const char *filename, ssgEntity *ent ) +{ + save_fd = fopen ( filename, SSG_SAVE_TEXT_FILE ) ; + + if ( save_fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveASE: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + + int result = ssgSaveASE ( save_fd, ent ) ; + + fclose ( save_fd ) ; + + return result ; +} diff --git a/src/ssg/ssgSaveATG.cxx b/src/ssg/ssgSaveATG.cxx new file mode 100644 index 0000000..cf8b8fc --- /dev/null +++ b/src/ssg/ssgSaveATG.cxx @@ -0,0 +1,160 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveATG.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ +/* + .scenery writer for SSG/PLIB + ATG = ascii Terra Gear + + Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in May 2001 + +*/ + +#include +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + + + +int ssgSaveATG( const char* fname, ssgEntity *ent ) { + FILE *fd = fopen ( fname, "w" ) ; + int i; + + if ( fd == NULL ) { + ulSetError ( UL_WARNING, "ssgSaveATG: Failed to open '%s' for writing", + fname ); + return FALSE ; + } + + ssgVertexArray* vertices = new ssgVertexArray(); + ssgIndexArray* indices = new ssgIndexArray (); + + fprintf(fd, "# Created by ssgSaveATG. Original graph structure was:\n"); + ent->print(fd, "#", 0); + + sgMat4 ident; + sgMakeIdentMat4( ident ); + ssgSimpleStateArray ssa; // = new ssgSimpleStateArray(); + ssgIndexArray* materialIndices = new ssgIndexArray(); + ssgTexCoordArray * texCoordArray = new ssgTexCoordArray(); + + ssgAccumVerticesAndFaces( ent, ident, vertices, indices, /* epsilon = */ -1, &ssa, + materialIndices, texCoordArray); + assert ( vertices->getNum() == texCoordArray->getNum() ); + for (i = 0; i < vertices->getNum(); i++) { + fprintf(fd, "v %f %f %f\n", vertices->get(i)[0], + vertices->get(i)[1], + vertices->get(i)[2]); + } +// hack calc normal + + ssgNormalArray *na=new ssgNormalArray (vertices->getNum()); + sgVec3 myVec3; + myVec3[0]=0.0; myVec3[1]=0.0; myVec3[2]=1.0; + sgVec3 n; + int ind1, ind2, ind3; + for (i = 0; i < vertices->getNum(); i++) + na->add(myVec3); + + + for (i = 0; i < indices->getNum(); i += 3) { + ind1 = *indices->get(i); + ind2 = *indices->get(i + 1); + ind3 = *indices->get(i + 2); + if ((vertices->get(ind1)!=NULL) && (vertices->get(ind2)!=NULL) && + (vertices->get(ind3)!=NULL)) // wk: kludge: why can it be NULL? + { sgMakeNormal( n, + vertices->get(ind1), + vertices->get(ind2), + vertices->get(ind3) ); + + sgCopyVec3( na->get(ind1), n ); + sgCopyVec3( na->get(ind2), n ); + sgCopyVec3( na->get(ind3), n ); + } + } + + + + + + + + for (i = 0; i < vertices->getNum(); i++) + { float *f = na->get(i); + fprintf(fd, "vn %f %f %f\n", f[0], f[1], f[2]); + } + for (i = 0; i < texCoordArray->getNum(); i++) + fprintf(fd, "vt %f %f\n", texCoordArray->get(i)[0], texCoordArray->get(i)[1]); + + // output all faces + int runningIndex=0, lastIndex = -1; + for (i = 0; i < indices->getNum(); i += 3) { + + // output material + int matIndex = *(materialIndices->get(runningIndex++)); + if (( matIndex >= 0 ) && ( lastIndex != matIndex )) + { ssgSimpleState * ss = ssa.get( matIndex ); + if ( ss->getTextureFilename() != NULL) + { // remove .rgb + char *s1, *s2, * s = new char [ strlen(ss->getTextureFilename()) +1 ]; + assert ( s != NULL ); + strcpy(s, ss->getTextureFilename()); + char *p = strrchr(s, '.'); + if ( p != NULL ) + { + if (((p[2]=='a') || (p[2]=='A')) && ((p[3]=='f') || (p[3]=='F'))) + // *.?af textures are special: + // the name before the ':' is not unique, you have to use the first character of the extension as + // well. The tool af2rgb, which converts *.?af into rgb-files, also appends this char to the filename. + { p[0]=p[1]; + p[1]=0; + } + else + *p = 0; + } + s2 = s; + s1 = strrchr(s, '/'); + if ( s1 != NULL ) + s2 = ++s1; + s1 = strrchr(s2, '\\'); + if ( s1 != NULL ) + s2 = ++s1; + fprintf(fd, "# usemtl %s\n", s2); + delete [] s; + } + lastIndex = matIndex; + } + // output face + fprintf(fd, "f %d/%d %d/%d %d/%d\n", *indices->get(i), *indices->get(i), + *indices->get(i + 1), *indices->get(i + 1), + *indices->get(i + 2), *indices->get(i + 2) ); + } + assert ( runningIndex == materialIndices->getNum() ); + delete materialIndices; + + fclose( fd ) ; + + delete vertices; + delete indices ; + + return TRUE; +} diff --git a/src/ssg/ssgSaveDXF.cxx b/src/ssg/ssgSaveDXF.cxx new file mode 100644 index 0000000..15f2967 --- /dev/null +++ b/src/ssg/ssgSaveDXF.cxx @@ -0,0 +1,179 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveDXF.cxx 2169 2012-08-14 17:28:50Z wolfram_kuss $ +*/ + +// +// DXF export for SSG/PLIB +// Ported from IVCON by Dave McClurg (dpm@efn.org) in March-2000 +// + +#include "ssgLocal.h" + +static FILE *fileout ; + +static void save_vtx_table ( ssgVtxTable *vt ) +{ + GLenum mode = vt -> getPrimitiveType () ; + if (( mode == GL_LINES ) || ( mode == GL_LINE_LOOP) || ( mode == GL_LINE_STRIP)) + { + int num_vert = vt -> getNumVertices () ; + num_vert = num_vert - ( num_vert & 1 ) ; //discard odd vertex + int local_num_lines = vt -> getNumLines(); + + for ( int j = 0; j < local_num_lines ; j ++ ) + { + sgVec3 vert1, vert2; + short iv1, iv2; + vt -> getLine (j, &iv1, &iv2); + + sgCopyVec3 ( vert1, vt->getVertex ( iv1 ) ) ; + sgCopyVec3 ( vert2, vt->getVertex ( iv2 ) ) ; + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "LINE\n" ); + fprintf ( fileout, "8\n" ); + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "10\n" ); + fprintf ( fileout, "%f\n", vert1[0] ); + fprintf ( fileout, "20\n" ); + fprintf ( fileout, "%f\n", vert1[1] ); + fprintf ( fileout, "30\n" ); + fprintf ( fileout, "%f\n", vert1[2] ); + fprintf ( fileout, "11\n" ); + fprintf ( fileout, "%f\n", vert2[0] ); + fprintf ( fileout, "21\n" ); + fprintf ( fileout, "%f\n", vert2[1] ); + fprintf ( fileout, "31\n" ); + fprintf ( fileout, "%f\n", vert2[2] ); + } + } + else if ( mode == GL_TRIANGLES || + mode == GL_TRIANGLE_FAN || + mode == GL_TRIANGLE_STRIP ) + { + int num_face = vt -> getNumTriangles () ; + for ( int j = 0; j < num_face; j++ ) + { + short face[3]; + vt -> getTriangle ( j, &face[0], &face[1], &face[2] ) ; + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "3DFACE\n" ); + fprintf ( fileout, "8\n" ); + fprintf ( fileout, "Cube\n" ); + + sgVec3 vert; + for ( int ivert = 0; ivert < 3; ivert++ ) { + + sgCopyVec3 ( vert, vt->getVertex ( face[ivert] ) ) ; + + fprintf ( fileout, "1%d\n", ivert ); + fprintf ( fileout, "%f\n", vert[0] ); + fprintf ( fileout, "2%d\n", ivert ); + fprintf ( fileout, "%f\n", vert[1] ); + fprintf ( fileout, "3%d\n", ivert ); + fprintf ( fileout, "%f\n", vert[2] ); + } + fprintf ( fileout, "13\n"); + fprintf ( fileout, "%f\n", vert[0] ); + fprintf ( fileout, "23\n"); + fprintf ( fileout, "%f\n", vert[1] ); + fprintf ( fileout, "33\n"); + fprintf ( fileout, "%f\n", vert[2] ); + } + } + else + ulSetError ( UL_WARNING, "ssgSaveDXF: OpenGL mode %d not implmented yet. Parts or all of the model are ignored!' for writing", (int)mode ) ; +} + + +static void save_entities ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + save_entities ( br -> getKid ( i ) ) ; + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { + ssgVtxTable *vt = (ssgVtxTable *) e ; + save_vtx_table ( vt ) ; + } +} + + +int ssgSaveDXF ( const char *filename, ssgEntity *ent ) +{ + fileout = fopen ( filename, SSG_SAVE_TEXT_FILE); + + if ( fileout == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveDXF: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + +/* + Initialize. +*/ + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "SECTION\n" ); + fprintf ( fileout, "2\n" ); + fprintf ( fileout, "HEADER\n" ); + fprintf ( fileout, "999\n" ); + fprintf ( fileout, "%s created by SSG.\n", filename ); + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "ENDSEC\n" ); + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "SECTION\n" ); + fprintf ( fileout, "2\n" ); + fprintf ( fileout, "TABLES\n" ); + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "ENDSEC\n" ); + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "SECTION\n" ); + fprintf ( fileout, "2\n" ); + fprintf ( fileout, "BLOCKS\n" ); + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "ENDSEC\n" ); + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "SECTION\n" ); + fprintf ( fileout, "2\n" ); + fprintf ( fileout, "ENTITIES\n" ); + + save_entities ( ent ) ; + + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "ENDSEC\n" ); + fprintf ( fileout, "0\n" ); + fprintf ( fileout, "EOF\n" ); +/* + Close. +*/ + fclose ( fileout ) ; + return TRUE; +} diff --git a/src/ssg/ssgSaveFLT.cxx b/src/ssg/ssgSaveFLT.cxx new file mode 100644 index 0000000..0f03f76 --- /dev/null +++ b/src/ssg/ssgSaveFLT.cxx @@ -0,0 +1,680 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveFLT.cxx 1744 2003-01-04 02:55:56Z sjbaker $ +*/ + +/******************************************************* + ** ssgSaveFLT.cxx + ** + ** Written by Steve Baker + ** Last updated: 2002-10-15 + ** + ** This was written to be a part of Stephen J Bakers + ** PLIB (http://plib.sourceforge.net) + *******************************************************/ + +#include +#include "ssgLocal.h" + + +struct FLT_texture +{ + char fname [ 200 ] ; /* 200 is the OpenFlight limit - don't increase it! */ +} ; + + +struct FLT_vertex +{ + sgVec3 vert ; + sgVec3 norm ; + sgVec4 col ; + sgVec2 tex ; +} ; + + +static ulList *texList = NULL ; +static ulList *vtxList = NULL ; +static FILE *save_fd = NULL ; +static int max_bank = 1 ; +static int curr_bank = 0 ; + +static int addToTexturePool ( char *tex ) +{ + int max = texList -> getNumEntities () ; + + for ( int p = strlen(tex) ; p >= 0 ; p-- ) + if ( tex[p] == '/' ) + { + tex = & tex[p+1] ; + break ; + } + + for ( int i = 0 ; i < max ; i++ ) + { + FLT_texture *target = (FLT_texture *)( texList -> getEntity ( i ) ) ; + + if ( strcmp ( target->fname, tex ) == 0 ) + return i ; + } + + FLT_texture *add = new FLT_texture ; + + strncpy ( add->fname, tex, 199 ) ; + + texList -> addEntity ( add ) ; + return max ; +} + + +static int addToVertexPool ( FLT_vertex *vert ) +{ + int max = vtxList -> getNumEntities () ; + + for ( int i = 0 ; i < max ; i++ ) + { + FLT_vertex *target = (FLT_vertex *)( vtxList -> getEntity ( i ) ) ; + + if ( memcmp ( target, vert, sizeof ( FLT_vertex ) ) == 0 ) + return i ; + } + + FLT_vertex *add = new FLT_vertex ; + + *add = *vert ; + + vtxList -> addEntity ( add ) ; + return max ; +} + + +static int writeChar ( char val ) +{ + fwrite ( & val, sizeof(char), 1, save_fd ) ; + return sizeof(char) ; +} + + +static int writeShort ( short val ) +{ + writeChar ( (val >> 8) & 0xFF ) ; + writeChar ( val & 0xFF ) ; + return sizeof(short) ; +} + + +static int writeInt( int val ) +{ + writeChar ( (val >> 24) & 0xFF ) ; + writeChar ( (val >> 16) & 0xFF ) ; + writeChar ( (val >> 8 ) & 0xFF ) ; + writeChar ( val & 0xFF ) ; + return sizeof(int) ; +} + + +static void swabInt ( int *x ) +{ + unsigned int t = (unsigned int) *x ; + + *x = ((t & 0x000000FF) << 24) | + ((t & 0x0000FF00) << 8) | + ((t & 0x00FF0000) >> 8) | + ((t & 0xFF000000) >> 24) ; +} + + +static void swabFloat ( float *x ) +{ + int *a = (int *) x ; + + swabInt ( a ) ; +} + + +static void swabDouble ( double *x ) +{ + int *a = (int *) x ; + int *b = a+1 ; + + swabInt ( a ) ; + swabInt ( b ) ; + + *a = *a ^ *b ; + *b = *a ^ *b ; + *a = *a ^ *b ; +} + + + +static int writeFloat ( float val ) +{ + if ( ulIsLittleEndian ) + swabFloat ( & val ) ; + + fwrite ( & val, sizeof(float), 1, save_fd ) ; + return sizeof(float) ; +} + + +static int writeDouble( double val ) +{ + if ( ulIsLittleEndian ) + swabDouble ( & val ) ; + + fwrite ( & val, sizeof(double), 1, save_fd ) ; + return sizeof(double) ; +} + + +static int writeString( char *str, int slen ) +{ + fwrite ( str, sizeof(char), slen, save_fd ) ; + return slen ; +} + + + +static void writeHeader () +{ + int len = 0; + + len += writeShort ( 1 ) ; + len += writeShort ( 2+2+8+4+4+32+2+2+2+2+2+1+1+4+4+ + 4+4+4+4+4+4+4+4+4+4+4+4+4+2+2+ + 4+8+8+8+8+2+2+4+4+2+2+2+2+4+8+ + 8+8+8+8+8+8+8+2+2+2+2+2+2+2+2+ + 4+2+2+2+8+8+2+2 ) ; + + len += writeString( " ", 8 ) ; /* ID */ + len += writeInt ( 1570 ) ; /* Format Rev level 15.70 */ + len += writeInt ( 0 ) ; /* Edit rev level */ + len += writeString( "Unknown Date. ", 32 ) ; /* Date/Time */ + len += writeShort ( 0 ) ; /* Next Group Node */ + len += writeShort ( 0 ) ; /* Next LOD Node */ + len += writeShort ( 0 ) ; /* Next Object Node */ + len += writeShort ( 0 ) ; /* Next Face node */ + len += writeShort ( 1 ) ; /* Unit multiplier */ + len += writeChar ( 0 ) ; /* Unit type Meters */ + len += writeChar ( 0 ) ; /* TexWhite */ + len += writeInt ( 0 ) ; /* Flags */ + + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Projection Type */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + + len += writeShort ( 0 ) ; /* Next DOF node */ + len += writeShort ( 1 ) ; /* Vertex storage type (DOUBLE!) */ + len += writeInt ( 100 ) ; /* Database Origin */ + + len += writeDouble( 0 ) ; /* SW DB X */ + len += writeDouble( 0 ) ; /* SW DB Y */ + len += writeDouble( 0 ) ; /* Delta X */ + len += writeDouble( 0 ) ; /* Delta Y */ + + len += writeShort ( 0 ) ; /* Next Sound node */ + len += writeShort ( 0 ) ; /* Next Path node */ + + len += writeInt ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Reserved */ + + len += writeShort ( 0 ) ; /* Next Clip node */ + len += writeShort ( 0 ) ; /* Next Text node */ + len += writeShort ( 0 ) ; /* Next BSP node */ + len += writeShort ( 0 ) ; /* Next Switch node */ + + len += writeInt ( 0 ) ; /* Reserved */ + + len += writeDouble( 0 ) ; /* SW Corner lat */ + len += writeDouble( 0 ) ; /* SW Corner lon */ + len += writeDouble( 0 ) ; /* NE Corner lat */ + len += writeDouble( 0 ) ; /* NE Corner lon */ + len += writeDouble( 0 ) ; /* Org Lat */ + len += writeDouble( 0 ) ; /* Org Lon */ + len += writeDouble( 0 ) ; /* Lambert upper lat */ + len += writeDouble( 0 ) ; /* Lambert upper lon */ + len += writeShort ( 0 ) ; /* Next Light source */ + len += writeShort ( 0 ) ; /* Next Light Point */ + len += writeShort ( 0 ) ; /* Next Road */ + len += writeShort ( 0 ) ; /* Next CAT */ + len += writeShort ( 0 ) ; /* Reserved */ + len += writeShort ( 0 ) ; /* Reserved */ + len += writeShort ( 0 ) ; /* Reserved */ + len += writeShort ( 0 ) ; /* Reserved */ + len += writeInt ( 0 ) ; /* Earth Ellipsoidal model (==WGS84) */ + len += writeShort ( 0 ) ; /* Next Adaptive node */ + len += writeShort ( 0 ) ; /* Next Curve node */ + len += writeShort ( 0 ) ; /* Reserved */ + len += writeDouble( 0 ) ; /* Delta Z to place */ + len += writeDouble( 0 ) ; /* Radius Z to place */ + len += writeShort ( 0 ) ; /* Next Mesh node */ + len += writeShort ( 0 ) ; /* Reserved */ +} + + +static void writePush () { writeShort ( 10 ) ; writeShort ( 4 ) ; } +static void writePop () { writeShort ( 11 ) ; writeShort ( 4 ) ; } + +static void writeObject () +{ + writeShort ( 4 ) ; + writeShort ( 2+2+8+4+2+2+2+2+2+2 ) ; + + static int next_name = 0 ; + char name [ 8 ] ; + sprintf ( name, "O%d", next_name++ ) ; + writeString ( name, 8 ) ; /* Name */ + writeInt ( 0x00000000 ) ; /* Flags */ + writeShort ( 0 ) ; /* Relative Priority */ + writeShort ( 0 ) ; /* Transparency (1-Alpha) */ + writeShort ( 0 ) ; /* Special ID 1 */ + writeShort ( 0 ) ; /* Special ID 2 */ + writeShort ( 0 ) ; /* Significance */ + writeShort ( 0 ) ; /* Layer */ +} + + + +static void writeFace ( ssgLeaf *leaf, int isLine ) +{ + writeShort ( 5 ) ; + writeShort ( 2+2+8+4+2+1+1+2+2+1+1+2+2+2+2+2+4+2+1+1+ + 4+1+1+2+4+4+4+2+2+4+4+2+2 ) ; + + static int next_name = 0 ; + char name [ 8 ] ; + sprintf ( name, "F%d", next_name++ ) ; + writeString ( name, 8 ) ; /* Name */ + writeInt ( 0 ) ; /* IR color code */ + writeShort ( 0 ) ; /* Relative Priority */ + writeChar ( isLine ? 2 : 0 ) ; /* Draw Type */ + writeChar ( 0 ) ; /* Texture-White */ + writeShort ( 0 ) ; /* Color name index */ + writeShort ( 0 ) ; /* Alternate Color name index */ + writeChar ( 0 ) ; + writeChar ( 0 ) ; /* Billboard */ + writeShort (-1 ) ; /* Detail Texture */ + + ssgSimpleState *s = (ssgSimpleState *)( leaf -> getState () ) ; + + if ( s != NULL && s -> getTextureFilename () != NULL ) + writeShort ( addToTexturePool ( s -> getTextureFilename () ) ) ; + else + writeShort (-1 ) ; /* No Texture */ + + writeShort (-1 ) ; /* Material */ + writeShort ( 0 ) ;/* XXXX */ /* SMC */ + writeShort ( 0 ) ; /* DFAD FID */ + writeInt ( 0 ) ; /* IR Material Code */ + writeShort ( 0 ) ; /* Transparent */ + writeChar ( 0 ) ; /* LOD generation control */ + writeChar ( 0 ) ; /* Line Style */ + writeInt ( 0x60000000 ) ; /* Flags == No Colour */ + writeChar ( 3 ) ; /* Light mode == Use Vertex colour & Normal */ + writeChar ( 0 ) ; + writeShort ( 0 ) ; + writeInt ( 0 ) ; + writeInt ( 0xFFFFFFFF ) ; /* Packed Color */ + writeInt ( 0xFFFFFFFF ) ; /* Packed Alt Color */ + writeShort ( 0 ) ; /* Texture mapping index */ + writeShort ( 0 ) ; + writeInt ( 0 ) ; /* Primary Color Index */ + writeInt ( 0 ) ; /* Secondary Color Index */ + writeShort ( 0 ) ; + writeShort ( 0 ) ; +} + +static void writeLeaf ( ssgLeaf *leaf ) +{ + int i ; + + writeObject () ; + writePush () ; + + for ( i = 0 ; i < leaf -> getNumLines () ; i++ ) + { + writeFace ( leaf, TRUE ) ; + + writePush () ; + writeShort ( 72 ) ; /* Vertex List */ + writeShort ( 2+2+4*2 ) ; + + short vv [ 2 ] ; + + leaf -> getLine ( i, &vv[0], &vv[1] ) ; + + for ( int j = 0 ; j < 2 ; j++ ) + { + FLT_vertex vert ; + sgCopyVec3 ( vert.vert, leaf -> getVertex ( vv[j] ) ) ; + sgCopyVec3 ( vert.norm, leaf -> getNormal ( vv[j] ) ) ; + sgCopyVec4 ( vert.col , leaf -> getColour ( vv[j] ) ) ; + sgCopyVec2 ( vert.tex , leaf -> getTexCoord ( vv[j] ) ) ; + + writeInt ( addToVertexPool ( & vert ) * 60 + 8 ) ; + } + + writePop () ; + } + + for ( i = 0 ; i < leaf -> getNumTriangles () ; i++ ) + { + writeFace ( leaf, FALSE ) ; + + writePush () ; + writeShort ( 72 ) ; /* Vertex List */ + writeShort ( 2+2+4*3 ) ; + + short vv [ 3 ] ; + + leaf -> getTriangle ( i, &vv[0], &vv[1], &vv[2] ) ; + + for ( int j = 0 ; j < 3 ; j++ ) + { + FLT_vertex vert ; + sgCopyVec3 ( vert.vert, leaf -> getVertex ( vv[j] ) ) ; + sgCopyVec3 ( vert.norm, leaf -> getNormal ( vv[j] ) ) ; + sgCopyVec4 ( vert.col , leaf -> getColour ( vv[j] ) ) ; + sgCopyVec2 ( vert.tex , leaf -> getTexCoord ( vv[j] ) ) ; + + writeInt ( addToVertexPool ( & vert ) * 60 + 8 ) ; + } + + writePop () ; + } + + writePop () ; +} + + +static void writeAnimationGroup () +{ + writeShort ( 2 ) ; + writeShort ( 2+2+8+2+2+4+2+2+2+1+1+4 ) ; + writeString ( "Animate", 8 ) ; /* Name */ + writeShort ( 0 ) ; /* Relative Priority */ + writeShort ( 0 ) ; + writeInt ( 0x40000000 ) ; /* Flags = Animate forwards */ + writeShort ( 100 ) ;/* Special ID 1 */ + writeShort ( 0 ) ; /* Special ID 2 */ + writeShort ( 0 ) ; /* Significance */ + writeChar ( 0 ) ; /* Layer */ + writeChar ( 0 ) ; + writeInt ( 0 ) ; +} + + +static void writeVanillaGroup () +{ + writeShort ( 2 ) ; + writeShort ( 2+2+8+2+2+4+2+2+2+1+1+4 ) ; + + static int next_name = 0 ; + char name [ 8 ] ; + sprintf ( name, "Q%d", next_name++ ) ; + writeString ( name, 8 ) ; /* Name */ + writeShort ( 0 ) ; /* Relative Priority */ + writeShort ( 0 ) ; + writeInt ( 0 ) ; /* Flags */ + writeShort ( 0 ) ; /* Special ID 1 */ + writeShort ( 0 ) ; /* Special ID 2 */ + writeShort ( 0 ) ; /* Significance */ + writeChar ( 0 ) ; /* Layer */ + writeChar ( 0 ) ; + writeInt ( 0 ) ; +} + + +static void writeGroup ( ssgBranch *bra ) +{ + writeShort ( 2 ) ; + writeShort ( 2+2+8+2+2+4+2+2+2+1+1+4 ) ; + + static int next_name = 0 ; + char name [ 8 ] ; + sprintf ( name, "G%d", next_name++ ) ; + writeString ( name, 8 ) ; /* Name */ + writeShort ( 0 ) ; /* Relative Priority */ + writeShort ( 0 ) ; + writeInt ( 0 ) ; /* Flags */ + writeShort ( 0 ) ; /* Special ID 1 */ + writeShort ( 0 ) ; /* Special ID 2 */ + writeShort ( 0 ) ; /* Significance */ + writeChar ( 0 ) ; /* Layer */ + writeChar ( 0 ) ; + writeInt ( 0 ) ; +} + + +static void writeEntity ( ssgEntity *ent ) +{ + if ( ent == NULL ) + return ; + + if ( ! ent -> isAKindOf ( ssgTypeBranch () ) ) + { + if ( ent -> isAKindOf ( ssgTypeTween () ) ) + ((ssgTween *)ent) -> setBank ( curr_bank ) ; + + writeLeaf ( (ssgLeaf *)ent ) ; + + if ( ent -> isAKindOf ( ssgTypeTween () ) ) + ((ssgTween *)ent) -> setBank ( 0 ) ; + + return ; + } + + if ( ((ssgBranch *)ent) -> getNumKids() == 0 ) + return ; + + writeGroup ( (ssgBranch *)ent ) ; + writePush () ; + + for ( int i = 0 ; i < ((ssgBranch *)ent) -> getNumKids() ; i++ ) + writeEntity ( ((ssgBranch *)ent) -> getKid ( i ) ) ; + + writePop () ; +} + + +static void collectPalettesFromLeaf ( ssgLeaf *leaf ) +{ + ssgSimpleState *s = (ssgSimpleState *)( leaf -> getState () ) ; + + if ( s != NULL && s -> getTextureFilename () != NULL ) + addToTexturePool ( s -> getTextureFilename () ) ; + + if ( leaf -> isAKindOf ( ssgTypeTween () ) ) + { + ssgTween *t = (ssgTween *) leaf ; + + if ( t -> getNumBanks () > max_bank ) max_bank = t -> getNumBanks () ; + + for ( int i = 0 ; i < t->getNumBanks() ; i++ ) + { + t -> setBank ( i ) ; + + for ( int i = 0 ; i < t -> getNumVertices () ; i++ ) + { + FLT_vertex vert ; + sgCopyVec3 ( vert.vert, t -> getVertex ( i ) ) ; + sgCopyVec3 ( vert.norm, t -> getNormal ( i ) ) ; + sgCopyVec4 ( vert.col , t -> getColour ( i ) ) ; + sgCopyVec2 ( vert.tex , t -> getTexCoord ( i ) ) ; + addToVertexPool ( & vert ) ; + } + } + + t -> setBank ( 0 ) ; + } + + for ( int i = 0 ; i < leaf -> getNumVertices () ; i++ ) + { + FLT_vertex vert ; + sgCopyVec3 ( vert.vert, leaf -> getVertex ( i ) ) ; + sgCopyVec3 ( vert.norm, leaf -> getNormal ( i ) ) ; + sgCopyVec4 ( vert.col , leaf -> getColour ( i ) ) ; + sgCopyVec2 ( vert.tex , leaf -> getTexCoord ( i ) ) ; + addToVertexPool ( & vert ) ; + } +} + +static void collectPalettes ( ssgEntity *ent ) +{ + if ( ent == NULL ) + return ; + + if ( ! ent -> isAKindOf ( ssgTypeBranch () ) ) + { + collectPalettesFromLeaf ( (ssgLeaf *)ent ) ; + return ; + } + + for ( int i = 0 ; i < ((ssgBranch *)ent) -> getNumKids() ; i++ ) + collectPalettes ( ((ssgBranch *)ent) -> getKid ( i ) ) ; +} + +static void writePalettes ( ssgEntity *root ) +{ + int i ; + + max_bank = 1 ; + + collectPalettes ( root ) ; + + for ( i = 0 ; i < texList -> getNumEntities () ; i++ ) + { + writeShort ( 64 ) ; /* Texture Palette */ + writeShort ( 2+2+200+4+4+4 ) ; + writeString ( ((FLT_texture *)(texList -> getEntity ( i )))->fname, 200 ) ; + writeInt ( i ) ; + writeInt ( i % 16 ) ; + writeInt ( i / 16 ) ; + } + + writeShort ( 67 ) ; /* Vertex Palette header. */ + writeShort ( 2+2+4 ) ; + writeInt ( 2+2+4 + + ( 2+2+2+2+8+8+8+4*3+4*2+4+4 ) * vtxList -> getNumEntities () ) ; + + for ( i = 0 ; i < vtxList -> getNumEntities () ; i++ ) + { + FLT_vertex *vv = (FLT_vertex *)(vtxList -> getEntity ( i )) ; + + writeShort ( 70 ) ; /* Vertex Palette with Normal and Texture */ + writeShort ( 2+2+2+2+8+8+8+4*3+4*2+4+4 ) ; + writeShort ( 0 ) ; /* Colour name index */ + writeShort ( 0x1000 ) ; /* Flags == Use Packed Colour */ + writeDouble ( vv -> vert [ 0 ] ) ; + writeDouble ( vv -> vert [ 1 ] ) ; + writeDouble ( vv -> vert [ 2 ] ) ; + writeFloat ( vv -> norm [ 0 ] ) ; + writeFloat ( vv -> norm [ 1 ] ) ; + writeFloat ( vv -> norm [ 2 ] ) ; + writeFloat ( vv -> tex [ 0 ] ) ; + writeFloat ( vv -> tex [ 1 ] ) ; + writeInt ( ( (int) ( vv -> col [ 3 ] * 255.0f ) << 24 ) + + ( (int) ( vv -> col [ 2 ] * 255.0f ) << 16 ) + + ( (int) ( vv -> col [ 1 ] * 255.0f ) << 8 ) + + ( (int) ( vv -> col [ 0 ] * 255.0f ) << 0 ) ) ; + writeInt ( 0 ) ; + } +} + + + +int ssgSaveFLT ( const char *fname, ssgEntity *root ) +{ + if ( ( save_fd = fopen ( fname, "wb") ) == NULL ) + { + fprintf ( stderr, "Could not open file '%s' for writing.\n", fname ) ; + return FALSE ; + } + + writeHeader () ; + + if ( texList == NULL ) texList = new ulList ( 20 ) ; + if ( vtxList == NULL ) vtxList = new ulList ( 200 ) ; + + writePalettes ( root ) ; + + writePush () ; + writeVanillaGroup () ; + writePush () ; + + if ( max_bank > 1 ) + { + writeAnimationGroup () ; + writePush () ; + } + + for ( int i = 0 ; i < max_bank ; i++ ) + { + curr_bank = i ; + writeEntity ( root ) ; + } + + if ( max_bank > 1 ) + writePop () ; + + writePop () ; + writePop () ; + + fclose ( save_fd ) ; + + /* Cleanup */ + +// This block is commented out because it +// seems to screw things up elsewhere. +// However, it needs to be there to avoid +// a memory leak. +// !! DEBUG ME!! +#ifdef PREVENT_MEMORY_LEAK + int max, i ; + + max = texList -> getNumEntities () ; + + for ( i = 0 ; i < max ; i++ ) + delete (FLT_texture *) texList -> getEntity ( i ) ; + delete texList ; + + max = vtxList -> getNumEntities () ; + + for ( i = 0 ; i < max ; i++ ) + delete (FLT_vertex *) vtxList -> getEntity ( i ) ; + delete vtxList ; +#endif + + return TRUE ; +} + + diff --git a/src/ssg/ssgSaveIV.cxx b/src/ssg/ssgSaveIV.cxx new file mode 100644 index 0000000..60e3a66 --- /dev/null +++ b/src/ssg/ssgSaveIV.cxx @@ -0,0 +1,301 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveIV.cxx 2011 2005-01-15 17:59:31Z sjbaker $ +*/ + +// +// IV ( Open Inventor ASCII ) export for SSG/PLIB +// Written by Bram Stolk (bram at sara.nl) +// +// Keep in mind that: +// ssg's Transform's are non-leaf nodes, which correspond to Inventor's +// Transform nodes which are leaf nodes. +// Inventor's Material nodes are scene graph leaf nodes. +// The corresponding State objects from ssg however, are not scene graph nodes. +// +// DONE: +// scene graph hierarchy +// transformation +// states +// colour per vertex +// normal per vertex +// textures +// texture coordinates +// alpha transparency +// +// TODO: +// lights +// rendermodes +// + + +#include + +#include "ssgLocal.h" + + +// auxiliary stuff for indenting the inventor file. +static int indentLevel ; +static void indent ( FILE *f ) +{ + for ( int i=0; igetTransform( m ) ; + fprintf + ( + f, + "MatrixTransform { matrix %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f }\n", + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3] + ) ; +} + + +// ssg leaf nodes contain the geometry, which we will convert to +// Inventor's IndexedFaceSet. +static void writeLeaf ( ssgLeaf *leaf, FILE *f ) +{ + int cnt = leaf->getNumTriangles() ; + int nv = leaf->getNumVertices() ; + int nn = leaf->getNumNormals() ; + int nc = leaf->getNumColours() ; + int nt = leaf->getNumTexCoords() ; + + ssgState *st = leaf->getState() ; + ssgSimpleState *state = 0 ; + char *textureName = 0 ; + int i; + + indent( f ); + fprintf( f, "# %d triangles, %d verts, %d normals, %d colours, %d texturecoords\n", cnt, nv,nn,nc,nt ) ; + + if ( st && st -> isAKindOf ( ssgTypeSimpleState() ) ) + { + state = (ssgSimpleState*) st ; + textureName = state->getTextureFilename(); + } + else + state = NULL ; + + if ( state || nc ) + { + indent( f ) ; + fprintf( f, "Material {\n" ) ; + indentLevel++ ; + + indent( f ) ; + fprintf( f, "diffuseColor " ) ; + if ( nc ) + { + // multiple colour values: colour-per-vertex + fprintf( f, "[ " ) ; + for ( i=0; igetColour( i ) ; + fprintf( f,"%f %f %f, ", col[0],col[1],col[2] ) ; + } + fprintf( f, "]\n" ) ; + } + else + { + // single colour value + float r=0, g=0, b=0; + if ( state ) + { + r = state->diffuse_colour[ 0 ] ; + g = state->diffuse_colour[ 1 ] ; + b = state->diffuse_colour[ 2 ] ; + } + fprintf( f, "%f %f %f\n", r,g,b ) ; + } + if ( state ) + { + indent( f ); + fprintf( f,"ambientColor %f %f %f\n", state->ambient_colour[0], state->ambient_colour[1], state->ambient_colour[2] ) ; + indent( f ); + fprintf( f,"specularColor %f %f %f\n", state->specular_colour[0], state->specular_colour[1], state->specular_colour[2] ) ; + indent( f ); + fprintf( f,"emissiveColor %f %f %f\n", state->emission_colour[0], state->emission_colour[1], state->emission_colour[2] ) ; + indent( f ); + fprintf( f, "shininess %f\n", state->shininess ) ; + if ( state->isEnabled( GL_BLEND ) ) + { + float a = state->diffuse_colour[ 3 ] ; + indent( f ) ; + fprintf( f, "transparency %f\n", 1.0-a ); + } + } + + indentLevel-- ; + indent( f ) ; + fprintf( f, "}\n" ) ; + } + + if ( textureName ) + { + indent( f ) ; + fprintf( f, "Texture2 { filename %c%s%c }\n", '"', textureName, '"' ) ; + } + + if ( nn ) + { + indent( f ) ; + fprintf( f, "Normal {\n") ; + indentLevel++ ; + + indent( f ) ; + fprintf( f, "vector [ " ) ; + for ( i=0; igetNormal( i ) ; + fprintf( f, "%f %f %f, ", nrm[ 0 ], nrm[ 1 ], nrm[ 2 ] ); + } + fprintf( f, " ]\n" ); + indentLevel-- ; + + indent( f ) ; + fprintf( f, "}\n" ) ; + } + + if ( nt && textureName ) + { + indent( f ) ; + fprintf( f, "SoTextureCoordinate2 { point [ " ) ; + for ( i=0; igetTexCoord( i ) ; + fprintf( f, "%f %f, ", t[ 0 ], t[ 1 ] ) ; + } + fprintf( f, " ] }\n" ) ; + } + + indent( f ) ; + fprintf( f, "Coordinate3 { point [ " ) ; + for ( i=0; igetVertex( i ) ; + assert( v ) ; + fprintf( f, "%f %f %f, ", v[ 0 ], v[ 1 ], v[ 2 ] ) ; + } + fprintf( f, " ] }\n" ) ; + + indent( f ) ; + fprintf( f, "IndexedFaceSet {\n" ) ; + + indent( f ) ; + fprintf( f, " coordIndex [ " ) ; + for ( i=0; igetTriangle( i, &idx0, &idx1, &idx2 ) ; + fprintf( f, "%d,%d,%d,-1,", idx0, idx1, idx2 ) ; + } + + fprintf( f, " ]\n" ) ; + indent( f ) ; + fprintf( f, "}\n" ) ; +} + + +// Handles pre-order traversal of the ssg hierarchy. +static void preHandle ( ssgEntity *ent, FILE *f ) +{ + // Put some comments in the inventor output file. + indent( f ) ; + const char *name = ent->getName() ; + fprintf( f, "# %s (%s)\n", ent->getTypeName(), (name)?name:"unnamed") ; + + if ( ent && ent -> isAKindOf ( ssgTypeBranch () ) ) + { + ssgBranch *branch = (ssgBranch*) ent ; + + // ssg's branch nodes are translated to inventor's separator node. + indent( f ) ; + fprintf( f,"Separator {\n" ) ; + indentLevel++ ; + + if ( branch -> isAKindOf ( ssgTypeTransform() ) ) + writeTransform( (ssgTransform*) branch, f ) ; + } + else + writeLeaf( (ssgLeaf*) ent, f ) ; +} + + +// Handles post-order traversal of the ssg hierarchy. +static void postHandle ( ssgEntity *ent, FILE *f ) +{ + if ( ent && ent -> isAKindOf ( ssgTypeBranch() ) ) + { + // For branches, we use IV's Seperator, which we need to close now. + indentLevel-- ; + indent( f ) ; + fprintf( f, "}\n" ) ; + } +} + + +// Traverses ssg hierarchy. +static void walkTree ( ssgEntity *ent, FILE *f ) +{ + preHandle( ent, f ) ; + if ( ent && ent -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *branch = (ssgBranch*) ent ; + + for ( int i=0; igetNumKids(); i++ ) + { + ssgEntity *kid = branch->getKid( i ); + assert( kid ) ; + walkTree( kid, f ) ; + } + } + postHandle( ent, f ) ; +} + + +// Entry point: save ssg hierarchy as an OpenInventor file. +int ssgSaveIV ( const char *filename, ssgEntity *ent ) +{ + FILE *f = fopen( filename, "w" ) ; + if ( !f ) + return FALSE ; + fprintf( f, "#Inventor V2.1 ascii\n" ) ; + fprintf + ( + f, + "#Export from plib version %d.%d.%d\n\n", + PLIB_MAJOR_VERSION, PLIB_MINOR_VERSION, PLIB_TINY_VERSION + ) ; + indentLevel = 0 ; + walkTree( ent, f ) ; + return TRUE ; +} + diff --git a/src/ssg/ssgSaveM.cxx b/src/ssg/ssgSaveM.cxx new file mode 100644 index 0000000..ee81636 --- /dev/null +++ b/src/ssg/ssgSaveM.cxx @@ -0,0 +1,70 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveM.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + + + +int ssgSaveM( const char* fname, ssgEntity *ent ) { + FILE *fd = fopen ( fname, "w" ) ; + int i; + + if ( fd == NULL ) { + ulSetError ( UL_WARNING, "ssgSaveM: Failed to open '%s' for writing", + fname ); + return FALSE ; + } + + ssgVertexArray* vertices = new ssgVertexArray(); + ssgIndexArray* indices = new ssgIndexArray (); + + fprintf(fd, "# Model output by ssgSaveM. Original graph structure was:\n"); + ent->print(fd, "#", 0); + + sgMat4 ident; + sgMakeIdentMat4( ident ); + ssgAccumVerticesAndFaces( ent, ident, vertices, indices, -1 ); + + for (i = 0; i < vertices->getNum(); i++) { + fprintf(fd, "Vertex %d %f %f %f\n", i+1, + vertices->get(i)[0], + vertices->get(i)[1], + vertices->get(i)[2]); + } + + for (i = 0; i < indices->getNum(); i += 3) { + fprintf(fd, "Face %d %d %d %d\n", (i/3)+1, + *indices->get(i ) + 1, + *indices->get(i + 1) + 1, + *indices->get(i + 2) + 1); + } + + fclose( fd ) ; + + delete vertices; + delete indices ; + + return TRUE; +} diff --git a/src/ssg/ssgSaveOBJ.cxx b/src/ssg/ssgSaveOBJ.cxx new file mode 100644 index 0000000..2f36715 --- /dev/null +++ b/src/ssg/ssgSaveOBJ.cxx @@ -0,0 +1,349 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveOBJ.cxx 2170 2012-08-14 17:51:34Z wolfram_kuss $ +*/ + +// +// Wavefront OBJ export for SSG/PLIB +// Ported from IVCON by Dave McClurg (dpm@efn.org) in March-2000 +// + +#include "ssgLocal.h" + +static FILE *fileout ; +static int total_vert ; +static int total_texture ; +static int total_normal ; +static ssgSimpleStateArray gSSL; + +/* unused +static void MakeIdentifier( char *Dest, const char *Source ) +// pDest is a buffer that is at least of size strlen(pSource)+1 +{ + char *pDest; + const char *pSource; + pDest = Dest; + pSource = Source; + + while ( *pSource != '\0' ) + { + if ((*pSource != '\\') && (*pSource != '.') && (*pSource != '/') && (*pSource != ' ')) + *pDest++ = *pSource++; + else + pSource++; + } + *pDest = '\0'; +}*/ + +static void save_vtx_table ( ssgVtxTable *vt ) +{ + float w = 1.0f ; + short iv1, iv2; + + GLenum mode = vt -> getPrimitiveType () ; + //fprintf ( fileout, "g test\n" ); wk: Pfusch fixme: is this necessary? NIV135 + char *s = vt->getName() ; + fprintf ( fileout, "\ng %s\n\n", s == NULL ? "Object" : s); + + + if (( mode == GL_LINES ) || ( mode == GL_LINE_LOOP) || ( mode == GL_LINE_STRIP)) + { + int num_vert = vt -> getNumVertices () ; + + // V: vertex coordinates. + for ( int i = 0; i < num_vert; i++ ) { + sgVec3 vert ; + sgCopyVec3 ( vert, vt -> getVertex ( i ) ) ; + fprintf ( fileout, "v %f %f %f %f\n", + vert[0], vert[1], vert[2], w ); + } + + + + // L: lines. + fprintf ( fileout, "\n" ); + int num_lines = vt -> getNumLines () ; + for ( int j = 0; j < num_lines; j ++ ) + { vt -> getLine ( j, &iv1, &iv2 ) ; + fprintf ( fileout, "l %d %d\n", + total_vert + j + 1, + total_vert + j + 2 ); + } + + total_vert += num_vert ; + } + else if ( mode == GL_TRIANGLES || + mode == GL_TRIANGLE_FAN || + mode == GL_TRIANGLE_STRIP ) + { + int num_vert = vt -> getNumVertices () ; + bool haveTexture = FALSE; + + ssgSimpleState* st = (ssgSimpleState*) vt->getState(); + if ( st ) + { + if ( NULL == gSSL.findMatch(st, TRUE) ) + gSSL.add(st); + haveTexture = TRUE; + fprintf ( fileout, "usemtl %s\n", st -> getName() ); + } + + // V: vertex coordinates. + for ( int i = 0; i < num_vert; i++ ) { + sgVec3 vert ; + sgCopyVec3 ( vert, vt -> getVertex ( i ) ) ; + fprintf ( fileout, "v %f %f %f %f\n", + vert[0], vert[1], vert[2], w ); + } + + if ( haveTexture ) + // VT: vertex coordinates. + { + long ntc = vt->getNumTexCoords(); + fprintf ( fileout, "\n" ); + //fprintf ( fileout, "# num_vert = %ld, NumTexCoords = %ld\n", (long)num_vert, ntc); + for ( int i = 0; i < num_vert; i++ ) { + sgVec2 tc ; + sgCopyVec2 ( tc, vt -> getTexCoord( i ) ) ; + fprintf ( fileout, "vt %f %f\n", + tc[0], tc[1] ); + } + } + + + // VN: Vertex face normal vectors. + bool haveNormals = ( vt -> getNumNormals () >= num_vert ) ; + if ( haveNormals ) + { + fprintf ( fileout, "\n" ); + for ( int i = 0; i < num_vert; i++ ) { + sgVec3 vert ; + sgCopyVec3 ( vert, vt -> getNormal ( i ) ) ; + fprintf ( fileout, "vn %f %f %f\n", + vert[0], vert[1], vert[2] ); + } + } + + // F: faces. + fprintf ( fileout, "\n" ); + int num_face = vt -> getNumTriangles () ; + for ( int j = 0; j < num_face; j++ ) + { + short face[3]; + vt -> getTriangle ( j, &face[0], &face[1], &face[2] ) ; + + fprintf ( fileout, "f" ); + for ( int ivert = 0; ivert < 3; ivert++ ) { + if ( haveNormals ) + { + if ( haveTexture ) + fprintf ( fileout, " %d/%d/%d", + total_vert + face[ivert] + 1, + total_texture + face[ivert] + 1, + total_normal + face[ivert] + 1 ); + else + fprintf ( fileout, " %d//%d", + total_vert + face[ivert] + 1, + total_normal + face[ivert] + 1 ); + } + else + fprintf ( fileout, " %d", + total_vert + face[ivert] + 1 ); + } + fprintf ( fileout, "\n" ); + } + + total_vert += num_vert ; + if ( haveNormals ) + total_normal += num_vert ; + if ( haveTexture ) + total_texture += num_vert ; + } +} + + +static void save_entities ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + save_entities ( br -> getKid ( i ) ) ; + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { + ssgVtxTable *vt = (ssgVtxTable *) e ; + save_vtx_table ( vt ) ; + } +} + + +/******************************************************************************/ + +int ssgSaveOBJ ( const char *filename, ssgEntity *ent ) + +/******************************************************************************/ + +/* + Purpose: + + OBJ_WRITE writes a Wavefront OBJ file. + + Example: + + # magnolia.obj + + mtllib ./vp.mtl + + g + v -3.269770 -39.572201 0.876128 + v -3.263720 -39.507999 2.160890 + ... + v 0.000000 -9.988540 0.000000 + g stem + s 1 + usemtl brownskn + f 8 9 11 10 + f 12 13 15 14 + ... + f 788 806 774 + + Modified: + + 01 September 1998 + + Author: + + John Burkardt +*/ +{ + fileout = fopen ( filename, SSG_SAVE_TEXT_FILE ) ; + + if ( fileout == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveOBJ: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + +/* + Initialize. +*/ + fprintf ( fileout, "# %s created by SSG.\n", filename ); + fprintf ( fileout, "\n" ); + + + int i; + char *MTL_FileName; + + if( strlen( filename ) > 4 ) + { + // decide on name for the MTL (material) file. Write a reference to the MTL file into the obj file (fileout) + + char *p, *p1; + MTL_FileName = new char[ strlen(filename)+1 ]; + strcpy ( MTL_FileName, filename ); + p = MTL_FileName + strlen ( MTL_FileName ) -4; // should point to '.' now + strcpy (p, ".mtl"); + + // Experiment tells that mtllib may not contain a path. + p = strrchr( MTL_FileName, '\\' ); + p1 = strrchr( MTL_FileName, '/' ); + if (( p == NULL) || (p < p1)) + p = p1; + if ( !p ) // MTL_FileName seems to contain no path + p = MTL_FileName; + else + p++; + + fprintf ( fileout, "mtllib %s\n", p ); // Kludge: Unused if nothing is textured + } + + total_vert = 0 ; + total_texture = 0; + total_normal = 0 ; + + save_entities ( ent ) ; + +/* + Close OBJ. +*/ + fclose ( fileout ) ; + // write MTL file + if ((gSSL.getNum() > 0) && ( strlen(filename) > 4)) + { + // open the file + fileout = fopen ( MTL_FileName, SSG_SAVE_TEXT_FILE ) ; + delete [] MTL_FileName; + + for(i = 0; i < gSSL.getNum(); i++) + { + ssgSimpleState *ss = gSSL.get(i); + fprintf ( fileout, "\n\nnewmtl %s\n", ss->getName()); + + /* From IT5 sample: + newmtl sitzkissen + Ka 0.300000 0.300000 0.300000 + Kd 0.700000 0.700000 0.700000 + Ks 0.735000 0.735000 0.735000 + Ns 20.000000 + Tr 0.000000 + illum 2 + map_Kd chair_demo_texture2.jpg + + */ + fprintf ( fileout, "Ka 0.7 0.7 0.7\n"); + fprintf ( fileout, "Kd 0.3 0.3 0.3\n"); + fprintf ( fileout, "Ks 1 1 1\n"); + fprintf ( fileout, "illum 2\n"); + fprintf ( fileout, "Ns 32\n"); // specular exponent + // Tr 0.000000 + + if (ss -> isEnabled ( GL_TEXTURE_2D ) ) + { + char *TextureName = ss->getTextureFilename(); + char *p = TextureName; + if ((p[0] == '.') && (p[1] == '/')) + p += 2; + //char *p = new char [ strlen(TextureName) + 1 ]; + //MakeIdentifier( p, TextureName ); + fprintf ( fileout, "map_Kd %s\n", p); + //delete [] p; + } + + /*fprintf ( fileout, "map_bump\n"); + fprintf ( fileout, "bump\n"); + fprintf ( fileout, "map_opacity\n"); + fprintf ( fileout, "map_d\n"); + fprintf ( fileout, "refl\n"); + fprintf ( fileout, "map_kS\n"); + fprintf ( fileout, "map_kA\n"); + fprintf ( fileout, "map_Ns\n");*/ + } + // close the MTL + fclose ( fileout ) ; + } + + gSSL.removeAll(); + + return TRUE; +} diff --git a/src/ssg/ssgSaveOFF.cxx b/src/ssg/ssgSaveOFF.cxx new file mode 100644 index 0000000..be63298 --- /dev/null +++ b/src/ssg/ssgSaveOFF.cxx @@ -0,0 +1,79 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveOFF.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ +/* + .off writer for SSG/PLIB + Warning: There are two formats called OFF, see comment in ssgLoadOFF + + Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in February 2001 + + We only support 2D and 3D data +*/ + +#include +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + + + +int ssgSaveOFF( const char* fname, ssgEntity *ent ) { + FILE *fd = fopen ( fname, "w" ) ; + int i; + + if ( fd == NULL ) { + ulSetError ( UL_WARNING, "ssgSaveOFF: Failed to open '%s' for writing", + fname ); + return FALSE ; + } + + ssgVertexArray* vertices = new ssgVertexArray(); + ssgIndexArray* indices = new ssgIndexArray (); + + fprintf(fd, "# Model output by ssgSaveOFF. Original graph structure was:\n"); + ent->print(fd, "#", 0); + + sgMat4 ident; + sgMakeIdentMat4( ident ); + ssgAccumVerticesAndFaces( ent, ident, vertices, indices, -1 ); + + fprintf(fd, "nOFF\n3\n"); // 3 dimensions + fprintf(fd, "%d %d 0\n", vertices->getNum(), indices->getNum()/3 ); + + for (i = 0; i < vertices->getNum(); i++) { + fprintf(fd, "%f %f %f\n", vertices->get(i)[0], + vertices->get(i)[1], + vertices->get(i)[2]); + } + + for (i = 0; i < indices->getNum(); i += 3) { + fprintf(fd, "3 %d %d %d\n", *indices->get(i ) , + *indices->get(i + 1) , + *indices->get(i + 2) ); + } + + fclose( fd ) ; + + delete vertices; + delete indices ; + + return TRUE; +} diff --git a/src/ssg/ssgSaveQHI.cxx b/src/ssg/ssgSaveQHI.cxx new file mode 100644 index 0000000..aca69e7 --- /dev/null +++ b/src/ssg/ssgSaveQHI.cxx @@ -0,0 +1,63 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveQHI.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + + + +int ssgSaveQHI( const char* fname, ssgEntity *ent ) { + FILE *fd = fopen ( fname, "w" ) ; + int i; + + if ( fd == NULL ) { + ulSetError ( UL_WARNING, "ssgSaveQHI: Failed to open '%s' for writing", + fname ); + return FALSE ; + } + + ssgVertexArray* vertices = new ssgVertexArray(); + + + sgMat4 ident; + sgMakeIdentMat4( ident ); + ssgAccumVerticesAndFaces( ent, ident, vertices, NULL, 0.0001f ); + + fprintf(fd, "3\n"); // Dimension + fprintf(fd, "%d\n", vertices->getNum()); // No of points + + // Points: + for (i = 0; i < vertices->getNum(); i++) { + fprintf(fd, "%f %f %f\n", vertices->get(i)[0], + vertices->get(i)[1], + vertices->get(i)[2]); + } + + + fclose( fd ) ; + + delete vertices; + + return TRUE; +} diff --git a/src/ssg/ssgSaveTRI.cxx b/src/ssg/ssgSaveTRI.cxx new file mode 100644 index 0000000..40b9bca --- /dev/null +++ b/src/ssg/ssgSaveTRI.cxx @@ -0,0 +1,111 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveTRI.cxx 2169 2012-08-14 17:28:50Z wolfram_kuss $ +*/ + +// +// TRI ( AC3D triangle file ) export for SSG/PLIB +// Written by Dave McClurg (dpm@efn.org) in March-2000 +// + +#include "ssgLocal.h" + +static FILE *fileout ; + + +static void save_vtx_table ( ssgVtxTable *vt ) +{ + GLenum mode = vt -> getPrimitiveType () ; + if ( mode == GL_TRIANGLES || + mode == GL_TRIANGLE_FAN || + mode == GL_TRIANGLE_STRIP ) + { + int num_tri = vt -> getNumTriangles () ; + for ( int i = 0; i < num_tri; i++ ) + { + short tri[3]; + vt -> getTriangle ( i, &tri[0], &tri[1], &tri[2] ) ; + + for ( int j = 0; j < 3; j ++ ) + { + sgVec3 vert; + sgCopyVec3 ( vert, vt->getVertex ( tri[j] ) ) ; + fprintf ( fileout, "%f %f %f ", vert[0], vert[1], vert[2] ) ; + } + + fprintf ( fileout, "0xFFFFFF\n" ) ; + } + } +} + + +static void save_entities ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + save_entities ( br -> getKid ( i ) ) ; + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { + ssgVtxTable *vt = (ssgVtxTable *) e ; + save_vtx_table ( vt ) ; + } +} + + +/******************************************************************************/ + +int ssgSaveTRI ( const char *filename, ssgEntity *ent ) + +/******************************************************************************/ + +/* + Purpose: + + writes an AC3D triangle file. + + Example: + + Each line contains 9 floating point values and a 1 hex value for color. + the 9 floating point values represent 3 vertices of a triangle + the color format is 0xRRGGBB (eg 0xffffff is white) + + 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0xffffff + +*/ +{ + fileout = fopen ( filename, SSG_SAVE_TEXT_FILE ) ; + + if ( fileout == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveTRI: Failed to open '%s' for writing", filename ) ; + return FALSE ; + } + + save_entities ( ent ) ; + + fclose ( fileout ) ; + return TRUE; +} diff --git a/src/ssg/ssgSaveVRML1.cxx b/src/ssg/ssgSaveVRML1.cxx new file mode 100644 index 0000000..b5a37d5 --- /dev/null +++ b/src/ssg/ssgSaveVRML1.cxx @@ -0,0 +1,449 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveVRML1.cxx 1568 2002-09-02 06:05:49Z sjbaker $ + + -------------------------------------------------------------------- + + This save routine was written by Warren Wilbur to support a sub-set + of the standard Virtual Reality Modelling Language v1.0 (i.e. VRML1.0). + The Version 1.0 Specification, 9-Nov-95 was used and should be available + at www.vrml.org + + When reporting bugs/difficulties please mention 'VRML' in the subject + of your email and post to the plib developers mailing list. +*/ + +#include +#include "ssgLocal.h" +#include "ssgLoaderWriterStuff.h" + +/* Function name: SaveVRML1MaterialNode + * + * Limitations: Before calling this function you must verify that + * at least one material exists whose textureName + * matches the one passed in to this function, + * otherwise you might save a empty Material node! + * + * Notes: Saving empty Material notes will create an + * unnecessary choking hazard for VRML loaders :( + */ + +static void SaveVRML1MaterialNode(FILE *fd, ssgIndexArray *materials_ptr, + ssgSimpleStateArray *ssa_ptr, + char *textureName, bool saveDiffuse, + bool saveAmbient, bool saveEmission, + bool saveSpecular) +{ + ssgSimpleState *ss_ptr; + int i; + + /* Tell VRML loaders that we are providing a single color for each + * face in the array of indices written below. */ + + fprintf(fd, " MaterialBinding { value PER_FACE }\n"); + fprintf(fd, " Material {\n"); + + if (saveDiffuse) + { + fprintf(fd, " diffuseColor [\n"); + for (i = 0; i < materials_ptr->getNum(); i++) + { + ss_ptr = ssa_ptr->get(*(materials_ptr->get(i))); + + /* If we are trying to save all untextured materials then check + * if either ptr is NULL */ + + if ( ( (textureName == NULL)&& + ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )|| + + /* If we are trying to save all materials that are textured by + * a specific texture then check if the texture filename matches */ + + ( (textureName != NULL)&&(ss_ptr != NULL)&& + (ss_ptr->getTextureFilename() != NULL)&& + (!strcmp(textureName, ss_ptr->getTextureFilename())) ) ) + { + float diffuse0, diffuse1, diffuse2; + + diffuse0 = ss_ptr->diffuse_colour[0]; + diffuse1 = ss_ptr->diffuse_colour[1]; + diffuse2 = ss_ptr->diffuse_colour[2]; + +#ifdef EXPERIMENTAL_ADD_AMBIENT_TO_DIFFUSE + diffuse0 += ss_ptr->ambient_colour[0]; + diffuse1 += ss_ptr->ambient_colour[1]; + diffuse2 += ss_ptr->ambient_colour[2]; +#endif //EXPERIMENTAL_ADD_AMBIENT_TO_DIFFUSE + +#ifdef EXPERIMENTAL_ADD_EMISSION_TO_DIFFUSE + diffuse0 += ss_ptr->emission_colour[0]; + diffuse1 += ss_ptr->emission_colour[1]; + diffuse2 += ss_ptr->emission_colour[2]; +#endif //EXPERIMENTAL_ADD_EMISSION_TO_DIFFUSE + +#ifdef EXPERIMENTAL_ADD_SPECULAR_TO_DIFFUSE + diffuse0 += ss_ptr->specular_colour[0]; + diffuse1 += ss_ptr->specular_colour[1]; + diffuse2 += ss_ptr->specular_colour[2]; +#endif //EXPERIMENTAL_ADD_SPECULAR_TO_DIFFUSE + + /* OpenGL caps the maximum RGB value for a colour to 1.0 when it + * calculates colours in a scene. If we don't cap the value OpenGL + * will do it for us. */ + + fprintf(fd, " %f %f %f,\n", diffuse0 > 1.0 ? 1.0:diffuse0, + diffuse1 > 1.0 ? 1.0:diffuse1, diffuse2 > 1.0 ? 1.0:diffuse2); + } + } + fprintf(fd, " ]\n"); //close diffuseColor array + } + + if (saveAmbient) + { + fprintf(fd, " ambientColor [\n"); + for (i = 0; i < materials_ptr->getNum(); i++) + { + ss_ptr = ssa_ptr->get(*(materials_ptr->get(i))); + + /* If we are trying to save all untextured materials then check + * if either ptr is NULL */ + + if ( ( (textureName == NULL)&& + ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )|| + + /* If we are trying to save all materials that are textured by + * a specific texture then check if the texture filename matches */ + + ( (textureName != NULL)&&(ss_ptr != NULL)&& + (ss_ptr->getTextureFilename() != NULL)&& + (!strcmp(textureName, ss_ptr->getTextureFilename())) ) ) + { + fprintf(fd, " %f %f %f,\n", ss_ptr->ambient_colour[0], + ss_ptr->ambient_colour[1], ss_ptr->ambient_colour[2]); + } + fprintf(fd, " ]\n"); //close ambientColor array + } + } + + if(saveEmission) + { + fprintf(fd, " emissiveColor [\n"); + for (i = 0; i < materials_ptr->getNum(); i++) + { + ss_ptr = ssa_ptr->get(*(materials_ptr->get(i))); + + /* If we are trying to save all untextured materials then check + * if either ptr is NULL */ + + if ( ( (textureName == NULL)&& + ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )|| + + /* If we are trying to save all materials that are textured by + * a specific texture then check if the texture filename matches */ + + ( (textureName != NULL)&&(ss_ptr != NULL)&& + (ss_ptr->getTextureFilename() != NULL)&& + (!strcmp(textureName, ss_ptr->getTextureFilename())) ) ) + { + fprintf(fd, " %f %f %f,\n", ss_ptr->emission_colour[0], + ss_ptr->emission_colour[1], ss_ptr->emission_colour[2]); + } + fprintf(fd, " ]\n"); //close emissionColor array + } + } + + if(saveSpecular) + { + fprintf(fd, " specularColor [\n"); + for (i = 0; i < materials_ptr->getNum(); i++) + { + ss_ptr = ssa_ptr->get(*(materials_ptr->get(i))); + + /* If we are trying to save all untextured materials then check + * if either ptr is NULL */ + + if ( ( (textureName == NULL)&& + ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )|| + + /* If we are trying to save all materials that are textured by + * a specific texture then check if the texture filename matches */ + + ( (textureName != NULL)&&(ss_ptr != NULL)&& + (ss_ptr->getTextureFilename() != NULL)&& + (!strcmp(textureName, ss_ptr->getTextureFilename())) ) ) + { + fprintf(fd, " %f %f %f,\n", ss_ptr->specular_colour[0], + ss_ptr->specular_colour[1], ss_ptr->specular_colour[2]); + } + fprintf(fd, " ]\n"); //close specularColor array + } + } + + fprintf(fd, " }\n"); //close Material node + return; +} + +/* The 'main' entry point for saving a model in VRML1.0 */ + +int ssgSaveVRML1( const char* fname, ssgEntity *ent ) { + ssgVertexArray *vertices_ptr; + ssgIndexArray *indices_ptr; + FILE *fd; + ssgSimpleStateArray ssa; + ssgTexCoordArray *texcoord_ptr; + ssgIndexArray *materials_ptr; + bool textured_faces_found, untextured_faces_found, + textureFacesAlreadySaved; + int i, j, index1, index2, index3; + ssgSimpleState *ss_ptr, *ss_ptr2; + + fd = fopen ( fname, "w" ) ; + if ( fd == NULL ) + { + ulSetError ( UL_WARNING, "ssgSaveVRML1: Failed to open '%s' for writing", + fname ); + return FALSE ; + } + + vertices_ptr = new ssgVertexArray(); + indices_ptr = new ssgIndexArray(); + materials_ptr = new ssgIndexArray(); + texcoord_ptr = new ssgTexCoordArray(); + + sgMat4 ident; + sgMakeIdentMat4( ident ); + ssgAccumVerticesAndFaces( ent, ident, vertices_ptr, indices_ptr, -1.0f, + &ssa, materials_ptr, texcoord_ptr); + + /* The spec requires every file to begin with these characters */ + + fprintf(fd, "#VRML V1.0 ascii\n\n"); + + /* Since a VRML file contains only one parent node we must use a + * node type that can have several child 'nodes' so we can save + * the materials, texture coordinates, vertices, and indices each + * as (seperate) child nodes. */ + + fprintf(fd, "Separator {\n"); + + /* Save all the individual vertices used in the model. It doesn't + * matter if there are duplicates... */ + + fprintf(fd, " Coordinate3 {\n point [\n"); + + for (i = 0; i < vertices_ptr->getNum(); i++) + { + fprintf(fd, " %f %f %f,\n", vertices_ptr->get(i)[0], + vertices_ptr->get(i)[1], vertices_ptr->get(i)[2]); + } + + fprintf(fd, " ]\n }\n"); //close point array and + //Coordinate3 + + /* Chcck if the model is textured at all. This test will help us parse + * out how to save the model since it may be totally textured, partially + * textured, or not textured at all. */ + + textured_faces_found = false; + untextured_faces_found = false; + for (i = 0; i < materials_ptr->getNum(); i++) + { + ss_ptr = ssa.get(*(materials_ptr->get(i))); + if ( (ss_ptr != NULL)&&(ss_ptr->getTextureFilename() != NULL) ) + { + textured_faces_found = true; + } + else + { + untextured_faces_found = true; + } + } + + if (untextured_faces_found) + { + /* Save all the material node fields which VRML supports. Note that the + * VRML spec discourages complicated uses of the Material Node. We + * cannot expect VRML implementations to support the full syntax + * of the Material Node including ambient, diffuse, specular, emissive, + * shininess, and transparency. We should be always be okay if we just + * use diffuse. */ + + SaveVRML1MaterialNode(fd, materials_ptr, &ssa, NULL, + true, false, false, false); + + /* Save all faces that are not textured in a single IndexedFaceSet node */ + + fprintf(fd, " IndexedFaceSet {\n coordIndex [\n"); + for (i = 0; i < indices_ptr->getNum(); i+=3) + { + ss_ptr = ssa.get(*(materials_ptr->get(i/3))); + + /* Make sure this face doesn't have a texture associated with it */ + + if ( (ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL) ) + { + index1 = *indices_ptr->get(i); + index2 = *indices_ptr->get(i+1); + index3 = *indices_ptr->get(i+2); + + /* Check for index overflow since PLIB stores it as a short */ + + if ( (index1 < 0)||(index2 < 0)||(index3 < 0) ) + { + ulSetError(UL_WARNING, "ssgSaveVRML1: Save error: index overflow, " + "value won't fit in 16bits."); + } + else + { + fprintf(fd, " %d, %d, %d, -1,\n", index1, index2, + index3); + } + } + } + fprintf(fd, " ]\n }\n"); //close coordIndex array and + //IndexedFaceSet + } + + if (textured_faces_found) + { + /* Save all texture coordinates (per-vertex) for all the textures in + * the model. It doesn't matter if there is one texture or more than + * one since we will specify which texture to use with the coordinates + * before saving the portion of the indexed face set which uses that + * texture. */ + + fprintf(fd, " TextureCoordinate2 {\n point [\n"); + for (i = 0; i < texcoord_ptr->getNum(); i++) + { + +/* In VrmlView Pro 3.0 (Linux) textured models appear correct Left-Right + * but the texture is reversed Top-Bottom. Enabling the INVERSE_REPEAT + * macro fixes the problem for VrmlView. I don't want to enable this until + * I figure out where the problem really is!? */ + +//#define INVERSE_REPEAT(a) (a > 0.0 ? 1.0 - a:a + 1.0) +#define INVERSE_REPEAT(a) a + + fprintf(fd, " %f %f,\n", texcoord_ptr->get(i)[0], + INVERSE_REPEAT(texcoord_ptr->get(i)[1])); + } + fprintf(fd, " ]\n }\n"); + + /* Now save separate Texture2 and IndexedFaceSet node pairs for each + * texture used in the model. Each of the IndexedFaceSet(s) will + * reference back to the initial vertices, materials, and texture + * coordinates (due to the lack of Seperator nodes in between). + * Find the first textured face starting at the i'th face. In this + * manner we will find the next texture used in the model and save + * all faces that are textured with it. */ + + for (i = 0; i < indices_ptr->getNum(); i+=3) + { + ss_ptr = ssa.get(*(materials_ptr->get(i/3))); + if ( (ss_ptr != NULL)&&(ss_ptr->getTextureFilename() != NULL) ) + { + /* We've found the next textured face. Since we save all + * faces using a texture when we find the first face using + * that texture we must check if the faces for this texture + * have already been saved. If we can find a face using + * this texture earlier in the list of faces then we know + * that it has already been saved. */ + + textureFacesAlreadySaved = false; + for (j = 0; j < i; j+=3) + { + ss_ptr2 = ssa.get(*(materials_ptr->get(j/3))); + if ( (ss_ptr2 != NULL)&& + (ss_ptr2->getTextureFilename() != NULL)&& + (!strcmp(ss_ptr->getTextureFilename(), + ss_ptr2->getTextureFilename())) ) + { + textureFacesAlreadySaved = true; + break; + } + } + + if (!textureFacesAlreadySaved) + { + fprintf(fd, " Texture2 {\n"); + fprintf(fd, " filename %s\n", ss_ptr->getTextureFilename()); +//TODO: support CLAMP mode as well. + fprintf(fd, " wrapS REPEAT\n"); + fprintf(fd, " wrapT REPEAT\n"); + fprintf(fd, " }\n"); + + /* Save all the materials needed by this following indexed face + * set. This will save all materials that have the same texture + * filename specified below. */ + + SaveVRML1MaterialNode(fd, materials_ptr, &ssa, + ss_ptr->getTextureFilename(), + true, false, false, false); + + fprintf(fd, " IndexedFaceSet {\n coordIndex [\n"); + for (j = i; j < indices_ptr->getNum(); j+=3) + { + /* Save each face which is textured by the Texture2 node defined + * above. */ + + ss_ptr2 = ssa.get(*(materials_ptr->get(j/3))); + if ( (ss_ptr2 != NULL)&& + (ss_ptr2->getTextureFilename() != NULL)&& + (!strcmp(ss_ptr->getTextureFilename(), + ss_ptr2->getTextureFilename())) ) + { + index1 = *indices_ptr->get(j); + index2 = *indices_ptr->get(j+1); + index3 = *indices_ptr->get(j+2); + + /* Check for index overflow since PLIB stores it as a + * short */ + + if ( (index1 < 0)||(index2 < 0)||(index3 < 0) ) + { + ulSetError(UL_WARNING, "ssgSaveVRML1: Save error: index " + "overflow, value won't fit in 16bits."); + } + else + { + fprintf(fd, " %d, %d, %d, -1,\n", + index1, index2, index3); + } + } + } + fprintf(fd, " ]\n }\n"); //close coordIndex array and + //IndexedFaceSet + } + } + } + } + + fprintf(fd, "}\n"); //close Seperator + fclose( fd ) ; + + delete vertices_ptr; + delete indices_ptr; + delete materials_ptr; + delete texcoord_ptr; + return TRUE; +} + + diff --git a/src/ssg/ssgSaveX.cxx b/src/ssg/ssgSaveX.cxx new file mode 100644 index 0000000..e712d5d --- /dev/null +++ b/src/ssg/ssgSaveX.cxx @@ -0,0 +1,39 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSaveX.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +// +// .X loader for SSG/PLIB +// .X is the 3D file format for Micro$ofts directX retained mode. +// Written by Wolfram Kuss (Wolfram.Kuss@t-online.de) in Oct/Nov of 2000 +// + +#include "ssgLocal.h" + + + +int ssgSaveX ( const char *filename, ssgEntity *ent ) +{ + ulSetError ( UL_WARNING, "ssgSaveX: Not implemented yet" ) ; + + return FALSE; +} diff --git a/src/ssg/ssgSelector.cxx b/src/ssg/ssgSelector.cxx new file mode 100644 index 0000000..bf1f308 --- /dev/null +++ b/src/ssg/ssgSelector.cxx @@ -0,0 +1,172 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSelector.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgSelector::copy_from ( ssgSelector *src, int clone_flags ) +{ + ssgBranch::copy_from ( src, clone_flags ) ; + + max_kids = src -> max_kids ; + selection = new unsigned char [ max_kids ] ; + memcpy ( selection, src -> selection, max_kids ) ; +} + + +ssgBase *ssgSelector::clone ( int clone_flags ) +{ + ssgSelector *b = new ssgSelector ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgSelector::ssgSelector ( int _max_kids ) +{ + type = ssgTypeSelector () ; + + max_kids = _max_kids ; + selection = new unsigned char [ max_kids ] ; + memset ( selection, 1, max_kids ) ; +} + +ssgSelector::~ssgSelector (void) +{ + delete [] selection ; +} + +void ssgSelector::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + int s = 0 ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid(), s++ ) + if ( selection [s] ) + e -> cull ( f, m, cull_result != SSG_INSIDE ) ; + + postTravTests ( SSGTRAV_CULL ) ; +} + +void ssgSelector::hot ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_HOT ) ) + return ; + + int hot_result = hot_test ( sp, m, test_needed ) ; + + if ( hot_result == SSG_OUTSIDE ) + return ; + + int s = 0 ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid(), s++ ) + if ( selection [s] ) + e -> hot ( sp, m, hot_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_HOT ) ; +} + + +void ssgSelector::los ( sgVec3 sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_LOS ) ) + return ; + + int los_result = los_test ( sp, m, test_needed ) ; + + if ( los_result == SSG_OUTSIDE ) + return ; + + int s = 0 ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid(), s++ ) + if ( selection [s] ) + e -> los ( sp, m, los_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_LOS ) ; +} + + +void ssgSelector::isect ( sgSphere *sp, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_ISECT ) ) + return ; + + int isect_result = isect_test ( sp, m, test_needed ) ; + + if ( isect_result == SSG_OUTSIDE ) + return ; + + int s = 0 ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid(), s++ ) + if ( selection [s] ) + e -> isect ( sp, m, isect_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_ISECT ) ; +} + + +int ssgSelector::load ( FILE *fd ) +{ + _ssgReadInt ( fd, & max_kids ) ; + delete [] selection ; + selection = new unsigned char [ max_kids ] ; + for ( int i=0; i getSizeOf () ; + total = src -> getNum () ; + limit = total ; + list = new char [ limit * size_of ] ; + own_mem = true ; + memcpy ( list, src->raw_get ( 0 ), limit * size_of ) ; +} + +ssgBase *ssgSimpleList::clone ( int clone_flags ) +{ + ssgSimpleList *b = new ssgSimpleList () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + +ssgBase *ssgNormalArray::clone ( int clone_flags ) +{ + ssgNormalArray *b = new ssgNormalArray () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + +ssgBase *ssgVertexArray::clone ( int clone_flags ) +{ + ssgVertexArray *b = new ssgVertexArray () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + +ssgBase *ssgTexCoordArray::clone ( int clone_flags ) +{ + ssgTexCoordArray *b = new ssgTexCoordArray () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + +ssgBase *ssgColourArray::clone ( int clone_flags ) +{ + ssgColourArray *b = new ssgColourArray () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + +ssgBase *ssgIndexArray::clone ( int clone_flags ) +{ + ssgIndexArray *b = new ssgIndexArray () ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + + + +void ssgVertexArray::print ( FILE *fd, char *indent, int how_much ) +{ + ssgSimpleList::print ( fd, indent, how_much ) ; + + if ( how_much < 4 ) + return; + + for ( unsigned int i = 0 ; i < total ; i++ ) + fprintf ( fd, "%s V%d) { %f, %f, %f }\n", indent, i, + get(i)[0], get(i)[1], get(i)[2] ) ; +} + + + + +void ssgNormalArray::print ( FILE *fd, char *indent, int how_much ) +{ + ssgSimpleList::print ( fd, indent, how_much ) ; + + if ( how_much < 4 ) + return; + + for ( unsigned int i = 0 ; i < total ; i++ ) + fprintf ( fd, "%s N%d) { %f, %f, %f }\n", indent, i, + get(i)[0], get(i)[1], get(i)[2] ) ; +} + + + +void ssgIndexArray::print ( FILE *fd, char *indent, int how_much ) +{ + ssgSimpleList::print ( fd, indent, how_much ) ; + + if ( how_much < 4 ) + return; + + for ( unsigned int i = 0 ; i < total ; i++ ) + fprintf ( fd, "%s I%d) { %d }\n", indent, i, + (int) (*get(i)) ) ; +} + +void ssgTexCoordArray::print ( FILE *fd, char *indent, int how_much ) +{ + ssgSimpleList::print ( fd, indent, how_much ) ; + + if ( how_much < 4 ) + return; + + for ( unsigned int i = 0 ; i < total ; i++ ) + fprintf ( fd, "%s T%d) { S=%f, T=%f }\n", indent, i, + get(i)[0], get(i)[1] ) ; +} + + + + +void ssgColourArray::print ( FILE *fd, char *indent, int how_much ) +{ + ssgSimpleList::print ( fd, indent, how_much ) ; + + if ( how_much < 4 ) + return; + + for ( unsigned int i = 0 ; i < total ; i++ ) + fprintf ( fd, "%s C%d) { R=%f, G=%f, B=%f, A=%f }\n", indent, i, + get(i)[0], get(i)[1], get(i)[2], get(i)[3] ) ; +} + + + +void ssgSimpleList::print ( FILE *fd, char *indent, int how_much ) +{ + ssgBase::print ( fd, indent, how_much ) ; + + fprintf ( fd, "%s Total # items = %d\n", indent, total ) ; + + if ( how_much < 3 ) + return; + + fprintf ( fd, "%s Size of items = %d bytes\n", indent, size_of ) ; +} + + + +int ssgSimpleList::load ( FILE *fd ) +{ + delete [] list ; + _ssgReadUInt ( fd, &size_of ) ; + _ssgReadUInt ( fd, &total ) ; + limit = total ; + list = new char [ limit * size_of ] ; + assert(list!=NULL); + // wk: The old code: + //_ssgReadFloat ( fd, limit * size_of / sizeof(float), (float *)list ) ; + // doesn't work since some ssgSimpleLists consist of shorts, so + // limit * size_of may not be divisible by sizeof(float). + // The new code works, but I am not 100% sure what we want in + // the event that there are machines with another sizeof(float). + _ssgReadBytes ( fd, limit * size_of , list) ; + + return ! _ssgReadError () ; +} + + + +int ssgSimpleList::save ( FILE *fd ) +{ + _ssgWriteUInt ( fd, size_of ) ; + _ssgWriteUInt ( fd, total ) ; + // see comment in ssgSimpleList::load + // _ssgWriteFloat( fd, total * size_of / sizeof(float), (float *)list ) ; + _ssgWriteBytes ( fd, total * size_of, list); + return ! _ssgWriteError () ; +} + + diff --git a/src/ssg/ssgSimpleState.cxx b/src/ssg/ssgSimpleState.cxx new file mode 100644 index 0000000..514cf46 --- /dev/null +++ b/src/ssg/ssgSimpleState.cxx @@ -0,0 +1,649 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgSimpleState.cxx 2167 2012-08-14 17:24:50Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +void ssgSimpleState::copy_from ( ssgSimpleState *src, int clone_flags ) +{ + ssgState::copy_from ( src, clone_flags ) ; + + ssgTexture *tex = src -> getTexture () ; + + ssgDeRefDelete(texture); + + if ( tex != NULL && ( clone_flags & SSG_CLONE_TEXTURE ) ) + texture = (ssgTexture *)( tex -> clone ( clone_flags ) ) ; + else + texture = tex ; + + if (texture != NULL) + texture->ref(); + + dont_care = src -> dont_care ; + enables = src -> enables ; + + colour_material_mode = src -> colour_material_mode ; + + sgCopyVec4 ( specular_colour, src -> specular_colour ) ; + sgCopyVec4 ( emission_colour, src -> emission_colour ) ; + sgCopyVec4 ( ambient_colour, src -> ambient_colour ) ; + sgCopyVec4 ( diffuse_colour, src -> diffuse_colour ) ; + + shade_model = src -> shade_model ; + shininess = src -> shininess ; + alpha_clamp = src -> alpha_clamp ; +} + +ssgBase *ssgSimpleState::clone ( int clone_flags ) +{ + ssgSimpleState *b = new ssgSimpleState ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +void _ssgForceLineState () +{ + _ssgCurrentContext->getState()->enables &= ~((1<getState()->enables & 0x3F ; + (*(__ssgDisableTable[turn_off]))() ; + _ssgCurrentContext->getState()->enables &= ~turn_off ; + + if ( ~ dont_care & ( (1<getState()->colour_material_mode != colour_material_mode ) + { + glColorMaterial ( GL_FRONT_AND_BACK, (GLenum) colour_material_mode ) ; + _ssgCurrentContext->getState()->colour_material_mode = colour_material_mode ; + indeterminate |= _ssgCurrentContext->getState()->enables & (1<getState()->specular_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec3 ( _ssgCurrentContext->getState()->emission_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec3 ( _ssgCurrentContext->getState()->ambient_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec4 ( _ssgCurrentContext->getState()->diffuse_colour, -1.0f, -1.0f, -1.0f, -1.0f ) ; + } + } + + if ( ~ dont_care & ( (1<getState()->shininess != shininess ) + { + glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, shininess ) ; + _ssgCurrentContext->getState()->shininess = shininess ; + } + + if ( ( ~ dont_care & (1<getState()->specular_colour, specular_colour ) ) + { + glMaterialfv ( GL_FRONT_AND_BACK, GL_SPECULAR, specular_colour ) ; + sgCopyVec3 ( _ssgCurrentContext->getState()->specular_colour, specular_colour ) ; + } + + if ( ( ~ dont_care & (1<getState()->emission_colour, emission_colour ) ) + { + glMaterialfv ( GL_FRONT_AND_BACK, GL_EMISSION, emission_colour ) ; + sgCopyVec3 ( _ssgCurrentContext->getState()->emission_colour, emission_colour ) ; + } + + if ( ( ~ dont_care & (1<getState()->ambient_colour, ambient_colour ) ) + { + glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, ambient_colour ) ; + sgCopyVec3 ( _ssgCurrentContext->getState()->ambient_colour, ambient_colour ) ; + } + + if ( ( ~ dont_care & (1<getState()->diffuse_colour, diffuse_colour ) ) + { + glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse_colour ) ; + sgCopyVec4 ( _ssgCurrentContext->getState()->diffuse_colour, diffuse_colour ) ; + } + } + + if ( ( ~ dont_care & (1<getState()->getTexture() != getTexture() ) + { + stats_bind_textures++ ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, getTextureHandle() ) ; +#else + /* For ancient SGI machines */ + glBindTextureEXT ( GL_TEXTURE_2D, getTextureHandle() ) ; +#endif + _ssgCurrentContext->getState()->setTexture ( getTexture () ) ; + } + + if ( ( ~ dont_care & (1<getState()->shade_model != shade_model ) + { + glShadeModel ( shade_model ) ; + _ssgCurrentContext->getState()->shade_model = shade_model ; + } + + if ( ( ~ dont_care & (1<getState()->alpha_clamp != alpha_clamp ) + { + glAlphaFunc ( GL_GREATER, alpha_clamp ) ; + _ssgCurrentContext->getState()->alpha_clamp = alpha_clamp ; + } + + int turn_on = ~dont_care & enables & ~_ssgCurrentContext->getState()->enables & 0x3F ; + (*(__ssgEnableTable [turn_on ]))() ; + _ssgCurrentContext->getState()->enables |= turn_on ; + + preDraw () ; +} + +void ssgSimpleState::force (void) +{ + preApply () ; + int turn_off = ~dont_care & ~enables & 0x3F ; + (*(__ssgDisableTable[turn_off]))() ; + _ssgCurrentContext->getState()->enables &= ~turn_off ; + + /* invalidate the material colours, since we do not know what the current mode is */ + sgSetVec3 ( _ssgCurrentContext->getState()->specular_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec3 ( _ssgCurrentContext->getState()->emission_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec3 ( _ssgCurrentContext->getState()->ambient_colour, -1.0f, -1.0f, -1.0f ) ; + sgSetVec4 ( _ssgCurrentContext->getState()->diffuse_colour, -1.0f, -1.0f, -1.0f, -1.0f ) ; + + if ( ~ dont_care & ( (1<getState()->colour_material_mode = colour_material_mode ; + } + + if ( ~ dont_care & (1<getState()->shininess = shininess ; + } + + if ( ~ dont_care & (1<getState()->diffuse_colour, diffuse_colour ) ; + } + + if ( ~ dont_care & (1<getState()->emission_colour, emission_colour ) ; + } + + if ( ~ dont_care & (1<getState()->ambient_colour, ambient_colour ) ; + } + + if ( ~ dont_care & (1<getState()->specular_colour, specular_colour ) ; + } + } + + if ( ~ dont_care & (1<getState()->setTexture ( getTexture () ) ; + } + + if ( ~ dont_care & (1<getState()->shade_model = shade_model ; + } + + if ( ~ dont_care & (1<getState()->alpha_clamp = alpha_clamp ; + } + + int turn_on = ~dont_care & enables & 0x3F ; + (*(__ssgEnableTable [turn_on ]))() ; + _ssgCurrentContext->getState()->enables |= turn_on ; + preDraw () ; +} + + + +int ssgSimpleState::isEnabled ( GLenum mode ) +{ + switch ( mode ) + { + case GL_TEXTURE_2D : + return enables & (1< ref () ; + + return ssgState::load(fd) ; +} + + +int ssgSimpleState::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, dont_care ) ; + _ssgWriteInt ( fd, enables ) ; + _ssgWriteInt ( fd, colour_material_mode ) ; + _ssgWriteVec4 ( fd, specular_colour ) ; + _ssgWriteVec4 ( fd, emission_colour ) ; + _ssgWriteVec4 ( fd, ambient_colour ) ; + _ssgWriteVec4 ( fd, diffuse_colour ) ; + _ssgWriteInt ( fd, (int) shade_model ) ; + _ssgWriteFloat ( fd, shininess ) ; + _ssgWriteFloat ( fd, alpha_clamp ) ; + + if ( ! _ssgSaveObject ( fd, texture ) ) + return FALSE ; + + return ssgState::save(fd) ; +} + +/* +wk: not implemented yet +Is ssgSimpleState the right place to do this? +use gkGetTexParamiv or so. + +int ssgSimpleState::getWrapU() +{ + int wrapu = TRUE, wrapv = TRUE ; + + if ( filename == NULL ) + return TRUE; + if ( filename[0] == 0 ) + return TRUE; + + GLuint texture_handle = getTextureHandle () ; +} +*/ + + +static void printStateString ( FILE *fd, unsigned int bits ) +{ + if ( bits & (1<" : getTextureFilename() ) ; + fprintf ( fd, "%s Shade Model = %d\n", indent, shade_model ) ; + fprintf ( fd, "%s Shininess = %f\n", indent, shininess ) ; + fprintf ( fd, "%s AlphaClamp = %f\n", indent, alpha_clamp ) ; + fprintf ( fd, "%s ColourMatMode= %s\n", indent, + (colour_material_mode == GL_AMBIENT) ? "GL_AMBIENT" : + (colour_material_mode == GL_DIFFUSE) ? "GL_DIFFUSE" : + (colour_material_mode == GL_AMBIENT_AND_DIFFUSE) ? + "GL_AMBIENT_AND_DIFFUSE" : + (colour_material_mode == GL_SPECULAR) ? "GL_SPECULAR" : + (colour_material_mode == GL_EMISSION) ? "GL_EMISSION" : "?????" ) ; + + fprintf ( fd, "%s Ambient : (%f,%f,%f,%f)\n", indent, + ambient_colour[0], ambient_colour[1], + ambient_colour[2], ambient_colour[3] ) ; + fprintf ( fd, "%s Diffuse : (%f,%f,%f,%f)\n", indent, + diffuse_colour[0], diffuse_colour[1], + diffuse_colour[2], diffuse_colour[3] ) ; + fprintf ( fd, "%s Specular : (%f,%f,%f,%f)\n", indent, + specular_colour[0], specular_colour[1], + specular_colour[2], specular_colour[3] ) ; + fprintf ( fd, "%s Emission : (%f,%f,%f,%f)\n", indent, + emission_colour[0], emission_colour[1], + emission_colour[2], emission_colour[3] ) ; +} + +// *********************************************************************** +// ******************** class ssgSimpleStateArray ************************ +// *********************************************************************** + + +int ssgSimpleStateArray::findIndex ( ssgSimpleState* st ) +{ + for ( int i = 0; i < getNum (); i++ ) + if ( get (i) == st ) + return i ; + return -1 ; +} + + +void ssgSimpleStateArray::collect ( ssgEntity *e ) +{ + removeAll () ; + collect_recursive (e); +} + + +void ssgSimpleStateArray::collect_recursive ( ssgEntity *e ) +{ + if ( e -> isAKindOf ( ssgTypeBranch() ) ) + { + ssgBranch *br = (ssgBranch *) e ; + + for ( int i = 0 ; i < br -> getNumKids () ; i++ ) + collect_recursive ( br -> getKid ( i ) ) ; + } + else + if ( e -> isAKindOf ( ssgTypeVtxTable() ) ) + { + ssgVtxTable* vt = (ssgVtxTable *) e ; + ssgState* st = vt -> getState () ; + if ( st && st -> isAKindOf ( ssgTypeSimpleState() ) ) + { + ssgSimpleState* ss = (ssgSimpleState*) vt -> getState () ; + if ( findIndex ( ss ) == -1 ) + add ( ss ) ; + } + } +} + + +void ssgSimpleStateArray::add ( ssgSimpleState* ss ) +{ + if ( ss ) + { + ss -> ref () ; + raw_add ( (char *) &ss ) ; + } +} + + +void ssgSimpleStateArray::removeAll () +{ + for ( int i = 0; i < getNum (); i++ ) + ssgDeRefDelete ( get (i) ) ; + ssgSimpleList::removeAll () ; +} + + +ssgSimpleState* ssgSimpleStateArray::findMatch ( ssgSimpleState* st, bool testNamesAsWell ) +{ + if ( st == NULL ) + return NULL ; + + for ( int i = 0; i < getNum (); i++ ) + { + ssgSimpleState *st2 = get (i) ; + + if ( st == st2 ) + return NULL ; //same pointer -- don't change state + + if (testNamesAsWell && ( 0 != strcmp(st->getName(), st2->getName())) ) + continue ; + + + if ( st->isEnabled ( GL_TEXTURE_2D ) != st2->isEnabled ( GL_TEXTURE_2D ) ) + continue ; + + if ( st->isEnabled ( GL_TEXTURE_2D ) && + st -> getTextureHandle () != st2 -> getTextureHandle () ) + continue ; + + if ( st->isEnabled ( GL_TEXTURE_2D ) && + (st -> getTextureHandle () == 0) && + (st2-> getTextureHandle () == 0) && + 0 != strcmp( st->getTextureFilename(), st2->getTextureFilename())) + continue ; + + if ( st->getCareAbout (SSG_GL_SPECULAR) != st2->getCareAbout (SSG_GL_SPECULAR) || + st->getCareAbout (SSG_GL_EMISSION) != st2->getCareAbout (SSG_GL_EMISSION) || + st->getCareAbout (SSG_GL_AMBIENT) != st2->getCareAbout (SSG_GL_AMBIENT) || + st->getCareAbout (SSG_GL_DIFFUSE) != st2->getCareAbout (SSG_GL_DIFFUSE) ) + continue ; + + if ( ! st->getCareAbout (SSG_GL_SPECULAR) && + ! sgEqualVec4 ( st->getMaterial (GL_SPECULAR), st2->getMaterial (GL_SPECULAR) ) ) + continue ; + + if ( ! st->getCareAbout (SSG_GL_EMISSION) && + ! sgEqualVec4 ( st->getMaterial (GL_EMISSION), st2->getMaterial (GL_EMISSION) ) ) + continue ; + + if ( ! st->getCareAbout (SSG_GL_AMBIENT) && + ! sgEqualVec4 ( st->getMaterial (GL_AMBIENT), st2->getMaterial (GL_AMBIENT) ) ) + continue ; + + if ( ! st->getCareAbout (SSG_GL_DIFFUSE) && + ! sgEqualVec4 ( st->getMaterial (GL_DIFFUSE), st2->getMaterial (GL_DIFFUSE) ) ) + continue ; + + if ( st -> isTranslucent () != st2 -> isTranslucent () || + st -> getShininess () != st2 -> getShininess () ) + continue ; + + return st2 ; + } + return NULL ; +} diff --git a/src/ssg/ssgState.cxx b/src/ssg/ssgState.cxx new file mode 100644 index 0000000..b4ff931 --- /dev/null +++ b/src/ssg/ssgState.cxx @@ -0,0 +1,136 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgState.cxx 1810 2003-11-20 14:01:05Z stromberg $ +*/ + + +#include "ssgLocal.h" + +static ssgStateCallback _ssgPendingPostDrawCB = NULL ; +static ssgState *_ssgPreviousState = NULL ; + +void ssgState::preApply () +{ + if ( _ssgPendingPostDrawCB ) + (*_ssgPendingPostDrawCB)(_ssgPreviousState) ; + + if ( preApplyCB ) (*preApplyCB)(this) ; +} + + +void ssgState::preDraw () +{ + if ( preDrawCB ) + { + (*preDrawCB)(this) ; + _ssgPendingPostDrawCB = postDrawCB ; + _ssgPreviousState = this ; + } +} + +void _ssgStartOfFrameInit () +{ + _ssgPendingPostDrawCB = NULL ; + _ssgPreviousState = NULL ; +} + +void _ssgEndOfFrameCleanup () +{ + if ( _ssgPendingPostDrawCB ) (*_ssgPendingPostDrawCB)(_ssgPreviousState) ; + + _ssgPendingPostDrawCB = NULL ; + _ssgPreviousState = NULL ; +} + +void ssgState::copy_from ( ssgState *src, int clone_flags ) +{ + ssgBase::copy_from ( src, clone_flags ) ; + setExternalPropertyIndex ( src -> getExternalPropertyIndex () ) ; + + if ( src -> isTranslucent () ) + setTranslucent () ; + else + setOpaque () ; + + setStateCallback( SSG_CALLBACK_PREDRAW, src->getStateCallback( SSG_CALLBACK_PREDRAW ) ); + setStateCallback( SSG_CALLBACK_POSTDRAW, src->getStateCallback( SSG_CALLBACK_POSTDRAW ) ); + setStateCallback( SSG_CALLBACK_PREAPPLY, src->getStateCallback( SSG_CALLBACK_PREAPPLY ) ); +} + + + +ssgState::ssgState (void) +{ + type = ssgTypeState () ; + + preApplyCB = NULL ; + preDrawCB = NULL ; + postDrawCB = NULL ; + + setOpaque () ; + setExternalPropertyIndex ( 0 ) ; +} + +ssgState::~ssgState (void) +{ + if ( _ssgPreviousState == this ) + { + _ssgPendingPostDrawCB = NULL ; + _ssgPreviousState = NULL ; + } +} + + +void ssgState::print ( FILE *fd, char *indent, int how_much ) +{ + ssgBase::print ( fd, indent, how_much ) ; + + if ( how_much < 2 ) + return; + + fprintf ( fd, "%s Translucent = %s\n", indent, translucent?"True":"False"); + fprintf ( fd, "%s ExternalProp = %d\n", indent, external_property_index ) ; +} + + +int ssgState::load ( FILE *fd ) +{ + _ssgReadInt ( fd, & translucent ) ; + _ssgReadInt ( fd, & external_property_index ) ; + + preApplyCB = NULL ; + preDrawCB = NULL ; + postDrawCB = NULL ; + + return ssgBase::load ( fd ) ; +} + + +int ssgState::save ( FILE *fd ) +{ + _ssgWriteInt ( fd, translucent ) ; + _ssgWriteInt ( fd, external_property_index ) ; + return ssgBase::save ( fd ) ; +} + + + + diff --git a/src/ssg/ssgStateSelector.cxx b/src/ssg/ssgStateSelector.cxx new file mode 100644 index 0000000..4d79990 --- /dev/null +++ b/src/ssg/ssgStateSelector.cxx @@ -0,0 +1,426 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgStateSelector.cxx 1950 2004-09-13 11:28:34Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +void ssgStateSelector::copy_from ( ssgStateSelector *src, int clone_flags ) +{ + ssgSimpleState::copy_from ( src, clone_flags ) ; + + nstates = src -> getNumSteps () ; + selection = src -> getSelectStep () ; + statelist = new ssgSimpleState * [ nstates ] ; + + for ( int i = 0 ; i < nstates ; i++ ) + { + ssgSimpleState *s = src -> getStep ( i ) ; + + if ( s != NULL && (clone_flags & SSG_CLONE_STATE_RECURSIVE) ) + statelist [ i ] = (ssgSimpleState *)( s -> clone ( clone_flags )) ; + else + statelist [ i ] = s ; + + //~~ T.G. needs ref count incremented + if (statelist [ i ] != NULL ) + statelist [ i ] -> ref(); + + } +} + + +ssgBase *ssgStateSelector::clone ( int clone_flags ) +{ + ssgStateSelector *b = new ssgStateSelector ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgStateSelector::ssgStateSelector () +{ + type = ssgTypeStateSelector () ; + nstates = 0 ; + selection = -1 ; + statelist = NULL ; +} + +ssgStateSelector::ssgStateSelector ( int ns ) +{ + type = ssgTypeStateSelector () ; + nstates = ns ; + selection = -1 ; + statelist = new ssgSimpleState * [ nstates ] ; + + for ( int i = 0 ; i < nstates ; i++ ) + statelist [ i ] = NULL ; +} + +ssgStateSelector::~ssgStateSelector (void) +{ + //~~ T.G. deref states before deleting list + for ( int i = 0 ; i < nstates ; i++ ) + ssgDeRefDelete( statelist [ i ] ); + delete [] statelist ; +} + +void ssgStateSelector::selectStep ( unsigned int s ) +{ + selection = s ; +} + +unsigned int ssgStateSelector::getSelectStep (void) +{ + return selection ; +} + +ssgSimpleState *ssgStateSelector::getCurrentStep (void) +{ + return ( selection < 0 || + selection >= nstates || + statelist [ selection ] == NULL ) ? this : statelist[selection] ; +} + +ssgSimpleState *ssgStateSelector::getStep ( int i ) +{ + return ( i < 0 || + i >= nstates || + statelist [ i ] == NULL ) ? this : statelist[i] ; +} + + + + +void ssgStateSelector::setStep (int i, ssgSimpleState *step) +{ + if ( i < 0 || i >= nstates ) return ; + + //~~ T.G. removed null test -- not necessary + ssgDeRefDelete ( statelist[i] ) ; + + statelist [ i ] = step ; + + if ( step != NULL ) + step -> ref () ; +} + + +int ssgStateSelector::isTranslucent (void) +{ + return getCurrentStep()->isTranslucent() ; +} + + +void ssgStateSelector::setTranslucent (void) +{ + getCurrentStep()->setTranslucent() ; +} + + + +void ssgStateSelector::setOpaque (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setOpaque() ; + else + s -> setOpaque() ; +} + + +void ssgStateSelector::force (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::force() ; + else + s -> force() ; +} + +void ssgStateSelector::apply (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::apply() ; + else + s -> apply() ; +} + + +void ssgStateSelector:: care_about ( int mode ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::care_about (mode) ; + else + s -> care_about (mode); +} + + +void ssgStateSelector::dont_care_about ( int mode ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::dont_care_about(mode) ; + else + s -> dont_care_about(mode); +} + + +int ssgStateSelector::isEnabled ( GLenum mode ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + return ssgSimpleState::isEnabled(mode) ; + else + return s -> isEnabled(mode) ; +} + +void ssgStateSelector::disable ( GLenum mode ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::disable(mode) ; + else + s -> disable(mode) ; +} + +void ssgStateSelector::enable ( GLenum mode ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::enable(mode) ; + else + s -> enable(mode) ; +} + + +void ssgStateSelector::setTexture ( char *fname, int _wrapu, int _wrapv, + int _mipmap ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setTexture ( fname, _wrapu, _wrapv, _mipmap ) ; + else + s -> setTexture ( fname, _wrapu, _wrapv, _mipmap ) ; +} + +char *ssgStateSelector::getTextureFilename (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + return ssgSimpleState::getTextureFilename() ; + else + return s -> getTextureFilename(); +} + +GLuint ssgStateSelector::getTextureHandle (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + return ssgSimpleState::getTextureHandle() ; + else + return s -> getTextureHandle() ; +} + +void ssgStateSelector::setTexture ( ssgTexture *tex ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setTexture(tex) ; + else + s -> setTexture(tex) ; +} + +void ssgStateSelector::setTextureFilename ( char *fname ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + ssgTexture* texture ; + if ( s == this ) + texture = ssgSimpleState::getTexture () ; + else + texture = s -> getTexture () ; + + if ( texture != NULL ) + texture -> setFilename ( fname ) ; +} + +void ssgStateSelector::setTexture ( GLuint tex ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + ssgTexture* texture ; + if ( s == this ) + texture = ssgSimpleState::getTexture () ; + else + texture = s -> getTexture () ; + + if ( texture != NULL ) + texture -> setHandle (tex) ; +} + +void ssgStateSelector::setColourMaterial(GLenum which) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setColourMaterial(which) ; + else + s -> setColourMaterial(which) ; +} + +void ssgStateSelector::setMaterial ( GLenum which, float r, float g, + float b, float a ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setMaterial(which,r,g,b,a) ; + else + s -> setMaterial(which,r,g,b,a) ; +} + + +void ssgStateSelector::setMaterial ( GLenum which, sgVec4 rgba ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setMaterial(which,rgba) ; + else + s -> setMaterial(which,rgba) ; +} + +float *ssgStateSelector::getMaterial ( GLenum which ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + return ssgSimpleState::getMaterial(which) ; + else + return s -> getMaterial(which) ; +} + +float ssgStateSelector::getShininess (void) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + return ssgSimpleState::getShininess() ; + else + return s -> getShininess() ; +} + +void ssgStateSelector::setShininess ( float sh ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setShininess(sh) ; + else + s -> setShininess(sh) ; +} + +void ssgStateSelector::setShadeModel ( GLenum model ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setShadeModel(model) ; + else + s -> setShadeModel(model) ; +} + +void ssgStateSelector::setAlphaClamp ( float clamp ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::setAlphaClamp(clamp) ; + else + s -> setAlphaClamp(clamp) ; +} + +void ssgStateSelector::print ( FILE *fd, char *indent, int how_much ) +{ + ssgStateSelector * s = (ssgStateSelector *) getCurrentStep() ; + + if ( s == this ) + ssgSimpleState::print(fd, indent, how_much) ; + else + s -> print(fd, indent, how_much) ; +} + + +int ssgStateSelector::load ( FILE *fd ) +{ + int i ; + + _ssgReadInt ( fd, & nstates ) ; + _ssgReadInt ( fd, & selection ) ; + + if (statelist != NULL) + { + for ( i = 0 ; i < nstates ; i++ ) + ssgDeRefDelete( statelist [ i ] ); + delete [] statelist ; + } + + statelist = new ssgSimpleState * [ nstates ] ; + for ( i = 0 ; i < nstates ; i++ ) + { + if ( ! _ssgLoadObject ( fd, (ssgBase **) &statelist[i], ssgTypeState () ) ) + return FALSE ; + } + + return ssgSimpleState::load(fd) ; +} + + +int ssgStateSelector::save ( FILE *fd ) +{ + + _ssgWriteInt ( fd, nstates ) ; + _ssgWriteInt ( fd, selection ) ; + for ( int i = 0 ; i < nstates ; i++ ) + { + if ( ! _ssgSaveObject ( fd, statelist[i] ) ) + return FALSE ; + } + + return ssgSimpleState::save(fd) ; +} + diff --git a/src/ssg/ssgStateTables.cxx b/src/ssg/ssgStateTables.cxx new file mode 100644 index 0000000..746f1fd --- /dev/null +++ b/src/ssg/ssgStateTables.cxx @@ -0,0 +1,221 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgStateTables.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +/* + Well, it would be nice to say that these tables + were automatically generated - but they weren't. + + So, there is some particularly nasty Macro-processing + going on to save my poor aching fingers. +*/ + +#define E0 { if ( ! _ssgCurrentContext->textureOverridden ()) glEnable ( GL_TEXTURE_2D ) ; } +#define E1 { if ( ! _ssgCurrentContext->cullfaceOverridden()) glEnable ( GL_CULL_FACE ) ; } +#define E2 glEnable ( GL_COLOR_MATERIAL ) ; +#define E3 glEnable ( GL_BLEND ) ; +#define E4 glEnable ( GL_ALPHA_TEST ) ; +#define E5 glEnable ( GL_LIGHTING ) ; + +#define D0 glDisable ( GL_TEXTURE_2D ) ; +#define D1 glDisable ( GL_CULL_FACE ) ; +#define D2 glDisable ( GL_COLOR_MATERIAL ) ; +#define D3 glDisable ( GL_BLEND ) ; +#define D4 glDisable ( GL_ALPHA_TEST ) ; +#define D5 glDisable ( GL_LIGHTING ) ; + +/* Do any combination of useful glEnable/glDisable commands */ + +static void enable_00 () { } +static void enable_01 () { E0 } +static void enable_02 () { E1 } +static void enable_03 () { E0 E1 } +static void enable_04 () { E2 } +static void enable_05 () { E0 E2 } +static void enable_06 () { E1 E2 } +static void enable_07 () { E0 E1 E2 } +static void enable_08 () { E3 } +static void enable_09 () { E0 E3 } +static void enable_10 () { E1 E3 } +static void enable_11 () { E0 E1 E3 } +static void enable_12 () { E2 E3 } +static void enable_13 () { E0 E2 E3 } +static void enable_14 () { E1 E2 E3 } +static void enable_15 () { E0 E1 E2 E3 } +static void enable_16 () { E4 } +static void enable_17 () { E0 E4 } +static void enable_18 () { E1 E4 } +static void enable_19 () { E0 E1 E4 } +static void enable_20 () { E2 E4 } +static void enable_21 () { E0 E2 E4 } +static void enable_22 () { E1 E2 E4 } +static void enable_23 () { E0 E1 E2 E4 } +static void enable_24 () { E3 E4 } +static void enable_25 () { E0 E3 E4 } +static void enable_26 () { E1 E3 E4 } +static void enable_27 () { E0 E1 E3 E4 } +static void enable_28 () { E2 E3 E4 } +static void enable_29 () { E0 E2 E3 E4 } +static void enable_30 () { E1 E2 E3 E4 } +static void enable_31 () { E0 E1 E2 E3 E4 } + +static void enable_32 () { E5 } +static void enable_33 () { E0 E5 } +static void enable_34 () { E1 E5 } +static void enable_35 () { E0 E1 E5 } +static void enable_36 () { E2 E5 } +static void enable_37 () { E0 E2 E5 } +static void enable_38 () { E1 E2 E5 } +static void enable_39 () { E0 E1 E2 E5 } +static void enable_40 () { E3 E5 } +static void enable_41 () { E0 E3 E5 } +static void enable_42 () { E1 E3 E5 } +static void enable_43 () { E0 E1 E3 E5 } +static void enable_44 () { E2 E3 E5 } +static void enable_45 () { E0 E2 E3 E5 } +static void enable_46 () { E1 E2 E3 E5 } +static void enable_47 () { E0 E1 E2 E3 E5 } +static void enable_48 () { E4 E5 } +static void enable_49 () { E0 E4 E5 } +static void enable_50 () { E1 E4 E5 } +static void enable_51 () { E0 E1 E4 E5 } +static void enable_52 () { E2 E4 E5 } +static void enable_53 () { E0 E2 E4 E5 } +static void enable_54 () { E1 E2 E4 E5 } +static void enable_55 () { E0 E1 E2 E4 E5 } +static void enable_56 () { E3 E4 E5 } +static void enable_57 () { E0 E3 E4 E5 } +static void enable_58 () { E1 E3 E4 E5 } +static void enable_59 () { E0 E1 E3 E4 E5 } +static void enable_60 () { E2 E3 E4 E5 } +static void enable_61 () { E0 E2 E3 E4 E5 } +static void enable_62 () { E1 E2 E3 E4 E5 } +static void enable_63 () { E0 E1 E2 E3 E4 E5 } + +static void disable_00 () { } +static void disable_01 () { D0 } +static void disable_02 () { D1 } +static void disable_03 () { D0 D1 } +static void disable_04 () { D2 } +static void disable_05 () { D0 D2 } +static void disable_06 () { D1 D2 } +static void disable_07 () { D0 D1 D2 } +static void disable_08 () { D3 } +static void disable_09 () { D0 D3 } +static void disable_10 () { D1 D3 } +static void disable_11 () { D0 D1 D3 } +static void disable_12 () { D2 D3 } +static void disable_13 () { D0 D2 D3 } +static void disable_14 () { D1 D2 D3 } +static void disable_15 () { D0 D1 D2 D3 } +static void disable_16 () { D4 } +static void disable_17 () { D0 D4 } +static void disable_18 () { D1 D4 } +static void disable_19 () { D0 D1 D4 } +static void disable_20 () { D2 D4 } +static void disable_21 () { D0 D2 D4 } +static void disable_22 () { D1 D2 D4 } +static void disable_23 () { D0 D1 D2 D4 } +static void disable_24 () { D3 D4 } +static void disable_25 () { D0 D3 D4 } +static void disable_26 () { D1 D3 D4 } +static void disable_27 () { D0 D1 D3 D4 } +static void disable_28 () { D2 D3 D4 } +static void disable_29 () { D0 D2 D3 D4 } +static void disable_30 () { D1 D2 D3 D4 } +static void disable_31 () { D0 D1 D2 D3 D4 } + +static void disable_32 () { D5 } +static void disable_33 () { D0 D5 } +static void disable_34 () { D1 D5 } +static void disable_35 () { D0 D1 D5 } +static void disable_36 () { D2 D5 } +static void disable_37 () { D0 D2 D5 } +static void disable_38 () { D1 D2 D5 } +static void disable_39 () { D0 D1 D2 D5 } +static void disable_40 () { D3 D5 } +static void disable_41 () { D0 D3 D5 } +static void disable_42 () { D1 D3 D5 } +static void disable_43 () { D0 D1 D3 D5 } +static void disable_44 () { D2 D3 D5 } +static void disable_45 () { D0 D2 D3 D5 } +static void disable_46 () { D1 D2 D3 D5 } +static void disable_47 () { D0 D1 D2 D3 D5 } +static void disable_48 () { D4 D5 } +static void disable_49 () { D0 D4 D5 } +static void disable_50 () { D1 D4 D5 } +static void disable_51 () { D0 D1 D4 D5 } +static void disable_52 () { D2 D4 D5 } +static void disable_53 () { D0 D2 D4 D5 } +static void disable_54 () { D1 D2 D4 D5 } +static void disable_55 () { D0 D1 D2 D4 D5 } +static void disable_56 () { D3 D4 D5 } +static void disable_57 () { D0 D3 D4 D5 } +static void disable_58 () { D1 D3 D4 D5 } +static void disable_59 () { D0 D1 D3 D4 D5 } +static void disable_60 () { D2 D3 D4 D5 } +static void disable_61 () { D0 D2 D3 D4 D5 } +static void disable_62 () { D1 D2 D3 D4 D5 } +static void disable_63 () { D0 D1 D2 D3 D4 D5 } + + +void (*__ssgEnableTable[64])() = +{ + enable_00, enable_01, enable_02, enable_03, enable_04, + enable_05, enable_06, enable_07, enable_08, enable_09, + enable_10, enable_11, enable_12, enable_13, enable_14, + enable_15, enable_16, enable_17, enable_18, enable_19, + enable_20, enable_21, enable_22, enable_23, enable_24, + enable_25, enable_26, enable_27, enable_28, enable_29, + enable_30, enable_31, + + enable_32, enable_33, enable_34, enable_35, enable_36, + enable_37, enable_38, enable_39, enable_40, enable_41, + enable_42, enable_43, enable_44, enable_45, enable_46, + enable_47, enable_48, enable_49, enable_50, enable_51, + enable_52, enable_53, enable_54, enable_55, enable_56, + enable_57, enable_58, enable_59, enable_60, enable_61, + enable_62, enable_63 +} ; + +void (*__ssgDisableTable[64])() = +{ + disable_00, disable_01, disable_02, disable_03, disable_04, + disable_05, disable_06, disable_07, disable_08, disable_09, + disable_10, disable_11, disable_12, disable_13, disable_14, + disable_15, disable_16, disable_17, disable_18, disable_19, + disable_20, disable_21, disable_22, disable_23, disable_24, + disable_25, disable_26, disable_27, disable_28, disable_29, + disable_30, disable_31, + + disable_32, disable_33, disable_34, disable_35, disable_36, + disable_37, disable_38, disable_39, disable_40, disable_41, + disable_42, disable_43, disable_44, disable_45, disable_46, + disable_47, disable_48, disable_49, disable_50, disable_51, + disable_52, disable_53, disable_54, disable_55, disable_56, + disable_57, disable_58, disable_59, disable_60, disable_61, + disable_62, disable_63 +} ; + diff --git a/src/ssg/ssgStatistics.cxx b/src/ssg/ssgStatistics.cxx new file mode 100644 index 0000000..03a30b1 --- /dev/null +++ b/src/ssg/ssgStatistics.cxx @@ -0,0 +1,18 @@ + +#include "ssgLocal.h" + + +ssgStatistics _ssgCurrStatistics ; + +void ssgStatistics::reset () +{ + vertex_count = leaf_count = 0 ; +} + + +ssgStatistics *ssgGetLatestStatistics () +{ + return & _ssgCurrStatistics ; +} + + diff --git a/src/ssg/ssgStats.cxx b/src/ssg/ssgStats.cxx new file mode 100644 index 0000000..b61ab46 --- /dev/null +++ b/src/ssg/ssgStats.cxx @@ -0,0 +1,95 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgStats.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#include "ssgLocal.h" + +int stats_num_vertices = 0 ; +int stats_isect_triangles = 0 ; +int stats_isect_test = 0 ; +int stats_cull_test = 0 ; +int stats_bind_textures = 0 ; +int stats_num_leaves = 0 ; + +int stats_hot_triangles = 0 ; +int stats_hot_test = 0 ; +int stats_hot_no_trav = 0 ; +int stats_hot_radius_reject=0 ; +int stats_hot_triv_accept = 0 ; +int stats_hot_straddle = 0 ; + +int stats_los_triangles = 0 ; +int stats_los_test = 0 ; +int stats_los_no_trav = 0 ; +int stats_los_radius_reject=0 ; +int stats_los_triv_accept = 0 ; +int stats_los_straddle = 0 ; + +static char stats_string [ 1024 ] ; + +char *ssgShowStats () +{ + sprintf ( stats_string, "V=%4d, L=%3d H=%3d IS=%3d IT=%3d HT=%3d CT=%3d BT=%3d\n", + stats_num_vertices , + stats_num_leaves , + stats_hot_triangles , + stats_isect_triangles, + stats_isect_test , + stats_hot_test , + stats_cull_test , + stats_bind_textures ) ; +/* + sprintf ( stats_string, "Tri=%d, Tst=%d NoTr=%d Rej=%d Acp=%d Str=%d\n", + stats_hot_triangles , + stats_hot_test , + stats_hot_no_trav , + stats_hot_radius_reject, + stats_hot_triv_accept , + stats_hot_straddle ) ; +*/ + + stats_num_vertices = 0 ; + stats_num_leaves = 0 ; + stats_isect_triangles = 0 ; + stats_isect_test = 0 ; + stats_cull_test = 0 ; + stats_bind_textures = 0 ; + + stats_hot_triangles = 0 ; + stats_hot_test = 0 ; + stats_hot_no_trav = 0 ; + stats_hot_radius_reject=0 ; + stats_hot_triv_accept = 0 ; + stats_hot_straddle = 0 ; + + stats_los_triangles = 0 ; + stats_los_test = 0 ; + stats_los_no_trav = 0 ; + stats_los_radius_reject=0 ; + stats_los_triv_accept = 0 ; + stats_los_straddle = 0 ; + + return stats_string ; +} + + + diff --git a/src/ssg/ssgTexTrans.cxx b/src/ssg/ssgTexTrans.cxx new file mode 100644 index 0000000..19c683e --- /dev/null +++ b/src/ssg/ssgTexTrans.cxx @@ -0,0 +1,117 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgTexTrans.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgTexTrans::copy_from ( ssgTexTrans *src, int clone_flags ) +{ + ssgBaseTransform::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgTexTrans::clone ( int clone_flags ) +{ + ssgTexTrans *b = new ssgTexTrans ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgTexTrans::ssgTexTrans ( sgCoord *c ) +{ + type = ssgTypeTexTrans () ; + setTransform ( c ) ; +} + +ssgTexTrans::ssgTexTrans (void) +{ + type = ssgTypeTexTrans () ; +} + +ssgTexTrans::~ssgTexTrans (void) +{ +} + +void ssgTexTrans::setTransform ( sgVec3 xyz ) +{ + sgMakeTransMat4 ( transform, xyz ) ; +} + +void ssgTexTrans::setTransform ( sgCoord *xform, float sx, float sy, float sz ) +{ + sgMakeCoordMat4 ( transform, xform ) ; + sgScaleVec3 ( transform[0], sx ) ; + sgScaleVec3 ( transform[1], sy ) ; + sgScaleVec3 ( transform[2], sz ) ; +} + +void ssgTexTrans::setTransform ( sgCoord *xform ) +{ + sgMakeCoordMat4 ( transform, xform ) ; +} + +void ssgTexTrans::setTransform ( sgMat4 xform ) +{ + sgCopyMat4 ( transform, xform ) ; +} + +void ssgTexTrans::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + _ssgLoadTexMatrix ( transform ) ; + glMatrixMode ( GL_TEXTURE ) ; + glLoadMatrixf ( (float *) transform ) ; + glMatrixMode ( GL_MODELVIEW ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> cull ( f, m, cull_result != SSG_INSIDE ) ; + + glMatrixMode ( GL_TEXTURE ) ; + glLoadIdentity () ; + glMatrixMode ( GL_MODELVIEW ) ; + _ssgUnloadTexMatrix () ; + + postTravTests ( SSGTRAV_CULL ) ; +} + + + +int ssgTexTrans::load ( FILE *fd ) +{ + return ssgBaseTransform::load(fd) ; +} + +int ssgTexTrans::save ( FILE *fd ) +{ + return ssgBaseTransform::save(fd) ; +} + + + diff --git a/src/ssg/ssgTexture.cxx b/src/ssg/ssgTexture.cxx new file mode 100644 index 0000000..e996918 --- /dev/null +++ b/src/ssg/ssgTexture.cxx @@ -0,0 +1,267 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgTexture.cxx 2123 2007-09-15 13:24:45Z fayjf $ +*/ + + +#include "ssgLocal.h" + + +void ssgTexture::copy_from ( ssgTexture *src, int clone_flags ) +{ + if ( this != src ) + { + /* Avoid meaningless assignments (OK) and corrupting file name (important) when copying to self */ + ssgBase::copy_from ( src, clone_flags ) ; + + wrapu = src -> wrapu ; + wrapv = src -> wrapv ; + mipmap = src -> mipmap ; + + setFilename ( src -> getFilename () ) ; + } + + alloc_handle () ; + ssgTextureInfo info ; + ssgLoadTexture( filename, &info ) ; + has_alpha = (info.alpha != 0) ; + setDefaultGlParams(wrapu, wrapv, mipmap); +} + + +ssgBase *ssgTexture::clone ( int clone_flags ) +{ + ssgTexture *b = new ssgTexture ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +void ssgTexture::setDefaultGlParams(int wrapu, int wrapv, int mipmap) +{ + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + wrapu ? GL_REPEAT : GL_CLAMP ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + wrapv ? GL_REPEAT : GL_CLAMP ) ; + +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, 0 ) ; +#else + glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ; +#endif +} + + +ssgTexture::ssgTexture () +{ + type = ssgTypeTexture () ; + + filename = NULL ; + + own_handle = FALSE ; + handle = 0 ; + + wrapu = TRUE ; + wrapv = TRUE ; + mipmap = TRUE ; + has_alpha = false ; +} + + +ssgTexture::ssgTexture ( const char *fname, GLubyte *image, + int xsize, int ysize, int zsize, + int _wrapu, int _wrapv ) +// fname is used when saving the ssgSimpleState to disk. +// If there is no associated file, you can use a dummy name. +{ + type = ssgTypeTexture () ; + + filename = NULL ; + + own_handle = FALSE ; + handle = 0 ; + + wrapu = _wrapu ; + wrapv = _wrapv ; + mipmap = TRUE ; + + setFilename ( fname ) ; + + alloc_handle () ; +#if 0 + ssgTextureInfo info ; + ssgLoadTexture( filename, &info ) ; + has_alpha = (info.alpha != 0) ; +#else + has_alpha = (zsize == 4) ; + ssgMakeMipMaps ( image, xsize, ysize, zsize ); +#endif + setDefaultGlParams(wrapu, wrapv, TRUE); +} + + +ssgTexture::ssgTexture ( const char *fname, int _wrapu, int _wrapv, int _mipmap ) +{ + type = ssgTypeTexture () ; + + filename = NULL ; + + own_handle = FALSE ; + handle = 0 ; + + wrapu = _wrapu ; + wrapv = _wrapv ; + mipmap = _mipmap ; + + setFilename ( fname ) ; + + alloc_handle () ; + ssgTextureInfo info ; + ssgLoadTexture( filename, &info ) ; + has_alpha = (info.alpha != 0) ; + setDefaultGlParams(wrapu, wrapv, mipmap); +} + + +ssgTexture::~ssgTexture (void) +{ + setFilename ( NULL ) ; + free_handle () ; +} + + +void ssgTexture::alloc_handle () +{ + free_handle () ; + + own_handle = TRUE ; + +#ifdef GL_VERSION_1_1 + glGenTextures ( 1, & handle ) ; + glBindTexture ( GL_TEXTURE_2D, handle ) ; +#else + /* This is only useful on some ancient SGI hardware */ + glGenTexturesEXT ( 1, & handle ) ; + glBindTextureEXT ( GL_TEXTURE_2D, handle ) ; +#endif +} + + +void ssgTexture::free_handle () +{ + if ( handle != 0 ) + { + if ( own_handle ) + { +#ifdef GL_VERSION_1_1 + glDeleteTextures ( 1, & handle ) ; +#else + /* This is only useful on some ancient SGI hardware */ + glDeleteTexturesEXT ( 1, & handle ) ; +#endif + } + + own_handle = FALSE ; + handle = 0 ; + } +} + + +void ssgTexture::setHandle ( GLuint _handle ) +{ + free_handle () ; + + own_handle = FALSE ; + handle = _handle ; +} + + +void ssgTexture::print ( FILE *fd, char *ident, int how_much ) +{ + fprintf ( fd, "%s%s: %s\n", ident, getTypeName (), getFilename () ) ; +} + + +int ssgTexture::load ( FILE *fd ) +{ + delete [] filename ; + + _ssgReadString ( fd, & filename ) ; + _ssgReadInt ( fd, & wrapu ) ; + _ssgReadInt ( fd, & wrapv ) ; + _ssgReadInt ( fd, & mipmap ) ; + + alloc_handle () ; + ssgTextureInfo info ; + ssgLoadTexture( filename, &info ) ; + has_alpha = (info.alpha != 0) ; + setDefaultGlParams(wrapu, wrapv, mipmap); + + return ssgBase::load ( fd ) ; +} + + +int ssgTexture::save ( FILE *fd ) +{ + _ssgWriteString ( fd, filename ) ; + _ssgWriteInt ( fd, wrapu ) ; + _ssgWriteInt ( fd, wrapv ) ; + _ssgWriteInt ( fd, mipmap ) ; + + return ssgBase::save ( fd ) ; +} + + +void ssgTextureArray::add ( ssgTexture* tex ) +{ + if ( tex ) + { + tex -> ref () ; + raw_add ( (char *) &tex ) ; + } +} + + +void ssgTextureArray::removeAll () +{ + for ( int i = 0; i < getNum (); i++ ) + ssgDeRefDelete ( get (i) ) ; + ssgSimpleList::removeAll () ; +} + + +ssgTexture* ssgTextureArray::findByFilename ( const char* fname ) +{ + for ( int i = 0; i < getNum (); i++ ) + { + ssgTexture *tex = get (i) ; + if ( ulStrEqual ( fname, tex->getFilename() ) ) + return tex ; + } + return NULL ; +} + + diff --git a/src/ssg/ssgTransform.cxx b/src/ssg/ssgTransform.cxx new file mode 100644 index 0000000..f4bce4d --- /dev/null +++ b/src/ssg/ssgTransform.cxx @@ -0,0 +1,242 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgTransform.cxx 1992 2004-12-01 08:28:05Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +void ssgTransform::copy_from ( ssgTransform *src, int clone_flags ) +{ + ssgBaseTransform::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgTransform::clone ( int clone_flags ) +{ + ssgTransform *b = new ssgTransform ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgTransform::ssgTransform ( sgCoord *c ) +{ + type = ssgTypeTransform () ; + setTransform ( c ) ; +} + +ssgTransform::ssgTransform (void) +{ + type = ssgTypeTransform () ; +} + +ssgTransform::~ssgTransform (void) +{ +} + +void ssgTransform::recalcBSphere (void) +{ + ssgBranch::recalcBSphere () ; + + if ( ! bsphere . isEmpty () ) + bsphere . orthoXform ( transform ) ; +} + +void ssgTransform::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_CULL ) ) + return ; + + int cull_result = cull_test ( f, m, test_needed ) ; + + if ( cull_result == SSG_OUTSIDE ) + return ; + + sgMat4 tmp ; + + sgCopyMat4 ( tmp, m ) ; + sgPreMultMat4 ( tmp, transform ) ; + + _ssgPushMatrix ( tmp ) ; + glPushMatrix () ; + glLoadMatrixf ( (float *) tmp ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> cull ( f, tmp, cull_result != SSG_INSIDE ) ; + + glPopMatrix () ; + _ssgPopMatrix () ; + + postTravTests ( SSGTRAV_CULL ) ; +} + +void ssgTransform::hot ( sgVec3 s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_HOT ) ) + return ; + + int hot_result = hot_test ( s, m, test_needed ) ; + + if ( hot_result == SSG_OUTSIDE ) + return ; + + sgMat4 tmp ; + + sgCopyMat4 ( tmp, m ) ; + sgPreMultMat4 ( tmp, transform ) ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> hot ( s, tmp, hot_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_HOT ) ; +} + +void ssgTransform::los ( sgVec3 s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_LOS ) ) + return ; + + int los_result = los_test ( s, m, test_needed ) ; + + if ( los_result == SSG_OUTSIDE ) + return ; + + sgMat4 tmp ; + + sgCopyMat4 ( tmp, m ) ; + sgPreMultMat4 ( tmp, transform ) ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> los ( s, tmp, los_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_LOS ) ; +} + +void ssgTransform::isect ( sgSphere *s, sgMat4 m, int test_needed ) +{ + if ( ! preTravTests ( &test_needed, SSGTRAV_ISECT ) ) + return ; + + int isect_result = isect_test ( s, m, test_needed ) ; + + if ( isect_result == SSG_OUTSIDE ) + return ; + + sgMat4 tmp ; + + sgCopyMat4 ( tmp, m ) ; + sgPreMultMat4 ( tmp, transform ) ; + + _ssgPushPath ( this ) ; + + for ( ssgEntity *e = getKid ( 0 ) ; e != NULL ; e = getNextKid() ) + e -> isect ( s, tmp, isect_result != SSG_INSIDE ) ; + + _ssgPopPath () ; + + postTravTests ( SSGTRAV_ISECT ) ; +} + +void ssgTransform::setTransform ( sgVec3 xyz ) +{ + sgMat4 tmp_trans; + sgMakeTransMat4 ( tmp_trans, xyz ) ; + setTransform ( tmp_trans ) ; +} + +void ssgTransform::setTransform ( sgCoord *xform ) +{ + sgMat4 tmp_trans; + sgMakeCoordMat4 ( tmp_trans, xform ) ; + setTransform ( tmp_trans ) ; +} + +void ssgTransform::setTransform ( sgCoord *xform, float sx, float sy, float sz ) +{ + sgMat4 tmp_trans; + sgMakeCoordMat4 ( tmp_trans, xform ) ; + sgScaleVec3 ( tmp_trans[0], sx ) ; + sgScaleVec3 ( tmp_trans[1], sy ) ; + sgScaleVec3 ( tmp_trans[2], sz ) ; + setTransform ( tmp_trans ); +} + +void ssgTransform::setTransform ( sgMat4 xform ) +{ + if ( sgEqualVec4( xform[0], transform[0] ) && + sgEqualVec4( xform[1], transform[1] ) && + sgEqualVec4( xform[2], transform[2] ) && + sgEqualVec4( xform[3], transform[3] ) ) + return; + + updateTransform () ; + sgCopyMat4 ( transform, xform ) ; + firsttime () ; + dirtyBSphere () ; +} + + +void ssgTransform::getNetTransform ( sgMat4 xform ) +{ + if ( getNumParents () > 0 ) + { + getParent ( 0 ) -> getNetTransform ( xform ) ; + sgPreMultMat4 ( xform, transform ) ; + } + else + sgCopyMat4 ( xform, transform ) ; +} + + +void ssgTransform::getLastNetTransform ( sgMat4 xform ) +{ + sgMat4 last_xform ; + getLastTransform ( last_xform ) ; + + if ( getNumParents () > 0 ) + { + getParent ( 0 ) -> getLastNetTransform ( xform ) ; + sgPreMultMat4 ( xform, last_xform ) ; + } + else + sgCopyMat4 ( xform, last_xform ) ; +} + + +int ssgTransform::load ( FILE *fd ) +{ + return ssgBaseTransform::load(fd) ; +} + +int ssgTransform::save ( FILE *fd ) +{ + return ssgBaseTransform::save(fd) ; +} + + diff --git a/src/ssg/ssgTween.cxx b/src/ssg/ssgTween.cxx new file mode 100644 index 0000000..751e895 --- /dev/null +++ b/src/ssg/ssgTween.cxx @@ -0,0 +1,500 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgTween.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include "ssgLocal.h" +#include "ul.h" + +static float current_tween_state = 0.0f ; +static int current_tween_mode = SSGTWEEN_STOP_AT_END ; + +const char *ssgTween::getTypeName (void) { return "ssgTween" ; } + +int _ssgGetCurrentTweenMode () { return current_tween_mode ; } +float _ssgGetCurrentTweenState () { return current_tween_state ; } + + +void _ssgSetCurrentTweenSettings ( float state, int mode ) +{ + _ssgSetRealCurrentTweenSettings ( state, mode ) ; + _ssgSetTweenState ( state, mode ) ; /* Add it into the Dlist for later */ +} + + +void _ssgSetRealCurrentTweenSettings ( float state, int mode ) +{ + current_tween_mode = mode ; + current_tween_state = state ; +} + + +void ssgTween::copy_from ( ssgTween *src, int clone_flags ) +{ + ssgLeaf::copy_from ( src, clone_flags ) ; + + gltype = src -> getPrimitiveType () ; + + if ( src->vertices != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + vertices = (ssgVertexArray *)( src -> vertices -> clone ( clone_flags )) ; + else + vertices = src -> vertices ; + + if ( src->normals != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + normals = (ssgNormalArray *)( src -> normals -> clone ( clone_flags )) ; + else + normals = src -> normals ; + + if ( src->texcoords != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + texcoords = (ssgTexCoordArray *)( src -> texcoords -> clone ( clone_flags )) ; + else + texcoords = src -> texcoords ; + + if ( src->colours != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + colours = (ssgColourArray *)( src -> colours -> clone ( clone_flags )) ; + else + colours = src -> colours ; + + if ( vertices != NULL ) vertices -> ref () ; + if ( normals != NULL ) normals -> ref () ; + if ( texcoords != NULL ) texcoords -> ref () ; + if ( colours != NULL ) colours -> ref () ; + + recalcBSphere () ; +} + +ssgBase *ssgTween::clone ( int clone_flags ) +{ + ssgTween *b = new ssgTween ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +void ssgTween::init () +{ + curr_bank = 0 ; + type = ssgTypeTween () ; + + render_vertices = new ssgVertexArray () ; + render_normals = new ssgNormalArray () ; + render_texcoords = new ssgTexCoordArray () ; + render_colours = new ssgColourArray () ; + + render_vertices -> ref () ; + render_normals -> ref () ; + render_texcoords -> ref () ; + render_colours -> ref () ; + + banked_vertices = new ulList ( 2 ) ; + banked_normals = new ulList ( 2 ) ; + banked_texcoords = new ulList ( 2 ) ; + banked_colours = new ulList ( 2 ) ; + + vertices = render_vertices ; + normals = render_normals ; + texcoords = render_texcoords ; + colours = render_colours ; + + recalcBSphere () ; +} + + +ssgTween::ssgTween () +{ + init () ; + gltype = GL_POINTS ; +} + + +ssgTween::ssgTween ( GLenum ty ) +{ + init () ; + gltype = ty ; +} + + +int ssgTween::newBank ( ssgVertexArray *vl, ssgNormalArray *nl, + ssgTexCoordArray *tl, ssgColourArray *cl ) +{ + int bank = banked_vertices -> getNumEntities () ; + + banked_vertices -> addEntity ( vl != NULL ? vl : + ( banked_vertices -> getEntity(bank-1) ) ) ; + + banked_normals -> addEntity ( nl != NULL ? nl : + ( banked_normals -> getEntity(bank-1) ) ) ; + + banked_texcoords -> addEntity ( tl != NULL ? tl : + ( banked_texcoords -> getEntity(bank-1) ) ) ; + + banked_colours -> addEntity ( cl != NULL ? cl : + ( banked_colours -> getEntity(bank-1) ) ) ; + + setBank ( bank ) ; + + vertices -> ref () ; + normals -> ref () ; + texcoords -> ref () ; + colours -> ref () ; + + dirtyBSphere () ; /* Cause parents to redo their bspheres */ + return bank ; +} + + + +int ssgTween::newBank ( int newVertices , int newNormals, + int newTexCoords, int newColours ) +{ + return newBank ( newVertices ? ( new ssgVertexArray () ) : NULL, + newNormals ? ( new ssgNormalArray () ) : NULL, + newTexCoords ? ( new ssgTexCoordArray () ) : NULL, + newColours ? ( new ssgColourArray () ) : NULL ) ; +} + + +void ssgTween::setBank ( int bank ) +{ + assert ( bank < banked_vertices -> getNumEntities () ) ; + + curr_bank = bank ; + + vertices = (ssgVertexArray *) banked_vertices -> getEntity ( bank ) ; + normals = (ssgNormalArray *) banked_normals -> getEntity ( bank ) ; + texcoords = (ssgTexCoordArray *) banked_texcoords -> getEntity ( bank ) ; + colours = (ssgColourArray *) banked_colours -> getEntity ( bank ) ; +} + + +void ssgTween::setVertices ( ssgVertexArray *vl ) +{ + banked_vertices -> replaceEntity ( curr_bank, vl ) ; + ssgVtxTable::setVertices ( vl ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ +} + +void ssgTween::setNormals ( ssgNormalArray *nl ) +{ + banked_normals -> replaceEntity ( curr_bank, nl ) ; + ssgVtxTable::setNormals ( nl ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ +} + +void ssgTween::setTexCoords ( ssgTexCoordArray *tl ) +{ + banked_texcoords -> replaceEntity ( curr_bank, tl ) ; + ssgVtxTable::setTexCoords ( tl ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ +} + +void ssgTween::setColours ( ssgColourArray *cl ) +{ + banked_colours -> replaceEntity ( curr_bank, cl ) ; + ssgVtxTable::setColours ( cl ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ +} + +ssgTween::~ssgTween () +{ + /* Need to ref these one more time to avoid problems when the base class destructor + deletes them */ + + vertices -> ref () ; + normals -> ref () ; + texcoords -> ref () ; + colours -> ref () ; + + for ( int i = 0 ; i < getNumBanks () ; i++ ) + { + ssgDeRefDelete ( (ssgVertexArray *) banked_vertices -> getEntity (i)) ; + ssgDeRefDelete ( (ssgNormalArray *) banked_normals -> getEntity (i)) ; + ssgDeRefDelete ( (ssgTexCoordArray *) banked_texcoords -> getEntity (i)) ; + ssgDeRefDelete ( (ssgColourArray *) banked_colours -> getEntity (i)) ; + } + + delete banked_vertices ; + delete banked_normals ; + delete banked_texcoords ; + delete banked_colours ; + + ssgDeRefDelete ( render_vertices ) ; + ssgDeRefDelete ( render_normals ) ; + ssgDeRefDelete ( render_texcoords ) ; + ssgDeRefDelete ( render_colours ) ; +} + + +void ssgTween::recalcBSphere () +{ + emptyBSphere () ; + bbox . empty () ; + + for ( int b = 0 ; b < banked_vertices -> getNumEntities () ; b++ ) + { + ssgVertexArray *va = (ssgVertexArray *) banked_vertices -> getEntity ( b ) ; + + for ( int i = 0 ; i < va -> getNum() ; i++ ) + bbox . extend ( va->get(i) ) ; + } + + extendBSphere ( & bbox ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ +} + + +void ssgTween::draw () +{ + if ( ! preDraw () ) + return ; + + if ( _ssgCurrentContext-> stateOverridden () ) + _ssgCurrentContext -> overriddenState () -> apply () ; + else + if ( hasState () ) + getState () -> apply () ; + + stats_num_leaves++ ; + stats_num_vertices += getNumVertices() ; + + float tstate = _ssgGetCurrentTweenState () ; + int num_banks = banked_vertices -> getNumEntities () ; + + if ( tstate < 0.0f ) tstate = 0.0f ; + + int state1 = (int) floor ( tstate ) ; + int state2 = state1 + 1 ; + float tween = tstate - (float) state1 ; + + if ( _ssgGetCurrentTweenMode () == SSGTWEEN_REPEAT ) + { + state1 %= num_banks ; + state2 %= num_banks ; + } + else + { + if ( state1 >= num_banks ) state1 = num_banks - 1 ; + if ( state2 >= num_banks ) state2 = num_banks - 1 ; + } + + if ( state1 == state2 ) tween = 0.0f ; + + int l1, l2 ; + + /* Lerp the vertices... */ + + ssgVertexArray *v1 = (ssgVertexArray *) banked_vertices->getEntity (state1) ; + ssgVertexArray *v2 = (ssgVertexArray *) banked_vertices->getEntity (state2) ; + l1 = v1 -> getNum () ; + l2 = v2 -> getNum () ; + + assert ( l1 == l2 ) ; + + if ( render_vertices -> getNum () < l1 ) + render_vertices -> setNum ( l1 ) ; + + if ( v1 == v2 ) + vertices = v1 ; + else + { + vertices = render_vertices ; + + for ( int i = 0 ; i < l1 ; i++ ) + sgLerpVec3 ( vertices->get(i),v1->get(i),v2->get(i), tween ) ; + } + + /* Lerp the normals */ + + ssgNormalArray *n1 = (ssgNormalArray *) banked_normals->getEntity (state1) ; + ssgNormalArray *n2 = (ssgNormalArray *) banked_normals->getEntity (state2) ; + l1 = n1 -> getNum () ; + l2 = n2 -> getNum () ; + + assert ( l1 == l2 ) ; + + if ( render_normals -> getNum () < l1 ) + render_normals -> setNum ( l1 ) ; + + if ( n1 == n2 ) + normals = n1 ; + else + { + normals = render_normals ; + + for ( int i = 0 ; i < l1 ; i++ ) + sgLerpVec3 ( normals->get(i),n1->get(i),n2->get(i), tween ) ; + } + + /* Lerp the texcoords */ + + ssgTexCoordArray *t1 = (ssgTexCoordArray *) banked_texcoords->getEntity (state1) ; + ssgTexCoordArray *t2 = (ssgTexCoordArray *) banked_texcoords->getEntity (state2) ; + l1 = t1 -> getNum () ; + l2 = t2 -> getNum () ; + + assert ( l1 == l2 ) ; + + if ( render_texcoords -> getNum () < l1 ) + render_texcoords -> setNum ( l1 ) ; + + if ( t1 == t2 ) + texcoords = t1 ; + else + { + texcoords = render_texcoords ; + + for ( int i = 0 ; i < l1 ; i++ ) + sgLerpVec2 ( texcoords->get(i),t1->get(i),t2->get(i), tween ) ; + } + + /* Lerp the colours */ + + ssgColourArray *c1 = (ssgColourArray *) banked_colours->getEntity (state1) ; + ssgColourArray *c2 = (ssgColourArray *) banked_colours->getEntity (state2) ; + l1 = c1 -> getNum () ; + l2 = c2 -> getNum () ; + + assert ( l1 == l2 ) ; + + if ( render_colours -> getNum () < l1 ) + render_colours -> setNum ( l1 ) ; + + if ( c1 == c2 ) + colours = c1 ; + else + { + colours = render_colours ; + + for ( int i = 0 ; i < l1 ; i++ ) + sgLerpVec4 ( colours->get(i),c1->get(i),c2->get(i), tween ) ; + } + + draw_geometry () ; + + setBank ( state1 ) ; + + if ( postDrawCB != NULL ) + (*postDrawCB)(this) ; +} + + + +void ssgTween::transform ( const sgMat4 m ) +{ + int prev_bank = curr_bank ; + + for ( int i = 0 ; i < getNumBanks(); i++ ) + { + // see if this bank has been transformed already + // (it is really a deeper problem, but this will work in simple cases) + int j ; + for ( j = 0 ; j < i ; j++ ) + if ( banked_vertices -> getEntity ( i ) == banked_vertices -> getEntity ( j ) ) + break; + if ( j == i ) + { + setBank ( i ) ; + ssgVtxTable::transform ( m ) ; + } + } + + setBank ( prev_bank ) ; +} + + + +int ssgTween::load ( FILE *fd ) +{ + sgVec3 temp; + int num_banks ; + + _ssgReadVec3 ( fd, temp ); bbox.setMin( temp ) ; + _ssgReadVec3 ( fd, temp ); bbox.setMax( temp ) ; + _ssgReadInt ( fd, (int *)(&gltype) ) ; + _ssgReadInt ( fd, & num_banks ) ; + + if ( ! ssgLeaf::load(fd) ) + return FALSE ; + + for ( int i = 0 ; i < num_banks ; i++ ) + { + if ( ! _ssgLoadObject (fd,(ssgBase **)&vertices, ssgTypeVertexArray() ) || + ! _ssgLoadObject (fd,(ssgBase **)&normals, ssgTypeNormalArray() ) || + ! _ssgLoadObject (fd,(ssgBase **)&texcoords, ssgTypeTexCoordArray()) || + ! _ssgLoadObject (fd,(ssgBase **)&colours, ssgTypeColourArray() ) ) + return FALSE ; + + newBank ( vertices, normals, texcoords, colours ) ; + } + + return TRUE ; +} + + +int ssgTween::save ( FILE *fd ) +{ + int num_banks = banked_vertices -> getNumEntities () ; + _ssgWriteVec3 ( fd, bbox.getMin() ) ; + _ssgWriteVec3 ( fd, bbox.getMax() ) ; + _ssgWriteInt ( fd, (int) gltype ) ; + _ssgWriteInt ( fd, num_banks ) ; + + if ( ! ssgLeaf::save(fd) ) + return FALSE ; + + for ( int i = 0 ; i < num_banks ; i++ ) + { + setBank ( i ) ; + + if ( ! _ssgSaveObject ( fd, vertices ) || + ! _ssgSaveObject ( fd, normals ) || + ! _ssgSaveObject ( fd, texcoords ) || + ! _ssgSaveObject ( fd, colours ) ) + return FALSE ; + } + + return TRUE ; +} + + + + +void ssgTween::print ( FILE *fd, char *indent, int how_much ) +{ + char in [ 100 ] ; + + if ( how_much == 0 ) + return ; + + sprintf ( in, "%s ", indent ); + + ssgLeaf ::print ( fd, indent, how_much ) ; + + vertices -> print ( fd, in, how_much ) ; + normals -> print ( fd, in, how_much ) ; + texcoords -> print ( fd, in, how_much ) ; + colours -> print ( fd, in, how_much ) ; +} + + + diff --git a/src/ssg/ssgTweenController.cxx b/src/ssg/ssgTweenController.cxx new file mode 100644 index 0000000..623e85c --- /dev/null +++ b/src/ssg/ssgTweenController.cxx @@ -0,0 +1,98 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgTweenController.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +const char *ssgTweenController::getTypeName (void) { return "ssgTweenController" ; } + +void ssgTweenController::copy_from ( ssgTweenController *src, int clone_flags ) +{ + selectBank ( src->getCurrBank () ) ; + ssgBranch::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgTweenController::clone ( int clone_flags ) +{ + ssgTweenController *b = new ssgTweenController ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + + +ssgTweenController::ssgTweenController (void) +{ + type = ssgTypeTweenController () ; + curr_bank = 0.0f ; +} + + +ssgTweenController::~ssgTweenController (void) +{ + removeAllKids () ; +} + + +void ssgTweenController::cull ( sgFrustum *f, sgMat4 m, int test_needed ) +{ + float old_state = _ssgGetCurrentTweenState () ; + int old_mode = _ssgGetCurrentTweenMode () ; + + _ssgSetCurrentTweenSettings ( curr_bank, mode ) ; + + ssgBranch::cull ( f, m, test_needed ) ; + + _ssgSetCurrentTweenSettings ( old_state, old_mode ) ; +} + + + +int ssgTweenController::load ( FILE *fd ) +{ + _ssgReadFloat ( fd, & curr_bank ) ; + + return ssgBranch::load ( fd ) ; +} + + +int ssgTweenController::save ( FILE *fd ) +{ + _ssgWriteFloat ( fd, curr_bank ) ; + return ssgBranch::save ( fd ) ; +} + + + +void ssgTweenController::print ( FILE *fd, char *indent, int how_much ) +{ + if ( how_much == 0 ) + return ; + + fprintf ( fd, "%sCurrent Bank = %f\n", indent, curr_bank ); + + ssgBranch::print ( fd, indent, how_much ) ; +} + + diff --git a/src/ssg/ssgVTable.cxx b/src/ssg/ssgVTable.cxx new file mode 100644 index 0000000..3098eb6 --- /dev/null +++ b/src/ssg/ssgVTable.cxx @@ -0,0 +1,869 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgVTable.cxx 1938 2004-06-24 16:30:55Z sjbaker $ +*/ + + +#include "ssgLocal.h" + +void ssgVTable::copy_from ( ssgVTable *src, int clone_flags ) +{ + ssgLeaf::copy_from ( src, clone_flags ) ; + + gltype = src -> getPrimitiveType () ; + indexed = src -> isIndexed () ; + + num_vertices = src -> getNumVertices () ; + num_normals = src -> getNumNormals () ; + num_colours = src -> getNumColours () ; + num_texcoords = src -> getNumTexCoords () ; + + /* + I should probably copy these arrays if SSG_CLONE_GEOMETRY + is set - but life is short and ssgVTable is obsolete - + and the inability to free the space that would create + was the main reason to obsolete ssgVTable. + */ + + src -> getVertexList ( (void **) & vertices , & v_index ) ; + src -> getNormalList ( (void **) & normals , & n_index ) ; + src -> getTexCoordList ( (void **) & texcoords, & t_index ) ; + src -> getColourList ( (void **) & colours , & c_index ) ; + + recalcBSphere () ; +} + + +ssgBase *ssgVTable::clone ( int clone_flags ) +{ + ssgVTable *b = new ssgVTable ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgVTable::ssgVTable () +{ + type = ssgTypeVTable () ; + gltype = GL_POINTS ; + indexed = FALSE ; + num_vertices = 0 ; v_index = NULL ; vertices = NULL ; + num_normals = 0 ; n_index = NULL ; normals = NULL ; + num_texcoords = 0 ; t_index = NULL ; texcoords = NULL ; + num_colours = 0 ; c_index = NULL ; colours = NULL ; +} + + +ssgVTable::ssgVTable ( GLenum ty, + int nv, unsigned short *vi, sgVec3 *vl, + int nn, unsigned short *ni, sgVec3 *nl, + int nt, unsigned short *ti, sgVec2 *tl, + int nc, unsigned short *ci, sgVec4 *cl ) +{ + type = ssgTypeVTable () ; + gltype = ty ; + + indexed = TRUE ; + num_vertices = nv ; v_index = vi ; vertices = vl ; + num_normals = nn ; n_index = ni ; normals = nl ; + num_texcoords = nt ; t_index = ti ; texcoords = tl ; + num_colours = nc ; c_index = ci ; colours = cl ; + + recalcBSphere () ; +} + + + +ssgVTable::ssgVTable ( GLenum ty, + int nv, sgVec3 *vl, + int nn, sgVec3 *nl, + int nt, sgVec2 *tl, + int nc, sgVec4 *cl ) +{ + gltype = ty ; + type = ssgTypeVTable () ; + + indexed = FALSE ; + num_vertices = nv ; vertices = vl ; + num_normals = nn ; normals = nl ; + num_texcoords = nt ; texcoords = tl ; + num_colours = nc ; colours = cl ; + + recalcBSphere () ; +} + + +ssgVTable::~ssgVTable () +{ +} + + +void ssgVTable::getTriangle ( int n, short *v1, short *v2, short *v3 ) +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + *v1 = 0 ; + *v2 = n+1 ; + *v3 = n+2 ; + return ; + + case GL_TRIANGLES : + *v1 = n*3 ; + *v2 = n*3+1 ; + *v3 = n*3+2 ; + return ; + + case GL_TRIANGLE_STRIP : + case GL_QUAD_STRIP : + if ( n & 1 ) + { + *v3 = n ; + *v2 = n+1 ; + *v1 = n+2 ; + } + else + { + *v1 = n ; + *v2 = n+1 ; + *v3 = n+2 ; + } + return ; + + case GL_QUADS : + *v1 = (n/2)*4 + (n&1) + 0 ; + *v2 = (n/2)*4 + (n&1) + 1 ; + *v3 = (n/2)*4 + (n&1) + 2 ; + return ; + + default : return ; + } +} + + +int ssgVTable::getNumTriangles () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + return getNumVertices() - 2 ; + + case GL_TRIANGLES : + return getNumVertices() / 3 ; + + case GL_TRIANGLE_STRIP : + return getNumVertices() - 2 ; + + case GL_QUADS : + return ( getNumVertices() / 4 ) * 2 ; + + case GL_QUAD_STRIP : + return ( ( getNumVertices() - 2 ) / 2 ) * 2 ; + + default : break ; + } + + return 0 ; +} + +int ssgVTable::getNumLines () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : + return 0; + case GL_LINES : + // wk: FIXME: check the 3 following formulas. I don't have an OpenGL bokk at hand currently :-( + return getNumVertices()/2; + case GL_LINE_LOOP : + return getNumVertices(); + case GL_LINE_STRIP : + return getNumVertices()-1; + default : break ; + } + assert(false); /* Should never get here */ + return 0 ; +} + +void ssgVTable::getLine ( int n, short *v1, short *v2 ) +{ + assert( n>=0 ); + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : + assert(false); + case GL_LINES : + // wk: FIXME: check the 3 following formulas. I don't have an OpenGL bokk at hand currently :-( + assert ( 2*n+1 < getNumVertices() ); + *v1 = 2*n; + *v2 = 2*n+1 ; + return ; + case GL_LINE_LOOP : + assert ( n < getNumVertices() ); + *v1 = n; + if ( n == getNumVertices()-1 ) + *v2 = 0; + else + *v2 = n+1; + return ; + case GL_LINE_STRIP : + assert ( n < getNumVertices()-1 ); + *v1 = n; + *v2 = n+1; + return; + default : + break ; + } + assert(false); /* Should never get here */ + return ; +} + +void ssgVTable::transform ( const sgMat4 m ) +{ + int i ; + + for ( i = 0 ; i < num_vertices ; i++ ) + sgXformPnt3 ( vertices[i], vertices[i], m ) ; + + for ( i = 0 ; i < num_normals ; i++ ) + sgXformVec3 ( normals[i], normals[i], m ) ; + + recalcBSphere () ; +} + + +void ssgVTable::recalcBSphere () +{ + emptyBSphere () ; + bbox . empty () ; + + int i ; + + if ( indexed ) + for ( i = 0 ; i < num_vertices ; i++ ) + bbox . extend ( vertices [ v_index [ i ] ] ) ; + else + for ( i = 0 ; i < num_vertices ; i++ ) + bbox . extend ( vertices [ i ] ) ; + + extendBSphere ( & bbox ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ + bsphere_is_invalid = FALSE ; +} + + +void ssgVTable::draw () +{ + if ( ! preDraw () ) + return ; + + if ( _ssgCurrentContext-> stateOverridden () ) + _ssgCurrentContext -> overriddenState () -> apply () ; + else + if ( hasState () ) + getState () -> apply () ; + + stats_num_leaves++ ; + stats_num_vertices += num_vertices ; + +#ifdef _SSG_USE_DLIST + if ( dlist ) + glCallList ( dlist ) ; + else +#endif + draw_geometry () ; + + if ( postDrawCB != NULL ) + (*postDrawCB)(this) ; +} + +void ssgVTable::drawHighlight ( sgVec4 /* colour */, int /* vertex id */ ) +{ +} + +void ssgVTable::drawHighlight ( sgVec4 /* colour */ ) +{ +} + +void ssgVTable::pick ( int baseName ) +{ + int i ; + + glPushName ( baseName ) ; + + if ( indexed ) + { + glBegin ( gltype ) ; + + for ( i = 0 ; i < num_vertices ; i++ ) + glVertex3fv( vertices [ v_index [ i ]] ) ; + + glEnd () ; + + /* Then test each vertex in turn */ + + for ( i = 0 ; i < num_vertices ; i++ ) + { + glLoadName ( baseName + i + 1 ) ; + glBegin ( GL_POINTS ) ; + glVertex3fv( vertices [ v_index [ i ]] ) ; + glEnd () ; + } + } + else + { + glBegin ( gltype ) ; + + for ( i = 0 ; i < num_vertices ; i++ ) + { + glLoadName ( baseName + i + 1 ) ; + glVertex3fv( vertices [ i ] ) ; + } + + glEnd () ; + + /* Then test each vertex in turn */ + + for ( i = 0 ; i < num_vertices ; i++ ) + { + glLoadName ( baseName + i + 1 ) ; + glBegin ( GL_POINTS ) ; + glVertex3fv( vertices [ i ] ) ; + glEnd () ; + } + } + + glPopName () ; +} + +void ssgVTable::draw_geometry () +{ + _ssgCurrStatistics . bumpVertexCount ( num_vertices ) ; + _ssgCurrStatistics . bumpLeafCount ( 1 ) ; + + if ( indexed ) + { + glBegin ( gltype ) ; + + if ( num_colours == 0 ) glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + if ( num_colours == 1 ) glColor4fv ( colours [ c_index [ 0 ]] ) ; + if ( num_normals == 1 ) glNormal3fv ( normals [ n_index [ 0 ]] ) ; + + for ( int i = 0 ; i < num_vertices ; i++ ) + { + if ( num_colours > 1 ) glColor4fv ( colours [ c_index [ i ]] ) ; + if ( num_normals > 1 ) glNormal3fv ( normals [ n_index [ i ]] ) ; + if ( num_texcoords > 1 ) glTexCoord2fv ( texcoords [ t_index [ i ]] ) ; + + glVertex3fv ( vertices [ v_index [ i ]] ) ; + } + + glEnd () ; + } + else + { + glBegin ( gltype ) ; + + if ( num_colours == 0 ) glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + if ( num_colours == 1 ) glColor4fv ( colours [ 0 ] ) ; + if ( num_normals == 1 ) glNormal3fv ( normals [ 0 ] ) ; + + for ( int i = 0 ; i < num_vertices ; i++ ) + { + if ( num_colours > 1 ) glColor4fv ( colours [ i ] ) ; + if ( num_normals > 1 ) glNormal3fv ( normals [ i ] ) ; + if ( num_texcoords > 1 ) glTexCoord2fv ( texcoords [ i ] ) ; + + glVertex3fv ( vertices [ i ] ) ; + } + + glEnd () ; + } +} + + + +void ssgVTable::hot_triangles ( sgVec3 s, sgMat4 m, int /* test_needed */ ) +{ + int nt = getNumTriangles () ; + + stats_hot_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + + /* + Does the X/Y coordinate lie outside the triangle's bbox, or + does the Z coordinate lie beneath the bbox ? + */ + + if ( ( s[0] < vv1[0] && s[0] < vv2[0] && s[0] < vv3[0] ) || + ( s[1] < vv1[1] && s[1] < vv2[1] && s[1] < vv3[1] ) || + ( s[0] > vv1[0] && s[0] > vv2[0] && s[0] > vv3[0] ) || + ( s[1] > vv1[1] && s[1] > vv2[1] && s[1] > vv3[1] ) || + ( s[2] < vv1[2] && s[2] < vv2[2] && s[2] < vv3[2] ) ) + continue ; + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + + if ( _ssgIsHotTest ) + { + /* No HOT from upside-down or vertical triangles */ + + if ( getCullFace() && plane [ 2 ] <= 0 ) + continue ; + + /* Find the point vertically below the text point + as it crosses the plane of the polygon */ + + float z = sgHeightOfPlaneVec2 ( plane, s ) ; + + /* No HOT from below the triangle */ + + if ( z > s[2] ) + continue ; + + /* Outside the vertical extent of the triangle? */ + + if ( ( z < vv1[2] && z < vv2[2] && z < vv3[2] ) || + ( z > vv1[2] && z > vv2[2] && z > vv3[2] ) ) + continue ; + } + + /* + Now it gets messy - the isect point is inside + the bbox of the triangle - but that's not enough. + Is it inside the triangle itself? + */ + + float e1 = s [0] * vv1[1] - s [1] * vv1[0] ; + float e2 = s [0] * vv2[1] - s [1] * vv2[0] ; + float e3 = s [0] * vv3[1] - s [1] * vv3[0] ; + float ep1 = vv1[0] * vv2[1] - vv1[1] * vv2[0] ; + float ep2 = vv2[0] * vv3[1] - vv2[1] * vv3[0] ; + float ep3 = vv3[0] * vv1[1] - vv3[1] * vv1[0] ; + + float ap = (float) fabs ( ep1 + ep2 + ep3 ) ; + float ai = (float) ( fabs ( e1 + ep1 - e2 ) + + fabs ( e2 + ep2 - e3 ) + + fabs ( e3 + ep3 - e1 ) ) ; + + if ( ai > ap * 1.01 ) + continue ; + + _ssgAddHit ( this, i, m, plane ) ; + } +} + +void ssgVTable::los_triangles ( sgVec3 s, sgMat4 m, int /* test_needed */ ) +{ + int nt = getNumTriangles () ; + + stats_los_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + SGfloat edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + SGfloat det,inv_det; + SGfloat /*t,*/u,v; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + sgVec3 cam; + cam[0] = m[0][3]; + cam[1] = m[1][3]; + cam[2] = m[2][3]; + //if ( _ssgIsLosTest ) + //{ + + /* find vectors for two edges sharing vert0 */ + sgSubVec3(edge1, vv2, vv1); + sgSubVec3(edge2, vv3, vv1); + + /* begin calculating determinant - also used to calculate U parameter */ + sgVectorProductVec3(pvec, s, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = sgScalarProductVec3(edge1, pvec); + + if (det > -0.0000001 && det < 0.0000001) continue; + inv_det = (float)1.0 / det; + + /* calculate distance from vert0 to ray origin */ + sgSubVec3(tvec, cam, vv1); + + /* calculate U parameter and test bounds */ + u = sgScalarProductVec3(tvec, pvec) * inv_det; + if (u < 0.0 || u > 1.0) + continue; + + /* prepare to test V parameter */ + sgVectorProductVec3(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + v = sgScalarProductVec3(s, qvec) * inv_det; + if (v < 0.0 || u + v > 1.0) + continue; + + /* calculate t, ray intersects triangle */ + //t = sgScalarProductVec3(edge2, qvec) * inv_det; + //} + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + _ssgAddHit ( this, i, m, plane ) ; + } +} + +void ssgVTable::isect_triangles ( sgSphere *s, sgMat4 m, int test_needed ) +{ + int nt = getNumTriangles () ; + + stats_isect_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + + if ( ! test_needed ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + + float dp = (float) fabs ( sgDistToPlaneVec3 ( plane, s->getCenter() ) ) ; + + if ( dp > s->getRadius() ) + continue ; + + /* + The BSphere touches the plane containing + the triangle - but does it actually touch + the triangle itself? Let's erect some + vertical walls around the triangle. + */ + + /* + Construct a 'wall' as a plane through + two vertices and a third vertex made + by adding the surface normal to the + first of those two vertices. + */ + + sgVec3 vvX ; + sgVec4 planeX ; + + sgAddVec3 ( vvX, plane, vv1 ) ; + sgMakePlane ( planeX, vv1, vv2, vvX ) ; + float dp1 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp1 > s->getRadius() ) + continue ; + + sgAddVec3 ( vvX, plane, vv2 ) ; + sgMakePlane ( planeX, vv2, vv3, vvX ) ; + float dp2 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp2 > s->getRadius() ) + continue ; + + sgAddVec3 ( vvX, plane, vv3 ) ; + sgMakePlane ( planeX, vv3, vv1, vvX ) ; + float dp3 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp3 > s->getRadius() ) + continue ; + + /* + OK, so we now know that the sphere + intersects the plane of the triangle + and is not more than one radius outside + the walls. However, you can still get + close enough to the wall and to the + triangle itself and *still* not + intersect the triangle itself. + + However, if the center is inside the + triangle then we don't need that + costly test. + */ + + if ( dp1 <= 0 && dp2 <= 0 && dp3 <= 0 ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + + /* + ...now we really need that costly set of tests... + + If the sphere penetrates the plane of the triangle + and the plane of the wall, then we can use pythagoras + to determine if the sphere actually intersects that + edge between the wall and the triangle. + + if ( dp_sqd + dp1_sqd > radius_sqd ) ...in! else ...out! + */ + + float r2 = s->getRadius() * s->getRadius() - dp * dp ; + + if ( dp1 * dp1 <= r2 || + dp2 * dp2 <= r2 || + dp3 * dp3 <= r2 ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + } +} + + +void ssgVTable::print ( FILE *fd, char *indent, int how_much ) +{ + ssgLeaf::print ( fd, indent, how_much ) ; +} + + + +int ssgVTable::load ( FILE *fd ) +{ + sgVec3 temp; + + _ssgReadVec3 ( fd, temp ); bbox.setMin( temp ) ; + _ssgReadVec3 ( fd, temp ); bbox.setMax( temp ) ; + _ssgReadInt ( fd, &indexed ) ; + _ssgReadInt ( fd, (int *)(&gltype) ) ; + + _ssgReadInt ( fd, &num_vertices ) ; + _ssgReadInt ( fd, &num_normals ) ; + _ssgReadInt ( fd, &num_texcoords) ; + _ssgReadInt ( fd, &num_colours ) ; + + int i ; + int max ; + + /* Vertices */ + + if ( indexed ) + { + v_index = new unsigned short [ num_vertices ] ; + _ssgReadUShort ( fd, num_vertices, v_index ) ; + + max = 0 ; + + for ( i = 0 ; i < num_vertices ; i++ ) + if ( v_index [ i ] > max ) max = v_index[i] ; + } + else + max = num_vertices ; + + vertices = new sgVec3 [ max ] ; + _ssgReadFloat ( fd, max * 3, (float *) vertices ) ; + + /* Normals */ + + if ( indexed ) + { + n_index = new unsigned short [ num_normals ] ; + _ssgReadUShort ( fd, num_normals, n_index ) ; + + max = 0 ; + + for ( i = 0 ; i < num_normals ; i++ ) + if ( n_index [ i ] > max ) max = n_index[i] ; + } + else + max = num_normals ; + + normals = new sgVec3 [ max ] ; + _ssgReadFloat ( fd, max * 3, (float *) normals ) ; + + /* Texture Coordinates */ + + if ( indexed ) + { + t_index = new unsigned short [ num_texcoords ] ; + _ssgReadUShort ( fd, num_texcoords, t_index ) ; + + max = 0 ; + + for ( i = 0 ; i < num_texcoords ; i++ ) + if ( t_index [ i ] > max ) max = t_index[i] ; + } + else + max = num_texcoords ; + + texcoords = new sgVec2 [ max ] ; + _ssgReadFloat ( fd, max * 2, (float *) texcoords ) ; + + /* Colours */ + + if ( indexed ) + { + c_index = new unsigned short [ num_colours ] ; + _ssgReadUShort ( fd, num_colours, c_index ) ; + + max = 0 ; + + for ( i = 0 ; i < num_colours ; i++ ) + if ( c_index [ i ] > max ) max = c_index[i] ; + } + else + max = num_colours ; + + colours = new sgVec4 [ max ] ; + _ssgReadFloat ( fd, max * 4, (float *) colours ) ; + + return ssgLeaf::load(fd) ; +} + +int ssgVTable::save ( FILE *fd ) +{ + _ssgWriteVec3 ( fd, bbox.getMin() ) ; + _ssgWriteVec3 ( fd, bbox.getMax() ) ; + _ssgWriteInt ( fd, indexed ) ; + _ssgWriteInt ( fd, (int) gltype ) ; + + _ssgWriteInt ( fd, num_vertices ) ; + _ssgWriteInt ( fd, num_normals ) ; + _ssgWriteInt ( fd, num_texcoords) ; + _ssgWriteInt ( fd, num_colours ) ; + + int i ; + int max ; + + /* Vertices */ + + if ( indexed ) + { + max = 0 ; + + for ( i = 0 ; i < num_vertices ; i++ ) + if ( v_index [ i ] > max ) max = v_index[i] ; + + _ssgWriteUShort ( fd, num_vertices, v_index ) ; + } + else + max = num_vertices ; + + _ssgWriteFloat ( fd, max * 3, (float *)vertices ) ; + + /* Normals */ + + if ( indexed ) + { + max = 0 ; + + for ( i = 0 ; i < num_normals ; i++ ) + if ( n_index [ i ] > max ) max = n_index[i] ; + + _ssgWriteUShort ( fd, num_normals, n_index ) ; + } + else + max = num_normals ; + + _ssgWriteFloat ( fd, max * 3, (float *)normals ) ; + + /* Texture Coordinates */ + + if ( indexed ) + { + max = 0 ; + + for ( i = 0 ; i < num_texcoords ; i++ ) + if ( t_index [ i ] > max ) max = t_index[i] ; + + _ssgWriteUShort ( fd, num_texcoords, t_index ) ; + } + else + max = num_texcoords ; + + _ssgWriteFloat ( fd, max * 2, (float *)texcoords ) ; + + /* Colours */ + + if ( indexed ) + { + max = 0 ; + + for ( i = 0 ; i < num_colours ; i++ ) + if ( c_index [ i ] > max ) max = c_index[i] ; + + _ssgWriteUShort ( fd, num_colours, c_index ) ; + } + else + max = num_colours ; + + _ssgWriteFloat ( fd, max * 4, (float *)colours ) ; + + return ssgLeaf::save(fd) ; +} + + + diff --git a/src/ssg/ssgVertSplitter.cxx b/src/ssg/ssgVertSplitter.cxx new file mode 100644 index 0000000..e23e608 --- /dev/null +++ b/src/ssg/ssgVertSplitter.cxx @@ -0,0 +1,351 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright 2003, Andrew Ross +// +// Walk through an indexed triangle list, splitting vertices that +// share "sharp" edges, and calculate normals for the resulting mesh. +// +// This code should really live inside the OptVertexList class in +// ssgOptimizer.cxx, whose data model is almost 1:1 compatible with +// the indexed triangle representation here. It is a separate file +// because it comes from separate code that Andy Ross wrote for a +// different project, and this was the cleanest way to do the port. +// +//////////////////////////////////////////////////////////////////////// + +#include "ssgVertSplitter.h" + +#include +#include "sg.h" + +// +//////////////////////////////////////////////////////////////////////// + +int ssgVertSplitter::origVert(int i) +{ + return _newVertMap[i]; +} + +ssgVertSplitter::ssgVertSplitter(int nVerts, int nTris) +{ + setSharpAngle(61); + _nVerts = _origVerts = _vertsAlloced = nVerts; + _verts = new float[3*_nVerts]; + _norms = new float[3*_nVerts]; + _nTris = nTris; + _tris = new Tri[3*_nTris]; + _triNorms = new float[3*_nTris]; + _geomVerts = new int[_nVerts]; + + _newVertMap = 0; +} + +ssgVertSplitter::~ssgVertSplitter() +{ + delete[] _verts; + delete[] _norms; + delete[] _tris; + delete[] _triNorms; + delete[] _newVertMap; + delete[] _geomVerts; +} + +void ssgVertSplitter::setTri(int tidx, int va, int vb, int vc) +{ + _tris[tidx].verts[0] = _tris[tidx].oVerts[0] = va; + _tris[tidx].verts[1] = _tris[tidx].oVerts[1] = vb; + _tris[tidx].verts[2] = _tris[tidx].oVerts[2] = vc; + _tris[tidx].degenerate = false; +} + +int* ssgVertSplitter::getTri(int t) +{ + return &(_tris[t].verts[0]); +} + +void ssgVertSplitter::setSharpAngle(float deg) +{ + _threshold = (float) cos(deg*SG_DEGREES_TO_RADIANS); +} + +int ssgVertSplitter::findTriWithVert(int fidx, int vidx, int* tris, int ntris) +{ + // check each triangle in the list for a matching edge + for(int t=0; tverts[i] == oldIdx) { + t->verts[i] = newIdx; + return; + } + } +} + +// Find geometrically identical vertices and fixup triangle indices to +// point to the first one. +void ssgVertSplitter::condenseGeometry() +{ + for(int i=0; i<_nVerts; i++) { + _geomVerts[i] = i; + for(int j=0; j= 0) continue; + + // No existing record, make one. + if(!used[sv] && sv >= _origVerts) fv = sv; + else if(!used[ov]) fv = ov; + else { fv = _nVerts++; _nextNewVert++; } + + int r = nvrecs++; + vrecs[r].sv = sv; + vrecs[r].ov = ov; + vrecs[r].fv = fv; + // __builtin_printf("s %d o %d f %d\n", sv, ov, fv); + + sgCopyVec3(vert(fv), vert(sv)); + sgCopyVec3(norm(fv), norm(sv)); + if(fv >= _origVerts) + _newVertMap[fv - _origVerts] = ov; + used[fv] = true; + _tris[i].verts[j] = fv; + } + } + delete[] vrecs; + delete[] used; +} + +void ssgVertSplitter::splitAndCalcNormals() +{ + int i, j; + float zero[] = {0,0,0}; + for(i=0; i<_nVerts; i++) sgCopyVec3(norm(i), zero); + + // First, find duplicate vertices and coalesce the geometry + condenseGeometry(); + + // Calculate a normal for each face, and count the number of + // references to each vertex. + float* faceNorms = _triNorms; + int* vCounts = new int[_nVerts]; + for(i=0; i<_nVerts; i++) + vCounts[i] = 0; + for(i=0; i<_nTris; i++) { + sgVec3 a, b; + float *fnorm = faceNorms + 3*i; + int* verts = _tris[i].verts ; + sgCopyVec3(a, vert(verts[1])); + sgSubVec3(a, vert(verts[0])); + sgCopyVec3(b, vert(verts[2])); + sgSubVec3(b, vert(verts[0])); + sgVectorProductVec3(fnorm, a, b); + sgNormaliseVec3(fnorm); + vCounts[verts[0]]++; + vCounts[verts[1]]++; + vCounts[verts[2]]++; + + if(verts[0]==verts[1] || verts[1]==verts[2] || verts[2]==verts[0]) + { + _tris[i].degenerate = true; + fnorm[0] = fnorm[1] = 0; + fnorm[2] = 1; + } + } + + // Build a table mapping vertices to arrays of triangle indices + // vTris[v] is a pointer to a list of vCounts[v] triangle indices. + int* vtspace = new int[3*_nTris]; + int** vTris = new int*[_nVerts]; + int* vtptr = vtspace; + for(i=0; i<3*_nTris; i++) vtspace[i] = -1; + for(i=0; i<_nVerts; i++) { + vTris[i] = vtptr; + vtptr += vCounts[i]; + } + int *vctmp = new int[_nVerts]; + for(i=0; i<_nVerts; i++) vctmp[i] = 0; + for(i=0; i<_nTris; i++) { + if(_tris[i].degenerate) continue; + for(int v=0; v<3; v++) + vTris[_tris[i].verts[v]][vctmp[_tris[i].verts[v]]++] = i; + } + delete[] vctmp; + + // Make some temp space for new vertices and their normals + int maxNewVerts = 8*_nVerts; + _newVertMap = new int[maxNewVerts]; + float* newNorms = new float[3*maxNewVerts]; + _nextNewVert = 0; + + // Now the hard part. For each vertex, pick a face. Walk forward + // and backwards from that face until you hit a sharp edge or + // already-done face, summing normals along the way and marking + // off the face as "done". If there are faces left, split off a + // *new* vertex for the remaining ones, pick a remaining face, and + // repeat. Repeat until we run out of faces. + for(int vidx=0; vidx<_nVerts; vidx++) { + while(1) { + // Pick a face + int fidx = -1; + bool someDone = false; + for(int face=0; face= 0) { + float* n = faceNorms + 3*vTris[vidx][nextidx]; + if(sgScalarProductVec3(n, lastNorm) < _threshold) break; + lastNorm = n; + sgAddVec3(fnorm, n); + int oldnext = nextidx; + nextidx = nextTri(nextidx, vidx, vTris[vidx], vCounts[vidx]); + if(someDone) + fixVidx(_tris + vTris[vidx][oldnext], vidx, realvert); + vTris[vidx][oldnext] = -1; + } + lastNorm = lastNorm0; + while(previdx >= 0) { + if(vTris[vidx][previdx] < 0) break; + float* n = faceNorms + 3*vTris[vidx][previdx]; + if(sgScalarProductVec3(n, lastNorm) < _threshold) break; + lastNorm = n; + sgAddVec3(fnorm, faceNorms + 3*vTris[vidx][previdx]); + int oldprev = previdx; + previdx = prevTri(previdx, vidx, vTris[vidx], vCounts[vidx]); + if(someDone) + fixVidx(_tris + vTris[vidx][oldprev], vidx, realvert); + vTris[vidx][oldprev] = -1; + } + } + }; + + // Reallocate the new vertex/normal tables. + _vertsAlloced = _nVerts + maxNewVerts; + float* newVerts = new float[3*_vertsAlloced]; + for(i=0; i<3*_nVerts; i++) + newVerts[i] = _verts[i]; + for(i=0; i<_nextNewVert; i++) + for(j=0; j<3; j++) + newVerts[3*(_nVerts+i)+j] = _verts[3*_newVertMap[i]+j]; + delete[] _verts; + _verts = newVerts; + + float* tmpnorms = new float[3*_vertsAlloced]; + for(i=0; i<3*_nVerts; i++) + tmpnorms[i] = _norms[i]; + for(i=0; i<3*_nextNewVert; i++) + tmpnorms[3*_nVerts+i] = newNorms[i]; + delete[] _norms; + _norms = tmpnorms; + + _nVerts += _nextNewVert; + + // Finally, renormalize everything + for(i=0; i<_nVerts; i++) + if(i >= _origVerts || _geomVerts[i] == i) sgNormaliseVec3(norm(i)); + for(i=0; i<_origVerts; i++) + if(_geomVerts[i] != i) sgCopyVec3(norm(i), norm(_geomVerts[i])); + + // Now, if necessary, duplicate the vertices that got "condensed" + // earlier by creating a new vertex and cloning the normal. + expandDuplicates(); + +#if 0 + static int allOrig = 0; + static int allFinal = 0; + allOrig += _origVerts; + allFinal += _nVerts; + fprintf(stderr, "%d -> %d (%d/%d)\n", _origVerts, _nVerts, + allOrig, allFinal); +#endif + + delete[] vCounts; + delete[] vtspace; + delete[] vTris; + delete[] newNorms; +} diff --git a/src/ssg/ssgVertSplitter.h b/src/ssg/ssgVertSplitter.h new file mode 100644 index 0000000..8a99aab --- /dev/null +++ b/src/ssg/ssgVertSplitter.h @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright 2003, Andrew Ross +// +// Walk through an indexed triangle list, splitting vertices that +// share "sharp" edges, and calculate normals for the resulting mesh. +// +// This code should really live inside the OptVertexList class in +// ssgOptimizer.cxx, whose data model is almost 1:1 compatible with +// the indexed triangle representation here. It is a separate file +// because it comes from separate code that Andy Ross wrote for a +// different project, and this was the cleanest way to do the port. +// +//////////////////////////////////////////////////////////////////////// + +#ifndef _SSGA_VERTSPLITTER_H +#define _SSGA_VERTSPLITTER_H + +class ssgVertSplitter { +public: + ssgVertSplitter(int nVerts, int nTris); + virtual ~ssgVertSplitter(); + void setSharpAngle(float deg); + + float* vert(int i) { return _verts + 3*i; } + float* norm(int i) { return _norms + 3*i; } + + // Initialize a triangle. The vertex indices might change. + void setTri(int tidx, int va, int vb, int vc); + + // Grab the new/different indices post-split + int* getTri(int t); + + // Number of new vertices (indices start after the original list) + int newVerts() { return _nextNewVert; } + + // Original vertex associated with a given "new" vertex + int origVert(int i); + + void splitAndCalcNormals(); + +private: + struct Tri { + int verts[3]; + int oVerts[3]; + bool degenerate; + }; + + void condenseGeometry(); + void expandDuplicates(); + + int findTriWithVert(int fidx, int vidx, int* tris, int ntris); + int nextTri(int fidx, int vidx, int* tris, int ntris); + int prevTri(int fidx, int vidx, int* tris, int ntris); + static void fixVidx(Tri* t, int oldIdx, int newIdx); + + float _threshold; + + int _origVerts; + int _vertsAlloced; + + int _nVerts; + float* _verts; + float* _norms; + + int _nTris; + Tri* _tris; + float* _triNorms; + + int* _newVertMap; + int _nextNewVert; + + int* _geomVerts; + + void DUMP(); +}; + +#endif // _SSGA_VERTSPLITTER_H diff --git a/src/ssg/ssgVtxArray.cxx b/src/ssg/ssgVtxArray.cxx new file mode 100644 index 0000000..ac61af4 --- /dev/null +++ b/src/ssg/ssgVtxArray.cxx @@ -0,0 +1,433 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgVtxArray.cxx 1992 2004-12-01 08:28:05Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +#define HL_DELTA 0.04f + +void ssgVtxArray::copy_from ( ssgVtxArray *src, int clone_flags ) +{ + ssgVtxTable::copy_from ( src, clone_flags ) ; + + ssgDeRefDelete ( indices ) ; + + if ( src->indices != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + indices = (ssgIndexArray *)( src -> indices -> clone ( clone_flags )) ; + else + indices = src -> indices ; + + if ( indices != NULL ) + indices -> ref () ; +} + +ssgBase *ssgVtxArray::clone ( int clone_flags ) +{ + ssgVtxArray *b = new ssgVtxArray ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgVtxArray::ssgVtxArray () : ssgVtxTable() +{ + type = ssgTypeVtxArray () ; + indices = NULL ; +} + + +ssgVtxArray::ssgVtxArray ( GLenum ty, + ssgVertexArray *vl, + ssgNormalArray *nl, + ssgTexCoordArray *tl, + ssgColourArray *cl, + ssgIndexArray *il ) : ssgVtxTable( ty, vl, nl, tl, cl ) +{ + type = ssgTypeVtxArray () ; + + indices = (il!=NULL) ? il : new ssgIndexArray () ; + + indices -> ref () ; +} + + +ssgVtxArray::~ssgVtxArray () +{ + ssgDeRefDelete ( indices ) ; +} + + +void ssgVtxArray::setIndices ( ssgIndexArray *il ) +{ + ssgDeRefDelete ( indices ) ; + indices = il ; + + if ( indices != NULL ) + indices -> ref () ; +} + + +void ssgVtxArray::drawHighlight ( sgVec4 colour ) +{ + _ssgForceLineState () ; + + glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ; + + glDisableClientState ( GL_COLOR_ARRAY ) ; + glDisableClientState ( GL_NORMAL_ARRAY ) ; + glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ; + glEnableClientState ( GL_VERTEX_ARRAY ) ; + + glVertexPointer ( 3, GL_FLOAT, 0, vertices->get(0) ) ; + + glPushAttrib ( GL_POLYGON_BIT ) ; + glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE ) ; + glColor4fv ( colour ) ; + int i = getNumIndices (); + short *ii = indices->get(0); + glDrawElements ( gltype, i, GL_UNSIGNED_SHORT, ii ) ; + glPopAttrib () ; + glPopClientAttrib () ; + glEnable ( GL_DEPTH_TEST ) ; +} + + +void ssgVtxArray::drawHighlight ( sgVec4 colour, int i ) +{ + _ssgForceLineState () ; + + if ( i < 0 || i >= getNumIndices () ) + return ; + + int ii = *( indices->get(i) ); + sgVec3 *vx = (sgVec3 *) vertices -> get(ii) ; + + float x = vx[0][0] ; + float y = vx[0][1] ; + float z = vx[0][2] ; + + sgVec3 t[6] ; + sgSetVec3 ( t[0], x-HL_DELTA,y,z ) ; + sgSetVec3 ( t[1], x+HL_DELTA,y,z ) ; + sgSetVec3 ( t[2], x,y-HL_DELTA,z ) ; + sgSetVec3 ( t[3], x,y+HL_DELTA,z ) ; + sgSetVec3 ( t[4], x,y,z-HL_DELTA ) ; + sgSetVec3 ( t[5], x,y,z+HL_DELTA ) ; + + glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ; + glDisableClientState ( GL_COLOR_ARRAY ) ; + glDisableClientState ( GL_NORMAL_ARRAY ) ; + glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ; + glEnableClientState ( GL_VERTEX_ARRAY ) ; + + glVertexPointer ( 3, GL_FLOAT, 0, t ) ; + glColor4fv ( colour ) ; + glLineWidth ( 4.0f ) ; + glDrawArrays ( GL_LINES, 0, 6 ) ; + glLineWidth ( 1.0f ) ; + glPopClientAttrib ( ) ; + glEnable ( GL_DEPTH_TEST ) ; +} + + + +void ssgVtxArray::pick ( int baseName ) +{ + int i ; + int num_vertices = getNumIndices () ; + + glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ; + glEnableClientState ( GL_VERTEX_ARRAY ) ; + glVertexPointer ( 3, GL_FLOAT, 0, vertices->get(0) ) ; + + /* Test the entire primitive. */ + + glPushName ( baseName ) ; + short *ii = indices->get(0); + glDrawElements ( gltype, num_vertices, GL_UNSIGNED_SHORT, ii ) ; + + /* Then test each vertex in turn */ + + for ( i = 0 ; i < num_vertices ; i++ ) + { + int ii = *( indices->get(i) ); + glLoadName ( baseName + i + 1 ) ; + glBegin ( GL_POINTS ) ; + glArrayElement ( ii ) ; + glEnd () ; + } + + glPopName () ; + + glPopClientAttrib ( ) ; +} + +void ssgVtxArray::removeUnusedVertices() +// this removes any vertices (including normal, TexCoords and colour) +// that are not referenced by the index array +{ + + bool doNormals = FALSE, doTexCoords = FALSE, doColours = FALSE; + + assert(vertices); + if(!indices) + { ulSetError( UL_WARNING, "indices == NULL\n"); + return; + } + if(normals) + if(1 < normals->getNum()) + doNormals = TRUE; + if(texcoords) + if(1 < texcoords->getNum()) + doTexCoords = TRUE; + if(colours) + if(1 < colours->getNum()) + doColours = TRUE; + + long * oldIndex2NewIndex = new long[vertices->getNum()]; + int i; + short oldIndex, newIndex; + for(i=0;igetNum();i++) + oldIndex2NewIndex[i]=-1; // marker for "not used" + + ssgVertexArray *newVL= new ssgVertexArray(); + ssgNormalArray *newNL = NULL; + ssgTexCoordArray *newTL = NULL; + ssgColourArray *newCL = NULL; + + if(doNormals) + newNL = new ssgNormalArray(); + if (doTexCoords) + newTL = new ssgTexCoordArray(); + if (doColours) + newCL = new ssgColourArray(); + + for(i=0; igetNum(); i++) + { oldIndex = *indices->get(i); + if (oldIndex2NewIndex[ oldIndex ] != -1) + indices->set(static_cast(oldIndex2NewIndex[ oldIndex ]), i); + else + { newIndex = newVL->getNum(); + indices->set(newIndex , i); + oldIndex2NewIndex[ oldIndex ] = newIndex; + newVL->add(vertices->get(oldIndex)); + if(doNormals) + newNL->add(normals->get(oldIndex)); + if (doTexCoords) + newTL->add(texcoords->get(oldIndex)); + if (doColours) + newCL->add(colours->get(oldIndex)); + } + } + vertices->deRef(); //ssgDeRefDelete(vertices); + vertices = newVL; + newVL->ref(); + + if(doNormals) + { normals->deRef(); //ssgDeRefDelete(normals); + normals = newNL; + newNL->ref(); + + } + if (doTexCoords) + { texcoords->deRef(); //ssgDeRefDelete(texcoords); + texcoords = newTL; + newTL->ref(); + } + if (doColours) + { colours->deRef(); //ssgDeRefDelete(colours); + colours = newCL; + newCL->ref(); + } + delete [] oldIndex2NewIndex; +} + +void ssgVtxArray::draw_geometry () +{ + int num_colours = getNumColours () ; + int num_normals = getNumNormals () ; + int num_texcoords = getNumTexCoords () ; + + sgVec3 *nm = (sgVec3 *) normals -> get(0) ; + sgVec4 *cl = (sgVec4 *) colours -> get(0) ; + + if ( num_colours == 0 ) glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + if ( num_colours == 1 ) glColor4fv ( cl [ 0 ] ) ; + if ( num_normals == 1 ) glNormal3fv ( nm [ 0 ] ) ; + + glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ; + + if ( num_colours > 1 ) + { + glEnableClientState ( GL_COLOR_ARRAY ) ; + glColorPointer ( 4, GL_FLOAT, 0, colours->get(0) ) ; + } + if ( num_normals > 1 ) + { + glEnableClientState ( GL_NORMAL_ARRAY ) ; + glNormalPointer ( GL_FLOAT, 0, normals->get(0) ) ; + } + if ( num_texcoords > 1 ) + { + glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ; + glTexCoordPointer ( 2, GL_FLOAT, 0, texcoords->get(0) ) ; + } + glEnableClientState ( GL_VERTEX_ARRAY ) ; + glVertexPointer ( 3, GL_FLOAT, 0, vertices->get(0) ) ; + + int i = getNumIndices (); + short *ii = indices->get(0); + + _ssgCurrStatistics . bumpVertexCount ( i ) ; + _ssgCurrStatistics . bumpLeafCount ( 1 ) ; + + glDrawElements ( gltype, i, GL_UNSIGNED_SHORT, ii ) ; + + glPopClientAttrib ( ) ; +} + + +void ssgVtxArray::getTriangle ( int n, short *v1, short *v2, short *v3 ) +{ + short vv1, vv2, vv3 ; + + ssgVtxTable::getTriangle ( n, &vv1, &vv2, &vv3 ) ; + + *v1 = *( indices -> get ( vv1 ) ) ; + *v2 = *( indices -> get ( vv2 ) ) ; + *v3 = *( indices -> get ( vv3 ) ) ; +} + + +int ssgVtxArray::getNumTriangles () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : return getNumIndices() - 2 ; + case GL_TRIANGLES : return getNumIndices() / 3 ; + case GL_TRIANGLE_STRIP : return getNumIndices() - 2 ; + case GL_QUADS : return ( getNumIndices() / 4 ) * 2 ; + case GL_QUAD_STRIP : return ( ( getNumIndices() - 2 ) / 2 ) * 2 ; + + default : break ; + } + + return 0 ; +} + +int ssgVtxArray::getNumLines () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : return 0 ; + case GL_LINES : return getNumIndices () / 2 ; + case GL_LINE_LOOP : return getNumIndices () ; + case GL_LINE_STRIP : return getNumIndices () - 1 ; + default : break ; + } + assert(false); /* Should never get here */ + return 0 ; +} + +void ssgVtxArray::getLine ( int n, short *v1, short *v2 ) +{ + assert( n>=0 ); + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : + assert(false); + case GL_LINES : + assert ( 2*n+1 < getNumIndices() ); + *v1 = *getIndex( 2*n ); + *v2 = *getIndex( 2*n+1 ); + return ; + case GL_LINE_LOOP : + assert ( n < getNumIndices() ); + *v1 = *getIndex( n ); + if ( n == getNumIndices()-1 ) + *v2 = *getIndex( 0 ); + else + *v2 = *getIndex( n+1 ); + return ; + case GL_LINE_STRIP : + assert ( n < getNumIndices()-1 ); + *v1 = *getIndex( n ); + *v2 = *getIndex( n+1 ); + return; + default : + break ; + } + assert(false); /* Should never get here */ + return ; +} + + +void ssgVtxArray::print ( FILE *fd, char *indent, int how_much ) +{ + char in [ 100 ] ; + + if ( how_much == 0 ) + return; // dont print anything + + sprintf ( in, "%s ", indent ); + + ssgVtxTable::print ( fd, indent, how_much ) ; + indices -> print ( fd, in, how_much ) ; +} + + +int ssgVtxArray::load ( FILE *fd ) +{ + if ( ! ssgVtxTable::load(fd) || + ! _ssgLoadObject ( fd, (ssgBase **) &indices, ssgTypeIndexArray () ) ) + return FALSE ; + + if ( indices != NULL) + indices -> ref () ; + + return TRUE ; +} + + +int ssgVtxArray::save ( FILE *fd ) +{ + if ( ! ssgVtxTable::save(fd) || + ! _ssgSaveObject ( fd, indices ) ) + return FALSE ; + + return TRUE ; +} + diff --git a/src/ssg/ssgVtxTable.cxx b/src/ssg/ssgVtxTable.cxx new file mode 100644 index 0000000..ba00e87 --- /dev/null +++ b/src/ssg/ssgVtxTable.cxx @@ -0,0 +1,1018 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgVtxTable.cxx 1992 2004-12-01 08:28:05Z wolfram_kuss $ +*/ + + +#include "ssgLocal.h" + +#define HL_DELTA 0.04f + +int ssgSimpleList::compare(ssgSimpleList *other, int print_result) +{ + int result = TRUE; + if (total !=other->total ) + { result = FALSE; + if(print_result) + printf("ERROR: total \n"); + } + else + if(print_result) + printf("Ok: total \n"); + if (size_of !=other->size_of ) + { result = FALSE; + if(print_result) + printf("ERROR: size_of \n"); + } + else + if(print_result) + printf("Ok: size_of \n"); + if (memcmp(list, other->list, total*size_of)) + { result = FALSE; + if(print_result) + printf("ERROR: list \n"); + } + else + if(print_result) + printf("Ok: list \n"); + + return result; +} + + +int ssgVtxTable::compare(ssgVtxTable *other, int print_result) +// returns TRUE, if equal. +// prints stuff to stdout. +{ + int result = TRUE; + if(print_result) + printf("comparing vertices\n"); + if(!vertices->compare(other->vertices, print_result)) + result=FALSE; + if(print_result) + printf("comparing normals\n"); + if(!normals->compare(other->normals, print_result)) + result=FALSE; + if(print_result) + printf("comparing texcoords\n"); + if(!texcoords->compare(other->texcoords, print_result)) + result=FALSE; + if(print_result) + printf("comparing colours \n"); + if(!colours ->compare(other->colours, print_result)) + result=FALSE; + // could be in ssgLeaf: + if(getState () == other->getState ()) + { + if(print_result) + printf("Ok: state\n"); + } + else + if(print_result) + printf("WARNING: state pointers differ. state contents is not investigated\n"); + + if (getNumVertices ()!=other->getNumVertices ()) + { result = FALSE; + if(print_result) + printf("ERROR: getNumVertices ()\n"); + } + else + if(print_result) + printf("Ok: getNumVertices ()\n"); + if (getNumNormals ()!=other->getNumNormals ()) + { result = FALSE; + if(print_result) + printf("ERROR: getNumNormals ()\n"); + } + else + if(print_result) + printf("Ok: getNumNormals ()\n"); + if (getNumColours ()!=other->getNumColours ()) + { result = FALSE; + if(print_result) + printf("ERROR: getNumColours ()\n"); + } + else + if(print_result) + printf("Ok: getNumColours ()\n"); + if (getNumTexCoords ()!=other->getNumTexCoords ()) + { result = FALSE; + if(print_result) + printf("ERROR: getNumTexCoords ()\n"); + } + else + if(print_result) + printf("Ok: getNumTexCoords ()\n"); + // end could be in leaf + return result; +} + +void ssgVtxTable::copy_from ( ssgVtxTable *src, int clone_flags ) +{ + ssgLeaf::copy_from ( src, clone_flags ) ; + + gltype = src -> getPrimitiveType () ; + + if ( src->vertices != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + vertices = (ssgVertexArray *)( src -> vertices -> clone ( clone_flags )) ; + else + vertices = src -> vertices ; + + if ( src->normals != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + normals = (ssgNormalArray *)( src -> normals -> clone ( clone_flags )) ; + else + normals = src -> normals ; + + if ( src->texcoords != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + texcoords = (ssgTexCoordArray *)( src -> texcoords -> clone ( clone_flags )) ; + else + texcoords = src -> texcoords ; + + if ( src->colours != NULL && ( clone_flags & SSG_CLONE_GEOMETRY ) ) + colours = (ssgColourArray *)( src -> colours -> clone ( clone_flags )) ; + else + colours = src -> colours ; + + if ( vertices != NULL ) vertices -> ref () ; + if ( normals != NULL ) normals -> ref () ; + if ( texcoords != NULL ) texcoords -> ref () ; + if ( colours != NULL ) colours -> ref () ; + + recalcBSphere () ; +} + +ssgBase *ssgVtxTable::clone ( int clone_flags ) +{ + ssgVtxTable *b = new ssgVtxTable ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgVtxTable::ssgVtxTable () +{ + type = ssgTypeVtxTable () ; + gltype = GL_POINTS ; + vertices = NULL ; + normals = NULL ; + texcoords = NULL ; + colours = NULL ; +} + + +ssgVtxTable::ssgVtxTable ( GLenum ty, + ssgVertexArray *vl, + ssgNormalArray *nl, + ssgTexCoordArray *tl, + ssgColourArray *cl ) +{ + gltype = ty ; + type = ssgTypeVtxTable () ; + + vertices = (vl!=NULL) ? vl : new ssgVertexArray () ; + normals = (nl!=NULL) ? nl : new ssgNormalArray () ; + texcoords = (tl!=NULL) ? tl : new ssgTexCoordArray () ; + colours = (cl!=NULL) ? cl : new ssgColourArray () ; + + vertices -> ref () ; + normals -> ref () ; + texcoords -> ref () ; + colours -> ref () ; + + recalcBSphere () ; +} + +void ssgVtxTable::setVertices ( ssgVertexArray *vl ) +{ + ssgDeRefDelete ( vertices ) ; + vertices = vl ; + + if ( vertices != NULL ) + vertices -> ref () ; + + recalcBSphere () ; +} + +void ssgVtxTable::setNormals ( ssgNormalArray *nl ) +{ + ssgDeRefDelete ( normals ) ; + normals = nl ; + + if ( normals != NULL ) + normals -> ref () ; +} + +void ssgVtxTable::setTexCoords ( ssgTexCoordArray *tl ) +{ + ssgDeRefDelete ( texcoords ) ; + texcoords = tl ; + + if ( texcoords != NULL ) + texcoords -> ref () ; +} + +void ssgVtxTable::setColours ( ssgColourArray *cl ) +{ + ssgDeRefDelete ( colours ) ; + colours = cl ; + + if ( colours != NULL ) + colours -> ref () ; +} + +ssgVtxTable::~ssgVtxTable () +{ + ssgDeRefDelete ( vertices ) ; + ssgDeRefDelete ( normals ) ; + ssgDeRefDelete ( texcoords ) ; + ssgDeRefDelete ( colours ) ; +} + + +void ssgVtxTable::getTriangle ( int n, short *v1, short *v2, short *v3 ) +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + *v1 = 0 ; + *v2 = n+1 ; + *v3 = n+2 ; + return ; + + case GL_TRIANGLES : + *v1 = n*3 ; + *v2 = n*3+1 ; + *v3 = n*3+2 ; + return ; + + case GL_TRIANGLE_STRIP : + case GL_QUAD_STRIP : + if ( n & 1 ) + { + *v3 = n ; + *v2 = n+1 ; + *v1 = n+2 ; + } + else + { + *v1 = n ; + *v2 = n+1 ; + *v3 = n+2 ; + } + return ; + + case GL_QUADS : + *v1 = n*2 + 0 ; + *v2 = n*2 + 1 ; + *v3 = n*2 + 2 - (n&1)*4 ; + return ; + + default : return ; + } +} + + +int ssgVtxTable::getNumTriangles () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + return getNumVertices() - 2 ; + + case GL_TRIANGLES : + return getNumVertices() / 3 ; + + case GL_TRIANGLE_STRIP : + return getNumVertices() - 2 ; + + case GL_QUADS : + return ( getNumVertices() / 4 ) * 2 ; + + case GL_QUAD_STRIP : + return ( ( getNumVertices() - 2 ) / 2 ) * 2 ; + + case GL_POINTS : + case GL_LINES : + case GL_LINE_LOOP : + case GL_LINE_STRIP : + return 0; + default : break ; + } + + return 0 ; /* Should never get here...but you never know! */ +} + +int ssgVtxTable::getNumLines () +{ + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : + return 0; + case GL_LINES : + // wk: FIXME: check the 3 following formulas. I don't have an OpenGL bokk at hand currently :-( + return getNumVertices()/2; + case GL_LINE_LOOP : + return getNumVertices(); + case GL_LINE_STRIP : + return getNumVertices()-1; + default : break ; + } + assert(false); /* Should never get here */ + return 0 ; +} + +void ssgVtxTable::getLine ( int n, short *v1, short *v2 ) +{ + assert( n>=0 ); + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLES : + case GL_TRIANGLE_STRIP : + case GL_QUADS : + case GL_QUAD_STRIP : + case GL_POINTS : + assert(false); + case GL_LINES : + // wk: FIXME: check the 3 following formulas. I don't have an OpenGL bokk at hand currently :-( + assert ( 2*n+1 < getNumVertices() ); + *v1 = 2*n; + *v2 = 2*n+1 ; + return ; + case GL_LINE_LOOP : + assert ( n < getNumVertices() ); + *v1 = n; + if ( n == getNumVertices()-1 ) + *v2 = 0; + else + *v2 = n+1; + return ; + case GL_LINE_STRIP : + assert ( n < getNumVertices()-1 ); + *v1 = n; + *v2 = n+1; + return; + default : + break ; + } + assert(false); /* Should never get here */ + return ; +} + + +void ssgVtxTable::transform ( const sgMat4 m ) +{ + int i ; + int flags = sgClassifyMat4 ( m ) ; + + if ( flags == 0 ) + return ; + + + if ( ( flags & SG_PROJECTION ) ) + ulSetError ( UL_WARNING, "ssgVtxTable: Projection matrices currently not supported." ) ; + /* + note: it is possible to handle projections, but for each normal we would + have to know the corresponding vertex coordinates. setting: + n[3] = dot(v, n) / v[3] + and then transforming with the full 4x4 transposed inverse matrix would do it. + see the OpenGL spec. + */ + + + int num_vertices = getNumVertices () ; + int num_normals = getNumNormals () ; + for ( i = 0 ; i < num_vertices ; i++ ) + sgXformPnt3 ( vertices->get(i), vertices->get(i), m ) ; + + + + + if ( ( flags & ( SG_SCALE | SG_MIRROR | SG_NONORTHO ) ) ) + { + sgMat4 w ; + if ( ( flags & SG_NONORTHO ) ) + { + // use the transposed adjoint matrix (only the upper 3x3 is needed) + sgVectorProductVec3 ( w[0], m[1], m[2] ) ; + sgVectorProductVec3 ( w[1], m[2], m[0] ) ; + sgVectorProductVec3 ( w[2], m[0], m[1] ) ; + } + else + { + SGfloat scale = SG_ONE ; + + if ( ( flags & SG_SCALE ) ) + { + // prescale matrix to avoid renormalisation + scale = scale / sgLengthVec3 ( m[0] ) ; + } + + if ( ( flags & SG_MIRROR ) ) + { + // negate to keep normals consistent with triangle orientations + scale = - scale ; + } + + sgScaleVec3 ( w[0], m[0], scale ) ; + sgScaleVec3 ( w[1], m[1], scale ) ; + sgScaleVec3 ( w[2], m[2], scale ) ; + } + + for ( i = 0 ; i < num_normals ; i++ ) + sgXformVec3 ( normals->get(i), normals->get(i), w ) ; + } + else + for ( i = 0 ; i < num_normals ; i++ ) + sgXformVec3 ( normals->get(i), normals->get(i), m ) ; + + + if ( ( flags & SG_NONORTHO ) ) + { + for ( i = 0 ; i < num_normals ; i++ ) + sgNormaliseVec3 ( normals->get(i) ) ; + } + + + recalcBSphere () ; +} + + +void ssgVtxTable::recalcBSphere () +{ + emptyBSphere () ; + bbox . empty () ; + + int num_vertices = getNumVertices () ; + for ( int i = 0 ; i < num_vertices ; i++ ) + bbox . extend ( vertices->get(i) ) ; + + extendBSphere ( & bbox ) ; + dirtyBSphere () ; /* Cause parents to redo their bspheres */ + bsphere_is_invalid = FALSE ; +} + + +void ssgVtxTable::drawHighlight ( sgVec4 colour ) +{ + _ssgForceLineState () ; + + int i ; + int num_vertices = getNumVertices () ; + + sgVec3 *vx = (sgVec3 *) vertices -> get(0) ; + + glPushAttrib ( GL_POLYGON_BIT ) ; + glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE ) ; + glColor4fv ( colour ) ; + glBegin ( gltype ) ; + for ( i = 0 ; i < num_vertices ; i++ ) + glVertex3fv ( vx [ i ] ) ; + glEnd () ; + glPopAttrib () ; + glEnable ( GL_DEPTH_TEST ) ; +} + + +void ssgVtxTable::drawHighlight ( sgVec4 colour, int v ) +{ + _ssgForceLineState () ; + + int num_vertices = getNumVertices () ; + + if ( v < 0 || v >= num_vertices ) + return ; + + sgVec3 *vx = (sgVec3 *) vertices -> get(v) ; + + float x = vx[0][0] ; + float y = vx[0][1] ; + float z = vx[0][2] ; + + sgVec3 t[6] ; + sgSetVec3 ( t[0], x-HL_DELTA,y,z ) ; + sgSetVec3 ( t[1], x+HL_DELTA,y,z ) ; + sgSetVec3 ( t[2], x,y-HL_DELTA,z ) ; + sgSetVec3 ( t[3], x,y+HL_DELTA,z ) ; + sgSetVec3 ( t[4], x,y,z-HL_DELTA ) ; + sgSetVec3 ( t[5], x,y,z+HL_DELTA ) ; + glColor4fv ( colour ) ; + glLineWidth ( 4.0f ) ; + glBegin ( GL_LINES ) ; + glVertex3fv ( t[0] ) ; + glVertex3fv ( t[1] ) ; + glVertex3fv ( t[2] ) ; + glVertex3fv ( t[3] ) ; + glVertex3fv ( t[4] ) ; + glVertex3fv ( t[5] ) ; + glEnd () ; + glLineWidth ( 1.0f ) ; + glEnable ( GL_DEPTH_TEST ) ; +} + + +void ssgVtxTable::draw () +{ + if ( ! preDraw () ) + return ; + + if ( _ssgCurrentContext-> stateOverridden () ) + _ssgCurrentContext -> overriddenState () -> apply () ; + else + if ( hasState () ) + getState () -> apply () ; + + stats_num_leaves++ ; + stats_num_vertices += getNumVertices() ; + +#ifdef _SSG_USE_DLIST + if ( dlist ) + glCallList ( dlist ) ; + else +#endif + draw_geometry () ; + + if ( postDrawCB != NULL ) + (*postDrawCB)(this) ; +} + + +void ssgVtxTable::pick ( int baseName ) +{ + int i ; + int num_vertices = getNumVertices () ; + + sgVec3 *vx = (sgVec3 *) vertices -> get(0) ; + + /* Test the entire primitive. */ + + glPushName ( baseName ) ; + glBegin ( gltype ) ; + + for ( i = 0 ; i < num_vertices ; i++ ) + glVertex3fv ( vx [ i ] ) ; + + glEnd () ; + + /* Then test each vertex in turn */ + + for ( i = 0 ; i < num_vertices ; i++ ) + { + glLoadName ( baseName + i + 1 ) ; + glBegin ( GL_POINTS ) ; + glVertex3fv ( vx [ i ] ) ; + glEnd () ; + } + + glPopName () ; +} + + +void ssgVtxTable::draw_geometry () +{ + int num_vertices = getNumVertices () ; + int num_colours = getNumColours () ; + int num_normals = getNumNormals () ; + int num_texcoords = getNumTexCoords () ; + + if ( num_colours == 0 ) glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f ) ; + if ( num_colours == 1 ) glColor4fv ( colours -> get(0) ) ; + if ( num_normals == 1 ) glNormal3fv ( normals -> get(0) ) ; + + glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ) ; + + if ( num_colours > 1 ) + { + glEnableClientState ( GL_COLOR_ARRAY ) ; + glColorPointer ( 4, GL_FLOAT, 0, colours->get(0) ) ; + } + + if ( num_normals > 1 ) + { + glEnableClientState ( GL_NORMAL_ARRAY ) ; + glNormalPointer ( GL_FLOAT, 0, normals->get(0) ) ; + } + + if ( num_texcoords > 1 ) + { + glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ; + glTexCoordPointer ( 2, GL_FLOAT, 0, texcoords->get(0) ) ; + } + + glEnableClientState ( GL_VERTEX_ARRAY ) ; + glVertexPointer ( 3, GL_FLOAT, 0, vertices->get(0) ) ; + + _ssgCurrStatistics . bumpVertexCount ( num_vertices ) ; + _ssgCurrStatistics . bumpLeafCount ( 1 ) ; + + glDrawArrays ( gltype, 0, num_vertices ) ; + + glPopClientAttrib () ; +} + + + +void ssgVtxTable::hot_triangles ( sgVec3 s, sgMat4 m, int /* test_needed */ ) +{ + int nt = getNumTriangles () ; + + stats_hot_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + + /* + Does the X/Y coordinate lie outside the triangle's bbox, or + does the Z coordinate lie beneath the bbox ? + */ + + if ( ( s[0] < vv1[0] && s[0] < vv2[0] && s[0] < vv3[0] ) || + ( s[1] < vv1[1] && s[1] < vv2[1] && s[1] < vv3[1] ) || + ( s[0] > vv1[0] && s[0] > vv2[0] && s[0] > vv3[0] ) || + ( s[1] > vv1[1] && s[1] > vv2[1] && s[1] > vv3[1] ) || + ( s[2] < vv1[2] && s[2] < vv2[2] && s[2] < vv3[2] ) ) + continue ; + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + + if ( _ssgIsHotTest ) + { + /* No HOT from upside-down or vertical triangles */ + + if ( getCullFace() && plane [ 2 ] <= 0 ) + continue ; + + /* Find the point vertically below the text point + as it crosses the plane of the polygon */ + + float z = sgHeightOfPlaneVec2 ( plane, s ) ; + + /* No HOT from below the triangle */ + + if ( z > s[2] ) + continue ; + + /* Outside the vertical extent of the triangle? */ + + if ( ( z < vv1[2] && z < vv2[2] && z < vv3[2] ) || + ( z > vv1[2] && z > vv2[2] && z > vv3[2] ) ) + continue ; + } + + /* + Now it gets messy - the isect point is inside + the bbox of the triangle - but that's not enough. + Is it inside the triangle itself? + */ + + float e1 = s [0] * vv1[1] - s [1] * vv1[0] ; + float e2 = s [0] * vv2[1] - s [1] * vv2[0] ; + float e3 = s [0] * vv3[1] - s [1] * vv3[0] ; + float ep1 = vv1[0] * vv2[1] - vv1[1] * vv2[0] ; + float ep2 = vv2[0] * vv3[1] - vv2[1] * vv3[0] ; + float ep3 = vv3[0] * vv1[1] - vv3[1] * vv1[0] ; + + float ap = (float) fabs ( ep1 + ep2 + ep3 ) ; + float ai = (float) ( fabs ( e1 + ep1 - e2 ) + + fabs ( e2 + ep2 - e3 ) + + fabs ( e3 + ep3 - e1 ) ) ; + + if ( ai > ap * 1.01 ) + continue ; + + _ssgAddHit ( this, i, m, plane ) ; + } +} + +void ssgVtxTable::los_triangles ( sgVec3 s, sgMat4 m, int /* test_needed */ ) +{ + int nt = getNumTriangles () ; + + stats_los_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + SGfloat edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + SGfloat det,inv_det; + SGfloat /*t,*/u,v; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + sgVec3 cam; + cam[0] = m[0][3]; + cam[1] = m[1][3]; + cam[2] = m[2][3]; + //if ( _ssgIsLosTest ) + //{ + + /* find vectors for two edges sharing vert0 */ + sgSubVec3(edge1, vv2, vv1); + sgSubVec3(edge2, vv3, vv1); + + /* begin calculating determinant - also used to calculate U parameter */ + sgVectorProductVec3(pvec, s, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = sgScalarProductVec3(edge1, pvec); + + if (det > -0.0000001 && det < 0.0000001) continue; + inv_det = (float)1.0 / det; + + /* calculate distance from vert0 to ray origin */ + sgSubVec3(tvec, cam, vv1); + + /* calculate U parameter and test bounds */ + u = sgScalarProductVec3(tvec, pvec) * inv_det; + if (u < 0.0 || u > 1.0) + continue; + + /* prepare to test V parameter */ + sgVectorProductVec3(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + v = sgScalarProductVec3(s, qvec) * inv_det; + if (v < 0.0 || u + v > 1.0) + continue; + + /* calculate t, ray intersects triangle */ + //t = sgScalarProductVec3(edge2, qvec) * inv_det; + //} + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + _ssgAddHit ( this, i, m, plane ) ; + } +} + + +ssgVtxArray *ssgVtxTable::getAs_ssgVtxArray() +{ + + ssgIndexArray *indices = new ssgIndexArray (); + int i, no = 0; + switch ( getPrimitiveType () ) + { + case GL_POLYGON : + case GL_TRIANGLE_FAN : + case GL_TRIANGLE_STRIP : + no = getNumTriangles() + 2 ; + break; + + case GL_TRIANGLES : + no = getNumTriangles() * 3 ; + } + for(i=0;iadd(i); + + + ssgState * state = getState(); + char * name = getName(); + ssgVtxArray * s = new ssgVtxArray ( getGLtype(), + vertices, normals, texcoords, colours, indices); + s->setName(name); + s->setState(state); + return s; +} + + +void ssgVtxTable::isect_triangles ( sgSphere *s, sgMat4 m, int test_needed ) +{ + int nt = getNumTriangles () ; + + stats_isect_triangles += nt ; + + for ( int i = 0 ; i < nt ; i++ ) + { + short v1, v2, v3 ; + sgVec3 vv1, vv2, vv3 ; + sgVec4 plane ; + + if ( _ssgBackFaceCollisions ) + getTriangle ( i, &v1, &v3, &v2 ) ; + else + getTriangle ( i, &v1, &v2, &v3 ) ; + + sgXformPnt3 ( vv1, getVertex(v1), m ) ; + sgXformPnt3 ( vv2, getVertex(v2), m ) ; + sgXformPnt3 ( vv3, getVertex(v3), m ) ; + + sgMakePlane ( plane, vv1, vv2, vv3 ) ; + + if ( ! test_needed ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + + float dp = (float) fabs ( sgDistToPlaneVec3 ( plane, s->getCenter() ) ) ; + + if ( dp > s->getRadius() ) + continue ; + + /* + The BSphere touches the plane containing + the triangle - but does it actually touch + the triangle itself? Let's erect some + vertical walls around the triangle. + */ + + /* + Construct a 'wall' as a plane through + two vertices and a third vertex made + by adding the surface normal to the + first of those two vertices. + */ + + sgVec3 vvX ; + sgVec4 planeX ; + + sgAddVec3 ( vvX, plane, vv1 ) ; + sgMakePlane ( planeX, vv1, vv2, vvX ) ; + float dp1 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp1 > s->getRadius() ) + continue ; + + sgAddVec3 ( vvX, plane, vv2 ) ; + sgMakePlane ( planeX, vv2, vv3, vvX ) ; + float dp2 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp2 > s->getRadius() ) + continue ; + + sgAddVec3 ( vvX, plane, vv3 ) ; + sgMakePlane ( planeX, vv3, vv1, vvX ) ; + float dp3 = sgDistToPlaneVec3 ( planeX, s->getCenter() ) ; + + if ( dp3 > s->getRadius() ) + continue ; + + /* + OK, so we now know that the sphere + intersects the plane of the triangle + and is not more than one radius outside + the walls. However, you can still get + close enough to the wall and to the + triangle itself and *still* not + intersect the triangle itself. + + However, if the center is inside the + triangle then we don't need that + costly test. + */ + + if ( dp1 <= 0 && dp2 <= 0 && dp3 <= 0 ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + + /* + ...now we really need that costly set of tests... + + If the sphere penetrates the plane of the triangle + and the plane of the wall, then we can use pythagoras + to determine if the sphere actually intersects that + edge between the wall and the triangle. + + if ( dp_sqd + dp1_sqd > radius_sqd ) ...in! else ...out! + */ + + float r2 = s->getRadius() * s->getRadius() - dp * dp ; + + if ( dp1 * dp1 <= r2 || + dp2 * dp2 <= r2 || + dp3 * dp3 <= r2 ) + { + _ssgAddHit ( this, i, m, plane ) ; + continue ; + } + } +} + + +void ssgVtxTable::print ( FILE *fd, char *indent, int how_much ) +{ + char in [ 100 ] ; + + if ( how_much == 0 ) + return ; + + sprintf ( in, "%s ", indent ); + + ssgLeaf ::print ( fd, indent, how_much ) ; + + if ( vertices == NULL ) + fprintf ( fd, "%s No Vertices!\n", indent ) ; + else + vertices -> print ( fd, in, how_much ) ; + + if ( normals == NULL ) + fprintf ( fd, "%s No Normals!\n", indent ) ; + else + normals -> print ( fd, in, how_much ) ; + + if ( texcoords == NULL ) + fprintf ( fd, "%s No Texcoords!\n", indent ) ; + else + texcoords -> print ( fd, in, how_much ) ; + + if ( colours == NULL ) + fprintf ( fd, "%s No Colours!\n", indent ) ; + else + colours -> print ( fd, in, how_much ) ; +} + + + +int ssgVtxTable::load ( FILE *fd ) +{ + sgVec3 temp; + + _ssgReadVec3 ( fd, temp ); bbox.setMin( temp ) ; + _ssgReadVec3 ( fd, temp ); bbox.setMax( temp ) ; + _ssgReadInt ( fd, (int *)(&gltype) ) ; + + if ( ! ssgLeaf::load(fd) ) + return FALSE ; + + if ( ! _ssgLoadObject ( fd, (ssgBase **)&vertices, ssgTypeVertexArray () ) || + ! _ssgLoadObject ( fd, (ssgBase **)&normals, ssgTypeNormalArray () ) || + ! _ssgLoadObject ( fd, (ssgBase **)&texcoords, ssgTypeTexCoordArray () ) || + ! _ssgLoadObject ( fd, (ssgBase **)&colours, ssgTypeColourArray () ) ) + return FALSE ; + + if ( vertices != NULL ) vertices -> ref () ; + if ( normals != NULL ) normals -> ref () ; + if ( texcoords != NULL ) texcoords -> ref () ; + if ( colours != NULL ) colours -> ref () ; + + return TRUE ; +} + + +int ssgVtxTable::save ( FILE *fd ) +{ + _ssgWriteVec3 ( fd, bbox.getMin() ) ; + _ssgWriteVec3 ( fd, bbox.getMax() ) ; + _ssgWriteInt ( fd, (int) gltype ) ; + + if ( ! ssgLeaf::save(fd) ) + return FALSE ; + + if ( ! _ssgSaveObject ( fd, vertices ) || + ! _ssgSaveObject ( fd, normals ) || + ! _ssgSaveObject ( fd, texcoords ) || + ! _ssgSaveObject ( fd, colours ) ) + return FALSE ; + + return TRUE ; +} diff --git a/src/ssg/ssgconf.h b/src/ssg/ssgconf.h new file mode 100644 index 0000000..11b3cf0 --- /dev/null +++ b/src/ssg/ssgconf.h @@ -0,0 +1,55 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgconf.h 1959 2004-09-14 12:19:05Z wolfram_kuss $ +*/ + +#ifndef _INCLUDED_SSGCONF_H_ +#define _INCLUDED_SSGCONF_H_ + +/* ssgconf.h - machine configurable file for libssg */ + +#define _SSG_USE_DLIST 1 + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to an #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. + */ + +/* + For optional use of PNG textures, download the glpng library from + http://www.wyatt100.freeserve.co.uk/download.htm and + change the #undef to a #define in the following line. + */ + +#undef SSG_LOAD_PNG_SUPPORTED + +#define SSG_LOAD_SGI_SUPPORTED +#define SSG_LOAD_TGA_SUPPORTED +#define SSG_LOAD_BMP_SUPPORTED +#define SSG_LOAD_MDL_SUPPORTED +#define SSG_LOAD_MDL_BGL_TEXTURE_SUPPORTED +#define SSG_LOAD_XPL_SUPPORTED +#define SSG_LOAD_PCX_SUPPORTED +#endif diff --git a/src/ssgAux/Makefile.am b/src/ssgAux/Makefile.am new file mode 100644 index 0000000..0d42cac --- /dev/null +++ b/src/ssgAux/Makefile.am @@ -0,0 +1,41 @@ +if BUILD_SSGAUX + +lib_LIBRARIES = libplibssgaux.a + +include_HEADERS = ssgAux.h \ + ssgaShapes.h \ + ssgaLensFlare.h \ + ssgaParticleSystem.h \ + ssgaWaveSystem.h \ + ssgaScreenDump.h \ + ssgaSky.h \ + ssgaSphere.h \ + ssgaFire.h \ + ssgaBillboards.h + +libplibssgaux_a_SOURCES = ssgAux.cxx \ + ssgaShapes.cxx \ + ssgaPatch.cxx \ + ssgaParticleSystem.cxx \ + ssgaWaveSystem.cxx \ + ssgaLensFlare.cxx \ + ssgaLensFlareTexture.cxx \ + ssgaFire.cxx \ + ssgaScreenDump.cxx \ + ssgaSphere.cxx \ + ssgaCelestialBody.cxx \ + ssgaCloudLayer.cxx \ + ssgaStars.cxx \ + ssgaSkyDome.cxx \ + ssgaSky.cxx \ + ssgaTeapot.cxx \ + ssgaBillboards.cxx + +INCLUDES = -I$(top_srcdir)/src/sg -I$(top_srcdir)/src/ssg +INCLUDES += -I$(top_srcdir)/src/util + +endif + +EXTRA_DIST = ssgAux.dsp + + diff --git a/src/ssgAux/ssgAux.cxx b/src/ssgAux/ssgAux.cxx new file mode 100644 index 0000000..2d20796 --- /dev/null +++ b/src/ssgAux/ssgAux.cxx @@ -0,0 +1,36 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgAux.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgAux.h" + +static ssgBase *createCube () { return new ssgaCube ( 0 ) ; } +static ssgBase *createSphere () { return new ssgaSphere ( 0 ) ; } +static ssgBase *createCylinder () { return new ssgaCylinder ( 0 ) ; } + +void ssgaInit () +{ + ssgRegisterType ( ssgaTypeCube () , createCube ) ; + ssgRegisterType ( ssgaTypeSphere () , createSphere ) ; + ssgRegisterType ( ssgaTypeCylinder () , createCylinder ) ; +} diff --git a/src/ssgAux/ssgAux.dsp b/src/ssgAux/ssgAux.dsp new file mode 100644 index 0000000..156ea88 --- /dev/null +++ b/src/ssgAux/ssgAux.dsp @@ -0,0 +1,205 @@ +# Microsoft Developer Studio Project File - Name="ssgAux" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=ssgAux - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ssgAux.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ssgAux.mak" CFG="ssgAux - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ssgAux - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "ssgAux - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ssgAux - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\sg" /I "..\util" /I "..\ssg" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy ssgAux.h ..\..\ssgAux.h copy ssgaFire.h ..\..\ssgaFire.h copy ssgaLensFlare.h ..\..\ssgaLensFlare.h copy ssgaParticleSystem.h ..\..\ssgaParticleSystem.h copy ssgaShapes.h ..\..\ssgaShapes.h copy ssgaWaveSystem.h ..\..\ssgaWaveSystem.h copy ssgaSky.h ..\..\ssgaSky.h copy ssgaScreenDump.h ..\..\ssgaScreenDump.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "ssgAux - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\sg" /I "..\util" /I "..\ssg" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\ssgAux_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy ssgAux.h ..\..\ssgAux.h copy ssgaFire.h ..\..\ssgaFire.h copy ssgaLensFlare.h ..\..\ssgaLensFlare.h copy ssgaParticleSystem.h ..\..\ssgaParticleSystem.h copy ssgaShapes.h ..\..\ssgaShapes.h copy ssgaWaveSystem.h ..\..\ssgaWaveSystem.h copy ssgaSky.h ..\..\ssgaSky.h copy ssgaScreenDump.h ..\..\ssgaScreenDump.h + +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "ssgAux - Win32 Release" +# Name "ssgAux - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\ssgaCelestialBody.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaCloudLayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaFire.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaLensFlare.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaLensFlareTexture.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaParticleSystem.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaPatch.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaScreenDump.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaShapes.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaSky.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaSkyDome.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaSphere.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaStars.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaTeapot.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgAux.cxx +# End Source File +# Begin Source File + +SOURCE=.\ssgaWaveSystem.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\ssgaFire.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaLensFlare.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaParticleSystem.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaScreenDump.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaShapes.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaSky.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaSphere.h +# End Source File +# Begin Source File + +SOURCE=.\ssgAux.h +# End Source File +# Begin Source File + +SOURCE=.\ssgaWaveSystem.h +# End Source File +# End Group +# End Target +# End Project diff --git a/src/ssgAux/ssgAux.h b/src/ssgAux/ssgAux.h new file mode 100644 index 0000000..4292b6a --- /dev/null +++ b/src/ssgAux/ssgAux.h @@ -0,0 +1,63 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgAux.h 1772 2003-06-27 08:15:15Z sjbaker $ +*/ + + +#ifndef _SSGAUX_H_ +#define _SSGAUX_H_ 1 + +#include "ssg.h" +#include "ssgaShapes.h" +#include "ssgaParticleSystem.h" +#include "ssgaFire.h" +#include "ssgaWaveSystem.h" +#include "ssgaLensFlare.h" +#include "ssgaScreenDump.h" + + +#define _SSGA_TYPE_SHAPE 0x00008000 +#define _SSGA_TYPE_CUBE 0x00004000 +#define _SSGA_TYPE_SPHERE 0x00002000 +#define _SSGA_TYPE_CYLINDER 0x00001000 +#define _SSGA_TYPE_PATCH 0x00010000 +#define _SSGA_TYPE_TEAPOT 0x00020000 +#define _SSGA_TYPE_PARTICLESYSTEM 0x00040000 +#define _SSGA_TYPE_WAVESYSTEM 0x00080000 +#define _SSGA_TYPE_LENSFLARE 0x00100000 + +inline int ssgaTypeShape () { return _SSGA_TYPE_SHAPE | ssgTypeBranch ();} +inline int ssgaTypeCube () { return _SSGA_TYPE_CUBE | ssgaTypeShape ();} +inline int ssgaTypeSphere () { return _SSGA_TYPE_SPHERE | ssgaTypeShape ();} +inline int ssgaTypeCylinder() { return _SSGA_TYPE_CYLINDER | ssgaTypeShape ();} +inline int ssgaTypePatch () { return _SSGA_TYPE_PATCH | ssgaTypeShape ();} +inline int ssgaTypeTeapot () { return _SSGA_TYPE_TEAPOT | ssgaTypeShape ();} +inline int ssgaTypeParticleSystem () + { return _SSGA_TYPE_PARTICLESYSTEM | ssgaTypeShape ();} +inline int ssgaTypeWaveSystem () + { return _SSGA_TYPE_WAVESYSTEM | ssgaTypeShape ();} +inline int ssgaTypeLensFlare () + { return _SSGA_TYPE_LENSFLARE | ssgaTypeShape ();} + +void ssgaInit () ; + +#endif + diff --git a/src/ssgAux/ssgaBillboards.cxx b/src/ssgAux/ssgaBillboards.cxx new file mode 100644 index 0000000..14b2d4a --- /dev/null +++ b/src/ssgAux/ssgaBillboards.cxx @@ -0,0 +1,405 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaBillboards.cxx 1878 2004-02-25 15:39:34Z sjbaker $ +*/ + + +#include +#include "ssgaBillboards.h" + + +int ssgaBillboards::total_drawn = 0; + + +inline float max3f(const float v[3]) +{ + float tmp = v[0] >= v[1] ? v[0] : v[1]; + return tmp >= v[2] ? tmp : v[2]; +} + + +inline double random_value() +{ +#ifdef UL_WIN32 + return (double) rand() / RAND_MAX; +#else + return drand48(); +#endif +} + + + +ssgaBillboards::ssgaBillboards() + : array(16) +{ + sgSetVec3(up, 0, 0, 1); + + width = 1; + height = 1; + clip_dist = -1; + fade_dist = -1; + near_dist = -1; + draw_num = -1; + fade_num = -1; + alpha_ref = 0.1f; + + ssgSimpleState *ss = new ssgSimpleState; + ss->disable(GL_LIGHTING); + ss->disable(GL_BLEND); + ss->enable(GL_ALPHA_TEST); + ss->enable(GL_TEXTURE_2D); + setState(ss); +} + + + +ssgaBillboards::~ssgaBillboards() +{ +} + + + +void ssgaBillboards::recalcBSphere() +{ + int i; + sgBox box; + box.empty(); + for (i = 0; i < getNum(); i++) + box.extend(get(i)); + sgAddVec3(bsphere.center, box.min, box.max); + sgScaleVec3(bsphere.center, 0.5f); + sgSubVec3(box.max, box.min); + bsphere.radius = 0.5f * max3f(box.max); + for (i = 0; i < getNum(); i++) { + sgSphere sph; + sgCopyVec3(sph.center, get(i)); + float s = get(i)[3]; + sph.radius = 0.5f * s * width; + bsphere.extend(&sph); + sgAddScaledVec3(sph.center, up, s * height); + bsphere.extend(&sph); + } + bsphere_is_invalid = 0; +} + + +// static +void ssgaBillboards::initTexAlpha(GLuint texture, float athres) +{ + const GLenum format[] = { GL_NONE, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA }; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glBindTexture(GL_TEXTURE_2D, texture); + + for (int level = 0;; level++) { + GLint width = 0, height = 0, red = 0, alpha = 0; + int icomp, ocomp; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width ); + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &height); + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_RED_SIZE, &red ); + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_ALPHA_SIZE, &alpha ); + + if (width <= 0 || height <= 0) + break; + + if (red > 0) { + icomp = 3 + (alpha > 0); + ocomp = 4; + } else { + icomp = 1 + (alpha > 0); + ocomp = 2; + } + + GLubyte *pixels = new GLubyte [ width * height * ocomp ]; + + glGetTexImage(GL_TEXTURE_2D, level, format[icomp], GL_UNSIGNED_BYTE, pixels); + + for (int y = height; y--;) { + for (int x = width; x--;) { + float af = icomp % 2 ? 1.0f : (1.0f / 256.0f) * pixels[(y * width + x + 1) * icomp - 1]; + // default alpha_ref is 0.1, so match that: + af = af > athres ? 0.1f + 0.9f * random_value() : 0; + int a = (int) (256.0f * af); + pixels[(y * width + x + 1) * ocomp - 1] = a >= 255 ? 255 : a; + if (ocomp > icomp) + for (int k = 0; k < icomp; k++) + pixels[(y * width + x) * ocomp + k] = pixels[(y * width + x) * icomp + k]; + } + } + + glTexImage2D(GL_TEXTURE_2D, level, format[ocomp], width, height, 0, + format[ocomp], GL_UNSIGNED_BYTE, pixels); + + delete [] pixels; + + if (width == 1 && height == 1) + break; + } +} + + + +void ssgaBillboards::cull(sgFrustum *f, sgMat4 m, int test_needed) +{ + int cull_result = cull_test(f, m, test_needed); + + if (cull_result == SSG_OUTSIDE) + return; + + draw(m); +} + + +void ssgaBillboards::draw() +{ + sgMat4 m; + + glGetFloatv(GL_MODELVIEW_MATRIX, &m[0][0]); + + draw(m); +} + + +void ssgaBillboards::draw(const sgMat4 m) +{ + if (!draw_num) + return; + + sgVec4 z_axis = { m[0][2], m[1][2], m[2][2], m[3][2] }; + float dist = -1; + + if (clip_dist >= 0) { + const sgSphere *s = getBSphere(); + + float z = sgScalarProductVec3(s->getCenter(), z_axis) + z_axis[3]; + dist = -z - s->getRadius(); + + if (dist >= clip_dist) + return; + } + + if (preDrawCB != NULL && !(*preDrawCB)(this)) + return; + + + float aref = alpha_ref; + + if (fade_dist >= 0 && fade_dist < clip_dist && dist > fade_dist) + aref += (1.0f - alpha_ref) * (dist - fade_dist) / (clip_dist - fade_dist); + + + _ssgCurrentContext->setCullface(0); + getState()->apply(); + + glPushAttrib(GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glAlphaFunc(GL_GEQUAL, aref); + + glBegin(GL_QUADS); + + + int n = getNum(); + const float *p = get(0); + + if (draw_num > 0 && draw_num < n) + n = draw_num; + + if (fade_num >= 0 && fade_num < n && clip_dist > near_dist && near_dist > 1e-6f) { + float far_dist = fade_dist >= 0 && fade_dist < clip_dist ? fade_dist : clip_dist; + if (dist > near_dist) { + if (dist < far_dist) { + float fn = 1.0f / near_dist; + float ff = 1.0f / far_dist; + float fd = 1.0f / dist; + float s = (fd - fn) / (ff - fn); + n -= (int) ((n - fade_num) * s + 0.5f); + } else { + n = fade_num; + } + } + } + + if (up[2] == 1) { + + // fast path when Z is up + + float wx, wy, heightz, len; + + wx = z_axis[1]; + wy = -z_axis[0]; + len = sgHypot(wx, wy); + + if (len > 1e-6f) { + + wx *= 0.5f * width / len; + wy *= 0.5f * width / len; + heightz = height; + + for (int i = 0; i < n; i++) { + float x, y, z, s, dx, dy; + + x = *p++; + y = *p++; + z = *p++; + s = *p++; + + dx = s * wx; + dy = s * wy; + + glTexCoord2f(0, 0); + glVertex3f(x - dx, y - dy, z); + + glTexCoord2f(1, 0); + glVertex3f(x + dx, y + dy, z); + + z += s * heightz; + + glTexCoord2f(1, 1); + glVertex3f(x + dx, y + dy, z); + + glTexCoord2f(0, 1); + glVertex3f(x - dx, y - dy, z); + } + + total_drawn += n; + } + + } else { + + sgVec3 xy; + sgVectorProductVec3(xy, z_axis, up); + float len = sgLengthVec3(xy); + + if (len > 1e-6f) { + float wx, wy, wz, heightx, heighty, heightz; + + wx = 0.5f * width * xy[0] / len; + wy = 0.5f * width * xy[1] / len; + wz = 0.5f * width * xy[2] / len; + + heightx = height * up[0]; + heighty = height * up[1]; + heightz = height * up[2]; + + for (int i = 0; i < n; i++) { + float x, y, z, s, dx, dy, dz; + + x = *p++; + y = *p++; + z = *p++; + s = *p++; + + dx = s * wx; + dy = s * wy; + dz = s * wz; + + glTexCoord2f(0, 0); + glVertex3f(x - dx, y - dy, z - dz); + + glTexCoord2f(1, 0); + glVertex3f(x + dx, y + dy, z + dz); + + x += s * heightx; + y += s * heighty; + z += s * heightz; + + glTexCoord2f(1, 1); + glVertex3f(x + dx, y + dy, z + dz); + + glTexCoord2f(0, 1); + glVertex3f(x - dx, y - dy, z - dz); + } + + total_drawn += n; + } + } + + + glEnd(); + + glPopAttrib(); + + if (postDrawCB != NULL) + (*postDrawCB)(this); +} + + + +float *ssgaBillboards::getVertex(int i) +{ + if (i < 0 || i >= 4 * getNum()) + return _ssgVertex000; + + // pick any orientation + sgVec3 xy, axis = { 0, 0, 0 }; + axis[ sgAbs(up[0]) > sgAbs(up[1]) ] = 1; + sgVectorProductVec3(xy, up, axis); + sgNormaliseVec3(xy); + + const sgVec3 tab[4] = { {-0.5f,0.0f}, {0.5f,0.0f}, {0.5f,1.0f}, {-0.5f,1.0f} }; + const float *p = array.get(i / 4); + const float *d = tab[i % 4]; + static sgVec3 v; + for (i = 0; i < 3; i++) + v[0] = p[i] + p[3] * (width * xy[i] * d[0] + height * up[i] * d[1]); + return v; +} + + +float *ssgaBillboards::getTexCoord(int i) +{ + static sgVec2 t[4] = { {0,0}, {1,0}, {1,1}, {0,1} }; + return t[i & 3]; +} + + +void ssgaBillboards::getTriangle(int n, short *v1, short *v2, short *v3) +{ + *v1 = 2*n + 0; + *v2 = 2*n + 1; + *v3 = 2*n + 2 - 4*(n&1); +} + + +void ssgaBillboards::transform(const sgMat4 m) +{ + float scale = sgLengthVec3(m[0]); + for (int i = 0; i < getNum(); i++) + sgXformPnt3(array.get(i), array.get(i), m); + sgXformVec3(up, up, m); + sgNormaliseVec3(up); + width *= scale; + height *= scale; +} + + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/src/ssgAux/ssgaBillboards.h b/src/ssgAux/ssgaBillboards.h new file mode 100644 index 0000000..d863cb8 --- /dev/null +++ b/src/ssgAux/ssgaBillboards.h @@ -0,0 +1,227 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaBillboards.h 1844 2004-01-21 16:30:20Z stromberg $ +*/ + +/* + * ssgaBillboards + * + * Each billboard is represented by four values: X, Y, Z and scale. + * They are drawn as GL_QUADS using the texture defined in the state. + * + * Set the basic billboard size (each can be individually scaled): + * + * bb->setWidth(3.0f); + * bb->setHeight(8.0f); + * + * Set a texture image: + * + * bb->initTexAlpha(texture, 0.3f); // only once per texture, choose a suitable alpha threshold + * bb->setTexture(texture); + * + * Instantiate some billboards: + * + * for (i = 0; i < 1000; i++) + * bb->add( (drand48() - 0.5) * WORLD_SIZE_X, + * (drand48() - 0.5) * WORLD_SIZE_Y, + * Z, 0.75 + 0.5 * drand48() ); + * + * + * The billboards can be faded: + * + * bb->setClipRange(300.0f); // i.e. far clip + * bb->setFadeRange(200.0f); // starting fade here + * + * or, equivalently: + * + * bb->setFarRange(300.0f, 200.0f); // clip, fade + * + * + * The number of billboards drawn can be a function of distance: + * + * bb->setNearRange(50.0f); // draw all at this distance + * bb->setFadeNum(100); // number drawn at the fade distance (or clip distance if unset) + * + * Limit the number at the near range (defaults to all): + * + * bb->setDrawNum(500); + * + * + * Example code: plib/examples/src/ssg/shrubs/shrubs.cxx + * + * + * November 2003, Marten Stromberg + * + */ + + +#include "ssg.h" + + +class ssgaBillboards : public ssgLeaf +{ +protected: + + sgVec3 up; // vector pointing up (default: 0, 0, 1) + ssgColourArray array; // position and scale for each billboard + int draw_num; // only draw this many (-1 for all) + float width, height; // basic size of billboard + float clip_dist; // far clip distance (-1 disables) + float fade_dist; // fade-out distance (fade iff fade_dist < d < clip_dist) + float near_dist; // near distance for adjusting number of trees + int fade_num; // optionally adjust number of trees as a function of distance + float alpha_ref; // default 0.1 (initTexAlpha relies on that) + +public: + + ssgaBillboards(); + ~ssgaBillboards(); + + void setUp(const sgVec3 v) { sgNormaliseVec3(up, v); } + void setUp(float x, float y, float z) { sgVec3 v = { x, y, z }; setUp(v); } + const float *getUp() const { return up; } + + int getNum() { return array.getNum(); } // const + const float *get(int i) { return array.get((unsigned) i); } // const + + inline void add(float x, float y, float z, float scale = 1.0f); + inline void add(const sgVec4 xyzs); + + void removeLast() { array.removeLast(); dirtyBSphere(); } + void removeAll() { array.removeAll(); dirtyBSphere(); } + + void setWidth(float w) { width = w; } + float getWidth() const { return width; } + void setHeight(float h) { height = h; } + float getHeight() const { return height; } + + void setClipRange(float d) { clip_dist = d; } + float getClipRange() const { return clip_dist; } + void setFadeRange(float d) { fade_dist = d; } + float getFadeRange() const { return fade_dist; } + void setNearRange(float d) { near_dist = d; } + float getNearRange() const { return near_dist; } + + void setFarRange(float clip, float fade) { setFadeRange(fade); setClipRange(clip); } + void setFarRange(float clip) { setFarRange(clip, 0.75f * clip); } + void setNearFarRange(float near_, float clip, float fade) { setNearRange(near_); setFarRange(clip, fade); } + void setNearFarRange(float near_, float clip) { setNearRange(near_); setFarRange(clip); } + + void setDrawNum(int n) { draw_num = n; } + int getDrawNum() const { return draw_num; } + void setFadeNum(int n) { fade_num = n; } + int getFadeNum() const { return fade_num; } + + inline void setTexture(ssgTexture *tex); + inline ssgTexture *getTexture(); // const + + void setAlphaRef(float aref) { alpha_ref = aref; } // only when not using initTexAlpha + float getAlphaRef() const { return alpha_ref; } + + static void initTexAlpha(GLuint handle, float aref = 0.3f); // initialize alpha channel for smooth fading + static void initTexAlpha(ssgTexture *tex, float aref = 0.3f) { initTexAlpha(tex->getHandle(), aref); } + + + /* methods derived from ssgEntity and ssgLeaf */ + + virtual void recalcBSphere(); + + virtual void cull(sgFrustum *, sgMat4, int); + virtual void draw(); + + void draw(const sgMat4 m); + + virtual void draw_geometry() {} + virtual void pick(int) {} + + virtual void drawHighlight(sgVec4) {} + virtual void drawHighlight(sgVec4, int) {} + + virtual void isect_triangles(sgSphere *, sgMat4, int) {} + virtual void hot_triangles(sgVec3, sgMat4, int) {} + virtual void los_triangles(sgVec3, sgMat4, int) {} + + virtual const char *getTypeName() { return "ssgaBillboards"; } + + virtual GLenum getPrimitiveType() { return GL_QUADS; } + + virtual int getNumVertices() { return 4 * getNum(); } + virtual int getNumTexCoords() { return 4 * getNum(); } + virtual int getNumNormals() { return 0; } + virtual int getNumColours() { return 0; } + + virtual float *getVertex(int i); + virtual float *getTexCoord(int i); + virtual float *getNormal(int) { return _ssgNormalUp; } + virtual float *getColour(int) { return _ssgColourWhite; } + + virtual int getNumTriangles() { return 2 * getNum(); } + virtual void getTriangle(int n, short *v1, short *v2, short *v3); + virtual int getNumLines() { return 0; } + virtual void getLine(int, short *v1, short *v2) {} + + virtual void transform(const sgMat4 m); + + + // incremented each draw, use it as you like + static int total_drawn; + +}; + + + +inline void ssgaBillboards::add(float x, float y, float z, float w) +{ + array.add(x, y, z, w); + dirtyBSphere(); +} + + +inline void ssgaBillboards::add(const sgVec4 xyzs) +{ + array.add((float *) xyzs); + dirtyBSphere(); +} + + +inline void ssgaBillboards::setTexture(ssgTexture *tex) +{ + ssgSimpleState *ss = (ssgSimpleState *) getState(); + assert( ss && ss->isAKindOf( ssgTypeSimpleState() )); + ss->setTexture(tex); +} + + +inline ssgTexture *ssgaBillboards::getTexture() +{ + ssgSimpleState *ss = (ssgSimpleState *) getState(); + assert( ss && ss->isAKindOf( ssgTypeSimpleState() )); + return ss->getTexture(); +} + + +/* + Local Variables: + mode: C++ + c-basic-offset: 4 + c-file-offsets: ((substatement-open 0) (case-label 0)) + End: +*/ diff --git a/src/ssgAux/ssgaCelestialBody.cxx b/src/ssgAux/ssgaCelestialBody.cxx new file mode 100644 index 0000000..7e855fe --- /dev/null +++ b/src/ssgAux/ssgaCelestialBody.cxx @@ -0,0 +1,406 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaCelestialBody.cxx 1798 2003-09-26 14:54:20Z sjbaker $ +*/ + +// Written by Durk Talsma. Originally started October 1997, for distribution +// with the FlightGear project. Version 2 was written in August and +// September 1998. This code is based upon algorithms and data kindly +// provided by Mr. Paul Schlyter. (pausch@saaf.se). +// +// Separated out rendering pieces and converted to ssg by Curt Olson, +// March 2000 +// +// Moved into ssgAux, July 2003. + +#include +#include "ssgaSky.h" +#include "ssgaSphere.h" + + +static int ssgaCelestialBodyOrbPreDraw( ssgEntity *e ) +{ + ssgLeaf *f = (ssgLeaf *)e; + if ( f -> hasState () ) f->getState()->apply() ; + + glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT ); + + //glDisable( GL_DEPTH_TEST ); + glDisable( GL_FOG ); + glBlendFunc ( GL_SRC_ALPHA, GL_ONE ) ; + + return true; +} + + +static int ssgaCelestialBodyOrbPostDraw( ssgEntity *e ) +{ + glPopAttrib(); + + return true; +} + + +static int ssgaCelestialBodyHaloPreDraw( ssgEntity *e ) +{ + ssgLeaf *f = (ssgLeaf *)e; + if ( f -> hasState () ) f->getState()->apply() ; + + glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_FOG_BIT ); + + glDisable( GL_DEPTH_TEST ); + glDisable( GL_FOG ); + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + return true; +} + + +static int ssgaCelestialBodyHaloPostDraw( ssgEntity *e ) +{ + glPopAttrib(); + + return true; +} + + +ssgaCelestialBody::ssgaCelestialBody( void ) +{ + transform = 0; + body_angle = 0; + body_rotation = 0; + body_right_ascension = 0; + body_declination = 0; + body_dist = 0; +} + + +ssgaCelestialBody::~ssgaCelestialBody( void ) +{ + ssgDeRefDelete( transform ); +} + + +ssgBranch * ssgaCelestialBody::build( const char* body_tex_path, const char* halo_tex_path, double body_size ) +{ + ssgSimpleState *orb_state = NULL; + ssgSimpleState *halo_state = NULL; + + // set up the orb state + orb_state = new ssgSimpleState(); + if (body_tex_path) { + orb_state->setTexture( body_tex_path ); + orb_state->setShadeModel( GL_SMOOTH ); + orb_state->enable( GL_LIGHTING ); + orb_state->enable( GL_CULL_FACE ); + orb_state->enable( GL_TEXTURE_2D ); + orb_state->enable( GL_COLOR_MATERIAL ); + orb_state->setColourMaterial( GL_DIFFUSE ); + orb_state->setMaterial( GL_AMBIENT, 0, 0, 0, 1 ); + orb_state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + orb_state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + orb_state->enable( GL_BLEND ); + orb_state->enable( GL_ALPHA_TEST ); + orb_state->setAlphaClamp( 0.01f ); + } + else { + orb_state->setShadeModel( GL_SMOOTH ); + orb_state->disable( GL_LIGHTING ); + orb_state->enable( GL_CULL_FACE ); + orb_state->disable( GL_TEXTURE_2D ); + orb_state->enable( GL_COLOR_MATERIAL ); + orb_state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ); + orb_state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + orb_state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + orb_state->disable( GL_BLEND ); + orb_state->disable( GL_ALPHA_TEST ); + } + + if (halo_tex_path) { + + // build the halo + // GLuint texid; + // GLubyte *texbuf; + // texbuf = new GLubyte[64*64*3]; + // texid = makeHalo( texbuf, 64 ); + // my_glWritePPMFile("halo.ppm", texbuf, 64, 64, RGB); + + // set up the halo state + halo_state = new ssgSimpleState(); + halo_state->setTexture( halo_tex_path ); + halo_state->enable( GL_TEXTURE_2D ); + halo_state->disable( GL_LIGHTING ); + halo_state->setShadeModel( GL_SMOOTH ); + halo_state->disable( GL_CULL_FACE ); + halo_state->enable( GL_COLOR_MATERIAL ); + halo_state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ); + halo_state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + halo_state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + halo_state->enable( GL_ALPHA_TEST ); + halo_state->setAlphaClamp(0.01f); + halo_state->enable ( GL_BLEND ) ; + } + + return build( orb_state, halo_state, body_size ); +} + + +ssgBranch * ssgaCelestialBody::build( ssgSimpleState *orb_state, ssgSimpleState *halo_state, double body_size ) +{ + ssgVertexArray *halo_vl; + ssgTexCoordArray *halo_tl; + + // clean-up previous + ssgDeRefDelete( transform ); + + // build the ssg scene graph sub tree for the sky and connected + // into the provide scene graph branch + transform = new ssgTransform; + transform->ref(); + + cl = new ssgColourArray( 1 ); + sgVec4 color; + sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); + cl->add( color ); + + ssgBranch *orb = ssgaMakeSphere( orb_state, cl, (float)body_size, 15, 15, + ssgaCelestialBodyOrbPreDraw, ssgaCelestialBodyOrbPostDraw ); + + transform->addKid( orb ); + + // force a repaint of the colors with arbitrary defaults + repaint( 0.0 ); + + if (halo_state) { + + // Build ssg structure + float size = float(body_size * 10.0); + sgVec3 v3; + halo_vl = new ssgVertexArray; + sgSetVec3( v3, -size, 0.0, -size ); + halo_vl->add( v3 ); + sgSetVec3( v3, size, 0.0, -size ); + halo_vl->add( v3 ); + sgSetVec3( v3, -size, 0.0, size ); + halo_vl->add( v3 ); + sgSetVec3( v3, size, 0.0, size ); + halo_vl->add( v3 ); + + sgVec2 v2; + halo_tl = new ssgTexCoordArray; + sgSetVec2( v2, 0.0f, 0.0f ); + halo_tl->add( v2 ); + sgSetVec2( v2, 1.0, 0.0 ); + halo_tl->add( v2 ); + sgSetVec2( v2, 0.0, 1.0 ); + halo_tl->add( v2 ); + sgSetVec2( v2, 1.0, 1.0 ); + halo_tl->add( v2 ); + + ssgLeaf *halo = + new ssgVtxTable ( GL_TRIANGLE_STRIP, halo_vl, NULL, halo_tl, cl ); + halo->setState( halo_state ); + + halo->setCallback( SSG_CALLBACK_PREDRAW, ssgaCelestialBodyHaloPreDraw ); + halo->setCallback( SSG_CALLBACK_POSTDRAW, ssgaCelestialBodyHaloPostDraw ); + + transform->addKid( halo ); + } + + return transform; +} + + +bool ssgaCelestialBody::reposition( sgVec3 p, double angle, double rightAscension, double declination, double sol_dist ) +{ + sgMat4 T1, T2, GST, RA, DEC; + sgVec3 axis; + sgVec3 v; + + sgMakeTransMat4( T1, p ); + + sgSetVec3( axis, 0.0, 0.0, -1.0 ); + sgMakeRotMat4( GST, (float)angle, axis ); + + sgSetVec3( axis, 0.0, 0.0, 1.0 ); + sgMakeRotMat4( RA, (float)((rightAscension * SGD_RADIANS_TO_DEGREES) - 90.0), axis ); + + sgSetVec3( axis, 1.0, 0.0, 0.0 ); + sgMakeRotMat4( DEC, (float)(declination * SGD_RADIANS_TO_DEGREES), axis ); + + sgSetVec3( v, 0.0, (float)sol_dist, 0.0 ); + sgMakeTransMat4( T2, v ); + + sgMat4 TRANSFORM; + sgCopyMat4( TRANSFORM, T1 ); + sgPreMultMat4( TRANSFORM, GST ); + sgPreMultMat4( TRANSFORM, RA ); + sgPreMultMat4( TRANSFORM, DEC ); + sgPreMultMat4( TRANSFORM, T2 ); + + sgCoord skypos; + sgSetCoord( &skypos, TRANSFORM ); + + transform->setTransform( &skypos ); + + return true; +} + + +bool ssgaCelestialBody::repaint( double angle ) +{ + static double prev_angle = 9999.0; + + if (prev_angle != angle) { + + prev_angle = angle; + + double factor = 4*cos(angle); + + if (factor > 1) factor = 1.0; + if (factor < -1) factor = -1.0; + factor = factor/2 + 0.5f; + + sgVec4 color; + color[0] = (float)pow(factor, 0.25); + color[1] = (float)pow(factor, 0.50); + color[2] = (float)pow(factor, 4.0); + color[3] = 1.0; + + ssgaGammaCorrectRGB( color ); + + float *ptr; + ptr = cl->get( 0 ); + sgCopyVec4( ptr, color ); + } + + return true; +} + + +#if 0 +static GLuint makeHalo( GLubyte *texbuf, int width ) +{ + int texSize; + GLuint texid; + GLubyte *p; + int i,j; + double radius; + + // create a texture id +#ifdef GL_VERSION_1_1 + glGenTextures(1, &texid); + glBindTexture(GL_TEXTURE_2D, texid); +#elif GL_EXT_texture_object + glGenTexturesEXT(1, &texid); + glBindTextureEXT(GL_TEXTURE_2D, texid); +#else +# error port me +#endif + + glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + + // create the actual texture contents + texSize = width * width; + + if ( !texbuf ) { + cout << "ouch ..." << endl; + exit(-1); // Ugly! + } + + p = texbuf; + + radius = (double)(width / 2); + + GLubyte value; + double x, y, d; + for ( i = 0; i < width; i++ ) { + for ( j = 0; j < width; j++ ) { + x = fabs((double)(i - (width / 2))); + y = fabs((double)(j - (width / 2))); + d = sqrt((x * x) + (y * y)); + if (d < radius) { + // t is 1.0 at center, 0.0 at edge + double t = 1.0 - (d / radius); + + // inverse square looks nice + value = (int)((double) 0xff * (t*t)); + } else { + value = 0x00; + } + *p = value; + *(p+1) = value; + *(p+2) = value; + // *(p+3) = value; + + p += 3; + } + } + + /* glTexImage2D( GL_TEXTURE_2D, + 0, + GL_RGBA, + width, width, + 0, + GL_RGBA, GL_UNSIGNED_BYTE, + texbuf ); */ + + return texid; +} + + +#define RGB 3 // 3 bytes of color info per pixel +#define RGBA 4 // 4 bytes of color+alpha info +void my_glWritePPMFile(const char *filename, GLubyte *buffer, int win_width, int win_height, int mode) +{ + int i, j, k, q; + unsigned char *ibuffer; + FILE *fp; + int pixelSize = mode==GL_RGBA?4:3; + + ibuffer = (unsigned char *) malloc(win_width*win_height*RGB); + + fp = fopen(filename, "wb"); + fprintf(fp, "P6\n# CREATOR: glReadPixel()\n%d %d\n%d\n", + win_width, win_height, UCHAR_MAX); + q = 0; + for (i = 0; i < win_height; i++) { + for (j = 0; j < win_width; j++) { + for (k = 0; k < RGB; k++) { + ibuffer[q++] = (unsigned char) + *(buffer + (pixelSize*((win_height-1-i)*win_width+j)+k)); + } + } + } + + // *(buffer + (pixelSize*((win_height-1-i)*win_width+j)+k)); + + fwrite(ibuffer, sizeof(unsigned char), RGB*win_width*win_height, fp); + fclose(fp); + free(ibuffer); + + printf("wrote file (%d x %d pixels, %d bytes)\n", + win_width, win_height, RGB*win_width*win_height); +} +#endif diff --git a/src/ssgAux/ssgaCloudLayer.cxx b/src/ssgAux/ssgaCloudLayer.cxx new file mode 100644 index 0000000..64a5579 --- /dev/null +++ b/src/ssgAux/ssgaCloudLayer.cxx @@ -0,0 +1,524 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaCloudLayer.cxx 1793 2003-08-26 21:36:53Z nmcevoy $ +*/ + +// Written by Curtis Olson, started June 2000. +// +// Moved into ssgAux, July 2003. + +#include "ssgaSky.h" + +/* Nautical Miles to Meters */ +#define SG_NM_TO_METER 1852.0000 + +/* Radians to Nautical Miles. 1 nm = 1/60 of a degree */ +#define SG_NM_TO_RAD 0.00029088820866572159 + +/* Nautical Miles to Radians */ +#define SG_RAD_TO_NM 3437.7467707849392526 + +/* For divide by zero avoidance, this will be close enough to zero */ +#define SG_EPSILON 0.0000001 + +void calc_gc_course_dist( const sgVec2& start, const sgVec2& dest, double *course, double *dist ); + +// make an ssgSimpleState for a cloud layer given the named texture +ssgSimpleState *sgCloudMakeState( const char* path ); + + +ssgaCloudLayer::ssgaCloudLayer( void ) : + layer_root(new ssgRoot), + layer_transform(new ssgTransform), + enabled(true), + layer_span(0.0), + layer_asl(0.0), + layer_thickness(0.0), + layer_transition(0.0), + scale(4000.0), + speed(0.0), + direction(0.0), + last_lon(0.0), + last_lat(0.0), + last_x(0.0), + last_y(0.0) +{ + cl[0] = cl[1] = cl[2] = cl[3] = NULL; + vl[0] = vl[1] = vl[2] = vl[3] = NULL; + tl[0] = tl[1] = tl[2] = tl[3] = NULL; + layer[0] = layer[1] = layer[2] = layer[3] = NULL; + + layer_root->addKid(layer_transform); +} + + +ssgaCloudLayer::~ssgaCloudLayer() +{ + delete layer_root; // deletes layer_transform and layer as well +} + + +void +ssgaCloudLayer::build( const char *cloud_tex_path, float span, float elevation, float thickness, float transition ) +{ + ssgSimpleState *cloud_state = sgCloudMakeState( cloud_tex_path ); + build(cloud_state, span, elevation, thickness, transition); +} + + +void +ssgaCloudLayer::build( ssgSimpleState *cloud_state, float span, float elevation, float thickness, float transition ) +{ + layer_span = span; + layer_asl = elevation; + layer_thickness = thickness; + layer_transition = transition; + + scale = 4000.0; + last_lon = last_lat = -999.0f; + last_x = last_y = 0.0f; + + sgVec2 base; + sgSetVec2( base, (float)ssgaRandom(), (float)ssgaRandom() ); + + // build the cloud layer + sgVec4 color; + sgVec3 vertex; + sgVec2 tc; + + const float layer_scale = layer_span / scale; + const float mpi = SG_PI/4; + const float alt_diff = layer_asl * 1.5f; + + for (int i = 0; i < 4; i++) { + if ( layer[i] != NULL ) { + layer_transform->removeKid(layer[i]); // automatic delete + } + + vl[i] = new ssgVertexArray( 10 ); + cl[i] = new ssgColourArray( 10 ); + tl[i] = new ssgTexCoordArray( 10 ); + + sgSetVec3( vertex, layer_span*(i-2)/2, -layer_span, + (float)(alt_diff * (sin(i*mpi) - 2)) ); + + sgSetVec2( tc, base[0] + layer_scale * i/4, base[1] ); + + sgSetVec4( color, 1.0f, 1.0f, 1.0f, (i == 0) ? 0.0f : 0.15f ); + + cl[i]->add( color ); + vl[i]->add( vertex ); + tl[i]->add( tc ); + + for (int j = 0; j < 4; j++) { + + sgSetVec3( vertex, layer_span*(i-1)/2, layer_span*(j-2)/2, + (float)(alt_diff * (sin((i+1)*mpi) + sin(j*mpi) - 2)) ); + + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale * j/4 ); + + sgSetVec4( color, 1.0f, 1.0f, 1.0f, + ( (j == 0) || (i == 3)) ? + ( (j == 0) && (i == 3)) ? 0.0f : 0.15f : 1.0f ); + + cl[i]->add( color ); + vl[i]->add( vertex ); + tl[i]->add( tc ); + + sgSetVec3( vertex, layer_span*(i-2)/2, layer_span*(j-1)/2, + (float)(alt_diff * (sin(i*mpi) + sin((j+1)*mpi) - 2)) ); + + sgSetVec2( tc, base[0] + layer_scale * i/4, + base[1] + layer_scale * (j+1)/4 ); + + sgSetVec4( color, 1.0f, 1.0f, 1.0f, + ((j == 3) || (i == 0)) ? + ((j == 3) && (i == 0)) ? 0.0f : 0.15f : 1.0f ); + cl[i]->add( color ); + vl[i]->add( vertex ); + tl[i]->add( tc ); + } + + sgSetVec3( vertex, layer_span*(i-1)/2, layer_span, + (float)(alt_diff * (sin((i+1)*mpi) - 2)) ); + + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale ); + + sgSetVec4( color, 1.0f, 1.0f, 1.0f, (i == 3) ? 0.0f : 0.15f ); + + cl[i]->add( color ); + vl[i]->add( vertex ); + tl[i]->add( tc ); + + layer[i] = new ssgVtxTable(GL_TRIANGLE_STRIP, vl[i], NULL, tl[i], cl[i]); + layer_transform->addKid( layer[i] ); + + layer[i]->setState( cloud_state ); + } + + // force a repaint of the sky colors with arbitrary defaults + repaint( color ); +} + + +bool ssgaCloudLayer::repositionFlat( sgVec3 p, double dt ) +{ + sgMat4 T1; + + // combine p and asl (meters) to get translation offset + sgVec3 asl_offset; + if ( p[SG_Z] <= layer_asl ) { + sgSetVec3( asl_offset, p[SG_X], p[SG_Y], layer_asl ); + } + else { + sgSetVec3( asl_offset, p[SG_X], p[SG_Y], layer_asl + layer_thickness ); + } + + // Translate to elevation + sgMakeTransMat4( T1, asl_offset ); + + sgMat4 TRANSFORM; + sgCopyMat4( TRANSFORM, T1 ); + + sgCoord layerpos; + sgSetCoord( &layerpos, TRANSFORM ); + + layer_transform->setTransform( &layerpos ); + + // now calculate update texture coordinates + double sp_dist = speed*dt; + + if ( p[SG_X] != last_x || p[SG_Y] != last_y || sp_dist != 0 ) { + + // calculate cloud movement + double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0; + + ax = p[SG_X] - last_x; + ay = p[SG_Y] - last_y; + + if (sp_dist > 0) { + bx = cos(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist; + by = sin(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist; + } + + float xoff = (float)((ax + bx) / (2 * scale)); + float yoff = (float)((ay + by) / (2 * scale)); + + const float layer_scale = layer_span / scale; + + float *base, *tc; + + base = tl[0]->get( 0 ); + base[0] += xoff; + + // the while loops can lead to *long* pauses if base[0] comes + // with a bogus value. + // while ( base[0] > 1.0 ) { base[0] -= 1.0; } + // while ( base[0] < 0.0 ) { base[0] += 1.0; } + if ( base[0] > -10.0 && base[0] < 10.0 ) { + base[0] -= (int)base[0]; + } + else { + base[0] = 0.0; + ulSetError(UL_WARNING, "Warning: base1\n"); + } + + base[1] += yoff; + // the while loops can lead to *long* pauses if base[0] comes + // with a bogus value. + // while ( base[1] > 1.0 ) { base[1] -= 1.0; } + // while ( base[1] < 0.0 ) { base[1] += 1.0; } + if ( base[1] > -10.0 && base[1] < 10.0 ) { + base[1] -= (int)base[1]; + } + else { + base[1] = 0.0; + ulSetError(UL_WARNING, "Warning: base2\n"); + } + + for (int i = 0; i < 4; i++) { + tc = tl[i]->get( 0 ); + sgSetVec2( tc, base[0] + layer_scale * i/4, base[1] ); + + for (int j = 0; j < 4; j++) { + tc = tl[i]->get( j*2+1 ); + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale * j/4 ); + + tc = tl[i]->get( (j+1)*2 ); + sgSetVec2( tc, base[0] + layer_scale * i/4, + base[1] + layer_scale * (j+1)/4 ); + } + + tc = tl[i]->get( 9 ); + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale ); + } + + last_x = p[SG_X]; + last_y = p[SG_Y]; + } + + return true; +} + + +bool ssgaCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt ) +{ + sgMat4 T1, LON, LAT; + sgVec3 axis; + + // combine p and asl (meters) to get translation offset + sgVec3 asl_offset; + sgCopyVec3( asl_offset, up ); + sgNormalizeVec3( asl_offset ); + if ( alt <= layer_asl ) { + sgScaleVec3( asl_offset, layer_asl ); + } + else { + sgScaleVec3( asl_offset, layer_asl + layer_thickness ); + } + sgAddVec3( asl_offset, p ); + + // Translate to zero elevation + sgMakeTransMat4( T1, asl_offset ); + + // Rotate to proper orientation + sgSetVec3( axis, 0.0, 0.0, 1.0 ); + sgMakeRotMat4( LON, (float)(lon * SGD_RADIANS_TO_DEGREES), axis ); + + sgSetVec3( axis, 0.0, 1.0, 0.0 ); + sgMakeRotMat4( LAT, (float)(90.0 - lat * SGD_RADIANS_TO_DEGREES), axis ); + + sgMat4 TRANSFORM; + + sgCopyMat4( TRANSFORM, T1 ); + sgPreMultMat4( TRANSFORM, LON ); + sgPreMultMat4( TRANSFORM, LAT ); + + sgCoord layerpos; + sgSetCoord( &layerpos, TRANSFORM ); + + layer_transform->setTransform( &layerpos ); + + // now calculate update texture coordinates + if ( last_lon < -900 ) { + last_lon = lon; + last_lat = lat; + } + + double sp_dist = speed*dt; + + if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) { + + double course = 0.0, dist = 0.0; + if ( lon != last_lon || lat != last_lat ) { + sgVec2 start, dest; + sgSetVec2(start, (float)last_lon, (float)last_lat); + sgSetVec2(dest, (float)lon, (float)lat); + calc_gc_course_dist( dest, start, &course, &dist ); + } + + // calculate cloud movement + double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0; + + if (dist > 0.0) { + ax = cos(course) * dist; + ay = sin(course) * dist; + } + + if (sp_dist > 0) { + bx = cos(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist; + by = sin(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist; + } + + float xoff = (float)((ax + bx) / (2 * scale)); + float yoff = (float)((ay + by) / (2 * scale)); + + const float layer_scale = layer_span / scale; + + float *base, *tc; + + base = tl[0]->get( 0 ); + base[0] += xoff; + + // the while loops can lead to *long* pauses if base[0] comes + // with a bogus value. + // while ( base[0] > 1.0 ) { base[0] -= 1.0; } + // while ( base[0] < 0.0 ) { base[0] += 1.0; } + if ( base[0] > -10.0 && base[0] < 10.0 ) { + base[0] -= (int)base[0]; + } + else { + base[0] = 0.0; + ulSetError(UL_WARNING, "Warning: base1\n"); + } + + base[1] += yoff; + // the while loops can lead to *long* pauses if base[0] comes + // with a bogus value. + // while ( base[1] > 1.0 ) { base[1] -= 1.0; } + // while ( base[1] < 0.0 ) { base[1] += 1.0; } + if ( base[1] > -10.0 && base[1] < 10.0 ) { + base[1] -= (int)base[1]; + } + else { + base[1] = 0.0; + ulSetError(UL_WARNING, "Warning: base2\n"); + } + + for (int i = 0; i < 4; i++) { + tc = tl[i]->get( 0 ); + sgSetVec2( tc, base[0] + layer_scale * i/4, base[1] ); + + for (int j = 0; j < 4; j++) { + tc = tl[i]->get( j*2+1 ); + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale * j/4 ); + + tc = tl[i]->get( (j+1)*2 ); + sgSetVec2( tc, base[0] + layer_scale * i/4, + base[1] + layer_scale * (j+1)/4 ); + } + + tc = tl[i]->get( 9 ); + sgSetVec2( tc, base[0] + layer_scale * (i+1)/4, + base[1] + layer_scale ); + } + + last_lon = lon; + last_lat = lat; + } + + return true; +} + + +bool ssgaCloudLayer::repaint( sgVec3 fog_color ) +{ + float *color; + + for ( int i = 0; i < 4; i++ ) + for ( int j = 0; j < 10; ++j ) { + color = cl[i]->get( j ); + sgCopyVec3( color, fog_color ); + } + + return true; +} + + +void ssgaCloudLayer::draw() +{ + if (!enabled) + return; + + ssgCullAndDraw( layer_root ); +} + + +ssgSimpleState *sgCloudMakeState( const char* path ) +{ + ssgSimpleState *state = new ssgSimpleState(); + + state->setTexture( path ); + state->setShadeModel( GL_SMOOTH ); + state->disable( GL_LIGHTING ); + state->disable( GL_CULL_FACE ); + state->enable( GL_TEXTURE_2D ); + state->enable( GL_COLOR_MATERIAL ); + state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ); + state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + state->enable( GL_BLEND ); + state->enable( GL_ALPHA_TEST ); + state->setAlphaClamp( 0.01f ); + + return state; +} + + +void calc_gc_course_dist( const sgVec2& start, const sgVec2& dest, double *course, double *dist ) +{ + // d = 2*asin(sqrt((sin((lat1-lat2)/2))^2 + + // cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2)) + double cos_start_y = cos( start[SG_Y] ); + volatile double tmp1 = sin( (start[SG_Y] - dest[SG_Y]) * 0.5 ); + volatile double tmp2 = sin( (start[SG_X] - dest[SG_X]) * 0.5 ); + double d = 2.0 * asin( sqrt( tmp1 * tmp1 + + cos_start_y * cos(dest[SG_Y]) * tmp2 * tmp2)); + + *dist = d * SG_RAD_TO_NM * SG_NM_TO_METER; + + // We obtain the initial course, tc1, (at point 1) from point 1 to + // point 2 by the following. The formula fails if the initial + // point is a pole. We can special case this with: + // + // IF (cos(lat1) < EPS) // EPS a small number ~ machine precision + // IF (lat1 > 0) + // tc1= pi // starting from N pole + // ELSE + // tc1= 0 // starting from S pole + // ENDIF + // ENDIF + // + // For starting points other than the poles: + // + // IF sin(lon2-lon1)<0 + // tc1=acos((sin(lat2)-sin(lat1)*cos(d))/(sin(d)*cos(lat1))) + // ELSE + // tc1=2*pi-acos((sin(lat2)-sin(lat1)*cos(d))/(sin(d)*cos(lat1))) + // ENDIF + + // if ( cos(start[SG_Y]) < SG_EPSILON ) { + // doing it this way saves a transcendental call + double sin_start_y = sin( start[SG_Y] ); + if ( fabs(1.0-sin_start_y) < SG_EPSILON ) { + // EPS a small number ~ machine precision + if ( start[SG_Y] > 0 ) { + *course = SGD_PI; // starting from N pole + } else { + *course = 0; // starting from S pole + } + } else { + // For starting points other than the poles: + // double tmp3 = sin(d)*cos_start_y); + // double tmp4 = sin(dest[SG_Y])-sin(start[SG_Y])*cos(d); + // double tmp5 = acos(tmp4/tmp3); + + // Doing this way gaurentees that the temps are + // not stored into memory + double tmp5 = acos( (sin(dest[SG_Y]) - sin_start_y * cos(d)) / + (sin(d) * cos_start_y) ); + + // if ( sin( dest[SG_X] - start[SG_X] ) < 0 ) { + // the sin of the negative angle is just the opposite sign + // of the sin of the angle so tmp2 will have the opposite + // sign of sin( dest[SG_X] - start[SG_X] ) + if ( tmp2 >= 0 ) { + *course = tmp5; + } else { + *course = 2 * SGD_PI - tmp5; + } + } +} diff --git a/src/ssgAux/ssgaFire.cxx b/src/ssgAux/ssgaFire.cxx new file mode 100644 index 0000000..315651d --- /dev/null +++ b/src/ssgAux/ssgaFire.cxx @@ -0,0 +1,256 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +#include "ssg.h" +#include "ssgAux.h" + +#define EMBER_SCALE 3.0f + +void _ssgaFireParticleCreate ( ssgaParticleSystem *ps, + int idx, ssgaParticle *p ) +{ + ((ssgaFire *) ps) -> createParticle ( idx, p ) ; +} + + + +void _ssgaFireParticleUpdate ( float, ssgaParticleSystem *ps, + int idx, ssgaParticle *p ) +{ + ((ssgaFire *) ps) -> updateParticle ( idx, p ) ; +} + + + + +void ssgaFire::reInit () +{ + setCreationRate ( (float)(getNumParticles()) / max_ttl ) ; + + delete colourTable ; + delete sizeTable ; + + tableSize = (int)( 10.0f * max_ttl ) ; + + colourTable = new float [ tableSize * 4 ] ; + sizeTable = new float [ tableSize ] ; + + sgCopyVec4 ( & colourTable [ 0 ], hot_colour ) ; + sizeTable [ 0 ] = start_size ; + + for ( int i = 1 ; i < tableSize ; i++ ) + { + sizeTable [ i ] = sizeTable [ i-1 ] * 1.06f ; + + if ( sizeTable [ i ] >= 1.5 ) sizeTable [ i ] = 1.5 ; + + sgScaleVec3 ( & colourTable [ i * 4 ], + & colourTable [ (i-1)* 4 ], 0.90f ) ; + + colourTable [ i * 4 + 3 ] = 1.0f ; + } +} + + +void ssgaFire::createParticle ( int idx, ssgaParticle *p ) +{ + float xx = (float)(rand()%1000)/500.0f * radius - radius ; + float yy = (float)sqrt ( radius * radius - xx * xx ) ; + + yy = (float)(rand()%1000)/500.0f * yy - yy ; + + p -> time_to_live = max_ttl ; + p -> size = sizeTable [ 0 ] ; + + sgCopyVec4 ( p -> col, & colourTable [ 0 ] ) ; + + if ( (idx & 3) != 0 ) + { + sgSetVec3 ( p -> pos, xx, yy, -p->size ) ; + sgSetVec3 ( p -> vel, 0, 0, upward_speed ) ; + } + else + { + p->size *= EMBER_SCALE ; + + sgSetVec3 ( p -> pos, xx, yy, 0.0f ) ; + sgSetVec3 ( p -> vel, 0, 0, 0 ) ; + } + + sgSetVec3 ( p -> acc, 0, 0, 0 ) ; +} + + + + +void ssgaFire::updateParticle ( int idx, ssgaParticle *p ) +{ + int tick = (int)( ( max_ttl - p->time_to_live ) * 10.0f ) ; + + if ( tick >= tableSize ) + { + p->time_to_live = 0.0f ; + return ; + } + + if ( (idx & 3) != 0 ) + p -> size = sizeTable [ tick ] ; + else + p -> size = sizeTable [ tick ] * EMBER_SCALE ; + + sgCopyVec4 ( p -> col, & colourTable [ tick * 4 ] ) ; +} + + +static ssgSimpleState *fireState = NULL ; +static ssgTexture *fireTexture = NULL ; +static bool isfogged = true ; +static int preFireDraw ( ssgEntity * ) +{ + isfogged = ( glIsEnabled ( GL_FOG ) == GL_TRUE ) ; + + if ( isfogged ) + glDisable ( GL_FOG ) ; + + glBlendFunc ( GL_ONE, GL_ONE ) ; + return TRUE ; +} + + +static int postFireDraw ( ssgEntity * ) +{ + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + if ( isfogged ) + glEnable ( GL_FOG ) ; + + return TRUE ; +} + +ssgaFire::ssgaFire ( int num, + float _radius, + float height, + float speed ) + : ssgaParticleSystem ( num/2, + 0, 0, // <== Don't Change this!! + TRUE, 1.0f, height * 2.0f, + _ssgaFireParticleCreate, + _ssgaFireParticleUpdate ) +{ + radius = _radius ; + upward_speed = speed ; + start_size = 0.6f ; + + if ( fireState == NULL ) + { + /* + The texture constructor deletes the texture from + main memory after it's finished with it!! YIKES!! + */ + + unsigned char *t = new unsigned char [ 32 * 32 ] ; + memcpy ( t, _ssgaGetFireTexture(), 32 * 32 ) ; + + fireTexture = new ssgTexture ( "NONE", t, 32, 32, 1 ) ; + + fireState = new ssgSimpleState () ; + fireState -> setTexture ( fireTexture ) ; + fireState -> setTranslucent () ; + fireState -> enable ( GL_TEXTURE_2D ) ; + fireState -> enable ( GL_BLEND ) ; + fireState -> disable ( GL_LIGHTING ) ; + fireState -> ref () ; + } + + + tableSize = 0 ; + colourTable = NULL ; + sizeTable = NULL ; + + sgSetVec4 ( hot_colour, 1.0f, 0.2f, 0.1f, 1.0f ) ; + + setHeight ( height ) ; /* Forces a reInit */ + + setState ( fireState ) ; + setCallback ( SSG_CALLBACK_PREDRAW , preFireDraw ) ; + setCallback ( SSG_CALLBACK_POSTDRAW, postFireDraw ) ; + update ( 2.0f * max_ttl / (float)(getNumParticles()) ) ; +} + +void ssgaFire::update ( float t ) +{ + ssgaParticleSystem::update ( t ) ; +} + + +unsigned char *_ssgaGetFireTexture () ; + + +ssgaFire::~ssgaFire () +{ + tableSize = 0 ; + delete [] colourTable ; + delete [] sizeTable ; +} + + + + +static unsigned char fuzzyBlob [] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,5,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,1,5,9,14,17,19,20,20,20,17,14,10,5,1,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,5,12,19,25,29,33,35,36,37,36,33,30,25,20,13,6,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,2,10,19,27,34,40,45,48,51,52,53,51,49,45,40,35,28,20,11,3,0,0,0,0,0,0, + 0,0,0,0,0,4,13,23,33,41,48,55,60,64,67,69,69,67,65,61,56,49,42,34,25,15,5,0,0,0,0,0, + 0,0,0,0,4,14,26,36,46,55,63,70,75,80,83,85,85,83,80,76,70,64,56,47,37,27,16,5,0,0,0,0, + 0,0,0,2,13,26,37,48,59,68,77,84,91,95,99,101,101,99,96,91,85,78,69,60,50,39,27,15,3,0,0,0, + 0,0,1,10,23,36,48,60,71,81,90,99,105,111,115,117,117,115,111,106,100,92,82,72,61,50,38,25,12,1,0,0, + 0,0,5,19,33,46,59,71,83,94,104,113,120,126,130,133,133,131,127,121,114,105,95,84,72,60,47,34,21,7,0,0, + 0,1,12,27,41,55,68,81,94,105,116,126,134,141,146,149,149,147,142,135,127,118,107,95,83,70,56,43,29,14,2,0, + 0,5,19,34,48,63,77,90,104,116,128,139,148,156,162,165,165,162,157,149,140,129,118,105,92,78,64,50,36,21,6,0, + 0,9,25,40,55,70,84,99,113,126,139,151,161,170,177,181,181,178,171,163,152,140,128,114,100,86,72,57,42,27,11,0, + 1,14,29,45,60,75,91,105,120,134,148,161,173,184,192,196,197,193,185,175,163,150,136,122,107,92,77,62,47,31,16,2, + 3,17,33,48,64,80,95,111,126,141,156,170,184,196,206,212,212,207,197,185,172,158,143,128,113,97,82,66,50,35,19,4, + 4,19,35,51,67,83,99,115,130,146,162,177,192,206,219,227,228,220,208,194,179,163,148,132,117,101,85,69,53,37,21,5, + 5,20,36,52,69,85,101,117,133,149,165,181,196,212,227,241,242,229,214,198,182,167,151,135,119,103,87,71,54,38,22,6, + 5,20,37,53,69,85,101,117,133,149,165,181,197,212,228,242,243,230,214,199,183,167,151,135,119,103,87,71,55,39,22,6, + 4,20,36,51,67,83,99,115,131,147,162,178,193,207,220,229,230,221,209,194,179,164,148,133,117,101,85,69,53,38,22,6, + 3,17,33,49,65,80,96,111,127,142,157,171,185,197,208,214,214,209,199,187,173,159,144,129,113,98,82,67,51,35,19,4, + 1,14,30,45,61,76,91,106,121,135,149,163,175,185,194,198,199,194,187,176,164,151,137,123,108,93,78,63,47,32,16,2, + 0,10,25,40,56,70,85,100,114,127,140,152,163,172,179,182,183,179,173,164,154,142,129,115,101,87,72,57,42,27,12,1, + 0,5,20,35,49,64,78,92,105,118,129,140,150,158,163,167,167,164,159,151,142,131,119,106,93,79,65,51,36,22,7,0, + 0,1,13,28,42,56,69,82,95,107,118,128,136,143,148,151,151,148,144,137,129,119,108,96,84,71,57,44,30,15,2,0, + 0,0,6,20,34,47,60,72,84,95,105,114,122,128,132,135,135,133,129,123,115,106,96,85,74,61,49,35,22,8,0,0, + 0,0,1,11,25,37,50,61,72,83,92,100,107,113,117,119,119,117,113,108,101,93,84,74,63,51,39,26,13,2,0,0, + 0,0,0,3,15,27,39,50,60,70,78,86,92,97,101,103,103,101,98,93,87,79,71,61,51,40,28,16,4,0,0,0, + 0,0,0,0,5,16,27,38,47,56,64,72,77,82,85,87,87,85,82,78,72,65,57,49,39,28,17,6,0,0,0,0, + 0,0,0,0,0,5,15,25,34,43,50,57,62,66,69,71,71,69,67,63,57,51,44,35,26,16,6,0,0,0,0,0, + 0,0,0,0,0,0,3,12,21,29,36,42,47,50,53,54,55,53,51,47,42,36,30,22,13,4,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,7,14,21,27,31,35,37,38,39,38,35,32,27,22,15,8,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,2,6,11,16,19,21,22,22,22,19,16,12,7,2,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,6,6,6,4,2,1,0,0,0,0,0,0,0,0,0,0,0 +} ; + + +unsigned char *_ssgaGetFireTexture () { return fuzzyBlob ; } + diff --git a/src/ssgAux/ssgaFire.h b/src/ssgAux/ssgaFire.h new file mode 100644 index 0000000..5ac4b0f --- /dev/null +++ b/src/ssgAux/ssgaFire.h @@ -0,0 +1,77 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + + +#include "ssgAux.h" + + +class ssgaFire : public ssgaParticleSystem +{ + int tableSize ; + float *colourTable ; + float *sizeTable ; + + sgVec4 hot_colour ; + float max_ttl ; + float start_size ; + float upward_speed ; + float radius ; + + void reInit () ; + +public: + + void createParticle ( int idx, ssgaParticle *p ) ; + void updateParticle ( int idx, ssgaParticle *p ) ; + + ssgaFire ( int num_tris, float _radius = 1.0f, + float height = 5.0f, + float speed = 2.0f ) ; + + virtual ~ssgaFire () ; + + virtual void update ( float t ) ; + + void setUpwardSpeed ( float spd ) + { + upward_speed = spd ; + } + + void setHeight ( float hgt ) + { + max_ttl = hgt / upward_speed ; + getBSphere () -> setRadius ( hgt * 2.0f ) ; + getBSphere () -> setCenter ( 0, 0, 0 ) ; + reInit () ; + } + + void setHotColour ( sgVec4 col ) + { + sgCopyVec4 ( hot_colour, col ) ; + reInit () ; + } + + void getHotColour ( sgVec4 col ) { sgCopyVec4 ( col, hot_colour ) ; } + +} ; + +unsigned char *_ssgaGetFireTexture () ; + diff --git a/src/ssgAux/ssgaLensFlare.cxx b/src/ssgAux/ssgaLensFlare.cxx new file mode 100644 index 0000000..caa32b2 --- /dev/null +++ b/src/ssgAux/ssgaLensFlare.cxx @@ -0,0 +1,330 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +#include "ssgAux.h" + +#undef _SSG_PUBLIC +#include "../ssg/ssgLocal.h" + +/* + Lens flares are an effect that happens in the + lens - your eye or a camera lens. As such, they + are completely invisible if the light itself is + not visible - which should mean that if the light + is culled then so is the lens flare. This suggests + that the bsphere center should be at the light point + and the radius should be tiny. + + However, if you have a multi-facetted display, + pretending to be a single display rather than + a lot of separate 'cameras' then you may want to + set the SSGA_CONTINUOUS_DISPLAY_SURFACE flag. + + This should probably be realtime settable - but + to do it 'right', you'd need to know which edges + of the screen were 'shared' with other screens + and only allow the lens flare to cross those. +*/ + +// #define CONTINUOUS_DISPLAY_SURFACE 1 + + + + +sgMat4 _ssgaIdentity = +{ + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 1.0f } +} ; + +/* + This is based on code written by Stephen Coy - subsequently + re-written by Mark Kilgard - and now re-re-written by me. + Actually, only the original numbers for the dimensions of + the flare planes are from the original code. All the rest + is a complete rewrite because it has to work within a scene + graph API. +*/ + +struct _ssgaFlarePlane +{ + int type ; /* flare texture index, 0..5 */ + float loc ; /* postion on flare_axis */ + float scale ; + sgVec4 color ; +} ; + + +static _ssgaFlarePlane flare [] = +{ + { -1, 0.00f, 0.20f, { 0 , 0, 1, 1 }}, + { -1, 0.00f, 0.10f, { 0 , 1, 0, 1 }}, + { -1, 0.00f, 0.12f, { 1.0f, 0, 0, 1 }}, + { 1, 0.50f, 0.20f, { 0.3f, 0, 0, 1 }}, + { 2, -0.20f, 0.04f, { 0.6f, 0, 0, 1 }}, + { 3, 0.00f, 0.10f, { 0.4f, 0, 0, 1 }}, + { 3, 0.30f, 0.03f, { 0.3f, 0, 0, 1 }}, + { 3, 0.80f, 0.05f, { 0.3f, 0, 0, 1 }}, + { 0, 1.00f, 0.04f, { 0.3f, 0, 0, 1 }}, + { 4, 1.25f, 0.07f, { 0.5f, 0, 0, 1 }}, + { 5, 1.40f, 0.02f, { 0.6f, 0, 0, 1 }}, + { 5, 1.60f, 0.04f, { 0.4f, 0, 0, 1 }}, + { 5, 2.00f, 0.03f, { 0.2f, 0, 0, 1 }}, + { -999, 0, 0, { 0, 0, 0, 0 } } /* End marker */ +} ; + + +static float shineTexCoords [ 12 ][ 4 ][ 2 ] = +{ + { { 0.00, 0.00 }, { 0.25, 0.00 }, { 0.25, 0.50 }, { 0.00, 0.50 } }, + { { 0.25, 0.00 }, { 0.50, 0.00 }, { 0.50, 0.50 }, { 0.25, 0.50 } }, + { { 0.50, 0.00 }, { 0.75, 0.00 }, { 0.75, 0.50 }, { 0.50, 0.50 } }, + { { 0.00, 0.50 }, { 0.25, 0.50 }, { 0.25, 0.00 }, { 0.00, 0.00 } }, + { { 0.25, 0.50 }, { 0.50, 0.50 }, { 0.50, 0.00 }, { 0.25, 0.00 } }, + { { 0.50, 0.50 }, { 0.75, 0.50 }, { 0.75, 0.00 }, { 0.50, 0.00 } }, + { { 0.25, 0.00 }, { 0.00, 0.00 }, { 0.00, 0.50 }, { 0.25, 0.50 } }, + { { 0.50, 0.00 }, { 0.25, 0.00 }, { 0.25, 0.50 }, { 0.50, 0.50 } }, + { { 0.75, 0.00 }, { 0.50, 0.00 }, { 0.50, 0.50 }, { 0.75, 0.50 } }, + { { 0.25, 0.50 }, { 0.00, 0.50 }, { 0.00, 0.00 }, { 0.25, 0.00 } }, + { { 0.50, 0.50 }, { 0.25, 0.50 }, { 0.25, 0.00 }, { 0.50, 0.00 } }, + { { 0.75, 0.50 }, { 0.50, 0.50 }, { 0.50, 0.00 }, { 0.75, 0.00 } } +} ; + + +static float flareTexCoords [ 6 ][ 4 ][ 2 ] = +{ + { { 0.00f, 0.50f }, { 0.25f, 0.50f }, { 0.25f, 1.00f }, { 0.00f, 1.00f } }, + { { 0.25f, 0.50f }, { 0.50f, 0.50f }, { 0.50f, 1.00f }, { 0.25f, 1.00f } }, + { { 0.27f, 0.70f }, { 0.48f, 0.70f }, { 0.48f, 0.80f }, { 0.27f, 0.80f } }, + { { 0.50f, 0.50f }, { 0.75f, 0.50f }, { 0.75f, 1.00f }, { 0.50f, 1.00f } }, + { { 0.75f, 0.50f }, { 1.00f, 0.50f }, { 1.00f, 1.00f }, { 0.75f, 1.00f } }, + { { 0.75f, 0.00f }, { 1.00f, 0.00f }, { 1.00f, 0.50f }, { 0.75f, 0.50f } } +} ; + + +void ssgaLensFlare::copy_from ( ssgaLensFlare *src, int clone_flags ) +{ + ssgaShape::copy_from ( src, clone_flags ) ; +} + +ssgBase *ssgaLensFlare::clone ( int clone_flags ) +{ + ssgaLensFlare *b = new ssgaLensFlare ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgaLensFlare::ssgaLensFlare (void):ssgaShape () +{ + type = ssgaTypeLensFlare () ; + regenerate () ; +} + +ssgaLensFlare::ssgaLensFlare (int nt):ssgaShape (nt) +{ + type = ssgaTypeLensFlare () ; + regenerate () ; +} + +ssgaLensFlare::~ssgaLensFlare (void) {} + +static ssgSimpleState *flareState = NULL ; +static ssgTexture *flareTexture = NULL ; + +const char *ssgaLensFlare::getTypeName(void) { return "ssgaLensFlare" ; } + +static int preDraw ( ssgEntity * ) +{ + glDisable ( GL_DEPTH_TEST ) ; + glBlendFunc ( GL_ONE, GL_ONE ) ; + return TRUE ; +} + + +static int postDraw ( ssgEntity * ) +{ + glEnable ( GL_DEPTH_TEST ) ; + glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + return TRUE ; +} + + +void ssgaLensFlare::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + for ( ntriangles = 0 ; flare[ntriangles/2].type >= -1 ; ntriangles += 2 ) + /* Count the number of triangles */ ; + + vt = new ssgVtxTable () ; + v0 = new ssgVertexArray ( 2 * ntriangles ) ; + n0 = new ssgNormalArray ( 2 * ntriangles ) ; + c0 = new ssgColourArray ( 2 * ntriangles ) ; + t0 = new ssgTexCoordArray ( 2 * ntriangles ) ; + + vt -> setPrimitiveType ( GL_QUADS ) ; + + addKid ( vt ) ; + + if ( flareState == NULL ) + { + /* + The texture constructor deletes the texture from + main memory after it's finished with it!! YIKES!! + */ + + unsigned char *t = new unsigned char [ 256 * 128 ] ; + memcpy ( t, _ssgaGetLensFlareTexture(), 256 * 128 ) ; + + flareTexture = new ssgTexture ( "NONE", t, 256, 128, 1 ) ; + + flareState = new ssgSimpleState () ; + flareState -> setTexture ( flareTexture ) ; + flareState -> setTranslucent () ; + flareState -> enable ( GL_TEXTURE_2D ) ; + flareState -> enable ( GL_BLEND ) ; + flareState -> disable ( GL_LIGHTING ) ; + flareState -> ref () ; + setKidState ( flareState ) ; + } + + + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , preDraw /* getKidPreDrawCB */ ); + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, postDraw /* getKidPostDrawCB*/ ); + + sgVec3 v ; sgVec3 n ; sgVec2 t ; sgVec4 c ; + + for ( int i = 0 ; i < ntriangles * 2 ; i++ ) + { + sgSetVec3 ( v, 0, 0, 0 ) ; v0->add ( v ) ; + sgSetVec3 ( n, 0, -1, 0 ) ; n0->add ( n ) ; + sgSetVec4 ( c, 1,1,1,1 ) ; c0->add ( c ) ; + sgSetVec2 ( t, 0, 0 ) ; t0->add ( t ) ; + } + + vt -> setVertices ( v0 ) ; + vt -> setNormals ( n0 ) ; + vt -> setColours ( c0 ) ; + vt -> setTexCoords ( t0 ) ; + +#ifndef CONTINUOUS_DISPLAY_SURFACE + vt -> getBSphere () -> setCenter ( 0, 0, 0 ) ; + vt -> getBSphere () -> setRadius ( 0.1f ) ; + getBSphere () -> setCenter ( 0, 0, 0 ) ; + getBSphere () -> setRadius ( 0.1f ) ; +#else + vt -> recalcBSphere () ; + recalcBSphere () ; +#endif +} + + +void ssgaLensFlare::update ( sgMat4 mat ) +{ + static int shine_tic = 0 ; + + sgVec3 flare_axis ; + + float *texCoords ; + + sgVec3 light_pos ; + float znear ; + + ssgGetNearFar ( & znear, NULL ) ; + sgNormalizeVec3 ( light_pos, mat[3] ) ; + sgScaleVec3 ( light_pos, znear * 2.0f ) ; /* To avoid being near-clipped */ + + sgSetVec3 ( flare_axis, -light_pos[0], -light_pos[1], 0.0 ) ; + + int vv = 0 ; + + for ( int i = 0 ; flare[ i ].type >= -1 ; i++ ) + { + sgVec3 position ; + float sz ; + + sz = flare[i].scale * znear * 2.0f ; + + sgAddScaledVec3 ( position, light_pos, flare_axis, flare[i].loc ) ; + + if ( flare[i].type < 0 ) + { + shine_tic = (shine_tic + 1) % 12 ; + + texCoords = & ( shineTexCoords [ shine_tic ][ 0 ][ 0 ] ) ; + } + else + texCoords = & ( flareTexCoords [ flare[i].type ][ 0 ][ 0 ] ) ; + + sgVec3 vx ; + + sgSetVec3 ( vx, position[0]+sz, position[1]-sz, position[2] ) ; + c0 -> set ( flare[i].color, vv ) ; + t0 -> set ( & texCoords[0], vv ) ; + v0 -> set ( vx, vv++ ) ; + + sgSetVec3 ( vx, position[0]+sz, position[1]+sz, position[2] ) ; + c0 -> set ( flare[i].color, vv ) ; + t0 -> set ( & texCoords[2], vv ) ; + v0 -> set ( vx, vv++ ) ; + + sgSetVec3 ( vx, position[0]-sz, position[1]+sz, position[2] ) ; + c0 -> set ( flare[i].color, vv ) ; + t0 -> set ( & texCoords[4], vv ) ; + v0 -> set ( vx, vv++ ) ; + + sgSetVec3 ( vx, position[0]-sz, position[1]-sz, position[2] ) ; + c0 -> set ( flare[i].color, vv ) ; + t0 -> set ( & texCoords[6], vv ) ; + v0 -> set ( vx, vv++ ) ; + } +} + + +void ssgaLensFlare::cull ( sgFrustum *f, sgMat4 m, int /* test_needed */ ) +{ + /* + Lens flares are best generated in eye-space (because they actually + occur inside the lens of your eye). Hence we call 'update' with + the current modelview matrix - it generates the geometry in eye-space - + which means that we have to render it with the identity matrix on the + stack. + */ + + update ( m ) ; + + _ssgPushMatrix ( _ssgaIdentity ) ; + + glPushMatrix () ; + glLoadMatrixf ( (float *) _ssgaIdentity ) ; + ssgBranch::cull ( f, _ssgaIdentity, FALSE ) ; + glPopMatrix () ; + + _ssgPopMatrix () ; +} + diff --git a/src/ssgAux/ssgaLensFlare.h b/src/ssgAux/ssgaLensFlare.h new file mode 100644 index 0000000..10b1be0 --- /dev/null +++ b/src/ssgAux/ssgaLensFlare.h @@ -0,0 +1,52 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + + +#include "ssgaShapes.h" + +class ssgaLensFlare : public ssgaShape +{ + ssgVtxTable *vt ; + ssgVertexArray *v0 ; + ssgNormalArray *n0 ; + ssgColourArray *c0 ; + ssgTexCoordArray *t0 ; + + void update ( sgMat4 m ) ; + +protected: + virtual void copy_from ( ssgaLensFlare *src, int clone_flags ) ; +public: + + ssgaLensFlare () ; + ssgaLensFlare ( int nt ) ; + + virtual ~ssgaLensFlare () ; + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + virtual void regenerate () ; + virtual const char *getTypeName ( void ) ; + virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ; +} ; + +unsigned char *_ssgaGetLensFlareTexture () ; + + diff --git a/src/ssgAux/ssgaLensFlareTexture.cxx b/src/ssgAux/ssgaLensFlareTexture.cxx new file mode 100644 index 0000000..741ec20 --- /dev/null +++ b/src/ssgAux/ssgaLensFlareTexture.cxx @@ -0,0 +1,2076 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +static unsigned char lens_flare_texture [] = +{ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,2,12,19,24,30,36,36, +35,26,20,12,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,10,29,54,77,96,112,123,131,135,137, +132,126,113,99,81,60,35,14,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,24,60,92,124,149,173,191,207,222,228,231,231, +229,224,214,199,177,155,131,100,68,32,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,30,73,114,151,187,219,233,242,239,235,231,227,227,227, +227,229,234,238,242,235,225,195,162,123,84,39,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +20,68,116,162,204,233,241,233,225,218,212,207,204,202,201,201, +201,203,206,211,217,223,231,240,237,214,172,128,81,30,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,46, +101,154,203,238,238,227,216,207,199,193,187,182,179,177,175,175, +176,178,182,186,191,197,205,214,224,235,239,215,166,115,60,11, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,12,66,128, +183,229,239,226,214,202,192,183,176,169,164,159,157,153,152,152, +153,156,159,163,168,174,181,189,199,210,222,236,239,197,141,82, +24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,8, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,21,81,143,207, +240,231,216,202,190,180,170,161,154,148,143,139,135,134,133,133, +134,135,136,142,147,152,160,168,177,187,199,212,227,241,220,159, +97,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,16,3,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,24,89,156,221,241, +225,209,194,180,170,158,149,142,134,128,124,120,119,114,113,112, +114,118,120,123,128,133,140,147,156,166,178,190,203,219,236,231, +173,107,36,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0, +0,7,0,0,0,12,0,0,0,0,0,0,9,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,12,10,2,3,6,0,12, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,21,89,159,224,237,219, +203,187,172,162,149,139,131,124,118,112,106,104,102,96,96,96, +96,99,104,105,112,116,122,129,138,147,157,171,185,197,214,234, +236,178,107,34,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,3, +0,0,0,0,5,21,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0, +3,0,0,0,7,10,0,0,0,0,0,11,10,0,0,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8,21,5,8,12,2,16, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,12,81,156,224,236,218,199, +182,170,155,142,131,124,114,108,102,96,90,88,86,82,80,80, +82,84,88,90,96,99,106,112,122,130,139,153,163,179,195,211, +231,237,174,102,27,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,33, +0,0,0,0,14,20,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,16,11,19, +26,12,0,0,17,10,0,0,0,0,5,22,2,0,0,10, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,21,10,21,15,7,36, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,8, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,66,143,221,237,218,197,179, +163,150,139,126,116,108,100,93,87,81,79,73,72,71,70,70, +70,72,72,76,80,86,91,98,106,115,124,133,147,163,179,195, +211,233,231,165,88,14,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,10,0,0,0,0,0,18,40, +0,0,0,0,32,17,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,3,13,5, +8,0,0,0,0,0,0,0,0,0,0,0,0,17,11,23, +50,58,0,0,20,3,0,0,0,0,25,9,0,5,13,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7,7,0,0,0,0,0,23,18,26,19,9,48, +0,0,0,0,0,3,12,0,0,0,0,0,0,8,8,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,46,128,207,241,219,199,179,163, +147,134,124,113,102,93,86,79,73,68,64,63,58,57,56,56, +56,58,62,64,68,72,79,85,91,100,109,122,131,147,163,179, +195,213,235,223,145,66,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,2,0,0,19,8,0,0,0,0,34,55, +0,0,0,10,49,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, +13,16,0,0,0,0,0,0,0,0,0,0,0,14,14,30, +39,24,0,11,28,0,12,2,0,17,24,0,3,25,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,18,7,0,0,0,0,16,32,36,38,17,20, +0,0,0,0,0,14,11,0,0,11,6,0,13,26,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,20,101,183,240,225,203,182,163,147, +133,123,109,99,89,81,73,68,63,57,55,50,48,48,46,46, +47,48,49,53,56,60,65,72,79,87,97,107,117,131,147,163, +179,197,219,241,204,123,36,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,6,6,0,10,21,0,0,3,3,25,62, +0,0,0,27,56,3,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,8,3,0,0, +23,28,19,2,0,0,0,14,2,0,0,0,0,16,23,9, +53,30,0,21,21,4,11,0,6,36,5,4,23,7,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,27,5,0,0,0,3,42,39,55,26,52, +0,0,0,0,0,27,4,0,3,12,0,9,32,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,68,154,229,231,209,187,170,150,134, +123,108,98,87,78,70,63,57,52,47,43,41,39,38,37,36, +38,39,40,43,47,51,55,62,68,76,85,94,107,117,131,147, +163,181,203,225,241,174,90,12,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +8,0,0,0,0,0,13,3,0,30,10,0,8,12,18,70, +0,0,6,61,47,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,3,15,11,0, +6,32,37,24,8,0,0,12,15,0,0,0,0,19,41,24, +78,50,0,38,15,21,10,0,35,18,3,33,17,0,0,0, +3,11,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,16,28,2,0,0,0,35,41,68,40,62, +0,0,0,0,13,27,0,3,17,2,6,38,10,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,30,116,203,239,216,194,172,155,139,124, +109,98,85,77,68,60,54,47,43,39,35,33,31,30,28,28, +29,30,32,35,37,41,47,52,58,66,74,83,93,107,120,133, +149,170,188,210,233,223,138,51,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +10,13,3,0,0,0,3,26,3,18,28,0,9,29,22,118, +6,3,35,93,24,0,0,6,0,0,3,16,9,22,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,3,16,23, +3,7,37,32,46,15,0,7,34,5,0,0,0,10,44,55, +82,42,5,44,7,29,0,25,36,3,34,23,0,0,0,13, +15,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,29,23,0,0,0,22,53,87,62,62, +5,0,10,0,29,15,0,21,16,5,39,18,0,0,0,0, +0,0,3,15,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,4,73,162,238,226,202,180,162,142,126,113, +99,87,77,67,58,51,45,39,35,31,28,26,23,22,22,22, +22,23,25,28,30,35,38,43,50,56,64,74,85,96,108,124, +139,155,175,196,220,241,184,95,14,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,13,27,5,0,0,0,17,25,3,38,9,2,50,34,130, +19,11,90,101,6,0,10,13,0,0,14,20,38,9,0,0, +0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17, +33,10,6,43,58,62,15,3,44,39,0,6,7,3,50,65, +92,44,23,39,24,21,8,49,7,30,33,0,0,6,21,22, +3,2,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0, +0,4,17,3,0,0,4,42,14,0,0,5,55,94,84,72, +30,14,18,7,43,3,10,34,6,40,26,0,4,5,0,0, +3,25,24,8,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,24,114,204,238,214,190,170,149,131,116,102, +89,78,68,58,50,43,37,32,28,25,21,20,18,18,16,16, +17,18,20,21,24,27,31,37,42,48,56,65,75,87,99,114, +129,146,164,186,208,232,225,136,45,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,12,35,12,0,0,2,35,27,30,33,0,59,53,149, +21,24,132,82,0,4,24,5,16,26,26,37,11,0,0,0, +0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +10,37,23,8,49,78,72,12,22,76,21,11,18,6,52,86, +104,45,39,32,43,5,44,26,29,43,3,4,14,38,19,3, +13,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,12,21,7, +0,0,7,26,9,0,0,12,44,6,0,0,48,101,113,84, +48,19,14,30,28,3,39,13,38,36,0,8,16,3,0,20, +47,30,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,60,151,233,227,202,180,158,139,124,108,93, +81,70,60,51,43,37,32,27,21,20,17,14,12,12,12,12, +12,12,13,16,19,21,26,29,35,42,49,58,67,79,91,104, +120,135,154,174,196,220,241,175,83,5,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7,38,22,0,0,9,53,31,51,7,53,72,149, +68,54,161,51,0,27,16,19,54,77,67,11,0,0,0,0, +10,21,12,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,33,41,12,52,96,82,11,65,79,12,32,19,50,102, +119,48,57,40,32,29,48,26,50,6,13,42,43,15,14,24, +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,25, +20,5,0,5,32,17,3,3,30,43,0,0,30,101,133,86, +77,44,21,58,13,28,34,34,48,3,20,28,3,14,52,58, +16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,10,92,187,241,216,192,170,149,131,114,100,86, +73,63,54,45,37,32,27,21,19,13,12,11,10,5,4,4, +4,10,11,12,13,16,20,24,29,35,43,51,60,71,83,96, +111,128,144,164,186,210,235,211,116,22,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,40,31,3,0,28,72,53,36,38,93,138, +58,88,166,20,18,35,28,77,101,78,9,0,0,0,11,28, +24,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,25,50,27,55,117,88,24,118,53,37,46,49,130, +161,61,61,51,22,60,34,58,12,27,56,44,18,30,24,3, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +17,33,21,3,5,38,41,27,6,48,31,0,12,96,147,98, +104,66,54,60,13,51,35,56,10,32,30,23,53,75,42,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,29,124,219,233,207,183,161,142,124,108,93,79, +68,57,47,39,32,27,21,19,13,12,8,4,0,0,0,0, +0,0,2,6,11,12,16,20,25,31,37,45,55,65,76,89, +104,120,136,156,177,201,226,233,147,52,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,43,46,6,2,59,78,70,32,99,137, +91,135,124,5,44,38,97,128,80,9,0,0,11,33,33,10, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,14,52,44,66,131,90,66,134,63,70,62,154, +195,80,71,51,55,52,63,31,56,80,50,42,40,13,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, +0,5,31,42,16,4,37,59,40,14,61,16,0,72,171,127, +125,83,94,41,51,47,63,19,44,46,64,91,69,17,0,0, +0,0,7,7,12,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,54,149,233,225,199,176,154,134,118,102,87,73, +63,52,43,35,28,21,19,13,11,5,0,0,0,0,0,0, +0,0,0,0,4,11,12,16,21,27,33,41,49,60,71,83, +97,112,129,150,170,193,218,242,173,78,3,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,56,66,18,36,98,100,66,103,163, +131,166,67,34,56,115,153,84,7,0,12,38,42,21,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0, +0,0,0,0,8,26,52,62,77,176,109,121,119,103,83,171, +205,114,85,58,73,70,51,96,100,73,73,32,3,0,0,0, +0,0,4,6,13,14,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,8,13,12, +5,0,0,15,45,40,14,32,75,55,36,63,7,41,176,166, +149,104,99,43,67,72,36,64,80,116,107,38,3,0,0,7, +18,26,22,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,77,173,242,218,193,169,148,128,112,96,81,68, +57,47,39,31,25,20,13,12,5,0,0,0,0,0,0,0, +0,0,0,0,0,4,11,13,19,23,29,37,45,55,65,79, +92,106,125,143,163,187,212,238,197,102,13,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,9,63,88,45,75,135,120,110,210, +165,161,45,75,138,189,84,6,7,38,53,27,3,0,0,0, +0,0,0,0,3,13,10,9,12,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,10,19,19,12,9,0,0,0, +4,14,11,3,0,14,38,66,81,110,188,133,155,137,118,178, +212,135,85,84,85,80,125,118,94,66,14,0,0,0,0,14, +22,30,19,6,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,15, +27,30,18,5,3,28,54,39,35,92,78,60,52,20,163,213, +181,125,82,80,89,84,103,139,144,75,13,0,16,34,45,41, +26,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,96,191,239,212,187,164,143,124,106,90,79,64, +55,43,35,28,21,17,12,8,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,12,14,20,27,33,41,51,63,73, +88,104,120,136,158,181,205,233,216,120,24,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,7,3,0,0,0,0,5,58,116,83,122,169,145,227, +189,133,75,150,202,80,12,39,60,35,6,0,0,0,0,0, +11,22,26,36,36,27,10,5,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,11,23,34,41,49,42, +25,10,12,26,30,22,12,37,88,115,141,189,179,188,166,193, +224,145,109,114,113,160,139,92,35,2,0,5,21,33,44,34, +18,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,6,2,0,0,0, +0,13,29,43,35,20,10,44,64,52,101,107,87,37,130,246, +205,144,97,118,137,181,178,125,35,19,51,87,96,71,36,6, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,19,112,207,235,207,182,159,139,120,104,88,73,63, +50,41,33,26,20,14,11,4,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,10,12,19,24,31,39,48,58,72, +84,98,116,134,154,176,202,227,228,135,39,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5,8,24,32,22,15,2,0,2,48,133,145,195,200,223, +213,126,157,189,77,49,76,48,9,0,0,5,22,28,44,48, +46,33,21,12,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,12,36,61, +85,95,78,56,48,49,65,57,47,78,135,181,226,235,224,217, +239,172,151,151,185,142,62,9,12,28,48,53,42,23,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,10,20,24,20,16, +16,12,0,4,21,43,56,40,39,64,78,126,141,102,92,235, +242,159,151,185,227,180,93,67,107,137,121,72,26,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,24,123,222,231,204,179,157,135,119,102,86,72,58, +48,39,31,23,18,12,10,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,6,12,17,22,29,37,46,56,68, +80,96,112,131,151,174,197,224,232,147,49,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,2,3,10,6,9,2, +0,0,0,0,10,22,40,53,56,44,22,28,130,225,243,241, +221,187,188,108,110,117,58,42,48,58,68,67,65,45,36,16, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,5,16,35,25,50,36,57, +54,61,75,93,107,100,92,95,102,90,90,146,225,251,251,247, +247,224,211,203,122,48,40,69,93,86,68,40,27,16,10,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,3,11,13, +26,46,50,56,46,32,34,41,60,67,81,116,154,177,137,213, +246,219,233,222,171,147,160,151,105,44,13,3,9,10,0,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,30,131,228,227,202,177,153,134,114,96,82,71,57, +48,38,30,22,18,12,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,12,14,20,28,36,45,56,66, +80,96,112,128,149,171,195,220,236,155,57,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,2,3,16,16,44,34, +59,72,64,66,61,50,42,39,54,83,107,107,85,149,237,251, +246,227,183,204,187,153,157,154,143,117,89,51,38,23,16,6, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,13,10,38,24,70, +94,105,122,129,139,140,154,158,158,163,173,171,190,235,251,251, +251,246,201,136,133,151,141,135,109,79,50,26,13,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,10,38,58,88,119,123,107,105,108,144,191,223,231,236, +251,251,245,216,192,153,89,47,38,43,36,32,22,13,0,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,36,135,231,227,201,175,152,133,113,96,80,70,56, +46,37,28,22,16,12,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,12,14,20,28,34,44,54,64, +80,96,112,128,148,169,194,219,239,161,61,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +16,31,44,59,76,88,102,114,124,129,147,204,230,221,242,251, +251,245,249,232,194,169,141,98,70,63,44,21,18,16,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,15,27,38,50,75,93,105,130,164,184,205,229,249,251, +251,242,200,189,179,118,61,27,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,18, +18,18,18,24,30,30,47,70,97,121,135,169,237,251,251,251, +251,251,251,249,212,160,142,127,124,114,98,79,74,39,19,24, +33,26,16,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,36,137,231,227,201,175,152,133,112,96,80,70,56, +46,36,28,22,16,12,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,12,14,20,27,34,44,54,64, +80,96,112,128,148,169,193,219,239,162,61,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,6, +35,35,40,63,73,68,84,116,127,139,170,214,243,251,251,251, +251,247,212,157,129,129,142,150,138,125,106,57,47,59,43,24, +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3,17,29,60,89,120,144,174,203,215,239,245, +251,247,214,165,113,64,42,27,8,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,18, +18,18,20,37,53,56,64,88,120,139,180,219,248,251,251,251, +251,251,242,243,243,208,181,166,151,130,117,102,83,68,57,48, +47,24,16,3,0,0,4,0,0,0,0,0,0,0,0,0, +0,0,0,35,132,229,227,201,176,153,134,114,96,82,70,56, +47,38,29,22,17,12,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,12,14,20,28,35,44,55,65, +80,96,112,128,149,170,195,220,238,156,59,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,3,27,41,56,76,106,158,193,193,183,210,248,251,251, +251,235,240,208,159,96,46,23,18,25,28,26,25,30,33,29, +36,18,16,24,0,2,3,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +23,42,67,88,112,154,163,202,237,247,232,189,150,177,163,218, +251,250,231,175,125,103,96,94,87,82,62,45,30,11,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,6,7,22, +31,36,42,44,51,50,57,85,132,167,212,211,215,213,232,251, +251,251,229,170,155,147,143,134,135,129,124,104,82,65,37,25, +9,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,26,126,224,229,203,178,156,135,118,99,84,72,58, +48,39,30,23,18,12,10,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,12,17,22,28,36,46,56,68, +80,96,112,129,151,172,196,223,233,150,51,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6, +18,26,29,48,62,75,96,107,109,106,113,137,214,245,239,251, +251,206,153,179,174,145,122,102,64,32,12,0,0,0,0,0, +0,0,0,6,0,2,3,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,2,8,10,25,42,68,80, +99,114,117,122,122,140,150,170,158,127,76,91,162,107,109,191, +247,244,194,148,124,78,49,42,40,42,47,58,76,79,55,30, +14,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,6,7,12, +11,3,0,0,6,31,60,102,128,126,128,147,137,171,245,251, +245,239,243,175,104,58,58,61,52,53,56,47,45,52,42,36, +23,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,20,113,214,234,206,182,159,136,120,104,88,72,62, +49,40,32,25,20,13,11,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,10,12,18,23,30,39,48,57,71, +82,98,114,134,152,176,200,227,231,137,40,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,6, +15,21,38,50,43,28,32,49,68,79,101,143,215,204,214,218, +233,237,112,75,130,136,111,81,88,82,68,49,26,11,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,20,18,14,24,28,31,30, +40,48,77,96,94,81,64,43,18,22,101,153,80,30,132,158, +229,203,192,94,76,100,72,37,18,16,14,0,0,4,18,19, +21,21,20,9,4,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,14,32,52,71,82,65,58,80,84,52,110,209,234,221, +226,199,225,189,123,86,38,12,14,18,14,10,9,4,0,0, +6,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,99,199,238,211,186,163,142,123,105,90,76,64, +53,43,35,28,21,16,12,6,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,12,14,20,26,33,41,49,61,72, +88,104,120,136,158,180,204,231,223,124,24,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12, +23,15,14,3,12,37,53,48,59,62,83,145,174,141,222,157, +159,225,158,35,48,88,97,99,63,49,60,60,50,44,38,20, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,3,8,21,43, +48,57,52,27,14,0,2,3,35,100,106,40,3,78,94,152, +238,153,181,127,56,28,60,61,43,21,3,0,0,0,0,0, +0,0,0,3,4,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, +18,18,37,57,52,33,20,35,56,26,8,68,131,165,178,144, +174,173,152,192,135,88,71,34,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,81,177,242,217,191,168,147,128,112,96,80,68, +56,47,37,30,24,19,13,11,4,0,0,0,0,0,0,0, +0,0,0,0,0,2,11,12,18,22,29,35,45,55,64,78, +90,106,123,142,163,185,210,236,202,105,16,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,2, +0,0,10,31,43,27,33,50,30,48,95,121,117,127,222,105, +82,184,164,89,7,34,69,55,79,68,33,30,46,44,34,25, +32,26,13,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,3,16,21,25,26, +17,8,0,0,0,0,9,48,81,58,16,0,20,106,38,168, +243,144,156,133,95,39,6,38,46,33,25,10,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16, +31,42,37,20,3,17,36,22,3,3,50,84,110,129,106,74, +127,168,103,141,146,102,65,58,43,7,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,60,155,235,223,197,174,152,133,116,99,86,72, +60,51,41,35,27,21,16,12,11,4,0,0,0,0,0,0, +0,0,0,0,2,8,12,16,20,27,32,39,48,57,70,81, +96,112,128,147,168,191,217,242,179,84,5,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +9,25,32,16,13,37,34,6,26,74,78,126,62,145,190,76, +51,133,164,104,45,0,24,60,36,45,62,47,15,17,34,38, +20,12,16,20,9,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,8,7,2,0, +0,0,0,0,0,14,48,58,21,5,0,0,68,65,19,178, +195,171,102,161,86,71,28,0,18,38,22,16,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,12,26,34, +21,5,0,3,22,20,5,0,0,42,54,58,87,86,56,52, +99,139,102,85,117,100,77,50,37,45,18,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,35,131,225,231,205,181,160,140,122,106,91,79, +65,55,47,38,31,26,20,16,12,11,5,2,0,0,0,0, +0,0,0,4,11,12,16,20,24,29,37,44,53,63,75,88, +102,118,135,154,176,199,224,236,155,60,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,19, +21,11,2,20,33,14,0,7,53,52,110,58,57,130,146,57, +25,87,139,91,74,16,0,16,54,27,16,31,45,31,5,8, +22,32,19,7,2,2,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,10,35,33,5,0,0,0,16,96,15,26,150, +144,177,54,155,98,63,60,20,0,5,24,25,3,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,9,22,18,11,0, +0,0,4,9,5,0,0,0,31,48,26,58,54,58,28,40, +81,100,108,52,86,86,67,59,41,18,38,24,3,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,14,100,195,240,214,189,168,147,129,112,98,85, +72,62,52,43,37,29,24,20,16,13,12,10,6,4,4,4, +4,4,10,12,12,16,20,24,29,35,42,49,58,69,81,94, +109,126,142,162,184,208,233,219,124,29,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,10,6, +0,4,23,18,3,0,5,45,36,76,71,18,57,117,117,35, +14,52,110,88,63,50,3,0,7,46,27,3,8,24,34,17, +2,3,12,22,12,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,20,12,2,0,0,0,0,62,66,0,26,123, +98,135,42,87,127,59,46,50,12,0,0,10,14,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8,6,2,0,0,0, +0,0,0,0,0,0,0,25,45,10,37,44,42,35,16,22, +60,89,102,42,60,70,59,54,43,37,8,22,32,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,68,162,237,224,199,177,156,138,122,106,91, +79,68,58,50,42,35,29,25,21,19,14,12,12,12,12,12, +12,12,12,14,18,20,24,29,35,40,48,56,65,77,89,102, +116,133,152,172,194,217,241,183,92,10,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4,13,4,0,0,3,35,27,46,76,11,20,55,98,82,28, +9,27,72,89,52,52,27,0,0,5,36,26,2,0,5,20, +25,8,0,0,3,8,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,6,0,0,0,0,0,14,96,19,0,28,106, +80,102,56,26,124,65,48,31,46,7,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,18,40,5,20,33,27,42,16,4,5, +32,81,79,66,24,57,65,36,50,31,28,5,10,20,13,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,32,123,214,235,210,187,166,147,130,115,100, +87,76,66,56,48,42,35,31,27,23,20,19,17,14,14,14, +14,17,18,20,22,27,29,35,40,46,54,63,73,85,97,110, +126,142,162,181,204,230,231,146,55,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,3,32,21,24,72,22,0,16,42,94,59,24, +7,18,55,74,56,36,43,5,0,0,3,25,28,3,0,0, +7,21,12,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,58,70,0,0,35,90, +75,77,52,12,76,96,33,41,23,37,3,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,12,37,6,5,15,24,15,39,0,0,0, +14,80,52,79,18,49,43,62,21,47,22,12,0,3,12,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7,84,172,239,222,199,178,157,139,124,109, +97,85,74,64,56,49,43,37,33,29,27,24,22,20,20,20, +20,22,23,26,29,32,37,42,48,54,62,72,82,93,107,122, +137,154,172,194,216,241,195,105,19,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,28,22,9,56,34,0,0,14,38,98,38,22, +0,10,39,59,61,27,30,31,0,0,0,0,18,25,3,0, +0,0,6,9,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,9,89,22,0,0,40,78, +62,63,37,15,28,95,43,29,23,20,29,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,32,5,0,5,10,13,21,21,0,0,0, +0,78,42,69,35,16,48,33,39,14,41,7,2,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,39,128,215,236,212,190,171,153,133,122, +107,94,83,74,65,58,51,45,41,37,33,31,29,28,28,27, +28,28,30,33,35,39,44,49,56,63,72,82,92,104,116,131, +147,164,186,206,230,230,149,62,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,19,3,38,34,3,0,2,11,39,94,19,16, +0,0,20,41,58,32,17,30,11,0,0,0,0,12,13,3, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,45,63,0,0,0,34,47, +56,55,34,12,4,60,62,13,32,10,19,20,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,25,5,0,0,0,10,2,28,9,0,0,0, +0,72,40,60,47,3,36,23,29,17,15,35,5,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7,81,166,239,227,203,185,163,147,131, +117,107,93,85,75,67,60,55,49,45,41,39,37,36,34,34, +35,36,39,41,45,48,53,58,65,73,82,92,102,116,129,145, +162,179,198,221,240,187,102,20,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,11,14,0,22,33,3,0,0,0,12,41,82,3,0, +0,0,15,28,45,40,9,9,28,2,0,0,0,0,3,15, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7,66,15,0,0,0,16,16, +42,44,33,12,0,24,56,19,8,26,0,15,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,0,0,0,0,7,20,0,0,0,0, +0,56,38,45,51,17,7,40,7,25,5,16,28,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,30,115,197,241,219,197,179,163,147, +131,117,107,96,87,79,71,65,60,55,51,48,46,45,44,44, +44,46,48,49,55,57,63,69,77,85,93,104,116,128,140,155, +173,195,213,237,218,134,52,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,7,7,0,8,29,5,0,0,0,0,10,39,56,0,0, +0,0,5,15,34,42,10,0,15,14,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,34,44,0,0,0,3,29,16, +28,29,21,11,0,4,40,34,3,10,8,0,5,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,9,0,0,0,0,0,0,0,8,8,0,0,0,0, +0,40,46,27,44,28,0,22,23,5,19,0,13,16,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,3,60,141,220,236,214,195,179,163, +147,131,120,108,99,91,83,76,71,65,63,58,56,56,54,54, +55,56,57,61,64,70,75,81,89,97,107,116,129,140,155,171, +189,211,231,232,161,80,8,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,6,0,2,22,3,0,0,0,0,0,10,31,28,0,0, +0,0,0,0,27,33,23,0,0,10,2,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,5,53,12,0,0,0,0,5,3, +0,14,14,0,0,0,16,32,9,0,12,5,0,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,27,51,17,40,34,3,3,32,5,3,2,0,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,11,82,159,231,234,211,195,179, +163,147,133,124,114,104,96,89,83,79,73,72,68,66,64,64, +65,68,71,72,78,81,88,94,102,110,122,131,145,155,171,188, +207,227,240,179,104,27,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,15,7,0,0,0,0,0,0,0,25,12,0,0, +0,0,0,0,14,20,35,0,0,3,5,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,23,34,0,0,0,0,0,0,0, +0,14,20,0,0,0,3,21,16,0,0,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12,45,2,36,25,14,0,15,19,0,0,0,0,11,7, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,24,97,173,236,231,211,195, +179,163,149,139,129,120,111,104,97,92,88,84,80,80,80,80, +80,80,82,88,90,96,102,109,116,126,137,147,162,173,189,207, +227,239,191,118,39,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,6,7,0,0,0,0,0,0,0,0,21,3,0,0, +0,0,0,0,3,12,27,13,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,34,6,0,0,0,0,0,0,0, +0,7,13,0,0,0,0,10,16,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,30,3,29,22,11,0,0,23,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,33,107,178,237,233,213, +197,181,170,155,146,135,128,120,112,106,104,98,96,96,96,96, +96,96,98,104,106,112,118,126,133,142,154,164,179,195,211,227, +239,195,125,51,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,11,0,0,0, +0,0,0,0,0,6,12,23,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,12,18,0,0,0,0,0,0,0,0, +0,3,18,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,16,6,15,20,14,3,0,7,12,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,36,107,174,231,235, +219,203,188,175,164,154,144,136,129,125,120,116,112,112,112,112, +112,112,114,120,123,128,135,142,152,162,172,186,198,213,231,240, +191,125,54,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,27,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,16,3,0,0,0,0,0,0,0,0, +16,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,10,8,5,23,8,7,0,0,9,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,34,102,165,223, +241,225,210,196,186,174,164,156,150,143,136,134,131,128,128,128, +128,129,134,136,142,147,154,162,172,181,194,206,221,237,232,179, +118,51,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,12,9,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0, +16,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,2,0,20,4,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,27,88,145, +204,241,233,220,208,196,186,177,170,163,158,154,151,149,148,148, +149,151,152,158,163,168,176,184,194,204,216,230,240,218,161,104, +39,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,11,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0, +12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,2,0,10,7,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,66, +123,174,223,241,232,220,210,201,193,187,181,176,174,171,169,169, +170,172,176,180,185,191,199,208,217,230,241,230,187,134,80,27, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +36,90,138,184,225,241,235,226,218,212,205,202,197,195,194,193, +195,196,200,204,210,217,224,233,241,231,195,149,102,52,8,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12,51,95,136,175,211,233,242,238,233,227,224,220,219,219, +220,223,227,231,236,242,236,219,183,146,105,62,20,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,45,83,116,147,173,197,216,228,232,236,239,239, +238,233,231,223,202,179,155,124,92,55,19,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,22,52,78,102,120,135,147,155,161,162, +156,150,137,124,105,84,60,29,10,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3,13,24,39,49,57,61,61, +59,51,40,24,16,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,4,6,8,10,10,10,10, +10,10,10,6,5,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,4,8,11,13,14,16,17,18,18,18,18, +18,18,18,16,15,13,11,8,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,8,11,14,17,19,20,22,24,25,26,26,26,26, +26,26,25,24,22,21,19,17,15,12,8,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,8,12,15,19,21,24,26,28,30,32,33,34,34,34,34, +34,34,33,32,30,29,27,25,22,19,17,13,8,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,6,6,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +12,16,19,23,26,29,31,34,36,38,40,40,42,42,42,42, +42,42,41,40,38,36,35,32,29,27,23,20,17,13,8,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,15,28,48,63,73,83,85,86,84, +77,64,50,31,18,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,2,12,19,24,30,36,36, +35,26,20,12,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,2,11,15, +19,23,27,30,33,36,39,42,44,46,47,48,50,50,50,50, +50,50,49,48,46,44,42,39,37,34,31,27,24,20,16,11, +5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,18,44,76,102,126,143,159,172,180,186,187,182, +174,163,149,129,108,84,52,22,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,10,29,54,77,96,112,123,131,135,137, +132,126,113,99,81,60,35,14,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,13,17,21, +27,29,33,37,41,44,47,49,51,53,55,56,58,58,58,58, +58,58,57,56,54,52,50,47,45,41,38,35,31,27,23,19, +13,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5,28,69,107,139,171,197,222,226,225,227,222,218,218,220, +227,227,227,222,203,179,147,116,77,38,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,24,60,92,124,149,173,191,207,222,228,231,231, +229,224,214,199,177,155,131,100,68,32,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,13,19,24,28, +33,37,41,45,47,51,54,57,59,61,63,64,66,66,66,66, +66,66,64,63,62,60,57,55,52,49,45,41,37,33,29,25, +20,15,11,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +28,73,119,158,196,226,227,209,183,161,144,133,124,117,117,123, +129,143,159,177,203,225,227,207,168,128,84,36,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,30,73,114,151,187,219,236,247,247,246,245,243,243,243, +243,245,245,247,246,238,225,195,162,123,84,39,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,9,15,20,25,29,35, +39,43,47,51,55,59,61,65,67,69,71,72,74,74,74,74, +74,74,72,71,69,67,65,62,59,55,53,49,45,40,36,31, +27,21,16,11,2,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,59, +111,159,206,229,212,175,141,114,88,65,48,32,25,22,22,24, +31,44,62,82,108,133,167,203,228,216,171,124,72,23,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +20,68,116,162,204,235,247,245,242,239,238,236,233,233,233,233, +233,233,235,237,239,241,244,247,239,214,172,128,81,30,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,9,16,21,27,32,37,41, +45,51,55,59,62,66,69,71,75,77,78,80,82,82,82,82, +82,82,80,79,77,75,73,69,67,63,59,55,51,47,43,37, +32,27,21,16,11,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,28,87,142, +195,228,207,163,121,84,50,22,8,0,0,0,0,0,0,0, +0,0,0,4,18,42,76,112,152,196,231,210,156,101,42,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,46, +101,154,203,239,246,242,239,236,232,230,228,225,224,223,223,223, +223,223,225,227,230,231,234,238,242,246,244,215,166,115,60,11, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,9,16,21,27,32,37,43,48, +53,57,61,65,69,73,76,79,82,84,86,88,89,90,90,90, +90,90,88,87,85,83,80,77,74,70,67,62,59,53,49,44, +38,34,28,22,16,11,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4, +4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,3,42,105,167,220, +218,170,121,74,32,6,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,24,63,109,156,210,226,181,121,58, +7,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,12,66,128, +183,231,248,242,238,234,230,226,223,220,217,215,215,214,213,213, +213,215,215,217,220,222,225,229,232,236,241,246,240,197,141,82, +24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,6,14,21,27,32,37,43,48,53, +59,64,68,72,77,80,83,87,89,92,94,96,97,98,98,98, +98,98,96,94,92,90,87,84,81,77,73,69,65,60,55,51, +45,40,34,28,21,16,11,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,4,7,10,10,10,10,10, +10,10,10,8,4,4,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,53,120,183,231,197, +139,85,35,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,24,72,126,183,229,199,135, +71,9,0,0,0,0,0,0,0,0,0,0,0,0,10,0, +0,0,0,0,0,0,0,0,0,0,0,0,21,81,143,207, +245,244,238,234,229,224,220,216,214,210,208,207,205,204,203,203, +203,205,206,207,210,213,216,220,224,228,232,238,243,246,220,159, +97,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,13,19,26,32,37,43,50,54,60, +65,69,75,79,83,87,91,94,97,100,102,104,105,106,106,106, +106,105,104,102,100,98,95,91,88,84,80,75,71,67,61,56, +51,45,40,34,27,21,15,6,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,8,10,12,12,12,12,12,12,12, +12,12,12,12,12,10,8,4,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,5,55,125,192,230,177,119, +58,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,44,104,161,222,209, +142,72,9,0,0,0,0,0,0,0,0,0,0,0,8,0, +0,0,0,0,0,0,0,0,0,0,0,24,89,156,221,248, +242,236,230,225,220,216,211,207,204,200,199,197,194,193,193,193, +193,194,196,198,200,203,206,210,214,219,224,229,234,240,246,233, +173,107,36,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,2,11,19,24,31,37,43,49,54,61,67, +71,77,81,85,91,93,98,101,105,107,109,111,113,114,114,114, +114,113,112,110,108,105,102,99,95,91,87,83,77,72,67,61, +56,51,45,38,32,27,19,13,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,8,11,12,12,12,14,14,18,18,18,18, +18,18,15,14,13,12,12,11,8,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,48,123,194,225,168,104,39, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,27,88,152,218, +212,140,68,7,0,0,0,0,0,0,0,0,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,21,89,159,225,247,240, +234,228,222,216,211,206,202,198,194,191,189,186,184,183,183,183, +183,184,186,188,191,194,198,200,206,210,215,220,226,232,239,245, +237,178,107,34,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8,16,23,29,35,43,48,54,61,67,72, +77,83,88,92,97,101,105,109,111,115,117,119,121,122,122,122, +122,121,120,118,115,113,109,106,101,99,93,88,83,78,73,67, +61,56,50,43,37,31,24,18,11,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,5,11,12,12,13,16,18,20,20,20,20,22,22, +21,20,20,20,19,18,14,12,12,11,8,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,35,113,190,227,167,97,30,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,18,80,148, +219,207,133,56,3,0,0,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,0,0,0,12,81,156,225,247,239,233, +226,220,214,208,203,198,193,190,184,182,179,176,175,174,173,173, +174,175,176,178,182,184,188,192,196,202,206,212,218,224,231,237, +244,237,174,102,27,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,13,21,27,34,40,47,53,59,66,72,77, +83,88,93,99,103,108,112,116,119,122,125,127,128,129,129,129, +129,128,127,125,123,119,117,113,109,104,100,95,90,85,78,73, +67,61,55,48,43,35,29,21,15,8,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,8,12,12,13,16,19,20,21,23,26,26,28,28,28, +28,27,26,24,22,20,20,18,14,12,12,8,4,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,21,96,175,231,173,99,29,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,81, +153,223,195,118,36,0,0,0,0,0,0,0,0,0,10,0, +0,0,0,0,0,0,0,0,5,66,143,221,247,239,231,225, +218,212,206,200,194,189,184,180,176,172,169,167,165,164,163,163, +164,165,167,168,172,175,179,184,188,193,198,204,210,217,223,231, +237,245,233,165,88,14,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,11,17,25,31,37,45,51,59,64,70,77,83, +88,94,99,105,109,115,119,123,126,128,131,134,135,137,137,137, +137,136,134,132,130,127,123,120,115,111,107,101,96,91,85,78, +72,67,59,53,46,40,33,27,19,12,2,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,8,12,12,14,19,20,22,26,28,28,30,32,34,34,34, +34,32,30,29,28,26,23,20,19,16,13,12,11,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,74,155,227,185,111,33,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20, +90,167,231,175,95,16,0,0,0,0,0,0,0,0,11,0, +0,0,0,0,0,0,0,0,46,128,207,248,240,233,225,218, +211,204,198,192,186,180,176,171,167,163,160,158,156,153,153,153, +153,155,157,159,162,166,170,174,179,184,190,196,203,209,217,223, +231,238,246,223,145,66,5,0,0,0,0,0,0,0,0,0, +0,0,0,2,14,21,28,35,43,49,56,63,69,75,83,88, +93,100,106,111,116,121,125,128,132,136,139,141,143,145,145,145, +145,143,142,140,137,134,130,127,123,117,112,107,101,96,90,83, +77,71,64,57,51,44,37,29,23,15,8,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +8,12,13,16,20,21,25,28,29,33,35,37,38,40,40,40, +40,39,37,36,34,31,28,26,21,20,18,13,12,11,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,43,128,211,208,128,48,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +28,107,188,224,148,64,3,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,20,101,183,245,242,234,226,218,211, +204,197,190,184,178,172,166,162,158,154,150,148,145,143,143,143, +143,145,148,150,152,156,160,166,171,176,182,189,195,202,209,217, +224,232,240,248,204,123,36,0,0,0,0,0,0,0,0,0, +0,0,0,0,16,25,32,39,45,53,60,67,73,80,87,93, +99,106,112,117,123,127,131,136,140,144,146,149,151,153,153,153, +153,151,149,147,144,140,136,132,128,123,117,112,107,101,94,88, +83,75,69,61,55,48,41,34,27,19,12,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,8, +12,13,16,20,22,27,29,33,36,39,42,44,46,48,48,48, +48,46,45,43,39,37,35,29,28,24,20,19,13,12,11,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,14,92,179,226,154,71,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,51,132,218,200,115,29,0,0,0,0,0,0,0,3,0, +0,0,0,0,0,0,4,68,154,231,244,236,228,220,212,204, +197,189,183,176,170,164,158,153,148,144,142,138,135,135,133,133, +135,135,137,140,144,148,152,157,162,168,175,181,188,195,202,210, +218,226,234,242,244,174,90,12,0,0,0,0,0,0,0,0, +0,0,0,0,15,28,35,43,50,57,64,71,77,85,91,98, +104,110,117,123,128,132,138,142,146,150,154,156,159,160,161,161, +161,159,157,154,152,148,143,138,133,129,123,117,112,106,99,93, +85,79,73,65,59,51,45,37,29,23,15,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12, +12,16,20,24,28,31,35,39,43,45,49,52,54,56,56,56, +56,55,53,50,47,43,40,36,32,28,24,20,19,13,12,8, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,51,139,223,187,102,23,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,7,81,165,229,161,73,5,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,30,116,203,248,238,230,222,214,206,198, +190,183,175,169,162,156,150,144,140,136,132,128,127,126,124,124, +126,127,128,130,134,138,144,149,154,161,167,173,181,188,196,204, +212,220,228,236,245,223,138,51,0,0,0,0,0,0,0,0, +0,0,0,0,12,31,39,45,53,61,67,75,82,89,95,102, +109,115,122,128,132,138,143,148,154,158,161,164,166,168,169,169, +169,167,164,162,158,154,150,146,140,135,129,123,117,110,104,97, +91,83,77,69,62,55,47,41,33,26,18,11,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,5,11,12, +16,20,24,28,32,36,40,45,49,53,57,61,64,65,66,66, +65,64,61,58,55,51,45,43,37,32,28,24,20,16,12,12, +5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,13,94,184,224,140,53,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,33,119,207,206,116,27,0,0,0,0,0,0,4,0, +0,0,0,0,0,4,73,162,239,242,234,225,216,208,200,192, +184,176,169,161,155,148,142,136,131,127,123,120,118,115,114,114, +114,117,119,122,127,130,135,140,147,153,159,167,175,182,190,198, +206,214,222,232,240,245,184,95,14,0,0,0,0,0,0,0, +0,0,0,0,8,34,41,49,57,63,71,78,85,93,99,107, +113,120,126,132,138,143,149,155,160,164,168,172,174,176,177,177, +177,175,172,170,164,162,156,151,146,140,133,128,122,115,108,101, +94,87,80,73,65,59,51,43,36,28,21,13,2,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,10,12,14, +19,21,27,32,36,42,46,51,57,61,65,71,73,75,78,78, +75,73,71,67,63,58,53,48,43,37,32,28,24,20,16,12, +11,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,42,132,220,187,97,15,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,5,74,164,229,156,64,0,0,0,0,0,0,3,0, +0,0,0,0,0,24,114,204,246,238,229,220,211,203,194,186, +178,170,162,155,147,141,134,128,123,119,113,111,107,105,104,104, +105,107,110,113,117,122,127,133,139,146,153,160,168,176,184,192, +200,209,218,226,236,244,225,136,45,0,0,0,0,0,0,0, +0,0,0,0,5,37,44,52,59,67,74,81,89,96,103,111, +117,124,130,137,143,149,155,161,166,171,175,179,182,184,185,185, +184,182,180,176,172,167,162,156,151,145,138,132,125,119,112,105, +98,91,83,76,69,61,53,46,38,31,23,15,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,12,12,18, +20,27,29,36,41,46,53,59,65,70,76,80,84,87,88,88, +88,85,81,77,71,66,60,54,48,43,37,32,27,21,19,13, +12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,76,170,227,147,56,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,33,125,218,194,100,14,0,0,0,0,0,3,0, +0,0,0,0,0,60,151,235,242,234,224,216,206,198,189,180, +172,164,156,148,141,133,128,121,115,110,105,101,97,96,95,94, +96,97,101,104,109,114,120,126,132,139,146,154,162,170,178,187, +196,204,214,222,231,240,246,175,83,5,0,0,0,0,0,0, +0,0,0,0,5,39,46,54,62,69,77,84,92,99,107,114, +121,128,134,140,148,154,161,167,172,178,182,186,189,192,193,193, +192,190,187,183,178,173,167,162,156,149,143,136,130,123,115,109, +101,93,86,79,71,63,56,49,41,33,25,18,10,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,10,12,14,20, +24,28,35,40,45,53,59,66,72,79,86,91,96,98,102,102, +98,96,92,87,81,74,68,61,54,48,41,36,29,26,20,16, +12,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,108,203,204,112,22,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,10,91,181,223,131,35,0,0,0,0,0,3,0, +0,0,0,0,10,92,187,247,239,230,220,211,202,193,184,176, +166,158,150,142,134,128,120,114,107,101,97,92,88,86,85,84, +86,88,91,95,100,106,112,118,126,132,140,148,156,165,174,182, +191,200,208,218,227,236,246,211,116,22,0,0,0,0,0,0, +0,0,0,0,5,41,49,56,64,72,79,87,95,102,109,117, +124,130,138,146,152,159,165,172,178,183,188,194,196,199,201,201, +200,197,194,190,185,179,173,167,160,154,146,140,132,126,119,111, +104,97,89,81,74,66,58,51,43,35,27,20,12,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,4,12,12,18,21, +27,31,37,44,51,58,66,73,81,88,95,103,108,112,113,113, +112,110,104,97,90,83,75,67,60,53,45,39,33,28,22,19, +13,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,42,137,227,173,80,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,56,149,227,161,66,0,0,0,0,0,2,0, +0,0,0,0,29,124,219,245,236,226,216,207,198,190,180,171, +162,153,144,136,128,121,114,106,99,93,87,83,79,77,74,74, +75,79,81,87,92,98,104,112,119,127,134,142,152,160,168,178, +186,196,206,214,224,232,242,235,147,52,0,0,0,0,0,0, +0,0,0,0,5,43,50,58,66,74,82,89,97,105,112,119, +127,134,141,148,156,163,170,176,183,188,194,199,204,207,209,209, +208,204,201,196,191,185,178,172,164,158,150,142,136,128,122,114, +107,99,91,83,76,68,60,52,44,37,29,21,13,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,12,14,20,23, +28,35,41,47,55,63,71,80,89,98,107,115,121,127,128,128, +128,122,117,109,100,92,82,74,65,57,49,43,36,29,26,20, +16,12,10,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,65,161,227,147,52,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,29,124,219,185,90,8,0,0,0,0,2,0, +0,0,0,0,54,149,236,242,232,223,214,204,194,184,176,167, +158,148,140,131,123,115,107,99,92,85,79,74,70,66,64,64, +65,69,73,78,84,90,98,105,113,121,128,138,146,156,164,174, +183,192,202,211,221,230,239,245,173,78,3,0,0,0,0,0, +0,0,0,0,5,44,52,60,68,76,83,91,99,107,114,122, +129,136,144,152,158,166,174,180,188,194,200,205,210,214,217,217, +215,212,207,202,196,188,182,176,168,161,154,146,138,131,124,116, +108,101,93,85,77,70,62,54,46,38,30,22,14,4,0,0, +0,0,0,0,0,0,0,0,0,0,2,10,12,16,20,26, +30,37,44,51,59,68,78,87,97,107,117,127,134,141,143,143, +143,135,128,120,110,100,89,79,71,61,53,45,39,33,27,21, +18,12,11,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7,88,183,222,126,29,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,14,101,198,207,112,19,0,0,0,0,1,0, +0,0,0,2,77,173,247,239,230,220,210,200,191,182,172,163, +154,144,136,127,119,110,101,93,85,78,72,65,61,57,55,55, +56,59,64,70,76,83,91,99,108,116,125,134,142,152,160,170, +180,190,199,208,217,228,238,247,197,102,13,0,0,0,0,0, +0,0,0,0,6,45,53,61,69,77,84,92,100,108,116,124, +131,139,146,154,162,169,177,184,192,198,204,212,218,222,224,225, +222,218,212,207,200,194,186,178,171,163,156,148,140,133,126,118, +110,102,94,86,78,71,63,55,47,39,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,4,11,12,18,21,28, +33,39,47,55,63,73,83,94,105,117,128,138,148,157,160,160, +159,150,140,130,119,108,97,86,75,65,57,49,41,35,28,22, +19,13,12,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,16,104,199,201,104,16,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3,80,177,223,128,31,0,0,0,0,0,0, +0,0,0,12,96,191,247,238,228,217,208,199,189,179,169,160, +150,142,132,123,113,105,97,87,79,72,64,57,52,47,46,45, +47,51,56,62,69,77,85,95,103,112,121,128,138,148,158,167, +176,186,196,206,215,225,234,245,216,120,24,0,0,0,0,0, +0,0,0,0,6,46,54,62,70,78,86,94,102,110,118,125, +132,140,148,155,163,171,179,187,194,202,210,216,223,228,232,233, +229,224,218,211,204,196,188,181,173,165,157,150,142,134,127,119, +112,104,96,88,80,72,64,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,4,12,13,19,22,28, +35,42,49,57,67,77,88,100,112,125,137,149,163,173,179,179, +175,164,154,140,127,115,103,89,80,69,59,51,43,36,30,25, +20,14,12,7,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,21,119,215,187,89,8,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,64,163,227,142,44,0,0,0,0,1,0, +0,0,0,19,112,207,246,236,225,215,207,197,186,176,167,158, +148,138,128,120,111,101,92,83,74,65,57,50,43,39,35,34, +37,42,48,56,63,71,81,89,99,108,118,127,135,145,154,165, +175,184,193,204,214,223,233,243,229,135,39,0,0,0,0,0, +0,0,0,0,6,46,54,62,70,78,86,94,102,110,118,126, +133,141,149,157,165,173,181,189,196,204,212,219,227,234,239,240, +236,228,221,214,206,198,191,183,175,167,159,151,143,135,128,120, +112,104,96,88,80,72,64,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,5,12,14,20,23,29, +36,43,51,59,70,80,90,104,118,129,145,161,175,187,198,199, +192,179,163,149,134,120,106,95,82,72,62,53,45,37,30,26, +20,14,12,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,27,128,226,176,79,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,54,152,226,152,53,0,0,0,0,0,0, +0,0,0,24,123,222,245,233,224,215,205,194,184,175,165,156, +145,135,127,118,107,97,88,79,70,61,52,43,36,29,25,24, +28,34,42,49,58,67,77,87,96,105,114,125,134,143,152,162, +173,183,192,201,213,222,231,241,236,147,49,0,0,0,0,0, +0,0,0,0,6,46,54,62,70,78,86,94,102,110,118,126, +133,141,149,157,165,173,181,189,197,205,213,221,229,237,244,246, +239,231,223,215,207,199,191,183,175,167,159,151,143,135,128,120, +112,104,96,88,80,72,64,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,7,12,14,20,25,30, +36,44,52,62,72,81,95,105,120,135,151,167,185,203,218,220, +205,189,173,154,137,124,111,96,86,73,64,54,46,38,32,26, +20,15,12,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,35,132,226,170,71,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,45,146,226,158,60,0,0,0,0,0,0, +0,0,0,30,131,228,243,233,223,214,204,193,183,174,164,153, +143,135,126,115,105,96,86,77,66,57,47,39,29,22,16,16, +20,27,36,45,55,64,73,83,94,104,112,122,132,142,151,161, +171,181,191,201,211,221,231,241,239,155,57,0,0,0,0,0, +0,0,0,0,6,46,54,62,70,78,86,94,102,110,118,126, +133,141,149,157,165,173,181,189,197,205,212,220,228,235,242,242, +236,230,222,214,206,199,191,183,175,167,159,151,143,135,128,120, +112,104,96,88,80,72,64,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,7,12,14,20,26,30, +36,44,52,62,72,82,96,107,120,135,151,170,191,210,231,236, +215,195,175,159,143,128,112,96,86,74,64,54,46,38,32,26, +20,18,12,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,36,137,227,165,68,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,44,141,227,162,62,0,0,0,0,2,0, +0,0,0,36,135,231,243,233,223,213,203,193,183,173,163,153, +143,133,124,114,104,95,85,74,64,55,46,35,25,16,6,5, +15,23,32,42,53,62,72,82,92,102,112,122,131,141,151,160, +170,181,191,199,209,221,231,239,241,161,61,0,0,0,0,0, +0,0,0,0,6,46,54,62,70,78,86,94,102,110,118,126, +133,140,148,156,164,172,179,187,195,202,210,218,224,229,234,235, +231,226,219,212,204,197,189,181,173,166,158,150,142,135,128,120, +112,104,96,88,80,72,64,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,7,12,14,20,26,30, +36,44,52,62,72,81,95,106,120,135,151,168,186,205,221,225, +211,192,174,157,139,126,112,96,86,73,64,54,46,38,32,26, +20,17,12,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,36,135,227,170,69,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,44,143,227,159,60,0,0,0,0,2,0, +0,0,0,36,137,231,243,233,223,213,203,193,183,173,163,153, +143,133,124,114,104,94,84,74,64,55,45,34,24,16,5,2, +13,23,32,42,52,62,72,82,92,102,112,122,131,141,151,160, +169,181,191,199,209,221,231,239,241,162,61,0,0,0,0,0, +0,0,0,0,6,46,54,61,69,77,85,93,100,108,116,124, +131,139,147,154,162,170,178,185,192,200,206,212,218,223,226,227, +224,220,214,208,202,194,187,179,172,164,156,149,141,133,126,118, +110,102,95,87,79,71,63,56,48,40,32,24,16,6,0,0, +0,0,0,0,0,0,0,0,0,0,7,12,14,20,24,29, +36,44,51,60,70,80,92,104,119,132,147,163,179,193,202,206, +195,181,166,151,135,120,108,96,82,72,63,54,45,38,30,26, +20,14,12,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,30,129,226,174,77,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,51,150,227,154,55,0,0,0,0,2,0, +0,0,0,35,132,229,243,233,223,213,203,193,183,174,164,153, +143,135,126,114,105,96,86,75,65,56,47,37,28,20,15,13, +18,26,35,45,54,64,73,83,93,103,112,122,132,141,151,161, +171,181,191,201,211,221,231,240,241,156,59,0,0,0,0,0, +0,0,0,0,5,44,52,60,68,76,84,92,99,107,115,123, +129,137,145,152,160,167,174,182,188,195,202,207,212,216,219,219, +217,213,209,203,196,190,184,176,169,162,154,146,139,131,124,117, +109,101,93,86,78,70,62,54,46,38,30,22,15,5,0,0, +0,0,0,0,0,0,0,0,0,0,4,12,13,20,22,28, +35,42,50,58,67,79,88,100,113,127,139,153,164,177,183,184, +179,169,155,142,128,117,104,92,80,71,61,52,44,36,30,25, +20,14,12,8,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,24,120,219,184,88,7,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,63,159,225,144,47,0,0,0,0,2,0, +0,0,0,26,126,224,245,233,223,215,205,194,184,175,165,155, +145,135,127,117,107,97,88,79,69,59,51,42,34,27,23,23, +26,32,40,49,57,67,75,85,95,104,114,124,133,143,152,162, +173,183,191,201,212,222,231,241,237,150,51,0,0,0,0,0, +0,0,0,0,5,43,51,59,66,74,82,90,97,105,113,121, +128,134,142,149,156,164,170,178,184,191,196,202,206,209,211,211, +209,206,202,197,191,186,179,172,166,158,152,144,136,129,122,115, +107,99,92,84,76,68,60,53,45,37,29,22,14,4,0,0, +0,0,0,0,0,0,0,0,0,0,4,12,12,18,22,28, +34,40,47,55,64,73,84,95,107,119,130,140,152,160,167,167, +161,155,144,132,121,109,97,87,76,67,57,49,42,35,28,22, +20,14,12,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,16,106,207,199,102,14,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,78,175,227,131,31,0,0,0,0,3,0, +0,0,0,20,113,214,245,235,225,215,206,196,186,176,167,157, +148,137,128,119,110,101,91,81,73,64,56,48,42,36,32,32, +35,40,46,54,62,71,79,89,97,107,117,127,135,144,154,164, +174,183,193,204,213,223,232,242,232,137,40,0,0,0,0,0, +0,0,0,0,5,41,49,57,65,72,80,87,95,103,110,117, +125,132,139,146,154,160,166,172,179,185,190,194,198,201,203,203, +202,200,196,191,186,180,175,168,162,154,148,140,133,127,119,112, +105,97,89,82,74,67,59,51,43,36,28,20,12,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,10,12,18,20,26, +31,37,45,53,61,70,79,89,99,109,120,129,137,143,149,151, +145,139,131,123,112,102,92,81,71,63,55,47,39,33,28,21, +18,12,12,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8,90,189,215,120,24,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,11,96,191,214,114,20,0,0,0,0,4,0, +0,0,0,12,99,199,247,237,227,217,207,198,188,178,168,159, +150,140,130,122,113,104,95,87,78,70,62,56,49,45,42,42, +45,49,54,60,68,76,84,93,102,111,120,128,138,148,157,167, +176,185,196,206,215,224,234,245,223,124,24,0,0,0,0,0, +0,0,0,0,5,39,47,55,62,70,77,85,93,100,107,115, +122,128,135,142,148,156,162,167,173,179,184,188,191,194,195,195, +194,192,188,185,180,175,170,164,156,150,144,138,130,124,117,109, +101,94,87,79,72,64,57,49,41,34,26,18,10,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,12,14,20,24, +29,36,42,49,57,65,73,82,92,101,109,117,125,128,133,134, +129,127,119,111,102,94,84,76,67,59,51,43,37,30,26,20, +16,12,10,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,72,167,226,141,46,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,23,118,213,191,96,12,0,0,0,0,2,0, +0,0,0,3,81,177,246,239,230,220,210,200,191,182,172,162, +152,144,134,127,117,109,100,92,84,76,69,63,58,55,53,52, +54,57,62,68,74,82,90,98,106,115,124,132,142,150,160,169, +178,188,198,207,217,226,237,247,202,105,16,0,0,0,0,0, +0,0,0,0,4,37,45,52,60,67,75,83,89,97,104,111, +118,125,131,138,144,151,156,162,167,172,178,180,184,186,187,187, +186,184,182,178,174,170,164,158,152,146,140,132,127,120,113,106, +99,91,84,77,69,61,54,47,39,31,24,16,8,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,5,12,13,19,21, +28,33,38,45,51,59,67,75,83,91,99,105,112,114,120,120, +116,112,107,100,93,85,77,69,61,53,47,40,35,28,22,20, +14,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,48,143,228,169,74,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,50,145,228,167,72,0,0,0,0,0,3,0, +0,0,0,0,60,155,238,241,231,222,213,203,194,184,175,166, +156,148,138,130,122,114,106,98,90,83,77,71,67,64,62,62, +64,67,71,76,82,88,96,104,112,120,128,136,145,154,164,173, +182,191,200,210,220,230,239,246,179,84,5,0,0,0,0,0, +0,0,0,0,4,35,42,50,57,65,71,79,86,93,101,107, +115,121,128,132,140,146,151,156,162,166,170,173,176,178,179,179, +178,177,174,171,167,162,157,153,146,140,135,128,123,116,109,101, +95,88,81,73,67,59,51,44,37,29,21,14,4,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,10,12,16,20, +24,29,35,41,47,53,61,68,74,81,87,95,97,103,104,104, +104,100,95,89,84,76,70,62,56,49,43,36,30,27,20,18, +12,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,22,116,211,195,104,18,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,80,173,227,139,44,0,0,0,0,0,3,0, +0,0,0,0,35,131,225,244,234,225,216,206,198,188,179,170, +160,152,144,135,127,120,112,104,98,91,85,81,77,73,72,72, +73,75,79,84,90,96,102,110,118,126,133,142,150,158,168,176, +186,195,204,214,223,232,242,239,155,60,0,0,0,0,0,0, +0,0,0,0,4,31,39,47,53,61,69,75,83,89,97,103, +109,117,123,128,134,140,146,150,155,159,163,166,168,170,171,171, +171,169,166,164,160,156,151,146,140,135,130,125,117,112,105,99, +91,85,77,70,63,55,49,41,34,27,19,11,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,12,13,19, +21,27,31,37,43,48,54,61,67,73,78,82,87,89,90,90, +89,88,84,79,73,68,62,56,50,44,37,32,28,22,20,14, +12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,84,179,225,139,47,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,26,116,207,202,108,18,0,0,0,0,0,2,0, +0,0,0,0,14,100,195,247,238,229,220,210,200,192,184,174, +166,157,149,140,133,126,118,112,105,99,95,89,87,83,82,82, +83,85,89,93,98,104,110,117,124,131,138,147,155,164,172,181, +190,199,208,216,226,236,246,219,124,29,0,0,0,0,0,0, +0,0,0,0,3,29,36,43,51,58,65,72,79,85,93,99, +105,112,117,123,129,134,139,144,148,152,156,158,161,162,163,163, +163,161,159,156,154,148,146,140,135,130,125,120,113,107,101,93, +87,81,73,67,59,53,45,37,31,23,16,8,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,2,10,12,14, +20,23,28,32,37,43,48,53,59,63,68,72,76,79,80,80, +79,77,73,69,65,60,55,50,44,38,35,28,24,20,16,12, +11,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,51,142,226,176,87,9,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,64,154,228,164,75,3,0,0,0,0,0,2,0, +0,0,0,0,0,68,162,239,242,232,224,214,206,196,188,179, +171,162,154,147,139,132,126,119,113,108,103,99,96,94,92,92, +93,95,97,102,106,112,118,124,130,137,145,152,160,168,177,186, +194,203,212,222,230,239,246,183,92,10,0,0,0,0,0,0, +0,0,0,0,3,25,33,40,47,53,61,68,75,81,88,94, +101,107,112,117,123,128,132,138,141,145,148,151,153,155,155,155, +155,153,152,149,146,142,138,134,130,125,120,115,108,101,96,89, +83,77,69,63,56,49,42,35,27,20,13,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,5,12,12, +16,20,24,28,32,37,43,47,51,55,59,63,65,67,70,70, +68,66,64,61,57,53,48,43,38,35,29,26,20,19,13,12, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,18,104,193,218,129,42,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,23,108,196,216,127,34,0,0,0,0,0,0,2,0, +0,0,0,0,0,32,123,214,246,236,228,219,210,202,193,184, +176,168,161,153,146,139,132,127,121,116,112,108,105,104,102,102, +103,104,107,111,115,120,126,131,137,143,151,159,166,174,182,191, +200,208,216,226,234,244,233,146,55,0,0,0,0,0,0,0, +0,0,0,0,2,22,29,36,43,51,57,63,69,77,83,89, +96,101,107,112,117,123,127,130,134,138,140,143,145,147,147,147, +147,145,144,141,138,136,132,128,123,119,114,109,102,96,91,85, +78,72,65,59,52,45,37,31,24,17,8,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,2,8,12, +13,19,20,24,28,32,36,40,44,47,51,54,56,58,58,58, +58,57,55,52,49,45,41,37,33,29,27,21,19,13,12,10, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,63,150,227,175,90,12,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,68,154,229,173,84,7,0,0,0,0,0,0,1,0, +0,0,0,0,0,7,84,172,244,241,232,224,215,206,198,190, +182,175,167,159,153,146,140,134,128,125,121,118,114,112,112,112, +112,114,117,120,124,128,133,138,145,151,159,165,173,180,188,196, +205,213,222,230,238,248,195,105,19,0,0,0,0,0,0,0, +0,0,0,0,2,19,25,32,39,45,53,59,66,72,77,84, +91,96,101,107,111,116,121,125,128,130,133,136,137,139,139,139, +139,138,136,134,131,128,125,121,117,112,107,102,97,91,85,80, +74,67,61,53,47,41,35,27,20,13,2,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,10, +12,13,19,20,24,28,31,35,37,41,44,46,48,50,50,50, +50,48,47,44,42,38,35,32,28,24,21,19,14,12,11,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,20,105,191,222,140,57,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,35,120,203,212,126,38,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,39,128,215,246,238,229,220,212,204,196, +189,181,173,167,160,154,148,142,138,134,128,127,125,122,122,122, +122,124,127,128,132,136,142,147,152,159,165,172,179,187,194,202, +210,218,227,235,244,232,149,62,0,0,0,0,0,0,0,0, +0,0,0,0,1,15,21,28,35,41,48,54,61,67,72,78, +85,91,95,100,105,109,113,117,121,124,127,128,129,131,131,131, +131,130,128,127,125,122,119,115,111,107,101,96,91,85,80,75, +69,62,56,50,43,37,29,23,16,8,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +11,12,13,18,20,22,27,28,31,35,36,38,40,42,42,42, +42,41,39,37,35,33,29,27,24,20,19,14,12,11,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,55,140,221,194,113,33,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,94,174,230,162,77,7,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,7,81,166,240,243,234,226,218,210,203, +195,188,181,175,168,162,156,152,146,142,138,135,134,132,131,131, +132,133,135,138,142,145,150,155,160,166,173,179,186,193,201,209, +216,224,232,240,245,187,102,20,0,0,0,0,0,0,0,0, +0,0,0,0,0,11,17,24,30,37,43,50,56,61,67,74, +78,84,89,93,99,103,107,110,114,117,119,121,122,124,124,124, +124,123,122,119,117,115,111,107,103,99,95,91,85,80,75,69, +63,57,51,45,38,32,25,19,12,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,11,12,13,16,20,21,24,27,28,30,33,34,35,36,36, +35,34,33,30,28,27,25,21,20,18,13,12,11,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,12,87,169,231,173,95,21,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, +73,151,223,190,108,28,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,30,115,197,246,240,232,224,217,209, +202,195,188,182,176,170,165,160,156,152,148,145,143,142,141,141, +141,143,144,148,150,154,158,164,168,174,180,187,193,201,207,215, +223,230,238,246,218,134,52,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,2,13,19,26,32,38,45,51,56,61,67, +72,77,83,87,91,96,99,103,107,109,111,113,115,116,116,116, +116,115,114,112,109,107,103,101,97,93,88,84,79,75,69,64, +58,51,45,40,34,27,21,14,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5,10,12,12,14,18,20,21,22,26,27,28,28,28,28, +28,28,27,26,23,21,20,19,16,12,12,11,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,33,112,191,223,157,82,16,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,64, +137,215,209,133,52,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,3,60,141,220,246,239,231,223,217, +209,202,196,190,184,178,174,168,164,160,158,154,152,151,151,151, +151,152,154,157,160,164,168,172,177,182,188,194,201,207,215,221, +229,236,244,234,161,80,8,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,14,21,27,33,39,45,51,56,61, +67,72,77,81,85,89,93,95,99,101,104,106,107,108,108,108, +108,107,106,104,102,99,97,93,90,85,83,77,73,68,62,58, +52,46,40,35,29,22,16,9,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,8,11,12,12,14,18,20,20,20,22,22,22,22, +22,22,20,20,20,18,16,13,12,12,8,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,52,128,206,219,150,80,17,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,7,62,132, +204,219,149,72,7,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,11,82,159,233,245,237,231,223, +217,210,204,198,192,187,182,178,174,170,167,165,162,161,160,160, +161,162,164,167,169,173,176,181,186,191,196,202,209,215,221,228, +236,243,242,179,104,27,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,9,16,21,27,34,40,45,51,56, +61,65,69,75,77,82,85,89,91,94,96,98,99,100,100,100, +100,100,98,96,94,92,89,86,83,79,75,71,67,61,56,51, +45,40,35,29,24,18,11,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,10,12,12,12,14,14,18,18,18,20,20, +19,18,18,16,14,12,12,12,10,5,2,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,65,140,212,217,150,86,23, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,12,68,134,202, +222,158,85,14,0,0,0,0,0,0,0,0,0,0,3,0, +0,0,0,0,0,0,0,0,0,24,97,173,237,244,237,231, +224,218,212,206,200,196,191,186,183,180,176,175,173,171,170,169, +171,173,174,176,178,182,186,190,194,200,205,210,216,223,229,236, +243,244,191,118,39,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,11,16,22,28,34,39,44,49, +53,59,63,67,71,75,78,81,84,86,88,90,91,92,92,92, +92,92,90,89,87,84,82,79,75,72,68,64,59,55,51,45, +40,35,29,24,19,12,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,2,5,10,11,12,12,12,12,12,13,14, +12,12,12,12,12,12,10,5,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,9,72,142,211,220,159,96, +36,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,27,82,143,207,222, +159,90,22,0,0,0,0,0,0,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,33,107,178,237,245,238, +232,226,220,214,209,204,200,196,192,190,186,184,183,181,181,181, +181,183,183,185,188,191,195,199,203,208,213,218,224,230,236,243, +244,195,125,51,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,11,16,22,27,33,37,43, +47,52,56,60,64,67,71,74,76,78,80,82,83,84,84,84, +84,84,82,81,79,77,75,71,68,65,61,57,53,49,44,39, +35,29,24,19,13,3,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,4,7,10,10,11,12,12,12, +12,12,10,10,8,4,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,9,71,137,201,228,175, +120,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,12,51,105,161,219,217,153, +88,23,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,0,0,0,36,107,174,233,246, +240,234,228,222,218,214,208,206,202,199,196,193,192,191,191,191, +191,191,193,196,198,200,204,208,212,216,222,227,232,238,244,242, +191,125,54,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,2,11,16,21,27,32,37, +41,45,49,53,57,60,63,66,69,71,73,74,76,76,76,76, +76,76,74,73,71,69,67,64,61,58,54,51,46,43,37,33, +28,23,18,13,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,4,4,4,4, +4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,64,128,185,231, +202,148,100,52,16,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,10,41,87,136,188,231,201,142,80, +20,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,34,102,165,223, +248,242,236,232,226,222,218,214,211,208,206,204,201,201,199,199, +201,201,204,206,207,210,214,216,222,226,230,235,240,246,234,179, +118,51,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,11,16,21,25,29, +35,39,43,46,50,53,56,59,61,63,65,66,68,68,68,68, +68,68,66,65,64,61,59,57,53,51,47,43,39,35,31,27, +21,17,11,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,48,106,163, +218,226,185,140,99,60,28,10,0,0,0,0,0,0,0,0, +0,0,0,0,5,20,52,89,129,174,219,221,176,121,64,9, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,27,88,145, +204,244,245,240,236,231,227,224,221,217,215,214,213,211,209,209, +211,212,213,215,217,220,223,226,230,234,238,244,245,218,161,104, +39,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,8,13,19,23, +27,31,35,39,43,45,49,51,53,55,57,58,60,60,60,60, +60,60,59,58,56,54,52,49,46,43,39,37,33,29,24,20, +15,11,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,80, +131,181,224,224,190,154,119,90,64,40,24,17,12,10,10,11, +16,22,36,58,84,111,143,180,219,228,193,145,93,38,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,66, +123,174,223,245,244,240,236,232,230,228,225,223,222,221,221,221, +221,222,223,224,226,230,232,236,239,244,248,232,187,134,80,27, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,12,17, +21,25,29,31,35,38,41,43,46,48,49,50,52,52,52,52, +52,52,51,50,48,46,44,42,39,36,33,29,25,21,17,13, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7, +46,95,139,182,220,228,211,185,159,137,120,108,99,93,92,97, +105,120,135,153,179,205,228,223,192,150,106,60,16,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +36,90,138,184,225,246,246,242,239,238,234,233,231,231,231,231, +231,231,232,234,237,239,242,246,246,233,195,149,102,52,8,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,11, +13,17,21,25,28,31,33,36,38,40,42,42,44,44,44,44, +44,44,43,42,40,38,36,34,31,29,25,22,19,15,11,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,15,50,91,129,163,195,221,226,227,219,207,198,194,193,196, +204,215,227,226,223,203,171,138,101,60,20,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12,51,95,136,175,211,235,245,247,245,243,241,241,239,239, +240,241,242,245,247,246,239,219,183,146,105,62,20,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,11,13,17,21,23,26,28,30,32,34,35,36,36,36,36, +36,36,35,34,32,31,29,27,24,21,19,15,11,5,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,11,35,68,100,126,149,167,183,197,204,211,211,207, +199,187,174,153,131,108,76,44,16,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,45,83,116,147,173,197,216,229,236,239,241,241, +241,237,232,223,202,179,155,124,92,55,19,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,5,10,13,15,18,20,22,24,26,27,28,28,28,28, +28,28,27,26,25,23,21,19,17,13,11,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,14,30,52,72,88,98,107,110,112,108, +101,89,74,56,35,18,3,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,22,52,78,102,120,135,147,155,161,162, +156,150,137,124,105,84,60,29,10,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,6,11,13,15,16,18,19,20,20,20,20, +20,20,20,18,17,15,13,11,8,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7,12,16,18,18,17, +13,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3,13,24,39,49,57,61,61, +59,51,40,24,16,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,5,6,10,11,12,12,11,10, +10,10,10,8,7,4,3,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,2,5,8,7,4, +3,3,2,2,2,1,0,0,0,0,2,8,12,14,10,5, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +} ; + +unsigned char *_ssgaGetLensFlareTexture () { return lens_flare_texture ; } + diff --git a/src/ssgAux/ssgaParticleSystem.cxx b/src/ssgAux/ssgaParticleSystem.cxx new file mode 100644 index 0000000..d613bab --- /dev/null +++ b/src/ssgAux/ssgaParticleSystem.cxx @@ -0,0 +1,205 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + +*/ + +#include "ssgAux.h" + +ssgaParticleSystem::ssgaParticleSystem ( int num, int initial_num, + float _create_rate, int _ttf, + float sz, float bsphere_size, + ssgaParticleCreateFunc _particle_create, + ssgaParticleUpdateFunc _particle_update, + ssgaParticleDeleteFunc _particle_delete ) : + ssgVtxTable ( GL_QUADS, + new ssgVertexArray ( num * 4, new sgVec3 [ num * 4 ] ), + new ssgNormalArray ( num * 4, new sgVec3 [ num * 4 ] ), + new ssgTexCoordArray ( num * 4, new sgVec2 [ num * 4 ] ), + new ssgColourArray ( num * 4, new sgVec4 [ num * 4 ] ) ) +{ + turn_to_face = _ttf ; + create_error = 0 ; + create_rate = _create_rate ; + particle_create = _particle_create ; + particle_update = _particle_update ; + particle_delete = _particle_delete ; + + size = sz ; + + num_particles = num ; + num_verts = num * 4 ; + + getBSphere () -> setRadius ( bsphere_size ) ; + getBSphere () -> setCenter ( 0, 0, 0 ) ; + + particle = new ssgaParticle [ num ] ; + + int i ; + + for ( i = 0 ; i < num_verts ; i++ ) + { + sgSetVec3 ( getNormal ( i ), 0, -1, 0 ) ; + sgSetVec4 ( getColour ( i ), 1, 1, 1, 1 ) ; + sgZeroVec3 ( getVertex ( i ) ) ; + } + + for ( i = 0 ; i < num_particles ; i++ ) + { + sgSetVec2 ( getTexCoord ( i*4+0 ), 0, 0 ) ; + sgSetVec2 ( getTexCoord ( i*4+1 ), 1, 0 ) ; + sgSetVec2 ( getTexCoord ( i*4+2 ), 1, 1 ) ; + sgSetVec2 ( getTexCoord ( i*4+3 ), 0, 1 ) ; + } + + num_active = 0 ; + + if ( particle_create ) + for ( i = 0 ; i < initial_num ; i++ ) + (*particle_create) ( this, i, & particle [ i ] ) ; +} + + +void ssgaParticleSystem::draw_geometry () +{ + sgVec3 nxny, xxny, xxyy, nxyy ; + + float sz = size / 2.0f ; + + if ( turn_to_face ) + { + sgMat4 mat ; + + glGetFloatv ( GL_MODELVIEW_MATRIX, (float *) mat ) ; + + sgVec3 xx, yy ; + + sgSetVec3 ( xx, mat[0][0] * sz, mat[1][0] * sz, mat[2][0] * sz ) ; + sgSetVec3 ( yy, mat[0][1] * sz, mat[1][1] * sz, mat[2][1] * sz ) ; + + sgSetVec3 ( nxny, -xx[0]-yy[0], -xx[1]-yy[1], -xx[2]-yy[2] ) ; + sgSetVec3 ( nxyy, -xx[0]+yy[0], -xx[1]+yy[1], -xx[2]+yy[2] ) ; + sgSetVec3 ( xxny, xx[0]-yy[0], xx[1]-yy[1], xx[2]-yy[2] ) ; + sgSetVec3 ( xxyy, xx[0]+yy[0], xx[1]+yy[1], xx[2]+yy[2] ) ; + } + else + { + sgSetVec3 ( xxny , sz, 0, -sz ) ; + sgSetVec3 ( nxny , -sz, 0, -sz ) ; + sgSetVec3 ( nxyy , -sz, 0, sz ) ; + sgSetVec3 ( xxyy, sz, 0, sz ) ; + } + + int j = 0 ; + + for ( int i = 0 ; i < num_particles ; i++ ) + { + /* Make them disappear if not needed */ + + if ( particle[i].time_to_live <= 0.0f ) + continue ; + + sgCopyVec4 ( getColour ( j + 0 ), particle[i].col ) ; + sgCopyVec4 ( getColour ( j + 1 ), particle[i].col ) ; + sgCopyVec4 ( getColour ( j + 2 ), particle[i].col ) ; + sgCopyVec4 ( getColour ( j + 3 ), particle[i].col ) ; + + sgAddScaledVec3 ( getVertex ( j + 0 ), particle[i].pos, + nxny, particle[i].size ) ; + sgAddScaledVec3 ( getVertex ( j + 1 ), particle[i].pos, + xxny, particle[i].size ) ; + sgAddScaledVec3 ( getVertex ( j + 2 ), particle[i].pos, + xxyy, particle[i].size ) ; + sgAddScaledVec3 ( getVertex ( j + 3 ), particle[i].pos, + nxyy, particle[i].size ) ; + + j += 4 ; + } + + rawSetNumVertices ( j ) ; /* Avoid drawing more than 'j' vertices. */ + + if ( j > 0 ) + { + glDisable ( GL_CULL_FACE ) ; + glDepthMask ( 0 ) ; + + ssgVtxTable::draw_geometry () ; + + glDepthMask ( 1 ) ; + glEnable ( GL_CULL_FACE ) ; + } +} + + +ssgaParticleSystem::~ssgaParticleSystem () +{ + if ( particle_delete ) + for ( int i = 0 ; i < num_particles ; i++ ) + if ( particle [ i ] . time_to_live >= 0.0 ) + (*particle_delete) ( this, i, & particle [ i ] ) ; + + delete [] particle ; +} + + +void ssgaParticleSystem::update ( float t ) +{ + int i ; + + create_error += create_rate * t ; + + num_active = 0 ; + + /* Call the update routine for all the particles */ + + if ( particle_update ) + { + for ( i = 0 ; i < num_particles ; i++ ) + if ( particle [ i ] . time_to_live > 0.0f ) + { + particle [ i ] . update ( t ) ; + (*particle_update) ( t, this, i, & particle [ i ] ) ; + } + } + else + for ( i = 0 ; i < num_particles ; i++ ) + if ( particle [ i ] . time_to_live > 0.0f ) + particle [ i ] . update ( t ) ; + + /* Check for death of particles */ + + for ( i = 0 ; i < num_particles ; i++ ) + if ( particle [ i ] . time_to_live <= 0.0 ) + { + if ( particle_delete ) + (*particle_delete) ( this, i, & particle [ i ] ) ; + + particle [ i ] . pos [ 2 ] = -1000000.0f ; + + if ( create_error >= 1.0f && particle_create ) + { + (*particle_create) ( this, i, & particle [ i ] ) ; + create_error -= 1.0f ; + } + } + else + num_active++ ; +} + + diff --git a/src/ssgAux/ssgaParticleSystem.h b/src/ssgAux/ssgaParticleSystem.h new file mode 100644 index 0000000..a55ed5c --- /dev/null +++ b/src/ssgAux/ssgaParticleSystem.h @@ -0,0 +1,115 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + + +class ssgaParticle +{ +public: + + sgVec4 col ; + sgVec3 pos ; + sgVec3 vel ; + sgVec3 acc ; + + float size ; + + float time_to_live ; + void *userData ; + + void update ( float dt ) + { + sgAddScaledVec3 ( vel, acc, dt ) ; + sgAddScaledVec3 ( pos, vel, dt ) ; + time_to_live -= dt ; + } + + ssgaParticle () + { + sgSetVec4 ( col, 1, 1, 1, 1 ) ; + sgZeroVec3 ( pos ) ; + sgZeroVec3 ( vel ) ; + sgZeroVec3 ( acc ) ; + time_to_live = 0 ; + userData = NULL ; + size = 1.0f ; + } + +} ; + + +class ssgaParticleSystem ; + + +typedef void (* ssgaParticleCreateFunc) ( ssgaParticleSystem *ps, + int index, + ssgaParticle *p ) ; + +typedef void (* ssgaParticleUpdateFunc) ( float deltaTime, + ssgaParticleSystem *ps, + int index, + ssgaParticle *p ) ; + +typedef void (* ssgaParticleDeleteFunc) ( ssgaParticleSystem *ps, + int index, + ssgaParticle *p ) ; + +class ssgaParticleSystem : public ssgVtxTable +{ + int num_particles ; + int num_verts ; + int turn_to_face ; + int num_active ; + ssgaParticle *particle ; + + float create_error ; + float create_rate ; + + float size ; + + ssgaParticleCreateFunc particle_create ; + ssgaParticleUpdateFunc particle_update ; + ssgaParticleDeleteFunc particle_delete ; + +public: + + ssgaParticleSystem ( int num, int initial_num, + float _create_rate, int _turn_to_face, + float sz, float bsphere_size, + ssgaParticleCreateFunc _particle_create, + ssgaParticleUpdateFunc _particle_update = NULL, + ssgaParticleDeleteFunc _particle_delete = NULL ) ; + + virtual ~ssgaParticleSystem () ; + virtual void update ( float t ) ; + + void setSize ( float sz ) { size = sz ; } + float getSize () { return size ; } + + void draw_geometry () ; + + void setCreationRate ( float cr ) { create_rate = cr ; } + float getCreationRate () { return create_rate ; } + + int getNumParticles () { return num_particles ; } + int getNumActiveParticles () { return num_active ; } +} ; + + diff --git a/src/ssgAux/ssgaPatch.cxx b/src/ssgAux/ssgaPatch.cxx new file mode 100644 index 0000000..3d46dc4 --- /dev/null +++ b/src/ssgAux/ssgaPatch.cxx @@ -0,0 +1,238 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaPatch.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ssgAux.h" +#include + +typedef float sgVec9 [9] ; + +void ssgaPatch::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + if ( ntriangles <= 2 ) levels = -1 ; else + if ( ntriangles <= 18 ) levels = 0 ; else + if ( ntriangles <= 4*18 ) levels = 1 ; else + if ( ntriangles <= 16*18 ) levels = 2 ; else + if ( ntriangles <= 64*18 ) levels = 3 ; else + if ( ntriangles <= 256*18 ) levels = 4 ; else + if ( ntriangles <= 1024*18 ) levels = 5 ; else + if ( ntriangles <= 4096*18 ) levels = 6 ; else levels = 7 ; + + if ( ntriangles == 0 || control_points == NULL ) + return ; + + makePatch ( control_points, levels ) ; + + /* + vt0 -> setState ( getKidState () ) ; + vt0 -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt0 -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + vt0 -> recalcBSphere () ; + */ + + recalcBSphere () ; +} + + +static float *sgMidPointVec9 ( sgVec9 a, sgVec9 b ) +{ + static sgVec9 r ; + + r[0] = (a[0] + b[0])/2.0f ; + r[1] = (a[1] + b[1])/2.0f ; + r[2] = (a[2] + b[2])/2.0f ; + r[3] = (a[3] + b[3])/2.0f ; + r[4] = (a[4] + b[4])/2.0f ; + r[5] = (a[5] + b[5])/2.0f ; + r[6] = (a[6] + b[6])/2.0f ; + r[7] = (a[7] + b[7])/2.0f ; + r[8] = (a[8] + b[8])/2.0f ; + + return r ; +} + + +void ssgaPatch::makeHSpline ( sgVec9 points[4], sgVec9 newVts[7] ) +{ + sgVec9 temp ; + + memcpy ( newVts[0], points[0],sizeof(sgVec9)); + memcpy ( newVts[1], sgMidPointVec9 ( points[0], points[1] ),sizeof(sgVec9)); + memcpy ( temp , sgMidPointVec9 ( points[1], points[2] ),sizeof(sgVec9)); + memcpy ( newVts[2], sgMidPointVec9 ( newVts[1], temp ),sizeof(sgVec9)); + memcpy ( newVts[5], sgMidPointVec9 ( points[2], points[3] ),sizeof(sgVec9)); + memcpy ( newVts[4], sgMidPointVec9 ( temp , newVts[5] ),sizeof(sgVec9)); + memcpy ( newVts[3], sgMidPointVec9 ( newVts[2], newVts[4] ),sizeof(sgVec9)); + memcpy ( newVts[6], points[3],sizeof(sgVec9)); +} + + +void ssgaPatch::makeVSplines ( sgVec9 hv[4][7], sgVec9 nv[7][7] ) +{ + sgVec9 temp ; + + for ( int col = 0 ; col < 7 ; col++ ) + { + memcpy ( nv[0][col], hv[0][col], sizeof(sgVec9) ) ; + memcpy ( nv[1][col],sgMidPointVec9(hv[0][col], hv[1][col]), sizeof(sgVec9)); + memcpy ( temp ,sgMidPointVec9(hv[1][col], hv[2][col]), sizeof(sgVec9)); + memcpy ( nv[2][col],sgMidPointVec9(nv[1][col], temp ), sizeof(sgVec9)); + memcpy ( nv[5][col],sgMidPointVec9(hv[2][col], hv[3][col]), sizeof(sgVec9)); + memcpy ( nv[4][col],sgMidPointVec9(temp , nv[5][col]), sizeof(sgVec9)); + memcpy ( nv[3][col],sgMidPointVec9(nv[2][col], nv[4][col]), sizeof(sgVec9)); + memcpy ( nv[6][col], hv[3][col], sizeof(sgVec9) ) ; + } +} + + +void ssgaPatch::writePatch ( sgVec9 points[4][4] ) +{ + sgVec3 nn = { 0,0,1 } ; + + /* + Write three strips of 8 vertices each + */ + + for ( int i = 0 ; i < 3 ; i++ ) + { + ssgVertexArray *va = new ssgVertexArray ( 8 ) ; + ssgTexCoordArray *ta = new ssgTexCoordArray ( 8 ) ; + ssgColourArray *ca = new ssgColourArray ( 8 ) ; + ssgNormalArray *na = new ssgNormalArray ( 8 ) ; + + for ( int j = 0 ; j < 4 ; j++ ) + { + sgVec3 vv ; + + sgSetVec3 ( vv, points [ i ][j][0] * size[0] + center[0], + points [ i ][j][1] * size[1] + center[1], + points [ i ][j][2] * size[2] + center[2] ) ; + + va -> add ( vv ) ; + ta -> add ( & ( points [ i ][j][ 3 ] ) ) ; + ca -> add ( & ( points [ i ][j][ 5 ] ) ) ; + na -> add ( nn ) ; + + sgSetVec3 ( vv, points [i+1][j][0] * size[0] + center[0], + points [i+1][j][1] * size[1] + center[1], + points [i+1][j][2] * size[2] + center[2] ) ; + + va -> add ( vv ) ; + ta -> add ( & ( points [i+1][j][ 3 ] ) ) ; + ca -> add ( & ( points [i+1][j][ 5 ] ) ) ; + na -> add ( nn ) ; + } + + ssgVtxTable *vt = new ssgVtxTable ( GL_TRIANGLE_STRIP, va,na,ta,ca ) ; + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + addKid ( vt ) ; + } +} + + + +void ssgaPatch::makePatch ( float points[4][4][9], int level ) +{ + if ( level <= 0 ) + { + writePatch ( points ) ; + return ; + } + + sgVec9 newHVerts [ 4 ][ 7 ] ; + sgVec9 newVertices [ 7 ][ 7 ] ; + + makeHSpline ( points[0], newHVerts[0] ) ; + makeHSpline ( points[1], newHVerts[1] ) ; + makeHSpline ( points[2], newHVerts[2] ) ; + makeHSpline ( points[3], newHVerts[3] ) ; + + makeVSplines ( newHVerts, newVertices ) ; + + sgVec9 patch[4][4] ; + int i ; + + for ( i = 0 ; i < 16 ; i++ ) + memcpy ( patch[i>>2][i&3], newVertices[i>>2][i&3], 9*sizeof(float) ) ; + + makePatch ( patch, level-1 ) ; + + for ( i = 0 ; i < 16 ; i++ ) + memcpy ( patch[i>>2][i&3], newVertices[i>>2][3+(i&3)], 9*sizeof(float) ) ; + + makePatch ( patch, level-1 ) ; + + for ( i = 0 ; i < 16 ; i++ ) + memcpy ( patch[i>>2][i&3], newVertices[3+(i>>2)][i&3], 9*sizeof(float) ) ; + + makePatch ( patch, level-1 ) ; + + for ( i = 0 ; i < 16 ; i++ ) + memcpy ( patch[i>>2][i&3], newVertices[3+(i>>2)][3+(i&3)], 9*sizeof(float)); + + makePatch ( patch, level-1 ) ; +} + + +void ssgaPatch::setControlPoint ( int s, int t, + float x, float y, float z, + float u, float v, + float r, float g, float b, float a ) +{ + control_points[s][t][0] = x ; + control_points[s][t][1] = y ; + control_points[s][t][2] = z ; + control_points[s][t][3] = u ; + control_points[s][t][4] = v ; + control_points[s][t][5] = r ; + control_points[s][t][6] = g ; + control_points[s][t][7] = b ; + control_points[s][t][8] = a ; +} + + +void ssgaPatch::setControlPoint ( int s, int t, + sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) +{ + memcpy ( &(control_points[s][t][0]), xyz , sizeof(sgVec3) ) ; + memcpy ( &(control_points[s][t][3]), uv , sizeof(sgVec2) ) ; + memcpy ( &(control_points[s][t][5]), rgba, sizeof(sgVec4) ) ; +} + + +void ssgaPatch::getControlPoint ( int s, int t, + sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) +{ + memcpy ( xyz, &(control_points[s][t][0]), sizeof(sgVec3) ) ; + memcpy ( uv , &(control_points[s][t][3]), sizeof(sgVec2) ) ; + memcpy ( rgba, &(control_points[s][t][5]), sizeof(sgVec4) ) ; +} + + diff --git a/src/ssgAux/ssgaScreenDump.cxx b/src/ssgAux/ssgaScreenDump.cxx new file mode 100644 index 0000000..3b3a43c --- /dev/null +++ b/src/ssgAux/ssgaScreenDump.cxx @@ -0,0 +1,204 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaScreenDump.cxx 1996 2004-12-29 07:19:40Z sjbaker $ +*/ + + +#include "ssgAux.h" +#include + + +#include "ul.h" + +static void writeByte ( FILE *fd, unsigned char x ) +{ + fwrite ( & x, sizeof(unsigned char), 1, fd ) ; +} + + +static void writeShort ( FILE *fd, unsigned short x ) +{ + x = ulEndianBig16 ( x ) ; + fwrite ( & x, sizeof(unsigned short), 1, fd ) ; +} + + +static void writeInt ( FILE *fd, unsigned int x ) +{ + x = ulEndianBig32 ( x ) ; + fwrite ( & x, sizeof(unsigned int), 1, fd ) ; +} + + +void ssgaScreenDepthDump ( char *filename, + int xsize, int ysize, int frontBuffer ) +{ + FILE *fd = fopen ( filename, "wb" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, "Failed to open '%s' for writing screendepthdump.\n", + filename ) ; + return ; + } + + unsigned char *row = new unsigned char [ xsize ] ; + unsigned int *buffer = ssgaScreenDepthDump ( xsize, ysize, frontBuffer ) ; + + char type = 0 /* RGB_IMG_VERBATIM */ ; + short dim = 3 ; + short zsize = 3 ; + char bpp = 1 ; + int min = 0 ; + int max = 255 ; + short magic = 0x01DA /* RGB_IMG_MAGIC */ ; + int colormap = 0 ; + int i ; + + writeShort ( fd, magic ) ; + writeByte ( fd, type ) ; + writeByte ( fd, bpp ) ; + writeShort ( fd, dim ) ; + writeShort ( fd, xsize ) ; + writeShort ( fd, ysize ) ; + writeShort ( fd, zsize ) ; + writeInt ( fd, min ) ; + writeInt ( fd, max ) ; + writeInt ( fd, 0 ) ; /* Dummy field */ + + for ( i = 0 ; i < 80 ; i++ ) + writeByte ( fd, '\0' ) ; /* Name field */ + + writeInt ( fd, colormap ) ; + + for ( i = 0 ; i < 404 ; i++ ) + writeByte ( fd, 0 ) ; /* Dummy field */ + + for ( int z = 0 ; z < 3 ; z++ ) + for ( int y = 0 ; y < ysize ; y++ ) + { + for ( i = 0 ; i < xsize ; i++ ) + row [ i ] = ((buffer [ y * xsize + i ] >> (8*z)) & 0xFF) ; + + fseek ( fd, ( z * ysize + y ) * xsize + 512, SEEK_SET ) ; + fwrite ( row, 1, xsize, fd ) ; + } + + fclose ( fd ) ; + + delete row ; + delete buffer ; +} + + +void ssgaScreenDump ( char *filename, int xsize, int ysize, int frontBuffer ) +{ + FILE *fd = fopen ( filename, "wb" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, "Failed to open '%s' for writing screendump.\n", + filename ) ; + return ; + } + + unsigned char *row = new unsigned char [ xsize ] ; + unsigned char *buffer = ssgaScreenDump ( xsize, ysize, frontBuffer ) ; + + char type = 0 /* RGB_IMG_VERBATIM */ ; + short dim = 3 ; + short zsize = 3 ; + char bpp = 1 ; + int min = 0 ; + int max = 255 ; + short magic = 0x01DA /* RGB_IMG_MAGIC */ ; + int colormap = 0 ; + int i ; + + writeShort ( fd, magic ) ; + writeByte ( fd, type ) ; + writeByte ( fd, bpp ) ; + writeShort ( fd, dim ) ; + writeShort ( fd, xsize ) ; + writeShort ( fd, ysize ) ; + writeShort ( fd, zsize ) ; + writeInt ( fd, min ) ; + writeInt ( fd, max ) ; + writeInt ( fd, 0 ) ; /* Dummy field */ + + for ( i = 0 ; i < 80 ; i++ ) + writeByte ( fd, '\0' ) ; /* Name field */ + + writeInt ( fd, colormap ) ; + + for ( i = 0 ; i < 404 ; i++ ) + writeByte ( fd, 0 ) ; /* Dummy field */ + + for ( int z = 0 ; z < 3 ; z++ ) + for ( int y = 0 ; y < ysize ; y++ ) + { + for ( i = 0 ; i < xsize ; i++ ) + row [ i ] = buffer [ ( y * xsize + i ) * 3 + z ] ; + + fseek ( fd, ( z * ysize + y ) * xsize + 512, SEEK_SET ) ; + fwrite ( row, 1, xsize, fd ) ; + } + + fclose ( fd ) ; + + delete row ; + delete buffer ; +} + + +unsigned char *ssgaScreenDump ( int xsize, int ysize, int frontBuffer ) +{ + unsigned char *buffer = new unsigned char [ xsize * ysize * 3 ] ; + + if ( frontBuffer ) + glReadBuffer ( GL_FRONT ) ; + + glReadPixels( 0, 0, xsize, ysize, GL_RGB, GL_UNSIGNED_BYTE, + (void *) buffer ) ; + if ( frontBuffer ) + glReadBuffer ( GL_BACK ) ; + + return buffer ; +} + + + +unsigned int *ssgaScreenDepthDump ( int xsize, int ysize, int frontBuffer ) +{ + unsigned int *buffer = new unsigned int [ xsize * ysize ] ; + + if ( frontBuffer ) + glReadBuffer ( GL_FRONT ) ; + + glReadPixels( 0, 0, xsize, ysize, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, + (void *) buffer ) ; + if ( frontBuffer ) + glReadBuffer ( GL_BACK ) ; + + return buffer ; +} + + diff --git a/src/ssgAux/ssgaScreenDump.h b/src/ssgAux/ssgaScreenDump.h new file mode 100644 index 0000000..38c8616 --- /dev/null +++ b/src/ssgAux/ssgaScreenDump.h @@ -0,0 +1,21 @@ + +/* Dump to disk in '.rgb' format */ + +void ssgaScreenDump ( char *filename, + int width, int height, + int frontBuffer = TRUE ) ; + +/* Put low order 24 bits to disk (R=lsb, G=middle byte, B=msb) */ + +void ssgaScreenDepthDump ( char *filename, + int width, int height, + int frontBuffer = TRUE ) ; + +/* Dump to a memory buffer - three bytes per pixel */ + +unsigned char *ssgaScreenDump ( int width, int height, + int frontBuffer = TRUE ) ; + +unsigned int *ssgaScreenDepthDump ( int width, int height, + int frontBuffer = TRUE ) ; + diff --git a/src/ssgAux/ssgaShapes.cxx b/src/ssgAux/ssgaShapes.cxx new file mode 100644 index 0000000..d0db7cd --- /dev/null +++ b/src/ssgAux/ssgaShapes.cxx @@ -0,0 +1,860 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaShapes.cxx 2015 2005-02-02 15:48:09Z sjbaker $ +*/ + + +#include "ssgAux.h" +#include + +#define SIN(x) ((float)(sin(x))) +#define COS(x) ((float)(cos(x))) +#define ATAN2(x, y) ((float)(atan2(x, y))) + + +void ssgaShape::copy_from ( ssgaShape *src, int clone_flags ) +{ + ssgBranch::copy_from ( src, clone_flags ) ; + if ( src -> isCorrupt () ) makeCorrupt () ; + sgCopyVec4 ( colour, src->colour ) ; + sgCopyVec3 ( center, src->center ) ; + sgCopyVec3 ( size , src->size ) ; + ntriangles = src -> ntriangles ; + kidState = src -> getKidState () ; + kidPreDrawCB = src -> getKidPreDrawCB () ; + kidPostDrawCB = src -> getKidPostDrawCB () ; +} + + +void ssgaCube ::copy_from ( ssgaCube *src, int clone_flags ) { ssgaShape::copy_from ( src, clone_flags ) ;} +void ssgaSphere ::copy_from ( ssgaSphere *src, int clone_flags ) { ssgaShape::copy_from ( src, clone_flags ) ;} +void ssgaCylinder::copy_from ( ssgaCylinder *src, int clone_flags ) { ssgaShape::copy_from ( src, clone_flags ) ;} +void ssgaPatch ::copy_from ( ssgaPatch *src, int clone_flags ) { ssgaShape::copy_from ( src, clone_flags ) ;} +void ssgaTeapot ::copy_from ( ssgaTeapot *src, int clone_flags ) { ssgaShape::copy_from ( src, clone_flags ) ;} + + + +ssgBase *ssgaShape ::clone ( int clone_flags ) +{ +/* + ssgaShape *b = new ssgaShape ; + b -> copy_from ( this, clone_flags ) ; + return b ; +*/ + return NULL ; +} + + +ssgBase *ssgaCube ::clone ( int clone_flags ) +{ + ssgaCube *b = new ssgaCube ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgBase *ssgaSphere ::clone ( int clone_flags ) +{ + ssgaSphere *b = new ssgaSphere ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgBase *ssgaTeapot::clone ( int clone_flags ) +{ + ssgaTeapot *b = new ssgaTeapot ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgBase *ssgaPatch::clone ( int clone_flags ) +{ + ssgaPatch *b = new ssgaPatch ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgBase *ssgaCylinder::clone ( int clone_flags ) +{ + ssgaCylinder *b = new ssgaCylinder ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgaShape::ssgaShape (void) +{ + ntriangles = 50 ; + init () ; +} + +ssgaShape::ssgaShape ( int np ) +{ + ntriangles = np ; + init () ; +} + +void ssgaShape::init () +{ + type = ssgaTypeShape () ; + corrupted = FALSE ; + sgZeroVec3 ( center ) ; + sgSetVec4 ( colour, 1.0f, 1.0f, 1.0f, 1.0f ) ; + sgSetVec3 ( size, 1.0f, 1.0f, 1.0f ) ; + kidState = NULL ; + kidPreDrawCB = NULL ; + kidPostDrawCB = NULL ; +} + +ssgaCube ::ssgaCube ( void ):ssgaShape () {type=ssgaTypeCube ();regenerate();} +ssgaCube ::ssgaCube (int nt):ssgaShape (nt){type=ssgaTypeCube ();regenerate();} +ssgaPatch::ssgaPatch( void ):ssgaShape () {type=ssgaTypePatch();regenerate();} +ssgaPatch::ssgaPatch(int nt):ssgaShape (nt){type=ssgaTypePatch();regenerate();} +ssgaTeapot::ssgaTeapot( void ):ssgaShape () {type=ssgaTypeTeapot();regenerate();} +ssgaTeapot::ssgaTeapot(int nt):ssgaShape (nt){type=ssgaTypeTeapot();regenerate();} + +ssgaSphere ::ssgaSphere ( void ) : ssgaShape () +{ + type = ssgaTypeSphere () ; + latlong_style = TRUE ; + regenerate () ; +} + + +ssgaSphere ::ssgaSphere (int nt) : ssgaShape ( nt ) +{ + type = ssgaTypeSphere () ; + latlong_style = TRUE ; + regenerate () ; +} + + +ssgaCylinder::ssgaCylinder ( void ) : ssgaShape () +{ + type = ssgaTypeCylinder () ; + capped = TRUE ; + regenerate () ; +} + + +ssgaCylinder::ssgaCylinder (int nt) : ssgaShape ( nt ) +{ + type = ssgaTypeCylinder () ; + capped = TRUE ; + regenerate () ; +} + + +ssgaShape ::~ssgaShape (void) {} +ssgaCube ::~ssgaCube (void) {} +ssgaPatch ::~ssgaPatch (void) {} +ssgaTeapot ::~ssgaTeapot (void) {} +ssgaSphere ::~ssgaSphere (void) {} +ssgaCylinder::~ssgaCylinder (void) {} + +const char *ssgaShape ::getTypeName(void) { return "ssgaShape" ; } +const char *ssgaCube ::getTypeName(void) { return "ssgaCube" ; } +const char *ssgaSphere ::getTypeName(void) { return "ssgaSphere" ; } +const char *ssgaCylinder::getTypeName(void) { return "ssgaCylinder" ; } +const char *ssgaPatch ::getTypeName(void) { return "ssgaPatch" ; } +const char *ssgaTeapot ::getTypeName(void) { return "ssgaTeapot" ; } + + +void ssgaCube ::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + if ( ntriangles == 0 ) + return ; + + ssgVtxTable *vt0 = new ssgVtxTable () ; + ssgVtxTable *vt1 = new ssgVtxTable () ; + + ssgVertexArray *v0 = new ssgVertexArray ( 8 ) ; + ssgVertexArray *v1 = new ssgVertexArray ( 8 ) ; + ssgNormalArray *n0 = new ssgNormalArray ( 8 ) ; + ssgNormalArray *n1 = new ssgNormalArray ( 8 ) ; + ssgColourArray *c0 = new ssgColourArray ( 8 ) ; + ssgColourArray *c1 = new ssgColourArray ( 8 ) ; + ssgTexCoordArray *t0 = new ssgTexCoordArray ( 8 ) ; + ssgTexCoordArray *t1 = new ssgTexCoordArray ( 8 ) ; + + vt0 -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + vt1 -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + + addKid ( vt0 ) ; addKid ( vt1 ) ; + + vt0 -> setState ( getKidState () ) ; + vt1 -> setState ( getKidState () ) ; + vt0 -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt1 -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt0 -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + vt1 -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + sgVec3 v ; sgVec3 n ; sgVec2 t ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]-size[1]/2.0f, center[2]+size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 0 , 1 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]-size[1]/2.0f, center[2]-size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 0 , 0 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]+size[1]/2.0f, center[2]+size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 1 , 1 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]+size[1]/2.0f, center[2]-size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 1 , 0 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]+size[1]/2.0f, center[2]+size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 0 , 1 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 2 , 1 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]+size[1]/2.0f, center[2]-size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, 0 , 1 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 2 , 0 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]-size[1]/2.0f, center[2]+size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, -1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 3 , 1 ) ; t0->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]-size[1]/2.0f, center[2]-size[2]/2.0f ) ; v0->add ( v ) ; + sgSetVec3 ( n, -1 , 0 , 0 ) ; n0->add ( n ) ; + c0->add ( colour ) ; + sgSetVec2 ( t, 3 , 0 ) ; t0->add ( t ) ; + + vt0 -> setVertices ( v0 ) ; + vt0 -> setNormals ( n0 ) ; + vt0 -> setColours ( c0 ) ; + vt0 -> setTexCoords ( t0 ) ; + + vt0 -> recalcBSphere () ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]+size[1]/2.0f, center[2]-size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , -1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 0 , 0 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]+size[1]/2.0f, center[2]-size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , -1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 1 , 0 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]-size[1]/2.0f, center[2]-size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , -1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 0 , 1 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]-size[1]/2.0f, center[2]-size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , -1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 1 , 1 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]-size[1]/2.0f, center[2]+size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , -1 , 0 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 0 , 2 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]-size[1]/2.0f, center[2]+size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , -1 , 0 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 1 , 2 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]-size[0]/2.0f, center[1]+size[1]/2.0f, center[2]+size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , 1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 0 , 3 ) ; t1->add ( t ) ; + + sgSetVec3 ( v, center[0]+size[0]/2.0f, center[1]+size[1]/2.0f, center[2]+size[2]/2.0f ) ; v1->add ( v ) ; + sgSetVec3 ( n, 0 , 0 , 1 ) ; n1->add ( n ) ; + c1->add ( colour ) ; + sgSetVec2 ( t, 1 , 3 ) ; t1->add ( t ) ; + + vt1 -> setVertices ( v1 ) ; + vt1 -> setNormals ( n1 ) ; + vt1 -> setColours ( c1 ) ; + vt1 -> setTexCoords ( t1 ) ; + + vt1 -> recalcBSphere () ; + + recalcBSphere () ; +} + + +/* + This code is 'inspired' by the function 'sphere' written by + David Blythe for GLUT 3.5 and uses his values for the coordinates + of the initial Icosahedron. +*/ + +#define CZ 0.89442719099991f /* 2/sqrt(5) */ +#define SZ 0.44721359549995f /* 1/sqrt(5) */ +#define C1 0.951056516f /* cos(18) */ +#define S1 0.309016994f /* sin(18) */ +#define C2 0.587785252f /* cos(54) */ +#define S2 0.809016994f /* sin(54) */ +#define X1 (C1*CZ) +#define Y1 (S1*CZ) +#define X2 (C2*CZ) +#define Y2 (S2*CZ) + +#define P0 { 0, 0, 1 } +#define P1 { -X2, -Y2, SZ } +#define P2 { X2, -Y2, SZ } +#define P3 { X1, Y1, SZ } +#define P4 { 0, CZ, SZ } +#define P5 { -X1, Y1, SZ } +#define P6 { -X1, -Y1, -SZ } +#define P7 { 0, -CZ, -SZ } +#define P8 { X1, -Y1, -SZ } +#define P9 { X2, Y2, -SZ } +#define PA { -X2, Y2, -SZ } +#define PB { 0, 0, -1 } + +struct Triangle +{ + sgVec3 v0, v1, v2 ; +} ; + + +static Triangle icosahedron [ 20 ] = +{ + { P0, P1, P2 }, { P0, P5, P1 }, { P0, P4, P5 }, { P0, P3, P4 }, { P0, P2, P3 }, + { P1, P6, P7 }, { P6, P1, P5 }, { P5, PA, P6 }, { PA, P5, P4 }, { P4, P9, PA }, + { P9, P4, P3 }, { P3, P8, P9 }, { P8, P3, P2 }, { P2, P7, P8 }, { P7, P2, P1 }, + { P9, P8, PB }, { PA, P9, PB }, { P6, PA, PB }, { P7, P6, PB }, { P8, P7, PB } +} ; + + + +void ssgaSphere::regenerateTessellatedIcosahedron () +{ + int tris_per_strip = 1 ; + int nstrips = 1 ; + int nt = 20 ; + + while ( nt < ntriangles ) + { + nstrips++ ; + tris_per_strip += 2 ; + nt += tris_per_strip * 20 ; + } + + /* 20 sides of an Icosahedron */ + + for ( int s = 0 ; s < 20 ; s++ ) + { + Triangle *tri = & icosahedron [ s ] ; + + for ( int i = 0 ; i < nstrips ; i++ ) + { + /* + Create a tri-strip for each row + The number of triangles in each strip is two greater + than the last one. + */ + + sgVec3 v0, v1, v2, v3, va, vb ; + + /* + t->v[0] + /\ + / \ + /____\v1 + /\ /\ + / \ / \ + /____\/vb__\v0 + /\ /\ /\ + / \ / \ / \ + /____\/____\/va__\ + /\ /\ /\ /\ + / \ / \ / \ / \ + t->v[2] /____\/____\/____\/____\ t->v[1] + v3 v2 + */ + + /* + This should be a spherical interpolation - but that's slow and + nobody can tell the difference anyway + */ + + ssgVtxTable *vt = new ssgVtxTable ; + ssgVertexArray *vv = new ssgVertexArray ( i + 3 ) ; + ssgNormalArray *nn = new ssgNormalArray ( i + 3 ) ; + ssgColourArray *cc = new ssgColourArray ( i + 3 ) ; + ssgTexCoordArray *tt = new ssgTexCoordArray ( i + 3 ) ; + + addKid ( vt ) ; + + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + sgVec3 v ; sgVec3 n ; sgVec2 t ; + + vt -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + + sgLerpVec3 ( v0, tri->v1, tri->v0, (float)(i+1) / (float) nstrips ) ; + sgLerpVec3 ( v1, tri->v1, tri->v0, (float) i / (float) nstrips ) ; + sgLerpVec3 ( v2, tri->v1, tri->v2, (float)(i+1) / (float) nstrips ) ; + sgLerpVec3 ( v3, tri->v1, tri->v2, (float) i / (float) nstrips ) ; + + sgNormalizeVec3 ( v0 ) ; + sgNormalizeVec3 ( v1 ) ; + sgNormalizeVec3 ( v2 ) ; + sgNormalizeVec3 ( v3 ) ; + + sgSetVec3 ( v, center[0]+size[0]*v0[0]/2.0f, + center[1]+size[1]*v0[1]/2.0f, + center[2]+size[2]*v0[2]/2.0f ) ; + sgSetVec3 ( n, size[0]*v0[0], + size[1]*v0[1], + size[2]*v0[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, ATAN2(n[0],n[1])/(SG_PI*2.0f)+0.5f, 0.5f+v0[2]/2.0f ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + sgSetVec3 ( v, center[0]+size[0]*v1[0]/2.0f, + center[1]+size[1]*v1[1]/2.0f, + center[2]+size[2]*v1[2]/2.0f ) ; + sgSetVec3 ( n, size[0]*v1[0], + size[1]*v1[1], + size[2]*v1[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, ATAN2(n[0],n[1])/(SG_PI*2.0f)+0.5f, 0.5f+v1[2]/2.0f ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + for ( int j = 0 ; j < i ; j++ ) + { + sgLerpVec3 ( va, v0, v2, (float)(j+1) / (float) (i+1) ) ; + sgLerpVec3 ( vb, v1, v3, (float)(j+1) / (float) i ) ; + sgNormalizeVec3 ( va ) ; + sgNormalizeVec3 ( vb ) ; + + sgSetVec3 ( v, center[0]+size[0]*va[0]/2.0f, + center[1]+size[1]*va[1]/2.0f, + center[2]+size[2]*va[2]/2.0f ) ; + sgSetVec3 ( n, size[0]*va[0], + size[1]*va[1], + size[2]*va[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, ATAN2(n[0],n[1])/(SG_PI*2.0f)+0.5f, 0.5f+va[2]/2.0f ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + sgSetVec3 ( v, center[0]+size[0]*vb[0]/2.0f, + center[1]+size[1]*vb[1]/2.0f, + center[2]+size[2]*vb[2]/2.0f ) ; + sgSetVec3 ( n, size[0]*vb[0], + size[1]*vb[1], + size[2]*vb[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, ATAN2(n[0],n[1])/(SG_PI*2.0f)+0.5f, 0.5f+vb[2]/2.0f ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + } + + sgSetVec3 ( v, center[0]+size[0]*v2[0]/2.0f, + center[1]+size[1]*v2[1]/2.0f, + center[2]+size[2]*v2[2]/2.0f ) ; + sgSetVec3 ( n, size[0]*v2[0], + size[1]*v2[1], + size[2]*v2[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, ATAN2(n[0],n[1])/(SG_PI*2.0f)+0.5f, 0.5f+v2[2]/2.0f ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + vt -> setVertices ( vv ) ; + vt -> setNormals ( nn ) ; + vt -> setColours ( cc ) ; + vt -> setTexCoords ( tt ) ; + + vt -> recalcBSphere () ; + } + } + + recalcBSphere () ; +} + + +void ssgaSphere::regenerateLatLong () +{ + int stacks = (int) sqrt ( (double) ntriangles / 2.0f ) ; + + if ( stacks < 2 ) stacks = 2 ; + + int slices = ntriangles / stacks ; + + if ( slices < 3 ) slices = 3 ; + + for ( int i = 0 ; i < stacks ; i++ ) + { + ssgVtxTable *vt = new ssgVtxTable ; + ssgVertexArray *vv = new ssgVertexArray ( (slices+1)*2 ) ; + ssgNormalArray *nn = new ssgNormalArray ( (slices+1)*2 ) ; + ssgColourArray *cc = new ssgColourArray ( (slices+1)*2 ) ; + ssgTexCoordArray *tt = new ssgTexCoordArray ( (slices+1)*2 ) ; + + addKid ( vt ) ; + + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + sgVec3 v ; sgVec3 n ; sgVec2 t ; + + if ( i == stacks-1 ) /* North Pole */ + { + vt -> setPrimitiveType ( GL_TRIANGLE_FAN ) ; + + sgSetVec3 ( v, center[0], center[1], center[2]+size[2]/2.0f ) ; + sgSetVec3 ( n, 0, 0, 1 ) ; + sgSetVec2 ( t, 0.5f, 1 ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + for ( int j = slices ; j >= 0 ; j-- ) + { + float a = (j==0 || j==slices) ? 0.0f : (float) j * SG_PI * 2.0f / (float) slices ; + float b = (float) i * SG_PI / (float) stacks ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)*SIN(b)/2.0f, + center[1] + size[1]*COS(a)*SIN(b)/2.0f, + center[2] - size[2]* COS(b)/2.0f ) ; + sgSetVec3 ( n, SIN(a)*SIN(b)*size[0], + COS(a)*SIN(b)*size[1], + -COS(b)*size[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, (float) i /(float)stacks ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + } + } + else + if ( i == 0 ) /* South Pole */ + { + vt -> setPrimitiveType ( GL_TRIANGLE_FAN ) ; + + sgSetVec3 ( v, center[0], center[1], center[2]-size[2]/2.0f ) ; + sgSetVec3 ( n, 0, 0, -1 ) ; + sgSetVec2 ( t, 0.5, 0 ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + for ( int j = 0 ; j < slices+1 ; j++ ) + { + float a = (j==0 || j==slices) ? 0.0f : (float) j * SG_PI * 2.0f / (float) slices ; + float b = (float)(i+1) * SG_PI / (float) stacks ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)*SIN(b)/2.0f, + center[1] + size[1]*COS(a)*SIN(b)/2.0f, + center[2] - size[2]* COS(b)/2.0f ) ; + sgSetVec3 ( n, SIN(a)*SIN(b)*size[0], + COS(a)*SIN(b)*size[1], + -COS(b)*size[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, + (float)(i+1)/(float)stacks ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + } + } + else + { + vt -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + + for ( int j = 0 ; j < slices+1 ; j++ ) + { + float a = (j==0 || j==slices) ? 0.0f : (float) j * SG_PI * 2.0f / (float) slices ; + float b0 = (float) i * SG_PI / (float) stacks ; + float b1 = (float)(i+1) * SG_PI / (float) stacks ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)*SIN(b0)/2.0f, + center[1] + size[1]*COS(a)*SIN(b0)/2.0f, + center[2] - size[2]* COS(b0)/2.0f ) ; + sgSetVec3 ( n, SIN(a)*SIN(b0)*size[0], + COS(a)*SIN(b0)*size[1], + -COS(b0)*size[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, + (float)i/(float)stacks ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)*SIN(b1)/2.0f, + center[1] + size[1]*COS(a)*SIN(b1)/2.0f, + center[2] - size[2]* COS(b1)/2.0f ) ; + sgSetVec3 ( n, SIN(a)*SIN(b1)*size[0], + COS(a)*SIN(b1)*size[1], + -COS(b1)*size[2] ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, + (float)(i+1)/(float)stacks ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + } + } + + vt -> setVertices ( vv ) ; + vt -> setNormals ( nn ) ; + vt -> setColours ( cc ) ; + vt -> setTexCoords ( tt ) ; + + vt -> recalcBSphere () ; + } + + recalcBSphere () ; +} + + +void ssgaSphere::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + if ( ntriangles == 0 ) + return ; + + if ( latlong_style ) + regenerateLatLong () ; + else + regenerateTessellatedIcosahedron () ; +} + + + +void ssgaCylinder::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + if ( ntriangles == 0 ) + return ; + + int slices = 1 + ntriangles / 4 ; + + if ( slices < 3 ) slices = 3 ; + + ssgVtxTable *vt = new ssgVtxTable ; + ssgVertexArray *vv = new ssgVertexArray ( (slices+1)*2 ) ; + ssgNormalArray *nn = new ssgNormalArray ( (slices+1)*2 ) ; + ssgColourArray *cc = new ssgColourArray ( (slices+1)*2 ) ; + ssgTexCoordArray *tt = new ssgTexCoordArray ( (slices+1)*2 ) ; + + addKid ( vt ) ; + + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + sgVec3 v ; sgVec3 n ; sgVec2 t ; + + vt -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + + for ( int j = 0 ; j < slices+1 ; j++ ) + { + float a = (j==0 || j==slices) ? 0.0f : (float) j * SG_PI * 2.0f / (float) slices ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)/2.0f, + center[1] + size[1]*COS(a)/2.0f, + center[2] - size[2] / 2.0f ) ; + sgSetVec3 ( n, -SIN(a) * size[0], -COS(a) * size[1], 0 ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, 0 ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + sgSetVec3 ( v, center[0] + size[0]*SIN(a)/2.0f, + center[1] + size[1]*COS(a)/2.0f, + center[2] + size[2] / 2.0f ) ; + sgSetVec3 ( n, -SIN(a) * size[0], -COS(a) * size[1], 0 ) ; + sgNormalizeVec3 ( n ) ; + sgSetVec2 ( t, (float)j/(float)slices, 1 ) ; + vv->add(v) ; nn->add(n) ; cc->add(colour) ; tt->add(t) ; + + } + + vt -> setVertices ( vv ) ; + vt -> setNormals ( nn ) ; + vt -> setColours ( cc ) ; + vt -> setTexCoords ( tt ) ; + vt -> recalcBSphere () ; + + if ( capped ) + { + ssgVtxTable *vt0 = new ssgVtxTable ; + ssgVtxTable *vt1 = new ssgVtxTable ; + + ssgVertexArray *vv0 = new ssgVertexArray ( slices ) ; + ssgNormalArray *nn0 = new ssgNormalArray ( slices ) ; + ssgColourArray *cc0 = new ssgColourArray ( slices ) ; + ssgTexCoordArray *tt0 = new ssgTexCoordArray ( slices ) ; + + ssgVertexArray *vv1 = new ssgVertexArray ( slices ) ; + ssgNormalArray *nn1 = new ssgNormalArray ( slices ) ; + ssgColourArray *cc1 = new ssgColourArray ( slices ) ; + ssgTexCoordArray *tt1 = new ssgTexCoordArray ( slices ) ; + + addKid ( vt0 ) ; + addKid ( vt1 ) ; + + vt0 -> setState ( getKidState () ) ; + vt0 -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt0 -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + vt1 -> setState ( getKidState () ) ; + vt1 -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt1 -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + sgVec3 v ; sgVec3 n ; sgVec2 t ; + + vt0 -> setPrimitiveType ( GL_TRIANGLE_FAN ) ; + vt1 -> setPrimitiveType ( GL_TRIANGLE_FAN ) ; + + for ( int j = 0 ; j < slices ; j++ ) + { + float a0 = (float)(1+slices-j) * SG_PI * 2.0f / (float) slices ; + float a1 = (float) j * SG_PI * 2.0f / (float) slices ; + + /* Top */ + + sgSetVec3 ( v, center[0] + size[0]*SIN(a0)/2.0f, + center[1] + size[1]*COS(a0)/2.0f, + center[2] + size[2] / 2.0f ) ; + sgSetVec3 ( n, 0, 0, 1 ) ; + sgSetVec2 ( t, 0.5f + SIN(a0)/2.0f, 0.5f + COS(a0)/2.0f ) ; + vv0->add(v) ; nn0->add(n) ; cc0->add(colour) ; tt0->add(t) ; + + /* Bottom */ + + sgSetVec3 ( v, center[0] + size[0]*SIN(a1)/2.0f, + center[1] + size[1]*COS(a1)/2.0f, + center[2] - size[2] / 2.0f ) ; + sgSetVec3 ( n, 0, 0, -1 ) ; + sgSetVec2 ( t, 0.5f + SIN(a1)/2.0f, 0.5f + COS(a1)/2.0f ) ; + vv1->add(v) ; nn1->add(n) ; cc1->add(colour) ; tt1->add(t) ; + } + + vt0 -> setVertices ( vv0 ) ; vt1 -> setVertices ( vv1 ) ; + vt0 -> setNormals ( nn0 ) ; vt1 -> setNormals ( nn1 ) ; + vt0 -> setColours ( cc0 ) ; vt1 -> setColours ( cc1 ) ; + vt0 -> setTexCoords ( tt0 ) ; vt1 -> setTexCoords ( tt1 ) ; + vt0 -> recalcBSphere () ; vt1 -> recalcBSphere () ; + } + + recalcBSphere () ; +} + + +// XXX really need these (and ssgLocal.h is not accessible): +extern int _ssgLoadObject ( FILE *, ssgBase **, int ) ; +extern int _ssgSaveObject ( FILE *, ssgBase * ) ; + + +#define load_field(fp, name) (fread(&(name), 1, sizeof(name), fp) == sizeof(name)) +#define save_field(fp, name) (fwrite(&(name), 1, sizeof(name), fp) == sizeof(name)) + + +int ssgaShape::load ( FILE *fp ) +{ + return ( load_field ( fp, corrupted ) && + load_field ( fp, colour ) && + load_field ( fp, center ) && + load_field ( fp, size ) && + load_field ( fp, ntriangles ) && + _ssgLoadObject ( fp, (ssgBase **) &kidState, ssgTypeState () ) && + ssgBranch::load ( fp ) ) ; +} + +int ssgaShape::save ( FILE *fp ) +{ + return ( save_field ( fp, corrupted ) && + save_field ( fp, colour ) && + save_field ( fp, center ) && + save_field ( fp, size ) && + save_field ( fp, ntriangles ) && + _ssgSaveObject ( fp, kidState ) && + ssgBranch::save ( fp ) ) ; +} + + +int ssgaSphere::load ( FILE *fp ) +{ + return ( load_field ( fp, latlong_style ) && + ssgaShape::load ( fp ) ) ; +} + +int ssgaSphere::save ( FILE *fp ) +{ + return ( save_field ( fp, latlong_style ) && + ssgaShape::save ( fp ) ) ; +} + + +int ssgaTeapot::load ( FILE *fp ) +{ + return ssgaShape::load ( fp ) ; +} + +int ssgaTeapot::save ( FILE *fp ) +{ + return ssgaShape::save ( fp ) ; +} + + + +int ssgaPatch::load ( FILE *fp ) +{ + return ( load_field ( fp, levels ) && + fread(control_points, 1, 16*9*sizeof(float), fp) == 16*9*sizeof(float) && + ssgaShape::load ( fp ) ) ; +} + +int ssgaPatch::save ( FILE *fp ) +{ + return ( save_field ( fp, levels ) && + fwrite(control_points, 1, 16*9*sizeof(float), fp) == 16*9*sizeof(float) && + ssgaShape::save ( fp ) ) ; +} + + +int ssgaCylinder::load ( FILE *fp ) +{ + return ( load_field ( fp, capped ) && + ssgaShape::load ( fp ) ) ; +} + +int ssgaCylinder::save ( FILE *fp ) +{ + return ( save_field ( fp, capped ) && + ssgaShape::save ( fp ) ) ; +} + + diff --git a/src/ssgAux/ssgaShapes.h b/src/ssgAux/ssgaShapes.h new file mode 100644 index 0000000..b68463a --- /dev/null +++ b/src/ssgAux/ssgaShapes.h @@ -0,0 +1,222 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaShapes.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#ifndef _SSGASHAPES_H_ +#define _SSGASHAPES_H_ 1 + +#include "ssg.h" + +typedef float sgVec9 [ 9 ] ; /* Needed for ssgaPatch */ + +class ssgaShape : public ssgBranch +{ + int corrupted ; + +protected: + virtual void copy_from ( ssgaShape *src, int clone_flags ) ; + + sgVec4 colour ; + sgVec3 center ; + sgVec3 size ; + + int ntriangles ; + + ssgState *kidState ; + ssgCallback kidPreDrawCB ; + ssgCallback kidPostDrawCB ; + + void init () ; + +protected: + + ssgState *getKidState () { return kidState ; } + ssgCallback getKidPreDrawCB () { return kidPreDrawCB ; } + ssgCallback getKidPostDrawCB () { return kidPostDrawCB ; } + +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaShape (void) ; + ssgaShape ( int numtris ) ; + virtual ~ssgaShape (void) ; + virtual const char *getTypeName(void) ; + + void makeCorrupt () { corrupted = TRUE ; } + int isCorrupt () { return corrupted ; } + + float *getCenter () { return center ; } + float *getSize () { return size ; } + int getNumTris () { return ntriangles ; } + + void setColour ( sgVec4 c ) { sgCopyVec4 ( colour, c ) ; regenerate () ; } + void setCenter ( sgVec3 c ) { sgCopyVec3 ( center, c ) ; regenerate () ; } + void setSize ( sgVec3 s ) { sgCopyVec3 ( size , s ) ; regenerate () ; } + void setSize ( float s ) { sgSetVec3 ( size,s,s,s) ; regenerate () ; } + void setNumTris ( int ntri ) { ntriangles = ntri ; regenerate () ; } + + void setKidState ( ssgState *s ) + { + kidState = s ; + + for ( int i = 0 ; i < getNumKids() ; i++ ) + ((ssgLeaf *)getKid(i)) -> setState ( s ) ; + } + + void setKidCallback ( int cb_type, ssgCallback cb ) + { + if ( cb_type == SSG_CALLBACK_PREDRAW ) + kidPreDrawCB = cb ; + else + kidPostDrawCB = cb ; + + for ( int i = 0 ; i < getNumKids() ; i++ ) + ((ssgLeaf *)getKid(i)) -> setCallback ( cb_type, cb ) ; + } + + virtual void regenerate () = 0 ; + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; +} ; + + + +class ssgaCube : public ssgaShape +{ +protected: + virtual void copy_from ( ssgaCube *src, int clone_flags ) ; +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaCube (void) ; + ssgaCube ( int numtris ) ; + virtual ~ssgaCube (void) ; + virtual const char *getTypeName(void) ; + virtual void regenerate () ; +} ; + + + +#define SSGA_HAVE_PATCH 1 + +class ssgaPatch : public ssgaShape +{ + int levels ; + sgVec9 control_points[4][4] ; + void makePatch ( sgVec9 points[4][4], int levels ) ; + void writePatch ( sgVec9 points[4][4] ) ; + void makeHSpline ( sgVec9 points[4] , sgVec9 nv[7] ) ; + void makeVSplines ( sgVec9 points[4][7], sgVec9 nv[7][7] ) ; + + +protected: + virtual void copy_from ( ssgaPatch *src, int clone_flags ) ; +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaPatch (void) ; + ssgaPatch ( int numtris ) ; + + void setControlPoint ( int s, int t, sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) ; + void setControlPoint ( int s, int t, + float x, float y, float z, + float u, float v, + float r, float g, float b, float a ) ; + void getControlPoint ( int s, int t, sgVec3 xyz, sgVec2 uv, sgVec4 rgba ) ; + + virtual ~ssgaPatch (void) ; + virtual const char *getTypeName(void) ; + virtual void regenerate () ; + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; +} ; + + + +#define SSGA_HAVE_TEAPOT 1 + +class ssgaTeapot : public ssgaShape +{ +protected: + virtual void copy_from ( ssgaTeapot *src, int clone_flags ) ; +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaTeapot (void) ; + ssgaTeapot ( int numtris ) ; + virtual ~ssgaTeapot (void) ; + virtual const char *getTypeName(void) ; + virtual void regenerate () ; + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; +} ; + + + +class ssgaSphere : public ssgaShape +{ + int latlong_style ; + + void regenerateLatLong () ; + void regenerateTessellatedIcosahedron () ; +protected: + virtual void copy_from ( ssgaSphere *src, int clone_flags ) ; +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaSphere (void) ; + ssgaSphere ( int numtris ) ; + virtual ~ssgaSphere (void) ; + virtual const char *getTypeName(void) ; + virtual void regenerate () ; + + void setLatLongStyle ( int ll ) { latlong_style = ll ; regenerate () ; } + int isLatLongStyle () { return latlong_style ; } + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; +} ; + + + +class ssgaCylinder : public ssgaShape +{ + int capped ; + +protected: + virtual void copy_from ( ssgaCylinder *src, int clone_flags ) ; +public: + virtual ssgBase *clone ( int clone_flags = 0 ) ; + ssgaCylinder (void) ; + ssgaCylinder ( int numtris ) ; + virtual ~ssgaCylinder (void) ; + virtual const char *getTypeName(void) ; + virtual void regenerate () ; + + void makeCapped ( int c ) { capped = c ; regenerate () ; } + int isCapped () { return capped ; } + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; +} ; + +#endif + diff --git a/src/ssgAux/ssgaSky.cxx b/src/ssgAux/ssgaSky.cxx new file mode 100644 index 0000000..13ec67b --- /dev/null +++ b/src/ssgAux/ssgaSky.cxx @@ -0,0 +1,402 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaSky.cxx 1798 2003-09-26 14:54:20Z sjbaker $ +*/ + +// Written by Curtis Olson, started December 1997. +// SSG-ified by Curtis Olson, February 2000. +// +// Moved into ssgAux, July 2003. + +#include "ssgaSky.h" + + +// Used for rise/set effects (flat earth - no rotation of skydome considered here ) +void calc_celestial_angles( const sgVec3 body, const sgVec3 view, double& angle, double& rotation ) +{ + sgVec3 pos; + sgSubVec3(pos, body, view); + angle = (90*SGD_DEGREES_TO_RADIANS) - atan2(pos[2], sqrt(pos[0]*pos[0] + pos[1]*pos[1])); + rotation = (90*SGD_DEGREES_TO_RADIANS) - atan2(pos[0], pos[1]); +} + + +ssgaSky::ssgaSky( void ) +{ + dome = 0; + planets = 0; + stars = 0; + sol_ref = 0; + pre_root = 0; + post_root = 0; + + effective_visibility = visibility = 10000.0; + + // near cloud visibility state variables + in_puff = false; + puff_length = 0; + puff_progression = 0; + ramp_up = 0.15; + ramp_down = 0.15; +} + + +ssgaSky::~ssgaSky( void ) +{ + delete dome; + delete planets; + delete stars; + delete pre_root; + delete post_root; +} + + +void ssgaSky::build( double h_radius, double v_radius, + int nplanets, sgdVec3 *planet_data, + int nstars, sgdVec3 *star_data ) +{ + // clean-up previous + delete dome; + delete planets; + delete stars; + delete pre_root; + delete post_root; + bodies.removeAll(); + clouds.removeAll(); + + // build new + pre_root = new ssgRoot; + post_root = new ssgRoot; + + pre_selector = new ssgSelector; + post_selector = new ssgSelector; + + pre_transform = new ssgTransform; + post_transform = new ssgTransform; + + bodies_transform = new ssgTransform; + stars_transform = new ssgTransform; + + dome = new ssgaSkyDome; + pre_transform -> addKid( dome->build( h_radius, v_radius ) ); + + planets = new ssgaStars; + stars_transform -> addKid( planets->build( nplanets, planet_data, h_radius ) ); + + stars = new ssgaStars; + stars_transform -> addKid( stars->build( nstars, star_data, h_radius ) ); + + pre_transform -> addKid( bodies_transform ); + pre_transform -> addKid( stars_transform ); + + pre_selector->addKid( pre_transform ); + pre_selector->clrTraversalMaskBits( SSGTRAV_HOT ); + + post_selector->addKid( post_transform ); + post_selector->clrTraversalMaskBits( SSGTRAV_HOT ); + + pre_root->addKid( pre_selector ); + post_root->addKid( post_selector ); +} + + +ssgaCelestialBody* +ssgaSky::addBody( const char *body_tex_path, const char *halo_tex_path, double size, double dist, bool sol ) +{ + ssgaCelestialBody* body = new ssgaCelestialBody; + bodies_transform->addKid( body->build( body_tex_path, halo_tex_path, size ) ); + bodies.add( body ); + + body -> setDist( dist ); + + if ( sol ) + sol_ref = body; + + return body; +} + + +ssgaCelestialBody* +ssgaSky::addBody( ssgSimpleState *orb_state, ssgSimpleState *halo_state, double size, double dist, bool sol ) +{ + ssgaCelestialBody* body = new ssgaCelestialBody; + bodies_transform->addKid( body->build( orb_state, halo_state, size ) ); + bodies.add( body ); + + body -> setDist( dist ); + + if ( sol ) + sol_ref = body; + + return body; +} + + +ssgaCloudLayer* +ssgaSky::addCloud( const char *cloud_tex_path, float span, float elevation, float thickness, float transition ) +{ + ssgaCloudLayer* cloud = new ssgaCloudLayer; + cloud->build ( cloud_tex_path, span, elevation, thickness, transition ); + clouds.add( cloud ); + return cloud; +} + + +ssgaCloudLayer* +ssgaSky::addCloud( ssgSimpleState *cloud_state, float span, float elevation, float thickness, float transition ) +{ + ssgaCloudLayer* cloud = new ssgaCloudLayer; + cloud->build ( cloud_state, span, elevation, thickness, transition ); + clouds.add( cloud ); + return cloud; +} + + +bool ssgaSky::repositionFlat( sgVec3 view_pos, double spin, double dt ) +{ + int i; + double angle; + double rotation; + sgCoord pos; + + for ( i = 0; i < bodies.getNum (); i++ ) { + ssgaCelestialBody *body = bodies.get(i); + body->reposition( view_pos, 0 ); + + // Calc angles for rise/set effects + body->getPosition ( & pos ); + calc_celestial_angles( pos.xyz, view_pos, angle, rotation ); + body->setAngle( angle ); + body->setRotation( rotation ); + } + + for ( i = 0; i < clouds.getNum (); i++ ) { + clouds.get(i)->repositionFlat( view_pos, dt ); + } + + planets->reposition( view_pos, 0 ); + stars->reposition( view_pos, 0 ); + + if ( sol_ref ) { + dome->repositionFlat( view_pos, sol_ref->getRotation() ); + } + else { + dome->repositionFlat( view_pos, spin ); + } + + return true; +} + + +bool ssgaSky::reposition( sgVec3 view_pos, sgVec3 zero_elev, sgVec3 view_up, double lon, double lat, double alt, double spin, double gst, double dt ) +{ + int i; + + double angle = gst * 15; // degrees + + dome->reposition( zero_elev, lon, lat, spin ); + + for ( i = 0; i < bodies.getNum (); i++ ) + bodies.get(i)->reposition( view_pos, angle ); + + for ( i = 0; i < clouds.getNum (); i++ ) + clouds.get(i)->reposition( zero_elev, view_up, lon, lat, alt, dt ); + + planets->reposition( view_pos, angle ); + stars->reposition( view_pos, angle ); + + return true; +} + + +bool ssgaSky::repaint( sgVec4 sky_color, sgVec4 fog_color, sgVec4 cloud_color, double sol_angle, + int nplanets, sgdVec3 *planet_data, + int nstars, sgdVec3 *star_data ) +{ + int i; + + if ( effective_visibility > 1000.0 ) { + // turn on sky + enable(); + + dome->repaint( sky_color, fog_color, sol_angle, effective_visibility ); + + for ( i = 0; i < bodies.getNum (); i++ ) + bodies.get(i)->repaint(); + + for ( i = 0; i < clouds.getNum (); i++ ) + clouds.get(i)->repaint( cloud_color ); + + planets->repaint( sol_angle, nplanets, planet_data ); + stars->repaint( sol_angle, nstars, star_data ); + } + else { + // turn off sky + disable(); + } + + return true; +} + + +void ssgaSky::preDraw() +{ + ssgCullAndDraw( pre_root ); +} + + +void ssgaSky::postDraw( float alt ) +{ + // Sort clouds in order of distance from alt (furthest to closest) + int i, j; + int num = clouds.getNum (); + if ( num > 0 ) { + + // Initialise cloud index + int *index = new int [ num ]; + for ( i = 0; i < num; i++ ) { + index [i] = i; + } + + // Sort cloud index + int temp; // holding variable + for ( i = 0; i < ( num - 1 ); i++ ) // to represent element to be compared + { + for( j = ( i + 1 ); j < num; j++ ) // to represent the rest of the elements + { + float d1 = (float)(fabs(alt - clouds.get(i)->getElevation())); + float d2 = (float)(fabs(alt - clouds.get(j)->getElevation())); + + if (d1 < d2) + { + temp = index[i]; + index[i] = index[j]; + index[j] = temp; + } + } + } + + float slop = 5.0; // if we are closer than this to a cloud layer, don't draw cloud + + for ( int i = 0; i < num; i++ ) { + + ssgaCloudLayer *cloud = clouds.get(index[i]); + + float asl = cloud->getElevation(); + float thickness = cloud->getThickness(); + + // draw cloud only if below or above cloud layer + if ( alt < asl - slop || alt > asl + thickness + slop ) + cloud->draw(); + } + + delete [] index; + } +} + + +void ssgaSky::modifyVisibility( float alt, float time_factor ) +{ + float effvis = visibility; + + for ( int i = 0; i < clouds.getNum (); ++i ) { + + ssgaCloudLayer *cloud = clouds.get(i); + + if ( cloud->isEnabled() ) { + + float asl = cloud->getElevation(); + float thickness = cloud->getThickness(); + float transition = cloud->getTransition(); + + float ratio = 1.0; + + if ( alt < asl - transition ) { + // below cloud layer + ratio = 1.0; + } + else if ( alt < asl ) { + // in lower transition + ratio = (asl - alt) / transition; + } + else if ( alt < asl + thickness ) { + // in cloud layer + ratio = 0.0; + } + else if ( alt < asl + thickness + transition ) { + // in upper transition + ratio = (alt - (asl + thickness)) / transition; + } + else { + // above cloud layer + ratio = 1.0; + } + + // accumulate effects from multiple cloud layers + effvis *= ratio; + + if ( ratio < 1.0 ) { + if ( ! in_puff ) { + // calc chance of entering cloud puff + double rnd = ssgaRandom(); + double chance = rnd * rnd * rnd; + if ( chance > 0.95 ) { // * (diff - 25) / 50.0 + in_puff = true; + puff_length = ssgaRandom() * 2.0; // up to 2 seconds + puff_progression = 0.0; + } + } + + if ( in_puff ) { + // modify actual_visibility based on puff envelope + if ( puff_progression <= ramp_up ) { + double x = 0.5 * SGD_PI * puff_progression / ramp_up; + double factor = 1.0 - sin( x ); + effvis = (float)(effvis * factor); + } + else if ( puff_progression >= ramp_up + puff_length ) { + double x = 0.5 * SGD_PI * + (puff_progression - (ramp_up + puff_length)) / + ramp_down; + double factor = sin( x ); + effvis = (float)(effvis * factor); + } + else { + effvis = 0.0; + } + + puff_progression += time_factor; + + if ( puff_progression > puff_length + ramp_up + ramp_down) { + in_puff = false; + } + } + + // never let visibility drop below 25 meters + if ( effvis <= 25.0 ) { + effvis = 25.0; + } + } + } + } // for + + effective_visibility = effvis; +} diff --git a/src/ssgAux/ssgaSky.h b/src/ssgAux/ssgaSky.h new file mode 100644 index 0000000..2d9287a --- /dev/null +++ b/src/ssgAux/ssgaSky.h @@ -0,0 +1,397 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaSky.h 2073 2006-03-27 08:09:54Z bram $ +*/ + +// Written by Curtis Olson, started December 1997. +// SSG-ified by Curtis Olson, February 2000. +// +// Moved into ssgAux, July 2003. + +#ifndef _INCLUDED_SSGASKY_H_ +#define _INCLUDED_SSGASKY_H_ + +#include "ssg.h" + +class ssgaCelestialBody; +class ssgaCelestialBodyList; +class ssgaCloudLayer; +class ssgaCloudLayerList; +class ssgaStars; +class ssgaSkyDome; +class ssgaSky; + + +class ssgaCelestialBody +{ +private: + + ssgTransform *transform; + ssgColourArray *cl; + + // used by repaint for rise/set effects + double body_angle; + double body_rotation; + + // used by reposition + double body_right_ascension; + double body_declination; + double body_dist; + +public: + + ssgaCelestialBody( void ); + ~ssgaCelestialBody( void ); + + ssgBranch *build( const char* body_tex_path, const char* halo_tex_path, double body_size ); + ssgBranch *build( ssgSimpleState *orb_state, ssgSimpleState *halo_state, double body_size ); + + bool reposition( sgVec3 p, double angle ) { + return reposition ( p, angle, body_right_ascension, body_declination, body_dist ); } + bool reposition( sgVec3 p, double angle, double rightAscension, double declination, double dist ); + + bool repaint() { return repaint ( body_angle ); } + bool repaint( double angle ); + + void getPosition ( sgCoord* p ) + { + sgMat4 Xform; + transform->getTransform(Xform); + sgSetCoord(p, Xform); + } + + void setAngle ( double angle ) { body_angle = angle; } + double getAngle () { return body_angle; } + + void setRotation ( double rotation ) { body_rotation = rotation; } + double getRotation () { return body_rotation; } + + void setRightAscension ( double ra ) { body_right_ascension = ra; } + double getRightAscension () { return body_right_ascension; } + + void setDeclination ( double decl ) { body_declination = decl; } + double getDeclination () { return body_declination; } + + void setDist ( double dist ) { body_dist = dist; } + double getDist () { return body_dist; } + + inline float *getColor() { return cl->get( 0 ); } +} ; + + +class ssgaCelestialBodyList : private ssgSimpleList +{ +public: + + ssgaCelestialBodyList ( int init = 3 ) + : ssgSimpleList ( sizeof(ssgaCelestialBody*), init ) { } + + ~ssgaCelestialBodyList () { removeAll(); } + + int getNum (void) { return total ; } + + ssgaCelestialBody* get ( unsigned int n ) + { + assert(nselect( 1 ); + post_selector->select( 1 ); + } + + // disable the sky + inline void disable() { + pre_selector->select( 0 ); + post_selector->select( 0 ); + } + + // current effective visibility + inline float getVisibility() const { return effective_visibility; } + inline void setVisibility( float v ) { + effective_visibility = visibility = v; + } +} ; + + +// return a random number between [0.0, 1.0) +inline double ssgaRandom(void) +{ + return(rand() / (double)RAND_MAX); +} + +//#if defined( macintosh ) +//const float system_gamma = 1.4; +//#elif defined (sgi) +//const float system_gamma = 1.7; +//#else // others +const float system_gamma = 2.5; +//#endif + +// simple architecture independant gamma correction function. +inline void ssgaGammaCorrectRGB(float *color, float reff = 2.5, float system = system_gamma) +{ + color[0] = (float)pow(color[0], reff/system); + color[1] = (float)pow(color[1], reff/system); + color[2] = (float)pow(color[2], reff/system); +}; + +inline void ssgaGammaCorrectC(float *color, float reff = 2.5, float system = system_gamma) +{ + *color = (float)pow(*color, reff/system); +}; + +inline void ssgaGammaRestoreRGB(float *color, float reff = 2.5, float system = system_gamma) +{ + color[0] = (float)pow(color[0], system/reff); + color[1] = (float)pow(color[1], system/reff); + color[2] = (float)pow(color[2], system/reff); +}; + +inline void ssgaGammaRestoreC(float *color, float reff = 2.5, float system = system_gamma) +{ + *color = (float)pow(*color, system/reff); +}; + +#endif diff --git a/src/ssgAux/ssgaSkyDome.cxx b/src/ssgAux/ssgaSkyDome.cxx new file mode 100644 index 0000000..ca343ed --- /dev/null +++ b/src/ssgAux/ssgaSkyDome.cxx @@ -0,0 +1,504 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaSkyDome.cxx 1798 2003-09-26 14:54:20Z sjbaker $ +*/ + +// Written by Curtis Olson, started December 1997. +// SSG-ified by Curtis Olson, February 2000. +// +// Moved into ssgAux, July 2003. + +#include +#include "ssgaSky.h" + + +// proportions of max dimensions fed to the build() routine +static const float center_elev = 1.0f; + +static const float upper_radius = 0.6f; +static const float upper_elev = 0.15f; + +static const float middle_radius = 0.9f; +static const float middle_elev = 0.08f; + +static const float lower_radius = 1.0f; +static const float lower_elev = 0.0f; + +static const float bottom_radius = 0.8f; +static const float bottom_elev = -0.1f; + + +static int ssgaSkyDomePreDraw( ssgEntity *e ) +{ + ssgLeaf *f = (ssgLeaf *)e; + if ( f -> hasState () ) f->getState()->apply() ; + + glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_FOG_BIT ); + + glDisable( GL_DEPTH_TEST ); + glDisable( GL_FOG ); + + return true; +} + + +static int ssgaSkyDomePostDraw( ssgEntity *e ) +{ + glPopAttrib(); + + return true; +} + + +ssgaSkyDome::ssgaSkyDome( void ) +{ + dome_transform = 0; +} + + +ssgaSkyDome::~ssgaSkyDome( void ) +{ + ssgDeRefDelete( dome_transform ); +} + + +// initialize the sky object and connect it into our scene graph +ssgBranch * ssgaSkyDome::build( double hscale, double vscale ) +{ + sgVec4 color; + double theta; + int i; + + // clean-up previous + ssgDeRefDelete( dome_transform ); + + // create new + dome_transform = new ssgTransform; + dome_transform->ref(); + + // set up the state + dome_state = new ssgSimpleState(); + dome_state->setShadeModel( GL_SMOOTH ); + dome_state->disable( GL_LIGHTING ); + dome_state->disable( GL_CULL_FACE ); + dome_state->disable( GL_TEXTURE_2D ); + dome_state->enable( GL_COLOR_MATERIAL ); + dome_state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ); + dome_state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + dome_state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + dome_state->disable( GL_BLEND ); + dome_state->disable( GL_ALPHA_TEST ); + + // initialize arrays + center_disk_vl = new ssgVertexArray( 14 ); + center_disk_cl = new ssgColourArray( 14 ); + + upper_ring_vl = new ssgVertexArray( 26 ); + upper_ring_cl = new ssgColourArray( 26 ); + + middle_ring_vl = new ssgVertexArray( 26 ); + middle_ring_cl = new ssgColourArray( 26 ); + + lower_ring_vl = new ssgVertexArray( 26 ); + lower_ring_cl = new ssgColourArray( 26 ); + + // initially seed to all blue + sgSetVec4( color, 0.0, 0.0, 1.0, 1.0 ); + + // generate the raw vertex data + sgVec3 center_vertex; + sgVec3 upper_vertex[12]; + sgVec3 middle_vertex[12]; + sgVec3 lower_vertex[12]; + sgVec3 bottom_vertex[12]; + + sgSetVec3( center_vertex, 0.0, 0.0, (float)(center_elev * vscale) ); + + for ( i = 0; i < 12; i++ ) { + theta = (i * 30.0) * SGD_DEGREES_TO_RADIANS; + + sgSetVec3( upper_vertex[i], + (float)(cos(theta) * upper_radius * hscale), + (float)(sin(theta) * upper_radius * hscale), + (float)(upper_elev * vscale)); + + sgSetVec3( middle_vertex[i], + (float)(cos(theta) * middle_radius * hscale), + (float)(sin(theta) * middle_radius * hscale), + (float)(middle_elev * vscale)); + + sgSetVec3( lower_vertex[i], + (float)(cos(theta) * lower_radius * hscale), + (float)(sin(theta) * lower_radius * hscale), + (float)(lower_elev * vscale)); + + sgSetVec3( bottom_vertex[i], + (float)(cos(theta) * bottom_radius * hscale), + (float)(sin(theta) * bottom_radius * hscale), + (float)(bottom_elev * vscale)); + } + + // generate the center disk vertex/color arrays + center_disk_vl->add( center_vertex ); + center_disk_cl->add( color ); + for ( i = 11; i >= 0; i-- ) { + center_disk_vl->add( upper_vertex[i] ); + center_disk_cl->add( color ); + } + center_disk_vl->add( upper_vertex[11] ); + center_disk_cl->add( color ); + + // generate the upper ring + for ( i = 0; i < 12; i++ ) { + upper_ring_vl->add( middle_vertex[i] ); + upper_ring_cl->add( color ); + + upper_ring_vl->add( upper_vertex[i] ); + upper_ring_cl->add( color ); + } + upper_ring_vl->add( middle_vertex[0] ); + upper_ring_cl->add( color ); + + upper_ring_vl->add( upper_vertex[0] ); + upper_ring_cl->add( color ); + + // generate middle ring + for ( i = 0; i < 12; i++ ) { + middle_ring_vl->add( lower_vertex[i] ); + middle_ring_cl->add( color ); + + middle_ring_vl->add( middle_vertex[i] ); + middle_ring_cl->add( color ); + } + middle_ring_vl->add( lower_vertex[0] ); + middle_ring_cl->add( color ); + + middle_ring_vl->add( middle_vertex[0] ); + middle_ring_cl->add( color ); + + // generate lower ring + for ( i = 0; i < 12; i++ ) { + lower_ring_vl->add( bottom_vertex[i] ); + lower_ring_cl->add( color ); + + lower_ring_vl->add( lower_vertex[i] ); + lower_ring_cl->add( color ); + } + lower_ring_vl->add( bottom_vertex[0] ); + lower_ring_cl->add( color ); + + lower_ring_vl->add( lower_vertex[0] ); + lower_ring_cl->add( color ); + + // force a repaint of the sky colors with ugly defaults + sgVec3 fog_color; + sgSetVec3( fog_color, 1.0, 1.0, 1.0 ); + repaint( color, fog_color, 0.0, 5000.0 ); + + // build the ssg scene graph sub tree for the sky and connected + // into the provide scene graph branch + ssgVtxTable *center_disk, *upper_ring, *middle_ring, *lower_ring; + + center_disk = new ssgVtxTable( GL_TRIANGLE_FAN, + center_disk_vl, NULL, NULL, center_disk_cl ); + + upper_ring = new ssgVtxTable( GL_TRIANGLE_STRIP, + upper_ring_vl, NULL, NULL, upper_ring_cl ); + + middle_ring = new ssgVtxTable( GL_TRIANGLE_STRIP, + middle_ring_vl, NULL, NULL, middle_ring_cl ); + + lower_ring = new ssgVtxTable( GL_TRIANGLE_STRIP, + lower_ring_vl, NULL, NULL, lower_ring_cl ); + + center_disk->setState( dome_state ); + upper_ring->setState( dome_state ); + middle_ring->setState( dome_state ); + lower_ring->setState( dome_state ); + + dome_transform->addKid( center_disk ); + dome_transform->addKid( upper_ring ); + dome_transform->addKid( middle_ring ); + dome_transform->addKid( lower_ring ); + + // not entirely satisfying. We are depending here that the first + // thing we add to a parent is the first drawn + center_disk->setCallback( SSG_CALLBACK_PREDRAW, ssgaSkyDomePreDraw ); + center_disk->setCallback( SSG_CALLBACK_POSTDRAW, ssgaSkyDomePostDraw ); + + upper_ring->setCallback( SSG_CALLBACK_PREDRAW, ssgaSkyDomePreDraw ); + upper_ring->setCallback( SSG_CALLBACK_POSTDRAW, ssgaSkyDomePostDraw ); + + middle_ring->setCallback( SSG_CALLBACK_PREDRAW, ssgaSkyDomePreDraw ); + middle_ring->setCallback( SSG_CALLBACK_POSTDRAW, ssgaSkyDomePostDraw ); + + lower_ring->setCallback( SSG_CALLBACK_PREDRAW, ssgaSkyDomePreDraw ); + lower_ring->setCallback( SSG_CALLBACK_POSTDRAW, ssgaSkyDomePostDraw ); + + return dome_transform; +} + + +bool ssgaSkyDome::repositionFlat( sgVec3 p, double spin ) +{ + sgMat4 T, SPIN; + sgVec3 axis; + + // Translate to view position + sgMakeTransMat4( T, p ); + + // Rotate to proper orientation + sgSetVec3( axis, 0.0, 0.0, 1.0 ); + sgMakeRotMat4( SPIN, (float)(spin * SGD_RADIANS_TO_DEGREES), axis ); + + sgMat4 TRANSFORM; + + sgCopyMat4( TRANSFORM, T ); + sgPreMultMat4( TRANSFORM, SPIN ); + + sgCoord skypos; + sgSetCoord( &skypos, TRANSFORM ); + + dome_transform->setTransform( &skypos ); + + return true; +} + + +bool ssgaSkyDome::reposition( sgVec3 p, double lon, double lat, double spin ) +{ + sgMat4 T, LON, LAT, SPIN; + sgVec3 axis; + + // Translate to view position + sgMakeTransMat4( T, p ); + + // Rotate to proper orientation + sgSetVec3( axis, 0.0, 0.0, 1.0 ); + sgMakeRotMat4( LON, (float)(lon * SGD_RADIANS_TO_DEGREES), axis ); + + sgSetVec3( axis, 0.0, 1.0, 0.0 ); + sgMakeRotMat4( LAT, (float)(90.0 - lat * SGD_RADIANS_TO_DEGREES), axis ); + + sgSetVec3( axis, 0.0, 0.0, 1.0 ); + sgMakeRotMat4( SPIN, (float)(spin * SGD_RADIANS_TO_DEGREES), axis ); + + sgMat4 TRANSFORM; + + sgCopyMat4( TRANSFORM, T ); + sgPreMultMat4( TRANSFORM, LON ); + sgPreMultMat4( TRANSFORM, LAT ); + sgPreMultMat4( TRANSFORM, SPIN ); + + sgCoord skypos; + sgSetCoord( &skypos, TRANSFORM ); + + dome_transform->setTransform( &skypos ); + + return true; +} + + +bool ssgaSkyDome::repaint( sgVec4 sky_color, sgVec4 fog_color, double sol_angle, double vis ) +{ + double diff; + sgVec3 outer_param, outer_amt, outer_diff; + sgVec3 middle_param, middle_amt, middle_diff; + int i, j; + + sol_angle *= SGD_RADIANS_TO_DEGREES; + + // Check for sunrise/sunset condition + if ( (sol_angle > 80.0) ) { // && (sol_angle < 100.0) ) + // 0.0 - 0.4 + sgSetVec3( outer_param, + (float)((10.0 - fabs(90.0 - sol_angle)) / 20.0), + (float)((10.0 - fabs(90.0 - sol_angle)) / 40.0), + (float)(-(10.0 - fabs(90.0 - sol_angle)) / 30.0)); + + sgSetVec3( middle_param, + (float)((10.0 - fabs(90.0 - sol_angle)) / 40.0), + (float)((10.0 - fabs(90.0 - sol_angle)) / 80.0), + 0.0 ); + + sgScaleVec3( outer_diff, outer_param, 1.0f / 6.0f ); + + sgScaleVec3( middle_diff, middle_param, 1.0f / 6.0f ); + } + else { + sgSetVec3( outer_param, 0.0, 0.0, 0.0 ); + sgSetVec3( middle_param, 0.0, 0.0, 0.0 ); + + sgSetVec3( outer_diff, 0.0, 0.0, 0.0 ); + sgSetVec3( middle_diff, 0.0, 0.0, 0.0 ); + } + + // calculate transition colors between sky and fog + sgCopyVec3( outer_amt, outer_param ); + sgCopyVec3( middle_amt, middle_param ); + + // + // First, recalulate the basic colors + // + + sgVec4 center_color; + sgVec4 upper_color[12]; + sgVec4 middle_color[12]; + sgVec4 lower_color[12]; + sgVec4 bottom_color[12]; + + double vis_factor; + + if ( vis < 3000.0 ) { + vis_factor = (vis - 1000.0) / 2000.0; + if ( vis_factor < 0.0 ) { + vis_factor = 0.0; + } + } + else { + vis_factor = 1.0; + } + + for ( j = 0; j < 3; j++ ) { + diff = sky_color[j] - fog_color[j]; + center_color[j] = sky_color[j] - (float)(diff * ( 1.0 - vis_factor )); + } + + for ( i = 0; i < 6; i++ ) { + for ( j = 0; j < 3; j++ ) { + diff = sky_color[j] - fog_color[j]; + + upper_color[i][j] = sky_color[j] - (float)(diff * ( 1.0 - vis_factor * 0.7)); + middle_color[i][j] = sky_color[j] - (float)(diff * ( 1.0 - vis_factor * 0.1)) + middle_amt[j]; + lower_color[i][j] = fog_color[j] + outer_amt[j]; + + if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; } + if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; } + if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; } + if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; } + if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; } + if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; } + } + upper_color[i][3] = middle_color[i][3] = lower_color[i][3] = 1.0; + + for ( j = 0; j < 3; j++ ) { + outer_amt[j] -= outer_diff[j]; + middle_amt[j] -= middle_diff[j]; + } + } + + sgSetVec3( outer_amt, 0.0, 0.0, 0.0 ); + sgSetVec3( middle_amt, 0.0, 0.0, 0.0 ); + + for ( i = 6; i < 12; i++ ) { + for ( j = 0; j < 3; j++ ) { + diff = sky_color[j] - fog_color[j]; + + upper_color[i][j] = sky_color[j] - (float)(diff * ( 1.0 - vis_factor * 0.7)); + middle_color[i][j] = sky_color[j] - (float)(diff * ( 1.0 - vis_factor * 0.1)) + middle_amt[j]; + lower_color[i][j] = fog_color[j] + outer_amt[j]; + + if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; } + if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; } + if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; } + if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; } + if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; } + if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; } + } + upper_color[i][3] = middle_color[i][3] = lower_color[i][3] = 1.0; + + for ( j = 0; j < 3; j++ ) { + outer_amt[j] += outer_diff[j]; + middle_amt[j] += middle_diff[j]; + } + } + + for ( i = 0; i < 12; i++ ) { + sgCopyVec4( bottom_color[i], fog_color ); + } + + // + // Second, assign the basic colors to the object color arrays + // + + float *slot; + int counter; + + // update the center disk color arrays + counter = 0; + slot = center_disk_cl->get( counter++ ); + // sgVec4 red; + // sgSetVec4( red, 1.0, 0.0, 0.0, 1.0 ); + sgCopyVec4( slot, center_color ); + for ( i = 11; i >= 0; i-- ) { + slot = center_disk_cl->get( counter++ ); + sgCopyVec4( slot, upper_color[i] ); + } + slot = center_disk_cl->get( counter++ ); + sgCopyVec4( slot, upper_color[11] ); + + // generate the upper ring + counter = 0; + for ( i = 0; i < 12; i++ ) { + slot = upper_ring_cl->get( counter++ ); + sgCopyVec4( slot, middle_color[i] ); + + slot = upper_ring_cl->get( counter++ ); + sgCopyVec4( slot, upper_color[i] ); + } + slot = upper_ring_cl->get( counter++ ); + sgCopyVec4( slot, middle_color[0] ); + + slot = upper_ring_cl->get( counter++ ); + sgCopyVec4( slot, upper_color[0] ); + + // generate middle ring + counter = 0; + for ( i = 0; i < 12; i++ ) { + slot = middle_ring_cl->get( counter++ ); + sgCopyVec4( slot, lower_color[i] ); + + slot = middle_ring_cl->get( counter++ ); + sgCopyVec4( slot, middle_color[i] ); + } + slot = middle_ring_cl->get( counter++ ); + sgCopyVec4( slot, lower_color[0] ); + + slot = middle_ring_cl->get( counter++ ); + sgCopyVec4( slot, middle_color[0] ); + + // generate lower ring + counter = 0; + for ( i = 0; i < 12; i++ ) { + slot = lower_ring_cl->get( counter++ ); + sgCopyVec4( slot, bottom_color[i] ); + + slot = lower_ring_cl->get( counter++ ); + sgCopyVec4( slot, lower_color[i] ); + } + slot = lower_ring_cl->get( counter++ ); + sgCopyVec4( slot, bottom_color[0] ); + + slot = lower_ring_cl->get( counter++ ); + sgCopyVec4( slot, lower_color[0] ); + + return true; +} diff --git a/src/ssgAux/ssgaSphere.cxx b/src/ssgAux/ssgaSphere.cxx new file mode 100644 index 0000000..4f2f65e --- /dev/null +++ b/src/ssgAux/ssgaSphere.cxx @@ -0,0 +1,125 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaSphere.cxx 2015 2005-02-02 15:48:09Z sjbaker $ +*/ + +// Pulled straight out of MesaGLU/quadratic.c +// +// Original gluSphere code is Copyright (C) 1999-2000 Brian Paul and +// licensed under the GPL +// +// Moved into ssgAux, July 2003. + +#include "ul.h" +#include "ssgaSphere.h" + + +ssgBranch *ssgaMakeSphere( + ssgSimpleState *state, ssgColourArray *cl, + float radius, int slices, int stacks, + ssgCallback predraw, ssgCallback postdraw ) +{ + double rho, drho, theta, dtheta; + float x, y, z; + float s, t, ds, dt; + int i, j, imin, imax; + float nsign = 1.0; + ssgBranch *sphere = new ssgBranch; + sgVec2 vec2; + sgVec3 vec3; + + drho = SGD_PI / stacks; + dtheta = (2.0 * SGD_PI) / slices; + + /* texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y + axis t goes from -1.0/+1.0 at z = -radius/+radius (linear along + longitudes) cannot use triangle fan on texturing (s coord. at + top/bottom tip varies) */ + + ds = 1.0f / slices; + dt = 1.0f / stacks; + t = 1.0; /* because loop now runs from 0 */ + imin = 0; + imax = stacks; + + /* build slices as quad strips */ + for ( i = imin; i < imax; i++ ) { + ssgVertexArray *vl = new ssgVertexArray(); + ssgNormalArray *nl = new ssgNormalArray(); + ssgTexCoordArray *tl = new ssgTexCoordArray(); + + rho = i * drho; + s = 0.0; + for ( j = 0; j <= slices; j++ ) { + theta = (j == slices) ? 0.0 : j * dtheta; + x = (float)(-sin(theta) * sin(rho)); + y = (float)(cos(theta) * sin(rho)); + z = (float)(nsign * cos(rho)); + + sgSetVec3( vec3, x*nsign, y*nsign, z*nsign ); + sgNormalizeVec3( vec3 ); + nl->add( vec3 ); + + sgSetVec2( vec2, s, t ); + tl->add( vec2 ); + + sgSetVec3( vec3, x*radius, y*radius, z*radius ); + vl->add( vec3 ); + + x = (float)(-sin(theta) * sin(rho+drho)); + y = (float)(cos(theta) * sin(rho+drho)); + z = (float)(nsign * cos(rho+drho)); + + sgSetVec3( vec3, x*nsign, y*nsign, z*nsign ); + sgNormalizeVec3( vec3 ); + nl->add( vec3 ); + + sgSetVec2( vec2, s, t-dt ); + tl->add( vec2 ); + s += ds; + + sgSetVec3( vec3, x*radius, y*radius, z*radius ); + vl->add( vec3 ); + } + + ssgLeaf *slice = + new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl ); + + if ( vl->getNum() != nl->getNum() ) { + ulSetError(UL_FATAL, "bad sphere1\n"); + exit(-1); + } + if ( vl->getNum() != tl->getNum() ) { + ulSetError(UL_FATAL, "bad sphere2\n"); + exit(-1); + } + slice->setState( state ); + slice->setCallback( SSG_CALLBACK_PREDRAW, predraw ); + slice->setCallback( SSG_CALLBACK_POSTDRAW, postdraw ); + + sphere->addKid( slice ); + + t -= dt; + } + + return sphere; +} + diff --git a/src/ssgAux/ssgaSphere.h b/src/ssgAux/ssgaSphere.h new file mode 100644 index 0000000..cebdce8 --- /dev/null +++ b/src/ssgAux/ssgaSphere.h @@ -0,0 +1,33 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaSphere.h 2000 2005-01-09 13:13:12Z bram $ +*/ + + +#include "ssg.h" + + +// return a sphere object as an ssgBranch (connected to the specified ssgSimpleState) +ssgBranch *ssgaMakeSphere( + ssgSimpleState *state, ssgColourArray *cl, + float radius, int slices, int stacks, + ssgCallback predraw, ssgCallback postdraw ); + diff --git a/src/ssgAux/ssgaStars.cxx b/src/ssgAux/ssgaStars.cxx new file mode 100644 index 0000000..052cc64 --- /dev/null +++ b/src/ssgAux/ssgaStars.cxx @@ -0,0 +1,242 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaStars.cxx 1798 2003-09-26 14:54:20Z sjbaker $ +*/ + +// Written by Durk Talsma. Originally started October 1997, for distribution +// with the FlightGear project. Version 2 was written in August and +// September 1998. This code is based upon algorithms and data kindly +// provided by Mr. Paul Schlyter. (pausch@saaf.se). +// +// Separated out rendering pieces and converted to ssg by Curt Olson, +// March 2000 +// +// Moved into ssgAux, July 2003. + +#include "ssgaSky.h" + + +static int ssgaStarPreDraw( ssgEntity *e ) +{ + ssgLeaf *f = (ssgLeaf *)e; + if ( f -> hasState () ) f->getState()->apply() ; + + glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_FOG_BIT ); + + //glDisable( GL_DEPTH_TEST ); + glDisable( GL_FOG ); + // glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; + + return true; +} + + +static int ssgaStarPostDraw( ssgEntity *e ) +{ + glPopAttrib(); + + return true; +} + + +ssgaStars::ssgaStars( void ) : + stars_transform(0), + old_phase(-1) +{ +} + + +ssgaStars::~ssgaStars( void ) +{ + ssgDeRefDelete( stars_transform ); +} + + +ssgBranch * ssgaStars::build( int num, sgdVec3 *star_data, double star_dist ) +{ + sgVec4 color; + + // clean-up previous + ssgDeRefDelete( stars_transform ); + + // create new + stars_transform = new ssgTransform; + stars_transform->ref(); + + if ( star_data == NULL ) + ulSetError(UL_WARNING, "null star data passed to ssgaStars::build()"); + + // set up the orb state + state = new ssgSimpleState(); + state->disable( GL_LIGHTING ); + state->disable( GL_CULL_FACE ); + state->disable( GL_TEXTURE_2D ); + state->enable( GL_COLOR_MATERIAL ); + state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE ); + state->setMaterial( GL_EMISSION, 0, 0, 0, 1 ); + state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 ); + state->enable( GL_BLEND ); + state->disable( GL_ALPHA_TEST ); + + vl = new ssgVertexArray( num ); + cl = new ssgColourArray( num ); + // cl = new ssgColourArray( 1 ); + // sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); + // cl->add( color ); + + // Build ssg structure + sgVec3 p; + for ( int i = 0; i < num; ++i ) { + // position seeded to arbitrary values + sgSetVec3( p, + (float)( star_dist * cos( star_data[i][0] ) + * cos( star_data[i][1] )), + (float)( star_dist * sin( star_data[i][0] ) + * cos( star_data[i][1] )), + (float)( star_dist * sin( star_data[i][1] ))); + vl->add( p ); + + // color (magnitude) + sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); + cl->add( color ); + } + + ssgLeaf *stars_obj = + new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl ); + stars_obj->setState( state ); + stars_obj->setCallback( SSG_CALLBACK_PREDRAW, ssgaStarPreDraw ); + stars_obj->setCallback( SSG_CALLBACK_POSTDRAW, ssgaStarPostDraw ); + + stars_transform->addKid( stars_obj ); + + return stars_transform; +} + + +bool ssgaStars::reposition( sgVec3 p, double angle ) +{ + sgMat4 T1, GST; + sgVec3 axis; + + sgMakeTransMat4( T1, p ); + + sgSetVec3( axis, 0.0, 0.0, -1.0 ); + sgMakeRotMat4( GST, (float)angle, axis ); + + sgMat4 TRANSFORM; + sgCopyMat4( TRANSFORM, T1 ); + sgPreMultMat4( TRANSFORM, GST ); + + sgCoord skypos; + sgSetCoord( &skypos, TRANSFORM ); + + stars_transform->setTransform( &skypos ); + + return true; +} + + +bool ssgaStars::repaint( double sol_angle, int num, sgdVec3 *star_data ) +{ + double mag, nmag, alpha, factor, cutoff; + float *color; + + int phase; + + // determine which star structure to draw + if ( sol_angle > (0.5 * SGD_PI + 10.0 * SGD_DEGREES_TO_RADIANS ) ) { + // deep night + factor = 1.0; + cutoff = 4.5; + phase = 0; + } + else if ( sol_angle > (0.5 * SGD_PI + 8.8 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 1.0; + cutoff = 3.8; + phase = 1; + } + else if ( sol_angle > (0.5 * SGD_PI + 7.5 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 0.95; + cutoff = 3.1; + phase = 2; + } + else if ( sol_angle > (0.5 * SGD_PI + 7.0 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 0.9; + cutoff = 2.4; + phase = 3; + } + else if ( sol_angle > (0.5 * SGD_PI + 6.5 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 0.85; + cutoff = 1.8; + phase = 4; + } + else if ( sol_angle > (0.5 * SGD_PI + 6.0 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 0.8; + cutoff = 1.2; + phase = 5; + } + else if ( sol_angle > (0.5 * SGD_PI + 5.5 * SGD_DEGREES_TO_RADIANS ) ) { + factor = 0.75; + cutoff = 0.6; + phase = 6; + } + else { + // early dusk or late dawn + factor = 0.7; + cutoff = 0.0; + phase = 7; + } + + if( phase != old_phase ) { + old_phase = phase; + for ( int i = 0; i < num; ++i ) { + // if ( star_data[i][2] < min ) { min = star_data[i][2]; } + // if ( star_data[i][2] > max ) { max = star_data[i][2]; } + + // magnitude ranges from -1 (bright) to 4 (dim). The + // range of star and planet magnitudes can actually go + // outside of this, but for our purpose, if it is brighter + // that -1, we'll color it full white/alpha anyway and 4 + // is a convenient cutoff point which keeps the number of + // stars drawn at about 500. + + // color (magnitude) + mag = star_data[i][2]; + if ( mag < cutoff ) { + nmag = ( 4.5 - mag ) / 5.5; // translate to 0 ... 1.0 scale + // alpha = nmag * 0.7 + 0.3; // translate to a 0.3 ... 1.0 scale + alpha = nmag * 0.85 + 0.15; // translate to a 0.15 ... 1.0 scale + alpha *= factor; // dim when the sun is brighter + } + else { + alpha = 0.0; + } + + if (alpha > 1.0) { alpha = 1.0; } + if (alpha < 0.0) { alpha = 0.0; } + + color = cl->get( i ); + sgSetVec4( color, 1.0, 1.0, 1.0, (float)alpha ); + } + } + + return true; +} diff --git a/src/ssgAux/ssgaTeapot.cxx b/src/ssgAux/ssgaTeapot.cxx new file mode 100644 index 0000000..9099002 --- /dev/null +++ b/src/ssgAux/ssgaTeapot.cxx @@ -0,0 +1,342 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaTeapot.cxx 2015 2005-02-02 15:48:09Z sjbaker $ +*/ + + +#include "ssgAux.h" +#include + +static int patch [][ 17 ] = +{ + /* Rim: */ + { 2, 102, 103, 104, 105, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15 }, + /* Body: */ + { 2, 12, 13, 14, 15, + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27 }, + { 2, 24, 25, 26, 27, + 29, 30, 31, 32, + 33, 34, 35, 36, + 37, 38, 39, 40 }, + /* Lid: */ + { 2, 96, 96, 96, 96, + 97, 98, 99, 100, + 101, 101, 101, 101, + 0, 1, 2, 3 }, + { 2, 0, 1, 2, 3, + 106, 107, 108, 109, + 110, 111, 112, 113, + 114, 115, 116, 117 }, + /* Handle: */ + { 1, 41, 42, 43, 44, + 45, 46, 47, 48, + 49, 50, 51, 52, + 53, 54, 55, 56 }, + { 1, 53, 54, 55, 56, + 57, 58, 59, 60, + 61, 62, 63, 64, + 28, 65, 66, 67 }, + /* Spout: */ + { 1, 68, 69, 70, 71, + 72, 73, 74, 75, + 76, 77, 78, 79, + 80, 81, 82, 83 }, + { 1, 80, 81, 82, 83, + 84, 85, 86, 87, + 88, 89, 90, 91, + 92, 93, 94, 95 }, + /* Bottom: */ + { 2, 118, 118, 118, 118, + 124, 122, 119, 121, + 123, 126, 125, 120, + 40, 39, 38, 37 }, + + /* End Marker: */ + { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 } +} ; + +static sgVec3 vertex [] = +{ + { 0.2000f, 0.0000f, 2.70000f }, /* 00 */ + { 0.2000f, -0.1120f, 2.70000f }, + { 0.1120f, -0.2000f, 2.70000f }, + { 0.0000f, -0.2000f, 2.70000f }, + { 1.3375f, 0.0000f, 2.53125f }, + { 1.3375f, -0.7490f, 2.53125f }, + { 0.7490f, -1.3375f, 2.53125f }, + { 0.0000f, -1.3375f, 2.53125f }, + { 1.4375f, 0.0000f, 2.53125f }, + { 1.4375f, -0.8050f, 2.53125f }, + + { 0.8050f, -1.4375f, 2.53125f }, /* 10 */ + { 0.0000f, -1.4375f, 2.53125f }, + { 1.5000f, 0.0000f, 2.40000f }, + { 1.5000f, -0.8400f, 2.40000f }, + { 0.8400f, -1.5000f, 2.40000f }, + { 0.0000f, -1.5000f, 2.40000f }, + { 1.7500f, 0.0000f, 1.87500f }, + { 1.7500f, -0.9800f, 1.87500f }, + { 0.9800f, -1.7500f, 1.87500f }, + { 0.0000f, -1.7500f, 1.87500f }, + + { 2.0000f, 0.0000f, 1.35000f }, /* 20 */ + { 2.0000f, -1.1200f, 1.35000f }, + { 1.1200f, -2.0000f, 1.35000f }, + { 0.0000f, -2.0000f, 1.35000f }, + { 2.0000f, 0.0000f, 0.90000f }, + { 2.0000f, -1.1200f, 0.90000f }, + { 1.1200f, -2.0000f, 0.90000f }, + { 0.0000f, -2.0000f, 0.90000f }, + { -2.0000f, 0.0000f, 0.90000f }, + { 2.0000f, 0.0000f, 0.45000f }, + + { 2.0000f, -1.1200f, 0.45000f }, /* 30 */ + { 1.1200f, -2.0000f, 0.45000f }, + { 0.0000f, -2.0000f, 0.45000f }, + { 1.5000f, 0.0000f, 0.22500f }, + { 1.5000f, -0.8400f, 0.22500f }, + { 0.8400f, -1.5000f, 0.22500f }, + { 0.0000f, -1.5000f, 0.22500f }, + { 1.5000f, 0.0000f, 0.15000f }, + { 1.5000f, -0.8400f, 0.15000f }, + { 0.8400f, -1.5000f, 0.15000f }, + + { 0.0000f, -1.5000f, 0.15000f }, /* 40 */ + { -1.6000f, 0.0000f, 2.02500f }, + { -1.6000f, -0.3000f, 2.02500f }, + { -1.5000f, -0.3000f, 2.25000f }, + { -1.5000f, 0.0000f, 2.25000f }, + { -2.3000f, 0.0000f, 2.02500f }, + { -2.3000f, -0.3000f, 2.02500f }, + { -2.5000f, -0.3000f, 2.25000f }, + { -2.5000f, 0.0000f, 2.25000f }, + { -2.7000f, 0.0000f, 2.02500f }, + + { -2.7000f, -0.3000f, 2.02500f }, /* 50 */ + { -3.0000f, -0.3000f, 2.25000f }, + { -3.0000f, 0.0000f, 2.25000f }, + { -2.7000f, 0.0000f, 1.80000f }, + { -2.7000f, -0.3000f, 1.80000f }, + { -3.0000f, -0.3000f, 1.80000f }, + { -3.0000f, 0.0000f, 1.80000f }, + { -2.7000f, 0.0000f, 1.57500f }, + { -2.7000f, -0.3000f, 1.57500f }, + { -3.0000f, -0.3000f, 1.35000f }, + + { -3.0000f, 0.0000f, 1.35000f }, /* 60 */ + { -2.5000f, 0.0000f, 1.12500f }, + { -2.5000f, -0.3000f, 1.12500f }, + { -2.6500f, -0.3000f, 0.93750f }, + { -2.6500f, 0.0000f, 0.93750f }, + { -2.0000f, -0.3000f, 0.90000f }, + { -1.9000f, -0.3000f, 0.60000f }, + { -1.9000f, 0.0000f, 0.60000f }, + { 1.7000f, 0.0000f, 1.42500f }, + { 1.7000f, -0.6600f, 1.42500f }, + + { 1.7000f, -0.6600f, 0.60000f }, /* 70 */ + { 1.7000f, 0.0000f, 0.60000f }, + { 2.6000f, 0.0000f, 1.42500f }, + { 2.6000f, -0.6600f, 1.42500f }, + { 3.1000f, -0.6600f, 0.82500f }, + { 3.1000f, 0.0000f, 0.82500f }, + { 2.3000f, 0.0000f, 2.10000f }, + { 2.3000f, -0.2500f, 2.10000f }, + { 2.4000f, -0.2500f, 2.02500f }, + { 2.4000f, 0.0000f, 2.02500f }, + + { 2.7000f, 0.0000f, 2.40000f }, /* 80 */ + { 2.7000f, -0.2500f, 2.40000f }, + { 3.3000f, -0.2500f, 2.40000f }, + { 3.3000f, 0.0000f, 2.40000f }, + { 2.8000f, 0.0000f, 2.47500f }, + { 2.8000f, -0.2500f, 2.47500f }, + { 3.5250f, -0.2500f, 2.49375f }, + { 3.5250f, 0.0000f, 2.49375f }, + { 2.9000f, 0.0000f, 2.47500f }, + { 2.9000f, -0.1500f, 2.47500f }, + + { 3.4500f, -0.1500f, 2.51250f }, /* 90 */ + { 3.4500f, 0.0000f, 2.51250f }, + { 2.8000f, 0.0000f, 2.40000f }, + { 2.8000f, -0.1500f, 2.40000f }, + { 3.2000f, -0.1500f, 2.40000f }, + { 3.2000f, 0.0000f, 2.40000f }, + { 0.0000f, 0.0000f, 3.15000f }, + { 0.8000f, 0.0000f, 3.15000f }, + { 0.8000f, -0.4500f, 3.15000f }, + { 0.4500f, -0.8000f, 3.15000f }, + + { 0.0000f, -0.8000f, 3.15000f }, /* 100 */ + { 0.0000f, 0.0000f, 2.85000f }, + { 1.4000f, 0.0000f, 2.40000f }, + { 1.4000f, -0.7840f, 2.40000f }, + { 0.7840f, -1.4000f, 2.40000f }, + { 0.0000f, -1.4000f, 2.40000f }, + { 0.4000f, 0.0000f, 2.55000f }, + { 0.4000f, -0.2240f, 2.55000f }, + { 0.2240f, -0.4000f, 2.55000f }, + { 0.0000f, -0.4000f, 2.55000f }, + + { 1.3000f, 0.0000f, 2.55000f }, /* 110 */ + { 1.3000f, -0.7280f, 2.55000f }, + { 0.7280f, -1.3000f, 2.55000f }, + { 0.0000f, -1.3000f, 2.55000f }, /* Next four verts kludged to make lid fit */ + { 1.4000f, 0.0000f, 2.40000f }, /* { 1.3000, 0.0000, 2.40000 }, */ + { 1.4000f, -0.7840f, 2.40000f }, /* { 1.3000, -0.7280, 2.40000 }, */ + { 0.7840f, -1.4000f, 2.40000f }, /* { 0.7280, -1.3000, 2.40000 }, */ + { 0.0000f, -1.4000f, 2.40000f }, /* { 0.0000, -1.3000, 2.40000 }, */ + { 0.0000f, 0.0000f, 0.00000f }, + { 1.4250f, -0.7980f, 0.00000f }, + + { 1.5000f, 0.0000f, 0.07500f }, /* 120 */ + { 1.4250f, 0.0000f, 0.00000f }, + { 0.7980f, -1.4250f, 0.00000f }, + { 0.0000f, -1.5000f, 0.07500f }, + { 0.0000f, -1.4250f, 0.00000f }, + { 1.5000f, -0.8400f, 0.07500f }, + { 0.8400f, -1.5000f, 0.07500f } +} ; + + +void ssgaTeapot::regenerate () +{ + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + for ( int i = 0 ; patch[i][0] >= 0 ; i++ ) + { + ssgaPatch *p = new ssgaPatch ( ntriangles / 32 ) ; + int j ; + + for ( j = 0 ; j < 16 ; j++ ) + { + sgVec3 xyz ; + sgVec2 uv ; + + uv [ 0 ] = (float)(j&3)/3.0f ; + uv [ 1 ] = (float)(j>>2)/3.0f ; + + sgScaleVec3 ( xyz, vertex[patch[i][j+1]], 1.0f/2.5f ) ; + xyz [ 0 ] *= -1.0f ; + + p -> setControlPoint ( j>>2, j&3, xyz, uv, colour ) ; + } + + p -> setKidState ( getKidState () ) ; + p -> setKidCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + p -> setKidCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + p -> setColour ( colour ) ; + p -> setCenter ( getCenter () ) ; + p -> setSize ( getSize () ) ; + p -> regenerate () ; + + /* Make a mirror image in the Y axis */ + + sgVec3 xyz ; + sgVec2 uv ; + sgVec4 rgba ; + + ssgaPatch *p2 = new ssgaPatch ( ntriangles / 32 ) ; + + for ( j = 0 ; j < 16 ; j++ ) + { + p -> getControlPoint ( j>>2, 3-(j&3), xyz, uv, rgba ) ; + xyz [ 1 ] *= -1.0f ; + p2 -> setControlPoint ( j>>2, j&3, xyz, uv, rgba ) ; + } + + p2 -> setKidState ( getKidState () ) ; + p2 -> setKidCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + p2 -> setKidCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + p2 -> setColour ( colour ) ; + p2 -> setCenter ( getCenter () ) ; + p2 -> setSize ( getSize () ) ; + p2 -> regenerate () ; + + if ( patch[i][0] == 2 ) + { + /* Make a mirror images in the X axis */ + + ssgaPatch *p3 = new ssgaPatch ( ntriangles / 32 ) ; + ssgaPatch *p4 = new ssgaPatch ( ntriangles / 32 ) ; + + for ( j = 0 ; j < 16 ; j++ ) + { + p -> getControlPoint ( j>>2, 3-(j&3), xyz, uv, rgba ) ; + xyz [ 0 ] *= -1.0f ; + p3 -> setControlPoint ( j>>2, j&3, xyz, uv, rgba ) ; + + p2 -> getControlPoint ( j>>2, 3-(j&3), xyz, uv, rgba ) ; + xyz [ 0 ] *= -1.0f ; + p4 -> setControlPoint ( j>>2, j&3, xyz, uv, rgba ) ; + } + + p3 -> setKidState ( getKidState () ) ; + p3 -> setKidCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + p3 -> setKidCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + p3 -> setColour ( colour ) ; + p3 -> setCenter ( getCenter () ) ; + p3 -> setSize ( getSize () ) ; + p3 -> regenerate () ; + + p4 -> setKidState ( getKidState () ) ; + p4 -> setKidCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + p4 -> setKidCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + p4 -> setColour ( colour ) ; + p4 -> setCenter ( getCenter () ) ; + p4 -> setSize ( getSize () ) ; + p4 -> regenerate () ; + + for ( j = 0 ; j < p3 -> getNumKids () ; j++ ) + { + addKid ( p3->getKid(j) ) ; + addKid ( p4->getKid(j) ) ; + } + + p3 -> removeAllKids () ; + p4 -> removeAllKids () ; + delete p3 ; + delete p4 ; + } + + for ( j = 0 ; j < p -> getNumKids () ; j++ ) + { + addKid ( p->getKid(j) ) ; + addKid ( p2->getKid(j) ) ; + } + + p -> removeAllKids () ; + p2 -> removeAllKids () ; + delete p ; + delete p2 ; + } + + recalcBSphere () ; +} + + diff --git a/src/ssgAux/ssgaWaveSystem.cxx b/src/ssgAux/ssgaWaveSystem.cxx new file mode 100644 index 0000000..261cf48 --- /dev/null +++ b/src/ssgAux/ssgaWaveSystem.cxx @@ -0,0 +1,347 @@ + +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ssgaWaveSystem.cxx 1772 2003-06-27 08:15:15Z sjbaker $ +*/ + + +#include "ssgAux.h" +#include + +#define G 9.8f + +void ssgaWaveSystem::updateAnimation ( float tim ) +{ + if ( ntriangles <= 0 || + normals == NULL || colours == NULL || + texcoords == NULL || vertices == NULL || + orig_vertices == NULL ) + return ; + + int i ; + + float adjSpeed [ SSGA_MAX_WAVETRAIN ] ; + float sinHeading [ SSGA_MAX_WAVETRAIN ] ; + float cosHeading [ SSGA_MAX_WAVETRAIN ] ; + float length [ SSGA_MAX_WAVETRAIN ] ; + float lambda [ SSGA_MAX_WAVETRAIN ] ; + float height [ SSGA_MAX_WAVETRAIN ] ; + + /* Pre-adjust speed's to allow for wind speed. */ + + int num_trains = 0 ; + + for ( i = 0 ; i < SSGA_MAX_WAVETRAIN ; i++ ) + if ( train [ i ] != NULL ) + { + adjSpeed [num_trains] = train [ i ] -> getSpeed () * G * + tim / windSpeed ; + sinHeading [num_trains] = (float) -sin ( train[i]->getHeading () * + SG_DEGREES_TO_RADIANS ) ; + cosHeading [num_trains] = (float) cos ( train[i]->getHeading () * + SG_DEGREES_TO_RADIANS ) ; + length [num_trains] = train [ i ] -> getLength () ; + lambda [num_trains] = train [ i ] -> getLambda () ; + height [num_trains] = train [ i ] -> getWaveHeight () ; + num_trains++ ; + } + + for ( i = 0 ; i <= nstrips ; i++ ) + { + float fade_i = (i<2) ? 0.0f : (i<7) ? (float)(i-2)/5.0f : + (i>nstrips-2) ? 0.0f : + (i>nstrips-7) ? (float)(nstrips-i-2)/5.0f : 1.0f ; + + for ( int j = 0 ; j <= nstacks ; j++ ) + { + float fade_j = (j<2) ? 0.0f : (j<7) ? (float)(j-2)/5.0f : + (j>nstacks-2) ? 0.0f : + (j>nstacks-7) ? (float)(nstacks-j-2)/5.0f : 1.0f ; + + float edge_fade = fade_i * fade_j ; + + int idx = i * (nstrips+1) + j ; + + float xx = orig_vertices [idx][0] + center[0] ; + float yy = orig_vertices [idx][1] + center[1] ; + float zz = center[2] ; + + float x0 = xx + offset[0] ; + float y0 = yy + offset[1] ; + float z0 = vertices [idx][2] ; + + float depth = (gridGetter==NULL) ? 1000000.0f : gridGetter ( x0, y0 ) ; + + for ( int t = 0 ; t < num_trains ; t++ ) + { + float adjHeight = height [ t ] * edge_fade ; + float adjLength = ( depth < 0.2f ) ? 0.2f : + ( depth > length[t] ) ? length[t] : depth ; + + float phase = ( x0 * sinHeading[t] + y0 * cosHeading[t] ) / adjLength - + adjSpeed[t] - lambda[t] * z0 ; + + float delta = adjHeight * (float) sin ( phase ) ; + + xx += delta * sinHeading [ t ] ; + yy += delta * cosHeading [ t ] ; + zz += adjHeight * (float) -cos ( phase ) ; + } + + sgSetVec3 ( vertices [idx], xx, yy, zz ) ; + sgSetVec2 ( texcoords [idx], tu * x0 / size[0], tv * y0 /size[1] ) ; + } + } + + for ( i = 0 ; i < nstrips ; i++ ) + { + int i1 = i * (nstrips+1) ; + int i2 = (i+1) * (nstrips+1) ; + + for ( int j = 0 ; j < nstacks ; j++ ) + { + int idx1 = i1 + j ; + int idx2 = i2 + j ; + int idx3 = i1 + (j+1) ; + + sgVec3 ab ; sgSubVec3 ( ab, vertices[idx3], vertices[idx1] ) ; + sgVec3 ac ; sgSubVec3 ( ac, vertices[idx2], vertices[idx1] ) ; + + float nx = ab[1] * ac[2] - ab[2] * ac[1] ; + float ny = ab[2] * ac[0] - ab[0] * ac[2] ; + float nz = ab[0] * ac[1] - ab[1] * ac[0] ; + + /* About 10% of execution time is in this instruction! */ + float rlen = 1.0f / (float) sqrt ( nx * nx + ny * ny + nz * nz ) ; + + normals[idx1][0] = nx * rlen ; + normals[idx1][1] = ny * rlen ; + normals[idx1][2] = nz * rlen ; + } + } + + for ( i = 0 ; i < nstrips ; i++ ) + { + ssgVtxTable *vt = (ssgVtxTable *) getKid ( i ) ; + ssgVertexArray *vv = vt -> getVertices () ; + ssgNormalArray *nn = vt -> getNormals () ; + ssgColourArray *cc = vt -> getColours () ; + ssgTexCoordArray *tt = vt -> getTexCoords () ; + + int i1 = (i+1) * (nstrips+1) ; + int i2 = i * (nstrips+1) ; + + for ( int j = 0, jj = 0 ; j < nstacks + 1 ; j++, jj += 2, i1++, i2++ ) + { + vv -> set ( vertices [i1], jj ) ; vv -> set ( vertices [i2], jj+1 ) ; + nn -> set ( normals [i1], jj ) ; nn -> set ( normals [i2], jj+1 ) ; + cc -> set ( colours [i1], jj ) ; cc -> set ( colours [i2], jj+1 ) ; + tt -> set ( texcoords[i1], jj ) ; tt -> set ( texcoords[i2], jj+1 ) ; + } + } +} + + +void ssgaWaveSystem::copy_from ( ssgaWaveSystem *src, int clone_flags ) +{ + ssgaShape::copy_from ( src, clone_flags ) ; + + setDepthCallback ( src -> getDepthCallback () ) ; + setWindSpeed ( src -> getWindSpeed () ) ; + setWindDirn ( src -> getWindDirn () ) ; + setEdgeFlatten ( src -> getEdgeFlatten () ) ; +} + + +ssgBase *ssgaWaveSystem::clone ( int clone_flags ) +{ + ssgaWaveSystem *b = new ssgaWaveSystem ( getNumTris() ) ; + b -> copy_from ( this, clone_flags ) ; + return b ; +} + + +ssgaWaveSystem::ssgaWaveSystem ( int np ) : ssgaShape ( np ) +{ + type=ssgaTypeWaveSystem (); + + setDepthCallback ( NULL ) ; + setWindSpeed ( 1.0f ) ; + setWindDirn ( 0.0f ) ; + setEdgeFlatten ( 0.0f ) ; + + nstrips = nstacks = 0 ; + + offset [ 0 ] = offset [ 1 ] = 0.0f ; + + normals = NULL ; + colours = NULL ; + texcoords = NULL ; + vertices = NULL ; + orig_vertices = NULL ; + + tu = tv = 1.0f ; + + for ( int i = 0 ; i < SSGA_MAX_WAVETRAIN ; i++ ) + train [ i ] = NULL ; + + regenerate(); +} + + +ssgaWaveSystem::~ssgaWaveSystem (void) {} + +const char *ssgaWaveSystem::getTypeName(void) { return "ssgaWaveSystem" ; } + + +void ssgaWaveSystem::regenerate () +{ + delete[] normals ; + delete[] colours ; + delete[] texcoords ; + delete[] vertices ; + delete[] orig_vertices ; + + normals = NULL ; + colours = NULL ; + texcoords = NULL ; + vertices = NULL ; + orig_vertices = NULL ; + + nstrips = nstacks = 0 ; + + if ( kidState != NULL ) kidState -> ref () ; + removeAllKids () ; + if ( kidState != NULL ) kidState -> deRef () ; + + if ( ntriangles <= 0 ) + return ; + + int gridSize = (int) sqrt ( (float) ntriangles / 2.0f ) ; + + nstacks = gridSize ; + nstrips = gridSize ; + + if ( nstacks < 1 ) nstacks = 1 ; + if ( nstrips < 1 ) nstrips = 1 ; + + normals = new sgVec3 [ (nstacks+1) * (nstrips+1) ] ; + colours = new sgVec4 [ (nstacks+1) * (nstrips+1) ] ; + texcoords = new sgVec2 [ (nstacks+1) * (nstrips+1) ] ; + vertices = new sgVec3 [ (nstacks+1) * (nstrips+1) ] ; + orig_vertices = new sgVec3 [ (nstacks+1) * (nstrips+1) ] ; + + int i; + for ( i = 0 ; i <= nstrips ; i++ ) + for ( int j = 0 ; j <= nstacks ; j++ ) + { + int idx = i * (nstrips+1) + j ; + + float x = (float) j / (float) nstacks ; + float y = (float) i / (float) nstrips ; + + // if ( j == 0 ) x = -500.0f ; + // if ( j == nstacks ) x = 500.0f ; + // if ( i == 0 ) y = -500.0f ; + // if ( i == nstacks ) y = 500.0f ; + + sgSetVec3 ( vertices [idx], (x-0.5f) * size[0], + (y-0.5f) * size[1], 0.0f ) ; + sgSetVec3 ( normals [idx], 0.0f, 0.0f, 1.0f ) ; + sgSetVec2 ( texcoords[idx], x * tu, y * tv ) ; + sgCopyVec4 ( colours [idx], colour ) ; + sgCopyVec3 ( orig_vertices [ idx ], vertices [idx] ) ; + } + + for ( i = 0 ; i < nstrips ; i++ ) + { + ssgVtxTable *vt = new ssgVtxTable ; + ssgVertexArray *vv = new ssgVertexArray ( nstacks * 2 + 2 ) ; + ssgNormalArray *nn = new ssgNormalArray ( nstacks * 2 + 2 ) ; + ssgColourArray *cc = new ssgColourArray ( nstacks * 2 + 2 ) ; + ssgTexCoordArray *tt = new ssgTexCoordArray ( nstacks * 2 + 2 ) ; + + addKid ( vt ) ; + + vt -> setState ( getKidState () ) ; + vt -> setCallback ( SSG_CALLBACK_PREDRAW , getKidPreDrawCB () ) ; + vt -> setCallback ( SSG_CALLBACK_POSTDRAW, getKidPostDrawCB () ) ; + + vt -> setPrimitiveType ( GL_TRIANGLE_STRIP ) ; + + for ( int j = 0 ; j < nstacks + 1 ; j++ ) + { + int idx = (i+1) * (nstrips+1) + j ; + + vv -> add ( vertices [ idx ] ) ; nn -> add ( normals [ idx ] ) ; + cc -> add ( colours [ idx ] ) ; tt -> add ( texcoords[ idx ] ) ; + + idx = i * (nstrips+1) + j ; + + vv -> add ( vertices [ idx ] ) ; nn -> add ( normals [ idx ] ) ; + cc -> add ( colours [ idx ] ) ; tt -> add ( texcoords[ idx ] ) ; + } + + vt -> setVertices ( vv ) ; + vt -> setNormals ( nn ) ; + vt -> setColours ( cc ) ; + vt -> setTexCoords ( tt ) ; + + vt -> recalcBSphere () ; + } + + recalcBSphere () ; +} + + +// XXX really need these (and ssgLocal.h is not accessible): +extern int _ssgLoadObject ( FILE *, ssgBase **, int ) ; +extern int _ssgSaveObject ( FILE *, ssgBase * ) ; + + +#define load_field(fp, name) (fread(&(name), 1, sizeof(name), fp) == sizeof(name)) +#define save_field(fp, name) (fwrite(&(name), 1, sizeof(name), fp) == sizeof(name)) + + +int ssgaWaveSystem::load ( FILE *fp ) +{ + return ( load_field ( fp, windSpeed ) && + load_field ( fp, windHeading ) && + load_field ( fp, edgeFlatten ) && + load_field ( fp, tu ) && + load_field ( fp, tv ) && + ssgaShape::load ( fp ) ) ; +} + + + +int ssgaWaveSystem::save ( FILE *fp ) +{ + return ( save_field ( fp, windSpeed ) && + save_field ( fp, windHeading ) && + save_field ( fp, edgeFlatten ) && + save_field ( fp, tu ) && + save_field ( fp, tv ) && + ssgaShape::save ( fp ) ) ; +} + + + diff --git a/src/ssgAux/ssgaWaveSystem.h b/src/ssgAux/ssgaWaveSystem.h new file mode 100644 index 0000000..0e349f8 --- /dev/null +++ b/src/ssgAux/ssgaWaveSystem.h @@ -0,0 +1,131 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + + +#include "ssgaShapes.h" + +typedef float (* ssgaWSDepthCallback ) ( float x, float y ) ; + +#define SSGA_MAX_WAVETRAIN 16 + +class ssgaWaveTrain +{ + float height ; + float length ; + float lambda ; + float speed ; + float heading; + +public: + + ssgaWaveTrain () + { + height = 0.5f ; + length = 0.8f ; + lambda = 1.0f ; + speed = (float) sqrt ( 2.0f/3.0f ) ; + heading = 0.0f ; + } + + float getSpeed () { return speed ; } + void setSpeed ( float h ) { speed = h ; } + + float getLength () { return length ; } + void setLength ( float h ) { length = h ; } + + float getLambda () { return lambda ; } + void setLambda ( float h ) { lambda = h ; } + + float getHeading () { return heading ; } + void setHeading ( float h ) { heading = h ; } + + float getWaveHeight () { return height ; } + void setWaveHeight ( float h ) { height = h ; } +} ; + +class ssgaWaveSystem : public ssgaShape +{ + ssgaWSDepthCallback gridGetter ; + + sgVec2 offset ; + sgVec3 *normals ; + sgVec4 *colours ; + sgVec2 *texcoords ; + sgVec3 *vertices ; + sgVec3 *orig_vertices ; + + ssgaWaveTrain *train [ SSGA_MAX_WAVETRAIN ] ; + + float windSpeed ; + float windHeading ; + float edgeFlatten ; + + float tu, tv ; + + int nstrips ; + int nstacks ; + +protected: + virtual void copy_from ( ssgaWaveSystem *src, int clone_flags ) ; +public: + + ssgaWaveSystem ( int ntri ) ; + + virtual ~ssgaWaveSystem () ; + + virtual ssgBase *clone ( int clone_flags = 0 ) ; + virtual void regenerate () ; + virtual const char *getTypeName ( void ) ; + + virtual int load ( FILE * ) ; + virtual int save ( FILE * ) ; + + ssgaWSDepthCallback getDepthCallback () { return gridGetter ; } + + ssgaWaveTrain *getWaveTrain ( int i ) + { + return ( i < 0 || i >= SSGA_MAX_WAVETRAIN ) ? NULL : train [ i ] ; + } + + void setWaveTrain ( int i, ssgaWaveTrain *t ) + { + assert ( i >= 0 && i < SSGA_MAX_WAVETRAIN ) ; + train [ i ] = t ; + } + + float getWindSpeed () { return windSpeed ; } + float getWindDirn () { return windHeading ; } + float getEdgeFlatten () { return edgeFlatten ; } + float getTexScaleU () { return tu ; } + float getTexScaleV () { return tv ; } + + void setDepthCallback ( ssgaWSDepthCallback cb ) { gridGetter = cb ; } + void setWindSpeed ( float speed ) { windSpeed = speed ; } + void setWindDirn ( float dirn ) { windHeading = dirn ; } + void setEdgeFlatten ( float dist ) { edgeFlatten = dist ; } + void setTexScale ( float u, float v ) { tu = u ; tv = v ; } + + void setOffset ( sgVec2 _offset ) { sgCopyVec2 ( offset, _offset ) ; } + + void updateAnimation ( float t ) ; +} ; + + diff --git a/src/util/Makefile.am b/src/util/Makefile.am new file mode 100644 index 0000000..4000358 --- /dev/null +++ b/src/util/Makefile.am @@ -0,0 +1,13 @@ +if BUILD_UL + +lib_LIBRARIES = libplibul.a + +include_HEADERS = ul.h ulRTTI.h + +libplibul_a_SOURCES = ul.cxx ulClock.cxx ulError.cxx ulLinkedList.cxx \ + ulList.cxx ulLocal.h ulRTTI.cxx + +endif + +EXTRA_DIST = ul.dsp + diff --git a/src/util/ul.cxx b/src/util/ul.cxx new file mode 100644 index 0000000..b0743f8 --- /dev/null +++ b/src/util/ul.cxx @@ -0,0 +1,544 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ul.cxx 2129 2007-10-16 00:45:26Z fayjf $ +*/ + + +#include "ul.h" + +#include +#include + +#ifdef UL_MSVC +#include +#else +#include +#endif + +#ifdef UL_MSVC +#define SLASH "\\" +#else +#define SLASH "/" +#endif + + +struct _ulDir +{ + char dirname [ UL_NAME_MAX+1 ]; + ulDirEnt curr ; + +#ifdef UL_MSVC + WIN32_FIND_DATA data ; + HANDLE hFind ; + bool first ; + bool done ; +#else + DIR *dirp ; +#endif +} ; + + +void ulInit () +{ +} + + +ulDir* ulOpenDir ( const char* dirname ) +{ + ulDir* dir = new ulDir; + if ( dir != NULL ) + { + strcpy( dir->dirname, dirname ) ; + +#ifdef UL_MSVC + char search[_MAX_PATH]; + strcpy(search,dirname); + + //munch the directory seperator + int len = strlen(search); + if ( len>0 && strchr("/\\",search[len-1]) ) + search[len-1] = 0; + + //add the wildcard + strcat(search,"/*.*"); + + dir->first = true; + dir->done = false; + dir->hFind = FindFirstFile(search, &dir->data); + if (dir->hFind == INVALID_HANDLE_VALUE) + { + delete dir; + dir = NULL; + } +#else + dir->dirp = opendir(dirname) ; + + if ( dir->dirp == NULL ) + { + delete dir ; + dir = NULL ; + } +#endif + } + return dir; +} + + +ulDirEnt* ulReadDir ( ulDir* dir ) +{ + //read the next entry from the directory +#ifdef UL_MSVC + //update state + if ( dir->first ) + dir->first = false ; + else if ( !dir->done && !FindNextFile(dir->hFind,&dir->data) ) + dir->done = true ; + if ( dir->done ) + return NULL ; + + strcpy( dir->curr.d_name, dir->data.cFileName ) ; +#else + struct dirent* direntp = readdir( dir->dirp ); + if ( !direntp ) + return NULL ; + + strcpy( dir->curr.d_name, direntp->d_name ); +#endif + + //determine if this entry is a directory +#ifdef UL_MSVC + dir->curr.d_isdir = ( dir->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0; + #else + char path[ 1000 + UL_NAME_MAX+1 ]; + sprintf( path, "%s/%s", dir->dirname, dir->curr.d_name ); + struct stat buf ; + if ( stat(path,&buf) == 0 ) + dir->curr.d_isdir = (buf.st_mode & S_IFDIR) != 0 ; + else + dir->curr.d_isdir = false ; +#endif + + return( &dir->curr ) ; +} + + + +void ulCloseDir ( ulDir* dir ) +{ + if ( dir != NULL ) + { +#ifdef UL_MSVC + FindClose(dir->hFind); +#else + closedir ( dir->dirp ) ; +#endif + delete dir ; + } +} + +bool ulFileExists ( const char *fileName ) +{ +#ifdef UL_MSVC + struct _stat buf ; + + if ( _stat ( fileName, &buf ) < 0 ) + return false ; + + return ((S_IFREG & buf.st_mode ) !=0) ; +#else + struct stat buf ; + + if ( stat ( fileName, &buf ) < 0 ) + return false ; + + return ((S_ISREG ( buf.st_mode )) != 0) ; +#endif +} + +char* ulMakePath( char* path, const char* dir, const char* fname ) +{ + if ( fname ) + { + if ( !ulIsAbsolutePathName (fname) && + dir != NULL && dir[0] != '\0' ) + { + strcpy ( path, dir ) ; + strcat ( path, SLASH ) ; + strcat ( path, fname ) ; + } + else + strcpy ( path, fname ) ; + } + else + path [0] = 0 ; + return( path ); +} + + +static int recursiveFindFileInSubDirs ( char * filenameOutput, + const char * tPath, const char * tfnameInput ) +{ + int bFound = FALSE; + char tempString [ 1024 ]; + + ulMakePath ( filenameOutput, tPath, tfnameInput ) ; + + if ( ulFileExists ( filenameOutput ) ) + return TRUE; + + ulDir* dirp = ulOpenDir(tPath); + + if ( dirp != NULL ) + { + ulDirEnt* dp; + + while ( ! bFound && ((dp = ulReadDir(dirp)) != NULL ) ) + { + // I am doing recursive ulOpenDir/ulReadDirs here. + // I know this works under Windo$. + if ( dp->d_isdir && + strcmp( dp->d_name, ".") != 0 && + strcmp( dp->d_name, "..") != 0 ) + { + ulMakePath ( tempString, tPath, dp->d_name) ; + bFound = recursiveFindFileInSubDirs ( filenameOutput, + tempString, tfnameInput ); + } + + } + ulCloseDir ( dirp ) ; + } + + return bFound; +} + + + +void ulFindFile( char *filenameOutput, const char *path, + const char * tfnameInput, const char *sAPOM ) +/* + adds tfnameInput to the path and puts this into the buffer filenameOutput. + sAPOM is used iff path contains "$(APOM)" + + handles special chars in path: + + ";;" is replaced by ";" + "$$" is replaced by "$" + "$(APOM)" is replaced by sAPOM + + If there are ";" in path, the path-variable is interpreted as several + paths "segments", delimited by ";". The first file found by this function + is returned. + + It looks from left to right. + + A segment may end in $(...). ulFindFile will then look in in this + path and recursively in all the sub-paths + + Some examples: + + for loading *.MDl-models, it is very nice to set the texture path to + + $(APOM);$(APOM)/texture;$(APOM)/../texture + + This consits of three segments and tells ulFindFile to look in the + path of the model, in a subpath texture and in a path texture "besides" + the path of the model. Some *.mdl-models are shipped in a directory which + conatins a "texture"-directory, a "Model"-directory and others. In this + case you find the texture in $(APOM)/../texture + + Another example: You have all your textures in a directory-structure + under /roomplan. For example brick is under /roomplan/bricks, wood is + under /roomplan/wood, oak is under /roomplan/wood/oak. Then you should + use the following texture path: + + "/roomplan/$(...)" + + If you dont want all of the bells and whistles, just call: + _ssgMakePath ( filenameOutput, path, tfnameInput ) ; +*/ + +{ + + char temp_texture_path[1024], *s_ptr, *s_ptr1, *current_path; + + strncpy(temp_texture_path, path, 1024); + current_path = temp_texture_path; + s_ptr = temp_texture_path; + + while ( *s_ptr != 0 ) + { + if ( *s_ptr == ';' ) + { + if ( s_ptr [ 1 ] == ';' ) + { + // replace ";;" with ";" + s_ptr1 = ++s_ptr; // both pointers on second ";" + + while ( *s_ptr1 != 0) + { s_ptr1 [ 0 ] = s_ptr1 [ 1 ]; + s_ptr1++; + } + } + else + { + // found a single ';'. This delimits paths + *s_ptr++ = 0; + ulMakePath ( filenameOutput, current_path, tfnameInput ) ; + + if ( ulFileExists ( filenameOutput ) ) + return; // success! + + // this path doesnt hold the texture. Try next one + current_path = s_ptr; + } + } + else if ( *s_ptr == '$' ) + { + if ( s_ptr [ 1 ] == '$' ) + { + // replace "$$" with "$" + s_ptr1 = ++s_ptr; // both pointers on second "$" + + while ( *s_ptr1 != 0) + { + s_ptr1 [ 0 ] = s_ptr1 [ 1 ]; + s_ptr1++; + } + } + else + if ( 0 == strncmp( s_ptr, "$(APOM)", strlen("$(APOM)" ) ) ) + { + // replace "$(APOM)" by sAPOM + char temp_buffer[1024]; + * s_ptr = 0; + s_ptr += strlen ( "$(APOM)" ); + strcpy ( temp_buffer, s_ptr ); + strcat ( current_path, sAPOM); + s_ptr = & current_path [ strlen(current_path) ] ; // onto the 0 + strcat ( current_path, temp_buffer ); + } + else + if ( 0 == strncmp( s_ptr, "$(...)", strlen("$(...)" ) ) ) + { + //strcpy(temp_texture_path_for_recursion, current_path); + + char * nextPath=s_ptr; + nextPath += strlen("$(...)" ); + + while (*nextPath != 0 ) + { + if ( *nextPath == ';' ) + { + if ( nextPath[1] == ';' ) + nextPath++; // so its "add 2 " togehter with the ++ further down + else + { + *nextPath = 0; + break; // breaks the while + } + } + nextPath++; + } + + // This segment of the path ends with a 0 now + // ***** + + char tPath [ 1024 ]; + strcpy ( tPath, current_path ) ; + tPath [ (long) (s_ptr - current_path) ] = 0; + + // So, lets recurse into the sub-dirs: + // Here I just assume that the "$(...)" is the last thing in + // this segment + + if ( recursiveFindFileInSubDirs ( filenameOutput, + tPath, tfnameInput ) ) + return ; // success + + // ***** + // we handled the path-segment current_path containing the $(...) and + // didnt find the file, so go on to the nect segment + + current_path = nextPath; // points to a 0 if this was the last segment + s_ptr = current_path; + } + else + s_ptr++; + } + else // neither ';' nor '$' + s_ptr++; + } + + ulMakePath ( filenameOutput, current_path, tfnameInput ) ; // pfusch? kludge? +} + +///////////////////// string handling /////////////////////////////// + +/* + Strdup is *evil* - use this instead... +*/ + +char *ulStrDup ( const char *s ) +{ + int s_sz = strlen ( s ) + 1 ; + char *ret = new char [ s_sz ] ; + + memcpy ( ret, s, s_sz ) ; + return ret ; +} + + + +// string comparisons that are *not* case sensitive: + +/* + + + I'm sick of half the machines on the planet supporting + strncasecmp and the other half strnicmp - so here is my own + offering. + + **** ATTENTION **** + WK: If you used strnicmp, make sure you realise the completely + changed meaning of the return value - 0 means not equal! +*/ + +int ulStrNEqual ( const char *s1, const char *s2, int len ) +{ + int l1 = (s1==NULL) ? 0 : strlen ( s1 ) ; + int l2 = (s2==NULL) ? 0 : strlen ( s2 ) ; + + if ( l1 > len ) l1 = len ; + + if ( l2 < l1 || l1 < len ) + return FALSE ; + + for ( int i = 0 ; i < l1 ; i++ ) + { + char c1 = s1[i] ; + char c2 = s2[i] ; + + if ( c1 == c2 ) + continue ; + + if ( c1 >= 'a' && c1 <= 'z' ) + c1 = c1 - ('a'-'A') ; + + if ( c2 >= 'a' && c2 <= 'z' ) + c2 = c2 - ('a'-'A') ; + + if ( c1 != c2 ) + return FALSE ; + } + + return TRUE ; +} + + +/* + I'm sick of half the machines on the planet supporting + strcasecmp and the other half stricmp - so here is my own + offering. + + **** ATTENTION **** + WK: If you used stricmp, make sure you realise the completely + changed meaning of the return value - 0 means not equal! +*/ + +int ulStrEqual ( const char *s1, const char *s2 ) +{ + int l1 = (s1==NULL) ? 0 : strlen ( s1 ) ; + int l2 = (s2==NULL) ? 0 : strlen ( s2 ) ; + + if ( l1 != l2 ) return FALSE ; + + for ( int i = 0 ; i < l1 ; i++ ) + { + char c1 = s1[i] ; + char c2 = s2[i] ; + + if ( c1 == c2 ) + continue ; + + if ( c1 >= 'a' && c1 <= 'z' ) + c1 -= ('a'-'A') ; + + if ( c2 >= 'a' && c2 <= 'z' ) + c2 -= ('a'-'A') ; + + if ( c1 != c2 ) + return FALSE ; + } + + return TRUE ; +} + + +int ulIsAbsolutePathName ( const char *pathname ) +{ + /* + Is this an absolute pathname or a relative one? + */ + + if ( (pathname == NULL) || (pathname [0] == 0) ) + return FALSE; + +#ifdef UL_MSVC + + /* + Under WinDOS, it's an absolute path if it starts + with a slash *or* if it starts with a drive letter, + a colon and a slash. + */ + + return ( pathname[0] == SLASH[0] || pathname[0] == '/' ) || + ( + ( + ( pathname[0] >= 'a' && pathname[0] <= 'z' ) || + ( pathname[0] >= 'A' && pathname[0] <= 'Z' ) + ) && + pathname[1] == ':' && + ( pathname[2] == SLASH[0] || pathname[2] == '/' ) + ) ; +#elif defined(UL_MACINTOSH) + return (pathname [0] != ':' && strchr( pathname, ':') != NULL ); +#else + return pathname [0] == SLASH[0] ; +#endif +} + + +char *ulGetCWD ( char *result, int maxlength ) +{ + /* + Return the current working directory into 'result' - which + has enough space for 'maxlength-1' characters and a '\0'. + */ + +#ifdef UL_MSVC + return _getcwd ( result, maxlength ) ; +#else + return getcwd ( result, maxlength ) ; +#endif +} + + diff --git a/src/util/ul.dsp b/src/util/ul.dsp new file mode 100644 index 0000000..d48303f --- /dev/null +++ b/src/util/ul.dsp @@ -0,0 +1,130 @@ +# Microsoft Developer Studio Project File - Name="ul" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=ul - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ul.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ul.mak" CFG="ul - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ul - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "ul - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ul - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy release\*.lib ..\..\*.* copy ul.h ..\..\ul.h copy ulRTTI.h ..\..\ulRTTI.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "ul - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Debug\ul_d.lib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Copy Library to plib directory +PostBuild_Cmds=copy debug\*.lib ..\..\*.* copy ul.h ..\..\ul.h copy ulRTTI.h ..\..\ulRTTI.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "ul - Win32 Release" +# Name "ul - Win32 Debug" +# Begin Source File + +SOURCE=.\ul.cxx +# End Source File +# Begin Source File + +SOURCE=.\ul.h +# End Source File +# Begin Source File + +SOURCE=.\ulClock.cxx +# End Source File +# Begin Source File + +SOURCE=.\ulError.cxx +# End Source File +# Begin Source File + +SOURCE=.\ulLinkedList.cxx +# End Source File +# Begin Source File + +SOURCE=.\ulList.cxx +# End Source File +# Begin Source File + +SOURCE=.\ulLocal.h +# End Source File +# Begin Source File + +SOURCE=.\ulRTTI.cxx +# End Source File +# Begin Source File + +SOURCE=.\ulRTTI.h +# End Source File +# End Target +# End Project diff --git a/src/util/ul.h b/src/util/ul.h new file mode 100644 index 0000000..f01ba01 --- /dev/null +++ b/src/util/ul.h @@ -0,0 +1,851 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ul.h 2131 2008-03-11 02:23:50Z sjbaker $ +*/ + +// +// UL - utility library +// +// Contains: +// - necessary system includes +// - basic types +// - error message routines +// - high performance clocks +// - ulList +// - ulLinkedList +// - more to come (endian support, version ID) +// + +#ifndef _INCLUDED_UL_H_ +#define _INCLUDED_UL_H_ + +#include +#include +#include +#include +#include +#include + +/**********************\ +* * +* Determine OS type * +* * +\**********************/ + +#if defined(__CYGWIN__) + +#define UL_WIN32 1 +#define UL_CYGWIN 1 /* Windoze AND Cygwin. */ + +#elif defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) + +#define UL_WIN32 1 +#define UL_MSVC 1 /* Windoze AND MSVC. */ + +#elif defined(__BEOS__) + +#define UL_BEOS 1 + +#elif defined( macintosh ) + +#define UL_MACINTOSH 1 + +#elif defined(__APPLE__) + +#define UL_MAC_OSX 1 + +#elif defined(__linux__) + +#define UL_LINUX 1 + +#elif defined(__sgi) + +#define UL_IRIX 1 + +#elif defined(_AIX) + +#define UL_AIX 1 + +#elif defined(SOLARIS) || defined(sun) + +#define UL_SOLARIS 1 + +#elif defined(hpux) + +#define UL_HPUX 1 + +#elif (defined(__unix__) || defined(unix)) && !defined(USG) + +#define UL_BSD 1 + +#endif + + +/* + Add specialised includes/defines... +*/ + +#ifdef UL_WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#define UL_WGL 1 +#endif + +#ifdef UL_CYGWIN +#include +#define UL_WGL 1 +#endif + +#ifdef UL_BEOS +#include +#define UL_GLX 1 +#endif + +#ifdef UL_MACINTOSH +#include +#include +#define UL_AGL 1 +#endif + +#ifdef UL_MAC_OSX +#include +#define UL_CGL 1 +#endif + +#if defined(UL_LINUX) || defined(UL_BSD) || defined(UL_IRIX) || defined(UL_SOLARIS) || defined(UL_AIX) +#include +#include +#include +#define UL_GLX 1 +#endif + +#if defined(UL_BSD) +#include +#define UL_GLX 1 +#endif + +#include +#include +#include +#include +#include + +/* PLIB version macros */ + +#define PLIB_MAJOR_VERSION 1 +#define PLIB_MINOR_VERSION 8 +#define PLIB_TINY_VERSION 5 + +#define PLIB_VERSION (PLIB_MAJOR_VERSION*100 \ + +PLIB_MINOR_VERSION*10 \ + +PLIB_TINY_VERSION) + +/* SGI machines seem to suffer from a lack of FLT_EPSILON so... */ + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1.19209290e-07f +#endif + +#ifndef DBL_EPSILON +#define DBL_EPSILON 1.19209290e-07f +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* SUNWspro 4.2 and earlier need bool to be defined */ + +#if defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500 +typedef int bool ; +const int true = 1 ; +const int false = 0 ; +#endif + +/* Let's define our own "min" and "max" so that different operating systems + * don't complain + */ +#define ulMax(a,b) ((a)>(b)?(a):(b)) +#define ulMin(a,b) ((a)<(b)?(a):(b)) + + +/* + Basic Types +*/ + + +/* + High precision clocks. +*/ + +class ulClock +{ + double start ; + double now ; + double delta ; + double last_time ; + double max_delta ; + +#ifdef UL_WIN32 + static double res ; + static int perf_timer ; + void initPerformanceTimer () ; +#endif + + double getRawTime () const ; + +public: + + ulClock () { reset () ; } + + void reset () + { +#ifdef UL_WIN32 + initPerformanceTimer () ; +#endif + start = getRawTime () ; + now = 0.0 ; + max_delta = 0.2 ; + delta = 0.0000001 ; /* Faked so stoopid programs won't div0 */ + last_time = 0.0 ; + } + + void setMaxDelta ( double maxDelta ) { max_delta = maxDelta ; } + double getMaxDelta () const { return max_delta ; } + void update () ; + double getAbsTime () const { return now ; } + double getDeltaTime () const { return delta ; } + double getFrameRate () const { return 1.0 / delta ; } +} ; + + +inline void ulSleep ( int seconds ) +{ + if ( seconds >= 0 ) + { +#ifdef UL_WIN32 + Sleep ( 1000 * seconds ) ; +#else + sleep ( seconds ) ; +#endif + } +} + + +inline void ulMilliSecondSleep ( int milliseconds ) +{ + if ( milliseconds >= 0 ) + { +#ifdef UL_WIN32 + Sleep ( milliseconds ) ; +#else + usleep ( milliseconds * 1000 ) ; +#endif + } +} + + +/* + This is extern C to enable 'configure.in' to + find it with a C-coded probe. +*/ + +extern "C" void ulInit () ; + +/* + Error handler. +*/ + +enum ulSeverity +{ + UL_DEBUG, // Messages that can safely be ignored. + UL_WARNING, // Messages that are important. + UL_FATAL, // Errors that we cannot recover from. + UL_MAX_SEVERITY +} ; + + +typedef void (*ulErrorCallback) ( enum ulSeverity severity, char* msg ) ; + +void ulSetError ( enum ulSeverity severity, const char *fmt, ... ) ; +char* ulGetError ( void ) ; +void ulClearError ( void ) ; +ulErrorCallback ulGetErrorCallback ( void ) ; +void ulSetErrorCallback ( ulErrorCallback cb ) ; + +/* + Directory Reading +*/ + +#define UL_NAME_MAX 256 +typedef struct _ulDir ulDir ; +struct ulDirEnt +{ + char d_name [ UL_NAME_MAX+1 ]; + bool d_isdir ; +} ; + +int ulIsAbsolutePathName ( const char *pathname ) ; +char *ulGetCWD ( char *result, int maxlength ) ; + +ulDir* ulOpenDir ( const char* dirname ) ; +ulDirEnt* ulReadDir ( ulDir* dir ) ; +void ulCloseDir ( ulDir* dir ) ; + +// file handling + +char* ulMakePath( char* path, const char* dir, const char* fname ); + +bool ulFileExists ( const char *fileName ) ; + +void ulFindFile( char *filenameOutput, const char *path, + const char * tfnameInput, const char *sAPOM ) ; + + +/* + Endian handling +*/ + +static const int _ulEndianTest = 1; +#define ulIsLittleEndian (*((char *) &_ulEndianTest ) != 0) +#define ulIsBigEndian (*((char *) &_ulEndianTest ) == 0) + +inline void ulEndianSwap(unsigned int *x) +{ + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + + +inline void ulEndianSwap(unsigned short *x) +{ + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + + +inline void ulEndianSwap(float *x) { ulEndianSwap((unsigned int *)x); } +inline void ulEndianSwap(int *x) { ulEndianSwap((unsigned int *)x); } +inline void ulEndianSwap(short *x) { ulEndianSwap((unsigned short *)x); } + + +inline unsigned short ulEndianLittle16(unsigned short x) { + if (ulIsLittleEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline unsigned int ulEndianLittle32(unsigned int x) { + if (ulIsLittleEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline float ulEndianLittleFloat(float x) { + if (ulIsLittleEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline void ulEndianLittleArray16(unsigned short *x, int length) { + if (ulIsLittleEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline void ulEndianLittleArray32(unsigned int *x, int length) { + if (ulIsLittleEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline void ulEndianLittleArrayFloat(float *x, int length) { + if (ulIsLittleEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline void ulEndianBigArray16(unsigned short *x, int length) { + if (ulIsBigEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline void ulEndianBigArray32(unsigned int *x, int length) { + if (ulIsBigEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline void ulEndianBigArrayFloat(float *x, int length) { + if (ulIsBigEndian) { + return; + } else { + for (int i = 0; i < length; i++) { + ulEndianSwap(x++); + } + } +} + +inline unsigned short ulEndianBig16(unsigned short x) { + if (ulIsBigEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline unsigned int ulEndianBig32(unsigned int x) { + if (ulIsBigEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline float ulEndianBigFloat(float x) { + if (ulIsBigEndian) { + return x; + } else { + ulEndianSwap(&x); + return x; + } +} + +inline unsigned short ulEndianReadLittle16(FILE *f) { + unsigned short x; + fread(&x, 2, 1, f); + return ulEndianLittle16(x); +} + +inline unsigned int ulEndianReadLittle32(FILE *f) { + unsigned int x; + fread(&x, 4, 1, f); + return ulEndianLittle32(x); +} + +inline float ulEndianReadLittleFloat(FILE *f) { + float x; + fread(&x, 4, 1, f); + return ulEndianLittleFloat(x); +} + +inline unsigned short ulEndianReadBig16(FILE *f) { + unsigned short x; + fread(&x, 2, 1, f); + return ulEndianBig16(x); +} + +inline unsigned int ulEndianReadBig32(FILE *f) { + unsigned int x; + fread(&x, 4, 1, f); + return ulEndianBig32(x); +} + +inline float ulEndianReadBigFloat(FILE *f) { + float x; + fread(&x, 4, 1, f); + return ulEndianBigFloat(x); +} + +inline size_t ulEndianWriteLittle16(FILE *f, unsigned short x) { + x = ulEndianLittle16(x); + return fwrite( &x, 2, 1, f ); +} + +inline size_t ulEndianWriteLittle32(FILE *f, unsigned int x) { + x = ulEndianLittle32(x); + return fwrite( &x, 4, 1, f ); +} + +inline size_t ulEndianWriteLittleFloat(FILE *f, float x) { + x = ulEndianLittleFloat(x); + return fwrite( &x, 4, 1, f ); +} + +inline size_t ulEndianWriteBig16(FILE *f, unsigned short x) { + x = ulEndianBig16(x); + return fwrite( &x, 2, 1, f ); +} + +inline size_t ulEndianWriteBig32(FILE *f, unsigned int x) { + x = ulEndianBig32(x); + return fwrite( &x, 4, 1, f ); +} + +inline size_t ulEndianWriteBigFloat(FILE *f, float x) { + x = ulEndianBigFloat(x); + return fwrite( &x, 4, 1, f ); +} + + +/* + Windoze/BEOS code based on contribution from Sean L. Palmer +*/ + + +#ifdef UL_WIN32 + +class ulDynamicLibrary +{ + HMODULE handle ; + +public: + + ulDynamicLibrary ( const char *libname ) + { + char dllname[1024]; + strcpy ( dllname, libname ) ; + strcat ( dllname, ".dll" ) ; + handle = (HMODULE) LoadLibrary ( dllname ) ; + } + + void *getFuncAddress ( const char *funcname ) + { + return (void *) GetProcAddress ( handle, funcname ) ; //lint !e611 + } + + ~ulDynamicLibrary () + { + if ( handle != NULL ) + FreeLibrary ( handle ) ; + } +} ; + +#elif defined (UL_MACINTOSH) + +class ulDynamicLibrary +{ + CFragConnectionID connection; + OSStatus error; + +public: + + ulDynamicLibrary ( const char *libname ) + { + Str63 pstr; + int sz; + + sz = strlen (libname); + + if (sz < 64) { + + pstr[0] = sz; + memcpy (pstr+1, libname, sz); + + error = GetSharedLibrary (pstr, kPowerPCCFragArch, kReferenceCFrag, + &connection, NULL, NULL); + } + else + error = 1; + } + + ~ulDynamicLibrary () + { + if ( ! error ) + CloseConnection (&connection); + } + + void* getFuncAddress ( const char *funcname ) + { + if ( ! error ) { + + char* addr; + Str255 sym; + int sz; + + sz = strlen (funcname); + if (sz < 256) { + + sym[0] = sz; + memcpy (sym+1, funcname, sz); + + error = FindSymbol (connection, sym, &addr, 0); + if ( ! error ) + return addr; + } + } + + return NULL; + } +}; + +#elif defined (UL_MAC_OSX) + + +class ulDynamicLibrary +{ + + public: + + ulDynamicLibrary ( const char *libname ) + { + } + + ~ulDynamicLibrary () + { + } + + void* getFuncAddress ( const char *funcname ) + { + ulSetError ( UL_WARNING, "ulDynamicLibrary unsuppored on Mac OS X" ); + return NULL; + } +}; + +#elif defined (__BEOS__) + +class ulDynamicLibrary +{ + image_id *handle ; + +public: + + ulDynamicLibrary ( const char *libname ) + { + char addonname[1024] ; + strcpy ( addonname, libname ) ; + strcat ( addonname, ".so" ) ; + handle = new image_id ; + + *handle = load_add_on ( addonname ) ; + + if ( *handle == B_ERROR ) + { + delete handle ; + handle = NULL ; + } + } + + void *getFuncAddress ( const char *funcname ) + { + void *sym = NULL ; + + if ( handle && + get_image_symbol ( handle, "funcname", + B_SYMBOL_TYPE_TEXT, &sym ) == B_NO_ERROR ) + return sym ; + + return NULL ; + } + + ~ulDynamicLibrary () + { + if ( handle != NULL ) + unload_add_on ( handle ) ; + + delete handle ; + } +} ; + +# else + +/* + Linux/UNIX +*/ + +class ulDynamicLibrary +{ + void *handle ; + +public: + + ulDynamicLibrary ( const char *libname ) + { + char dsoname [ 1024 ] ; + strcpy ( dsoname, libname ) ; + strcat ( dsoname, ".so" ) ; + handle = (void *) dlopen ( dsoname, RTLD_NOW | RTLD_GLOBAL ) ; + + if ( handle == NULL ) + ulSetError ( UL_WARNING, "ulDynamicLibrary: %s\n", dlerror() ) ; + } + + void *getFuncAddress ( const char *funcname ) + { + return (handle==NULL) ? NULL : dlsym ( handle, funcname ) ; + } + + ~ulDynamicLibrary () + { + if ( handle != NULL ) + dlclose ( handle ) ; + } +} ; + +#endif + + +class ulList +{ +protected: + unsigned int total ; /* The total number of entities in the list */ + unsigned int limit ; /* The current limit on number of entities */ + unsigned int next ; /* The next entity when we are doing getNext ops */ + + void **entity_list ; /* The list. */ + + void sizeChk (void) ; + +public: + + ulList ( int init_max = 1 ) ; + virtual ~ulList (void) ; + + void *getEntity ( unsigned int n ) + { + next = n + 1 ; + return ( n >= total ) ? (void *) NULL : entity_list [ n ] ; + } + + virtual void addEntity ( void *entity ) ; + virtual void addEntityBefore ( int n, void *entity ) ; + virtual void removeEntity ( unsigned int n ) ; + + void removeAllEntities () ; + + void removeEntity ( void *entity ) + { + removeEntity ( searchForEntity ( entity ) ) ; + } + + virtual void replaceEntity ( unsigned int n, void *new_entity ) ; + + void replaceEntity ( void *old_entity, void *new_entity ) + { + replaceEntity ( searchForEntity ( old_entity ), new_entity ) ; + } + + void *getNextEntity (void) { return getEntity ( next ) ; } + + int getNumEntities (void) const { return total ; } + int searchForEntity ( void *entity ) const ; +} ; + + +typedef bool (*ulIterateFunc)( const void *data, void *user_data ) ; +typedef int (*ulCompareFunc)( const void *data1, const void *data2 ) ; + +/* + Linked list. +*/ + +class ulListNode ; + +class ulLinkedList +{ +protected: + + ulListNode *head ; + ulListNode *tail ; + + int nnodes ; + bool sorted ; + + void unlinkNode ( ulListNode *prev, ulListNode *node ) ; + + bool isValidPosition ( int pos ) const + { + if ( ( pos < 0 ) || ( pos >= nnodes ) ) + { + ulSetError ( UL_WARNING, "ulLinkedList: Invalid 'pos' %u", pos ) ; + return false ; + } + return true ; + } + +public: + + ulLinkedList () + { + head = tail = NULL ; + nnodes = 0 ; + sorted = true ; + } + + ~ulLinkedList () { empty () ; } + + int getNumNodes ( void ) const { return nnodes ; } + bool isSorted ( void ) const { return sorted ; } + + int getNodePosition ( void *data ) const ; + + void insertNode ( void *data, int pos ) ; + void prependNode ( void *data ) { insertNode ( data, 0 ) ; } + void appendNode ( void *data ) ; + + int insertSorted ( void *data, ulCompareFunc comparefn ) ; + + void removeNode ( void *data ) ; + void * removeNode ( int pos ) ; + + void * getNodeData ( int pos ) const ; + + void * forEach ( ulIterateFunc fn, void *user_data = NULL ) const ; + + void empty ( ulIterateFunc destroyfn = NULL, void *user_data = NULL ) ; +} ; + + +extern char *ulStrDup ( const char *s ) ; +extern int ulStrNEqual ( const char *s1, const char *s2, int len ); +extern int ulStrEqual ( const char *s1, const char *s2 ); + +//lint -restore + +#endif + diff --git a/src/util/ulClock.cxx b/src/util/ulClock.cxx new file mode 100644 index 0000000..f218385 --- /dev/null +++ b/src/util/ulClock.cxx @@ -0,0 +1,117 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ulClock.cxx 1735 2002-12-01 18:21:48Z sjbaker $ +*/ + + +#include +#include +#include "ul.h" + +#ifdef UL_CYGWIN + typedef long long _int64; + #define LARGEINTEGER _int64 +#endif + +#ifndef UL_WIN32 +# include +#endif + +#include + +#ifdef UL_WIN32 + +double ulClock::res ; +int ulClock::perf_timer = -1; + +void ulClock::initPerformanceTimer () +{ + if ( perf_timer == -1 ) + { + /* Use Performance Timer if it's available, mmtimer if not. */ + + __int64 frequency ; + + perf_timer = QueryPerformanceFrequency ( (LARGE_INTEGER *) & frequency ) ; + + if ( perf_timer ) + { + res = 1.0 / (double) frequency ; + perf_timer = 1 ; + } + } +} +#endif + +double ulClock::getRawTime () const +{ +#ifdef UL_WIN32 + + /* Use Performance Timer if it's available, mmtimer if not. */ + + if ( perf_timer ) + { + __int64 t ; + + QueryPerformanceCounter ( (LARGE_INTEGER *) &t ) ; + + return res * (double) t ; + } + + return (double) timeGetTime() * 0.001 ; + +#else + timeval tv ; + + gettimeofday ( & tv, NULL ) ; + + return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0 ; +#endif +} + + +void ulClock::update () +{ + now = getRawTime() - start ; + + delta = now - last_time ; + + /* + KLUDGE: If the frame rate drops below ~5Hz, then + control will be very difficult. It's + actually easier to give up and slow + down the action. max_delta defaults to + 200ms for that reason. + + KLUDGE: If update is called very rapidly, then + delta can be zero which causes some + programs to div0. So we'll clamp to + a millionth of a second. + */ + + if ( delta > max_delta ) delta = max_delta ; + if ( delta <= 0.0 ) delta = 0.0000001 ; + + last_time = now ; +} + + + diff --git a/src/util/ulError.cxx b/src/util/ulError.cxx new file mode 100644 index 0000000..c30c737 --- /dev/null +++ b/src/util/ulError.cxx @@ -0,0 +1,90 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ulError.cxx 1967 2004-10-02 12:20:43Z wolfram_kuss $ +*/ + + +#include "ul.h" + +static char _ulErrorBuffer [ 1024 ] = { '\0' } ; +static ulErrorCallback _ulErrorCB = 0 ; + +static const char* _ulSeverityText [ UL_MAX_SEVERITY ] = +{ + "DEBUG", + "WARNING", + "FATAL", +}; + + +void ulSetError ( enum ulSeverity severity, const char *fmt, ... ) +{ + va_list argp; + va_start ( argp, fmt ) ; + vsprintf ( _ulErrorBuffer, fmt, argp ) ; + va_end ( argp ) ; + + if ( _ulErrorCB ) + { + (*_ulErrorCB)( severity, _ulErrorBuffer ) ; + } + else + { + fprintf ( stderr, "%s: %s\n", + _ulSeverityText[ severity ], _ulErrorBuffer ) ; + if ( severity == UL_FATAL ) + { +#ifdef WIN32 + // A Windows user that does not start the program from the command line + // will not see output to stderr + ::MessageBox(0, _ulErrorBuffer, "fatal error!:", 0); +#endif + exit (1) ; + } + } +} + + +char* ulGetError ( void ) +{ + return _ulErrorBuffer ; +} + + +void ulClearError ( void ) +{ + _ulErrorBuffer [0] = 0 ; +} + + +ulErrorCallback ulGetErrorCallback ( void ) +{ + return _ulErrorCB ; +} + + +void ulSetErrorCallback ( ulErrorCallback cb ) +{ + _ulErrorCB = cb ; +} + + + diff --git a/src/util/ulLinkedList.cxx b/src/util/ulLinkedList.cxx new file mode 100644 index 0000000..96c246b --- /dev/null +++ b/src/util/ulLinkedList.cxx @@ -0,0 +1,272 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ulLinkedList.cxx 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + + +#include "ulLocal.h" + +void ulLinkedList::unlinkNode ( ulListNode *prev, ulListNode *node ) +{ + /* Is this the first node ? */ + if ( prev == NULL ) + head = node -> getNext () ; + else + prev -> setNext ( node -> getNext () ) ; + + /* Is this the last node ? */ + if ( node -> getNext () == NULL ) + tail = prev ; +} + + +int ulLinkedList::getNodePosition ( void *data ) const +{ + ulListNode *curr = head ; + int pos = 0 ; + + while ( curr != NULL ) + { + if ( curr -> getData () == data ) + return pos ; + + pos++ ; + curr = curr -> getNext () ; + } + + return -1 ; +} + + +void ulLinkedList::appendNode ( void *data ) +{ + ulListNode *new_node = new ulListNode ( data, NULL ) ; + + if ( head == NULL ) + head = new_node ; + else + tail -> setNext ( new_node ) ; + + tail = new_node ; + + if ( ++nnodes > 1 ) + sorted = false ; +} + +void ulLinkedList::insertNode ( void *data, int pos ) +{ + if ( pos == 0 ) + { + head = new ulListNode ( data, head ) ; + + if ( tail == NULL ) + tail = head ; + } + else + { + if ( ! isValidPosition ( pos ) ) + return ; + else + { + ulListNode *prev = head ; + + while ( --pos > 0 ) + prev = prev -> getNext () ; + + prev -> setNext ( new ulListNode ( data, prev -> getNext () ) ) ; + } + } + + if ( ++nnodes > 1 ) + sorted = false ; +} + + +int ulLinkedList::insertSorted ( void *data, ulCompareFunc comparefn ) +{ + if ( comparefn != NULL ) + { + if ( sorted ) + { + int pos = 0 ; + + if ( head == NULL ) + head = tail = new ulListNode ( data, NULL ) ; + else + { + ulListNode *curr = head, *prev = NULL ; + + while ( (*comparefn)( curr -> getData (), data ) < 0 ) + { + prev = curr ; + curr = curr -> getNext () ; + + pos++ ; + + if ( curr == NULL ) + { + tail = new ulListNode ( data, curr ) ; + prev -> setNext ( tail ) ; + + nnodes++ ; + return pos ; + } + } + + if ( prev == NULL ) + head = new ulListNode ( data, head ) ; + else + prev -> setNext ( new ulListNode ( data, curr ) ) ; + } + + nnodes++ ; + return pos ; + } + else + ulSetError ( UL_WARNING, + "ulLinkedList::insertSorted: This is not a sorted list !" ) ; + } + + return -1 ; +} + + +void ulLinkedList::removeNode ( void *data ) +{ + ulListNode *curr = head, *prev = NULL ; + + while ( curr != NULL ) + { + if ( curr -> getData () == data ) + { + unlinkNode ( prev, curr ) ; + + delete curr ; + + if ( --nnodes <= 0 ) + sorted = true ; + + return ; + } + + prev = curr ; + curr = curr -> getNext () ; + } + + ulSetError ( UL_WARNING, "ulLinkedList::removeNode: No such node" ) ; +} + +void * ulLinkedList::removeNode ( int pos ) +{ + if ( ! isValidPosition ( pos ) ) + return NULL ; + + ulListNode *curr = head, *prev = NULL ; + + while ( pos-- > 0 ) + { + prev = curr ; + curr = curr -> getNext () ; + } + + unlinkNode ( prev, curr ) ; + + void *datap = curr -> getData () ; + + delete curr ; + + if ( --nnodes <= 1 ) + sorted = true ; + + return datap ; +} + + +void * ulLinkedList::getNodeData ( int pos ) const +{ + if ( ! isValidPosition ( pos ) ) + return NULL ; + + ulListNode *node ; + + if ( pos == nnodes - 1 ) + node = tail ; + else + { + node = head ; + + while ( pos-- > 0 ) + node = node -> getNext () ; + } + + return node -> getData () ; +} + + +void * ulLinkedList::forEach ( ulIterateFunc fn, void *user_data ) const +{ + if ( fn != NULL ) + { + ulListNode *curr ; + + for ( curr = head ; curr != NULL ; curr = curr -> getNext () ) + { + if ( (*fn)( curr -> getData (), user_data ) == false ) + return curr -> getData () ; + } + } + + return NULL ; +} + + +void ulLinkedList::empty ( ulIterateFunc destroyfn, void *user_data ) +{ + ulListNode *curr = head ; + + if ( destroyfn != NULL ) + { + while ( curr != NULL ) + { + ulListNode *next = curr -> getNext () ; + + (*destroyfn) ( curr -> getData (), user_data ) ; + + delete curr ; + curr = next ; + } + } + else + { + while ( curr != NULL ) + { + ulListNode *next = curr -> getNext () ; + + delete curr ; + curr = next ; + } + } + + head = tail = NULL ; + nnodes = 0 ; + sorted = true ; +} + diff --git a/src/util/ulList.cxx b/src/util/ulList.cxx new file mode 100644 index 0000000..c6b6abf --- /dev/null +++ b/src/util/ulList.cxx @@ -0,0 +1,109 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ulList.cxx 1732 2002-11-30 00:41:50Z sjbaker $ +*/ + + +#include "ul.h" + + +ulList::ulList ( int init ) +{ + total = 0 ; + next = 0 ; + entity_list = new void * [ limit = (init <= 0) ? 1 : init ] ; +} + + +ulList::~ulList (void) +{ + removeAllEntities () ; + delete [] entity_list ; +} + + +void ulList::addEntity ( void *entity ) +{ + sizeChk () ; + entity_list [ total++ ] = entity ; +} + + +void ulList::addEntityBefore ( int i, void *entity ) +{ + sizeChk () ; + memmove ( &entity_list[i+1], &entity_list[i], sizeof(void *) * (total-i) ) ; + entity_list [ i ] = entity ; + total++ ; +} + + +void ulList::sizeChk (void) +{ + /* Room for one more Entity? */ + + if ( total >= limit ) + { + limit += limit ; + void **nlist = new void * [ limit ] ; + memmove ( nlist, entity_list, sizeof(void *) * total ) ; + delete [] entity_list ; + entity_list = nlist ; + } +} + + +int ulList::searchForEntity ( void *entity ) const +{ + for ( unsigned int i = 0 ; i < total ; i++ ) + if ( entity_list [ i ] == entity ) + return (int) i ; + + return -1 ; +} + +void ulList::removeAllEntities () +{ + while ( total > 0 ) + removeEntity ( (unsigned int) 0 ) ; +} + +void ulList::removeEntity ( unsigned int n ) +{ + memmove ( &(entity_list[n]), &(entity_list[n+1]), + sizeof(void *) * (total-n-1) ) ; + total-- ; + + if ( next >= n ) + next-- ; +} + + + +void ulList::replaceEntity ( unsigned int n, void *new_entity ) +{ + if ( n >= 0 ) + entity_list [ n ] = new_entity; + else + addEntity ( new_entity ) ; +} + + diff --git a/src/util/ulLocal.h b/src/util/ulLocal.h new file mode 100644 index 0000000..e7e7cea --- /dev/null +++ b/src/util/ulLocal.h @@ -0,0 +1,52 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: ulLocal.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +#ifndef _UL_LOCAL_H_ +#define _UL_LOCAL_H_ 1 + +#include "ul.h" + +class ulListNode +{ +protected: + + ulListNode *next ; + void *data ; + +public: + + ulListNode ( void *dt, ulListNode *next_node ) + { + data = dt ; + next = next_node ; + } + + void * getData ( void ) const { return data ; } + void setData ( void *d ) { data = d ; } + + ulListNode * getNext ( void ) const { return next ; } + void setNext ( ulListNode *n ) { next = n ; } +} ; + +#endif + diff --git a/src/util/ulRTTI.cxx b/src/util/ulRTTI.cxx new file mode 100644 index 0000000..8d82626 --- /dev/null +++ b/src/util/ulRTTI.cxx @@ -0,0 +1,109 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + +#include "ulRTTI.h" + +/* Originally written by: Alexandru C. Telea */ + + +static const ulRTTITypeinfo *RTTI_base_null_type [] = { 0 } ; + +const ulRTTITypeinfo* ulRTTIdyntypeid::a[] = { 0 } ; + +const ulRTTITypeinfo ulRTTITypeinfo::null_type ( "NULL", RTTI_base_null_type, + 0, 0 ) ; + + +ulRTTITypeinfo::ulRTTITypeinfo ( const char *name, const ulRTTITypeinfo *bb[], + void* (*f1)(int,void*),void* (*f2)() ) +{ + /* Create default ulRTTITypeinfo */ + n = ulStrDup ( name ) ; + + b = bb ; /* ns = 0 ; subtypes = 0 ; */ + + cast = f1 ; /* Attach casting func */ + new_obj = f2 ; /* Attach creation func */ + + for ( int i = 0 ; b[i] ; i++ ) + /* Add this as subtype to all its basetypes */ + /* REMARK: Harmless const castaway */ + ((ulRTTITypeinfo**)b)[i]->add_subtype ( this ) ; +} + +ulRTTITypeinfo::~ulRTTITypeinfo () +{ + delete [] n ; + for ( int i = 0 ; b[i] ; i++ ) + /* Del this subtype from all its basetypes */ + /* REMARK: Harmless const castaway */ + ((ulRTTITypeinfo**)b)[i]->del_subtype ( this ) ; +} + +void ulRTTITypeinfo::add_subtype ( const ulRTTITypeinfo *t ) +/* + Adds t as last ulRTTITypeinfo in the 'subtypes' list. For this, the list is + realloc'd with one extra entry. +*/ +{ + const ulRTTITypeinfo **ptr = new const ulRTTITypeinfo*[ns+1] ; + int i ; for ( i = 0 ; i < ns ; i++ ) ptr[i] = subtypes[i] ; + ptr[i] = t ; + ns++ ; + delete[] subtypes ; + subtypes = ptr ; +} + +void ulRTTITypeinfo::del_subtype ( const ulRTTITypeinfo* t ) +/* Searches for t in the subtypes list of this and removes it, if found. */ +{ + int i ; for ( i = 0 ; i < ns && subtypes[i] != t ; i++ ) ; + if ( i < ns ) + for(; i < ns-1 ; i++ ) subtypes[i] = subtypes[i+1] ; +} + +void * ulRTTITypeinfo::create ( const ulRTTITypeinfo* bt, const char *c ) const +/* + Tries to create an obj of type-name given by char*. Searches for this type + in the type-DAG rooted by this, creates it and returns it as cast to 'bt', + where bt is either this or a direct base of this. +*/ +{ + void *p = NULL ; int i ; + + if ( !strcmp ( c, n ) ) /* Want to create an obj of this type ? */ + /* Yes, do it if this type is instantiable. */ + p = (new_obj) ? new_obj () : 0 ; + else /* No, try with subclasses... */ + for ( i = 0 ; i < ns && + !( ( p = subtypes[i]->create ( this, c ) ) ) ; i++ ) ; + /* Succeeded creating on ith subclass branch ? */ + if ( !p ) return 0 ; /* Couldn't create it in any way, abort. */ + if ( bt == this ) + i = -1 ; /* Must cast to this's own type (i==-1) */ + else + /* Search to which base of this we should cast */ + for ( i = 0 ; b[i] && b[i] != bt ; i++ ) ; + + /* Found: cast to ith base of this */ + return cast(i,p) ; /* Cast to ith base of to this, return as void* */ +} + diff --git a/src/util/ulRTTI.h b/src/util/ulRTTI.h new file mode 100644 index 0000000..e8c5216 --- /dev/null +++ b/src/util/ulRTTI.h @@ -0,0 +1,420 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net */ + + +/* + Originally written by: Alexandru C. Telea +*/ + +/* + This file provides support for RTTI and generalized (virtual-base to derived + and separate hierarchy branches) casting. There is also support for RT obj + creation from type names. + + In order to enable these features for a class, two things should be done: + + 1) insert the text UL_TYPE_DATA (without ';') in the class-decl. + 2) in the .C file where the class's implementation resides, insert the + following (without';'): + + UL_RTTI_DEF(classname) + if the class has no bases with RTTI + + UL_RTTI_DEFn(classname,b1,...bn) + if the class has bases b1,...bn with RTTI + + Use UL_RTTI_DEF_INST instead of UL_RTTI_DEF if you want to enable RT + obj creation for classname. You should provide then a public default + ctor. + + RTTI is used via a class called ulRTTItypeid. A typeid describes a type of a + class. [..] They should provide all necessary support for any kind of + RTTI/casting [..]. + + [..] + + REMARK: There are two classes related to RTTI: ulRTTItypeid and + ======= ulRTTITypeinfo. A ulRTTItypeid is, as it says, an 'id for a + type'. It actually wraps a ulRTTITypeinfo*, where a + ulRTTITypeinfo contains the actual encoding of a class type. + You can freely create/copy/destroy/manipulate ulRTTItypeid's, + but you should NEVER deal directly with ulRTTITypeinfo. A + ulRTTITypeinfo should actually be created ONLY by the + UL_TYPE_DATA macros, as part of a class definition, since the + ulRTTITypeinfo encodes a type info for an EXISTING class [..]. + All type-related stuff should be therefore handled via + ulRTTItypeid's. +*/ + +#ifndef _UL_RTTI_H_ +#define _UL_RTTI_H_ + +#include +#include "ul.h" + + +class ulRTTITypeinfo +/* Implementation of type-related info */ +{ +private: + + char *n ; /* type name */ + + /* + base types (NULL-ended array of ulRTTITypeinfo's for this's direct bases) + */ + const ulRTTITypeinfo** b ; + + int ns ; /* #subtypes of this type */ + const ulRTTITypeinfo **subtypes ; /* types derived from this type */ + + /* convenience type info for a 'null' type */ + static const ulRTTITypeinfo null_type ; + + void* (*new_obj)() ; /* func to create a new obj of this type */ + void* (*cast)(int,void*) ; /* + func to cast an obj of this type to ith + baseclass of it or to itself + */ + + /* adds a subtype to this's subtypes[] */ + void add_subtype ( const ulRTTITypeinfo * ) ; + + /* dels a subtype from this's subtypes[] */ + void del_subtype ( const ulRTTITypeinfo* ) ; + + friend class ulRTTItypeid ; /* for null_type */ + +public: + + ulRTTITypeinfo ( const char* name, const ulRTTITypeinfo* bb[], + void* (*)(int,void*),void* (*)() ) ; + ~ulRTTITypeinfo () ; + + /* Returns name of this ulRTTITypeinfo */ + const char* getname () const { return n ; } + + /* Compares 2 ulRTTITypeinfo objs */ + bool same ( const ulRTTITypeinfo *p ) const + { + /* + First, try to see if it's the same 'physical' ulRTTITypeinfo (which + should be the case, since we create them per-class and not per-obj). + */ + return ( this == p ) || !strcmp ( n, p->n ) ; + } + + /* true if the arg can be cast to this, else false */ + bool can_cast ( const ulRTTITypeinfo *p ) const + { + return same ( p ) || p->has_base ( this ) ; + } + + /* true if this has the arg as some base, else false */ + bool has_base ( const ulRTTITypeinfo *p ) const + { + for ( int i = 0 ; b[i] != NULL ; i++ ) /* for all bases of this... */ + /* match found, return 1 or no match, search deeper */ + if ( p->same ( b[i] ) || b[i]->has_base ( p ) ) return true ; + return false ; /* no match at all, return false */ + } + + /* get i-th subclass of this, if any, else NULL */ + const ulRTTITypeinfo * subclass ( int i = 0 ) const + { + return ( i >= 0 && i < ns ) ? subtypes[i] : NULL ; + } + + int num_subclasses () const { return ns ; } /* get # subclasses of this */ + + /* + search for a subclass named char*, create obj of it and return it cast to + the ulRTTITypeinfo* type, which is either this or a direct base of this. + */ + void * create ( const ulRTTITypeinfo *, const char * ) const ; + + /* Returns true if this type has a default ctor, else false */ + bool can_create () const { return new_obj != NULL ; } +} ; + + +class ulRTTItypeid +/* Main class for RTTI interface */ +{ +protected: + + /* ulRTTItypeid implementation (the only data-member) */ + const ulRTTITypeinfo* id ; + +public: + + /* Not for application use ! */ + const ulRTTITypeinfo* get_info () const { return id ; } + + ulRTTItypeid ( const ulRTTITypeinfo* p ) : id ( p ) { } + ulRTTItypeid () : id ( &ulRTTITypeinfo::null_type ) { } + + /* Compares 2 ulRTTItypeid objs */ + bool isSame ( ulRTTItypeid i ) const { return id->same ( i.id ) ; } + + /* true if the arg can be cast to this, else false */ + bool canCast ( ulRTTItypeid i ) const { return id->can_cast ( i.id ) ; } + + const char * getName () const { return id->getname () ; } + + /* Return # subclasses of this */ + int getNumSubclasses () const { return id->num_subclasses () ; } + + /* Return ith subclass of this */ + ulRTTItypeid getSubclass ( int i ) const { return id->subclass ( i ) ; } + + /* Return # baseclasses of this */ + int getNumBaseclasses () const + { + int i ; for ( i = 0 ; id->b[i] != NULL ; i++ ) ; + return i ; + } + + /* Return ith baseclass of this */ + ulRTTItypeid getBaseclass ( int i ) const { return id->b[i] ; } + + /* + Tries to create an instance of a subclass of this having of type given + by the ulRTTItypeid arg. If ok, it returns it casted to the class-type of + this and then to void* + */ + void * create ( ulRTTItypeid t ) const + { + return id->create ( id, t.getName () ) ; + } + + /* Returns true if this type is instantiable, else false */ + bool canCreate () const { return id->can_create () ; } +} ; + + +class ulRTTIdyntypeid : public ulRTTItypeid +/* + Class for dynamic type creation from user strings. Useful for creating + typeids at RT for comparison purposes. +*/ +{ +private: + + static const ulRTTITypeinfo *a[] ; + +public: + + ulRTTIdyntypeid ( const char *c ) : + /* create a dummy ulRTTITypeinfo */ + ulRTTItypeid ( new ulRTTITypeinfo ( c, a, NULL, NULL ) ) { } + + ~ulRTTIdyntypeid () { delete id ; /* delete the dummy ulRTTITypeinfo */ } +} ; + + + +/* + Macros +*/ + +/* + 'ulRTTItypeid' + UL_STATIC_TYPE_INFO(T) T=RTTI-class name. + Returns a ulRTTItypeid with T's type. If T + hasn't RTTI, a compile-time error occurs. +*/ + +#define UL_STATIC_TYPE_INFO(T) T::RTTI_sinfo() + + +/* + 'T*' + UL_PTR_CAST(T,p) T=RTTI-class, p=RTTI-class ptr. + Returns p cast to the type T as a T*, if cast is + possible, else returns NULL. If *p or T have no RTTI, + a compile-time error occurs. Note that p can point to + virtual base classes. Casting between separat branches + of a class hierarchy is also supported, as long as all + classes have RTTI. Therefore UL_PTR_CAST is a fully + general and safe operator. If p==NULL, the operator + returns NULL. +*/ + +#define UL_PTR_CAST(T,p) ((p != NULL)? (T*)((p)->RTTI_cast(UL_STATIC_TYPE_INFO(T))) : NULL) + + +/* 'T*' + UL_TYPE_NEW(T,t) T=RTTI-class, t=ulRTTItypeid + Returns a new object of type t cast to the type T as + a T*. t must represent a type identical to or derived + from T. If t is not a type derived from T or not an + instantiable type having a default constructor, NULL is + returned. */ + +#define UL_TYPE_NEW(T,t) ((T*)t.create(T)) + + + +/* + Definition of TYPE_DATA for a RTTI-class: introduces one static + ulRTTITypeinfo data-member and a couple of virtuals. +*/ + +#define UL_TYPE_DATA \ + protected: \ + static const ulRTTITypeinfo RTTI_obj; \ + static void* RTTI_scast(int,void*); \ + static void* RTTI_new(); \ + virtual ulRTTItypeid RTTI_vinfo() const { return &RTTI_obj; }\ + public: \ + static ulRTTItypeid RTTI_sinfo() { return &RTTI_obj; }\ + virtual void* RTTI_cast(ulRTTItypeid); + + + +/* + Definition of auxiliary data-structs supporting RTTI for a class: defines + the static ulRTTITypeinfo object of that class and its associated virtuals. +*/ + +/* Auxiliary definition of the construction method: */ +#define UL_RTTI_NEW(cls) void* cls::RTTI_new() { return new cls; } \ + const ulRTTITypeinfo cls::RTTI_obj(#cls,RTTI_base_ ## cls,cls::RTTI_scast,cls::RTTI_new); + +#define UL_RTTI_NO_NEW(cls) const ulRTTITypeinfo cls::RTTI_obj(#cls,RTTI_base_ ## cls,cls::RTTI_scast,NULL); + + + +/* + Top-level macros: +*/ + +#define UL_RTTI_DEF_BASE(cls) \ + static const ulRTTITypeinfo* RTTI_base_ ## cls [] = { NULL }; \ + void* cls::RTTI_cast(ulRTTItypeid t) \ + { \ + if (t.isSame(&RTTI_obj)) return this; \ + return NULL; \ + } \ + void* cls::RTTI_scast(int i,void* p) \ + { cls* ptr = (cls*)p; return ptr; } + + +#define UL_RTTI_DEF1_BASE(cls,b1) \ + static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ + { UL_STATIC_TYPE_INFO(b1).get_info(), NULL }; \ + void* cls::RTTI_cast(ulRTTItypeid t) \ + { \ + if (t.isSame(&RTTI_obj)) return this; \ + void* ptr; \ + if ((ptr=b1::RTTI_cast(t))) return ptr; \ + return NULL; \ + } \ + void* cls::RTTI_scast(int i,void* p) \ + { cls* ptr = (cls*)p; \ + switch(i) \ + { case 0: return (b1*)ptr; } \ + return ptr; \ + } + + +#define UL_RTTI_DEF2_BASE(cls,b1,b2) \ + static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ + { UL_STATIC_TYPE_INFO(b1).get_info(), \ + UL_STATIC_TYPE_INFO(b2).get_info(), NULL }; \ + void* cls::RTTI_cast(ulRTTItypeid t) \ + { \ + if (t.isSame(&RTTI_obj)) return this; \ + void* ptr; \ + if ((ptr=b1::RTTI_cast(t))) return ptr; \ + if ((ptr=b2::RTTI_cast(t))) return ptr; \ + return NULL; \ + } \ + void* cls::RTTI_scast(int i,void* p) \ + { cls* ptr = (cls*)p; \ + switch(i) \ + { case 0: return (b1*)ptr; \ + case 1: return (b2*)ptr; \ + } \ + return ptr; \ + } + +#define UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ + static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ + { UL_STATIC_TYPE_INFO(b1).get_info(), \ + UL_STATIC_TYPE_INFO(b2).get_info(), \ + UL_STATIC_TYPE_INFO(b3).get_info(), NULL }; \ + void* cls::RTTI_cast(ulRTTItypeid t) \ + { \ + if (t.isSame(&RTTI_obj)) return this; \ + void* ptr; \ + if ((ptr=b1::RTTI_cast(t))) return ptr; \ + if ((ptr=b2::RTTI_cast(t))) return ptr; \ + if ((ptr=b3::RTTI_cast(t))) return ptr; \ + return NULL; \ + } \ + void* cls::RTTI_scast(int i,void* p) \ + { cls* ptr = (cls*)p; \ + switch(i) \ + { case 0: return (b1*)ptr; \ + case 1: return (b2*)ptr; \ + case 2: return (b3*)ptr; \ + } \ + return ptr; \ + } + + + +#define UL_RTTI_DEF_INST(cls) \ + UL_RTTI_DEF_BASE(cls) \ + UL_RTTI_NEW(cls) + +#define UL_RTTI_DEF(cls) \ + UL_RTTI_DEF_BASE(cls) \ + UL_RTTI_NO_NEW(cls) + +#define UL_RTTI_DEF1_INST(cls,b1) \ + UL_RTTI_DEF1_BASE(cls,b1) \ + UL_RTTI_NEW(cls) + +#define UL_RTTI_DEF1(cls,b1) \ + UL_RTTI_DEF1_BASE(cls,b1) \ + UL_RTTI_NO_NEW(cls) + +#define UL_RTTI_DEF2_INST(cls,b1,b2) \ + UL_RTTI_DEF2_BASE(cls,b1,b2) \ + UL_RTTI_NEW(cls) + +#define UL_RTTI_DEF2(cls,b1,b2) \ + UL_RTTI_DEF2_BASE(cls,b1,b2) \ + UL_RTTI_NO_NEW(cls) + +#define UL_RTTI_DEF3_INST(cls,b1,b2,b3) \ + UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ + UL_RTTI_NEW(cls) + +#define UL_RTTI_DEF3(cls,b1,b2,b3) \ + UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ + UL_RTTI_NO_NEW(cls) + + +#endif + diff --git a/tools/AUTHORS b/tools/AUTHORS new file mode 100644 index 0000000..72ce79c --- /dev/null +++ b/tools/AUTHORS @@ -0,0 +1,62 @@ +PLIB was originally written by Steve Baker as a solo project - but +has subsequently been greatly enhanced and expanded by a 'cast of +thousands'. + +Most important of all - the 'official' PLIB developers: + + Alex Perry + Curtis Olson + Ben Woodhead + Dave McClurg + Gerard Decatrel + Gil Carter + Giancarlo Niccolai + James Jones + John Fay ("Mr. PUI") + Juergen Marquardt + Mark Vallevand + Marten Stromberg + Nathan Matias + Norman Vine + Per Liedman + Sam Stickland + Sebastian Ude + Steve Baker <--- Hmmm - must sort this list by SECOND name! + Will Lachance + Wolfram Kuss + +Apologies to anyone I've left out - please let me know +who you are and I'll add you in. + + Christopher St.John + Christian Mayer + Darrell Walisser + Dave Heil + Erik Hofman + Kevin Thompson + Karl Kremer + Negative0 <---- Who? Some kind of 1's complement arithmetic fanatic? + Mark Danks + Michael Kurth + Sean Palmer + Simon Britnell + Themie Gouthas + Tony Knienieder + Trent Gamblin + +Special mention must go to: + + Daryll Strauss (GLIDE port to Linux) + David Bucciarelli (Mesa -> GLIDE interface) + Brian Paul (Mesa) + Curtis Olson (FlightGear guru and all-round "Mr Nice Guy", + who nagged me into writing most of this stuff!) + Vasily Lewis (Tireless maintainer and sysadmin at Woodsoup + where PLIB was first hosted) + Toru Egashira (Who wrote a program called 'NSPmod' which was + the original basis of the SL 'MOD' player - + although the original code has been almost + entirely rewritten in the process of merging + it with SL. + Everyone at OPENGL-GAMEDEV-L and 3Dfx.glide.linux + diff --git a/tools/COPYING b/tools/COPYING new file mode 100644 index 0000000..eeb586b --- /dev/null +++ b/tools/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/tools/ChangeLog b/tools/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/tools/INSTALL b/tools/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/tools/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..5010b14 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = src + +EXTRA_DIST = autogen.sh README tools.dsw + +dist-hook: + tar cf - doc \ + | ( cd $(distdir) ; tar xf - ) + diff --git a/tools/NEWS b/tools/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/tools/README b/tools/README new file mode 100644 index 0000000..e69de29 diff --git a/tools/autogen.sh b/tools/autogen.sh new file mode 100755 index 0000000..02a1eb9 --- /dev/null +++ b/tools/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` + +AMFLAGS="--add-missing" +if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then + AMFLAGS=$AMFLAGS" --include-deps"; +fi + +echo "Running aclocal" +aclocal +echo "Running automake" +automake $AMFLAGS +echo "Running autoconf" +autoconf + +echo "======================================" +echo "Now you are ready to run './configure'" +echo "======================================" diff --git a/tools/configure.in b/tools/configure.in new file mode 100644 index 0000000..1efd2a4 --- /dev/null +++ b/tools/configure.in @@ -0,0 +1,161 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(src/af2rgb/af2rgb.cxx) +AM_INIT_AUTOMAKE(plib_tools, 1.7.0) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_INSTALL + +AC_LANG_CPLUSPLUS + + +dnl Command line arguments. + +dnl Don't use AC_HELP_STRING here as old versions of autoconf do not +dnl understand it. +AC_ARG_WITH(GL, +[ --with-GL=DIR set the prefix directory where GL resides], +GL_PREFIX=$withval, GL_PREFIX=auto) + +if test "x$GL_PREFIX" != "xauto"; then + LDFLAGS="$LDFLAGS -L$GL_PREFIX/lib" + CPPFLAGS="$CPPFLAGS -I$GL_PREFIX/include" +fi + + +CPPFLAGS="$CPPFLAGS -I/usr/local/include" +LDFLAGS="$LDFLAGS -L/usr/local/lib" + +dnl Checks for library functions. +dnl check for OpenGL related libraries + +AC_CANONICAL_HOST + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + LIBS="$LIBS -lwinmm -lglut32 -lglu32 -lopengl32" + ;; +*-apple-darwin*) + dnl Mac OS X + + LIBS="$LIBS -framework GLUT -lobjc -framework OpenGL -framework Carbon" + ;; +*) + dnl Check for X11 + + AC_PATH_XTRA + + LDFLAGS="$LDFLAGS $X_LIBS" + + LIBS="$LIBS $X_PRE_LIBS -lXi -lXmu -lXext -lX11 $X_EXTRA_LIBS -lm" + + dnl Reasonable stuff non-windoze variants ... :-) + + AC_CHECK_LIB(pthread, pthread_create) + + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList,, + AC_MSG_ERROR([could not find working GL library])) + fi + + AC_CHECK_LIB(GLU, gluLookAt) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt,, + AC_MSG_ERROR([could not find working GLU library])) + fi + + dnl check for glut...or freeglut + AC_CHECK_LIB(freeglut, glutGetModifiers) + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xno" ; then + dnl if no freeglut, check for Real GLUT + AC_CHECK_LIB(glut, glutGetModifiers,, + AC_MSG_ERROR([could not find working GLUT library])) + fi + + AC_CHECK_LIB(dl, dlclose) + + dnl Check SGI audio library + AC_CHECK_LIB(audio, ALopenport, SND_LIBS="-laudio") + ;; +esac + +dnl Check PLIB library +AC_CHECK_LIB(plibul, ulInit, true, +AC_MSG_ERROR([there seems to be a problem with the PLIB libraries])) + + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADER(windows.h, AC_DEFINE(WIN32)) + +case "${host}" in +*-*-cygwin* | *-*-mingw32*) + dnl CygWin under Windoze. + + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*-apple-darwin*) + dnl Mac OS X + + AC_CHECK_HEADERS(OpenGL/gl.h OpenGL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + AC_CHECK_HEADER(GLUT/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + ;; +*) + AC_CHECK_HEADERS(GL/gl.h GL/glu.h,, + AC_MSG_ERROR([OpenGL header files not found])) + + if test "x$ac_cv_lib_freeglut_glutGetModifiers" = "xyes"; then + AC_CHECK_HEADER(GL/freeglut.h, AC_DEFINE(FREEGLUT_IS_PRESENT), + AC_MSG_ERROR([freeglut header file not found])) + else + AC_CHECK_HEADER(GL/glut.h, AC_DEFINE(GLUT_IS_PRESENT), + AC_MSG_ERROR([GLUT header file not found])) + fi + ;; +esac + +AC_CHECK_HEADER(plib/ul.h,, AC_MSG_ERROR([PLIB header files not found])) + + +dnl Warning flags +WFLAG="-Wall" +case "$host" in +mips-sgi-irix*) + if test "x$CXX" = "xCC"; then + WFLAG="-fullwarn"; + fi + ;; +*) + if test "x$CXX" = "xicpc" || test "x$CXX" = "xicc"; then + WFLAG="-w1" + fi + ;; +esac + +CFLAGS="$CFLAGS $WFLAG" +CXXFLAGS="$CXXFLAGS $WFLAG" + + +AC_OUTPUT( \ + Makefile \ + src/Makefile \ + src/af2rgb/Makefile \ + ) + diff --git a/tools/doc/index.html b/tools/doc/index.html new file mode 100644 index 0000000..15323d2 --- /dev/null +++ b/tools/doc/index.html @@ -0,0 +1,65 @@ + + + + + PLIB Tools Home Page + + + + + + + + + +
+PLIB Logo +
+
+
+

+ STEVE'S + PORTABLE + GAME + TOOLS + PAGE. +

+
+
+
    +When the only tool you have is a hammer
    +every problem starts to look like a nail. +
+
+ +Sourceforge Logo +
+
+ +

Useful Utility Tools for PLIB Applications.

+This is a suite of tools that will hopefully prove useful for the +developers or users of PLIB-based applications. Currently, there is only one tool: +

The "af2rgb" texture converter.

+This portable utility converts textures, that is 2D bitmaps. It converts *.?af files as used with (old) Micro$oft +Flightsimulator into .rgb files that are understood by all plib versions. New plib versions can handle +*.?af textures, so this tool is only needed if you want to use the textures +with an old (for example 1.2.0) version of plib. +

+The usage is very simple:
+Go into the path of the textures you want to convert and call af2rgb. +The program will handle all *.?af files in that path (and ignore all other files). +As a parameter, give it the path where the new textures shall be placed. +If you don't give it a path, then they will be written into the current dir. +The new filename is always lowercase, so that the case is defined for the Un*x crowd. +The first letter of the extension is added to the filename, so that it is unique. +For example, lets say you have navion.1af and Navion.2af in your path. Af2rgb will create files +called navion1.rgb and navion2.rgb. + +
+
+ +Steve J. Baker. +<sjbaker1@airmail.net> +
+ + diff --git a/tools/src/Makefile.am b/tools/src/Makefile.am new file mode 100644 index 0000000..639093c --- /dev/null +++ b/tools/src/Makefile.am @@ -0,0 +1,10 @@ + +SUBDIRS = af2rgb + +bin_PROGRAMS = plibconvert + +plibconvert_SOURCES = plibconvert.cxx + +plibconvert_LDADD = -lplibssg -lplibsg -lplibul + + diff --git a/tools/src/af2rgb/Makefile.am b/tools/src/af2rgb/Makefile.am new file mode 100644 index 0000000..92dbb79 --- /dev/null +++ b/tools/src/af2rgb/Makefile.am @@ -0,0 +1,8 @@ +noinst_PROGRAMS = af2rgb + +af2rgb_SOURCES = af2rgb.cxx + +af2rgb_LDADD = -lplibssg -lplibsg -lplibul + +EXTRA_DIST = af2rgb.dsp + diff --git a/tools/src/af2rgb/af2rgb.cxx b/tools/src/af2rgb/af2rgb.cxx new file mode 100644 index 0000000..946c666 --- /dev/null +++ b/tools/src/af2rgb/af2rgb.cxx @@ -0,0 +1,206 @@ +// Convert textures made for MSFS to rgb textures. + + +// Written by Wolfram Kuss (w_kus@rz-online.de) in dec 2000 / jan 2001 +// with some help by Marten Strömberg. +// +// Published as part of Steve Baker's PLIB +// License is GPL + +#include +#include +#include +#include + + +// *.?af textures are always 256 x 256: +#define XSIZE 256 +#define YSIZE 256 +#define ZSIZE 4 + +static unsigned char texelsR [ XSIZE * YSIZE ]; +static unsigned char texelsG [ XSIZE * YSIZE ]; +static unsigned char texelsB [ XSIZE * YSIZE ]; +static unsigned char texelsA [ XSIZE * YSIZE ]; + +static FILE *fd; + +static size_t writeByte (unsigned char x ) +{ + return fwrite ( & x, sizeof(unsigned char), 1, fd ) ; +} + +static size_t writeShort (unsigned short x ) +{ + + if (ulIsLittleEndian) // this is the other way round compared to normally. + // obviously, this is because the .rgb format comes from SGI. + ulEndianSwap(&x); + + + return fwrite( & x, sizeof(unsigned short), 1, fd ) ; +} + +static size_t writeInt (unsigned int x) +{ + if (ulIsLittleEndian) // this is the other way round compared to normally. + // obviously, this is because the .rgb format comes from SGI. + ulEndianSwap(&x); + + return fwrite( & x, sizeof(unsigned int), 1, fd ) ; +} + + + + +#define SGI_IMG_MAGIC 0x01DA + +static int writeTexelsIntoSGI ( const char *fname ) +{ + + fd = fopen ( fname, "wb" ) ; + size_t NoWritten = 0; + + if ( fd == NULL ) + { + ulSetError(UL_WARNING, "ssgLoadTexture: Failed to open '%s' for writing.", fname ) ; + return false; + } + + // ******* write the header ***************** + + NoWritten += writeShort (SGI_IMG_MAGIC) ; + + NoWritten += writeByte (0) ; // don't use RLE + NoWritten += writeByte (1) ; // bpp + NoWritten += writeShort (3) ; // dimension + + NoWritten += writeShort ( XSIZE ) ; + NoWritten += writeShort ( YSIZE ) ; + NoWritten += writeShort ( ZSIZE ) ; + NoWritten += writeInt ( 0 ) ; + NoWritten += writeInt ( 255 ) ; + NoWritten += writeInt ( 0 ) ; /* Dummy field */ + + int i ; + + for ( i = 0 ; i < 80 ; i++ ) + NoWritten += writeByte ( 0 ) ; /* Name field */ + + NoWritten += writeInt ( 0 ) ; // colormap + + for ( i = 0 ; i < 404 ; i++ ) + NoWritten += writeByte ( 0 ) ; /* Dummy field */ + + // ************ write the body **************** + + NoWritten += fwrite ( texelsR, XSIZE, YSIZE, fd ) ; + NoWritten += fwrite ( texelsG, XSIZE, YSIZE, fd ) ; + NoWritten += fwrite ( texelsB, XSIZE, YSIZE, fd ) ; + NoWritten += fwrite ( texelsA, XSIZE, YSIZE, fd ) ; + + if(NoWritten!=91+404+4*YSIZE) + { ulSetError(UL_WARNING, "Only %ld records written instead of %d. Proceed at your own risc\n", + (long)NoWritten, 91+404+4*YSIZE); + + return false; + } + fclose ( fd ) ; + return true; +} + + + +int loadMDLIntoTexels ( const char *fname ) +// returns TRUE on success +{ + FILE *tfile; + if ( (tfile = fopen(fname, "rb")) == NULL) { + ulSetError(UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname ); + return false; + } + + fseek(tfile, 0, SEEK_END); + unsigned long file_length = ftell(tfile); + + if (file_length != 65536) { + ulSetError(UL_WARNING, "ssgLoadTexture: Wrong size of '%s'.", fname ); + return false; + } else { + fseek(tfile, 0, SEEK_SET); + + int c = 0; + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + unsigned char b; + if ( fread(&b, 1, 1, tfile) != 1) + return false; + texelsR[c] = ssgFsTexPalette[b*4 ]; + texelsG[c] = ssgFsTexPalette[b*4 + 1]; + texelsB[c] = ssgFsTexPalette[b*4 + 2]; + texelsA[c] = ssgFsTexPalette[b*4 + 3]; + c++; + } + } + fclose(tfile); + } + return true; +} + +void DoAllFiles( char *sDirectoryP ) +{ char newname [ 1024 ], sDirectory[1024], sFullPath[1024]; // todo fixme + + strcpy( sDirectory, sDirectoryP); + if ( ( sDirectoryP[ strlen(sDirectoryP)-1 ] != '\\') && + ( sDirectoryP[ strlen(sDirectoryP)-1 ] != '/') ) + strcat( sDirectory, "/"); +// ************** For all *.?af files in the current dir ************ + ulDir* dirp = ulOpenDir("."); + if ( dirp != NULL ) + { + ulDirEnt* dp; + while ( (dp = ulReadDir(dirp)) != NULL ) + { + if ( !dp->d_isdir ) + { size_t len = strlen(dp->d_name ); + if ( len >4 ) + if ((dp->d_name[len-1] == 'f') || (dp->d_name[len-1] == 'F')) + if ((dp->d_name[len-2] == 'a') || (dp->d_name[len-2] == 'A')) + if ((dp->d_name[len-4] == '.') || (dp->d_name[len-4] == '.')) + { strcpy( newname, dp->d_name ); + unsigned int i; + for (i=0; id_name, newname); + // ********** convert it ************** + if ( loadMDLIntoTexels ( dp->d_name )) + { strcpy(sFullPath, sDirectory); + strcat(sFullPath, newname); + writeTexelsIntoSGI ( sFullPath ); //lint !e534 + } + } + } + } + ulCloseDir(dirp); + } +} + +// converts all *.?af files from the current dir to *.rgb in the dir givven in the argument +int main(int argc, char* argv[]) +{ + if ( argc > 2 ) + ulSetError(UL_WARNING, "All arguments after the first are ignored!" ); + if ( argc >= 2 ) + DoAllFiles( argv[1] ); + else + DoAllFiles("."); + return 0; +} + diff --git a/tools/src/af2rgb/af2rgb.dsp b/tools/src/af2rgb/af2rgb.dsp new file mode 100644 index 0000000..6e219ca --- /dev/null +++ b/tools/src/af2rgb/af2rgb.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="af2rgb" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=af2rgb - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "af2rgb.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "af2rgb.mak" CFG="af2rgb - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "af2rgb - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "af2rgb - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "af2rgb - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg.lib ssg.lib ul.lib opengl32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\\" + +!ELSEIF "$(CFG)" == "af2rgb - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sg_d.lib ssg_d.lib ul_d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\\" + +!ENDIF + +# Begin Target + +# Name "af2rgb - Win32 Release" +# Name "af2rgb - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\af2rgb.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/tools/src/afm2txf/README b/tools/src/afm2txf/README new file mode 100644 index 0000000..66be37e --- /dev/null +++ b/tools/src/afm2txf/README @@ -0,0 +1,18 @@ +It's definitely a tool of the "duct tape and fishing line" variety. :) +It expects to find a ghostscript interpreter and ImageMagick's "mogrify" +program on the command line, takes a .afm font metrics file as its sole +argument, and expects that the font therein is accessible to ghostscript. +Basically, it should work out of the box on most linux distributions, +and nowhere else without a lot of work. + +If you have a TrueType font that you want to generate: + + export GS_FONTPATH= + ttf2afm yourfont.ttf > whatever.afm + font.pl whatever.afm + +Wait for a while (at 16x downsampling, this takes 1-2 minutes on my +Athlon 1800+), and the .txf file will appear in the current directory. +It will have the Postscript language name of the font. + +Andy diff --git a/tools/src/afm2txf/afm2txf.pl b/tools/src/afm2txf/afm2txf.pl new file mode 100755 index 0000000..baf1a51 --- /dev/null +++ b/tools/src/afm2txf/afm2txf.pl @@ -0,0 +1,283 @@ +#!/usr/bin/perl -w + +# afm2txf.pl 0.2 +# +# Generates .txf font textures from Type 1 fonts +# Requirements: Ghostscript, ImageMagick +# +# Usage: +# afm2txf.pl whatever.afm +# +# Changelog: +# 0.2 (06/28/2002): Generate fonts with proper padding +# 0.1 (06/28/2002): Initial version +# +# Copyright (C) 2002 Andrew James Ross +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +use strict; + +my $METRICS = shift or die; # AFM file + +# Texture size +my $TEXSIZ = 256; + +# Padding around each character, for mipmap separation +my $PADDING = 4; + +# Antialiasing multiplier. Should be 16 for production work. As low +# as 4 works well for testing. +my $DOWNSAMPLE = 16; + +# The 94 printable ASCII characters (and the space) and their +# postscript glyph names. We use names because not all postscript +# fonts are encoded using ASCII. AFM metrics generated by ttf2afm, in +# fact, don't have any numerical character IDs at all. In principle, +# this mechanism will work for any 8 bit font encoding, you just have +# to do the legwork of figuring out the name to ID mapping. +my %CHARS = ('space'=>32, 'exclam'=>33, 'quotedbl'=>34, + 'numbersign'=>35, 'dollar'=>36, 'percent'=>37, + 'ampersand'=>38, 'quotesingle'=>39, 'parenleft'=>40, + 'parenright'=>41, 'asterisk'=>42, 'plus'=>43, + 'comma'=>44, 'hyphen'=>45, 'period'=>46, 'slash'=>47, + 'zero'=>48, 'one'=>49, 'two'=>50, 'three'=>51, + 'four'=>52, 'five'=>53, 'six'=>54, 'seven'=>55, + 'eight'=>56, 'nine'=>57, 'colon'=>58, 'semicolon'=>59, + 'less'=>60, 'equal'=>61, 'greater'=>62, 'question'=>63, + 'at'=>64, 'A'=>65, 'B'=>66, 'C'=>67, 'D'=>68, 'E'=>69, + 'F'=>70, 'G'=>71, 'H'=>72, 'I'=>73, 'J'=>74, 'K'=>75, + 'L'=>76, 'M'=>77, 'N'=>78, 'O'=>79, 'P'=>80, 'Q'=>81, + 'R'=>82, 'S'=>83, 'T'=>84, 'U'=>85, 'V'=>86, 'W'=>87, + 'X'=>88, 'Y'=>89, 'Z'=>90, 'bracketleft'=>91, + 'backslash'=>92, 'bracketright'=>93, 'asciicircum'=>94, + 'underscore'=>95, 'grave'=>96, 'a'=>97, 'b'=>98, 'c'=>99, + 'd'=>100, 'e'=>101, 'f'=>102, 'g'=>103, 'h'=>104, + 'i'=>105, 'j'=>106, 'k'=>107, 'l'=>108, 'm'=>109, + 'n'=>110, 'o'=>111, 'p'=>112, 'q'=>113, 'r'=>114, + 's'=>115, 't'=>116, 'u'=>117, 'v'=>118, 'w'=>119, + 'x'=>120, 'y'=>121, 'z'=>122, 'braceleft'=>123, + 'bar'=>124, 'braceright'=>125, 'asciitilde'=>126); + +my %metrics = (); +my %positions = (); + +# +# Parse the font metrics. This is a 5 element array. All numbers are +# expressed as a fraction of the line spacing. +# 0: nominal width (distance to the next character) +# 1, 2: Coordinates of the lower left corner of the bounding box, +# relative to the nominal position. +# 3, 4: Size of the bounding box +# +print STDERR "Reading font metrics...\n"; +my $FONT; +my @lines = `cat $METRICS` + or die "Couldn't read metrics"; +foreach my $m (grep {/^(C|FontName) /} @lines) { + chomp $m; + if($m =~ /^FontName ([^\s]*)/) { $FONT = $1; next; } + + die "No name: $m" if $m !~ /N\s+([^\s]+)\s+;/; + my $name = $1; + + die "No box: $m" + if $m !~ /B\s+([-0-9]+)\s+([-0-9]+)\s+([-0-9]+)\s+([-0-9]+)\s+;/; + my ($left, $bottom, $right, $top) = ($1/1000, $2/1000, $3/1000, $4/1000); + + die "No width: $m" if $m !~ /WX\s+([-0-9]+)/; + my $nomwid = $1/1000; # nominal, not physical width! + + # The coordinates of the corner relative to the character + # "position" + my ($x, $y) = (-$left, -$bottom); + my ($w, $h) = ($right-$left, $top-$bottom); + + $metrics{$name} = [$nomwid, $x, $y, $w, $h]; +} + +die "No FontName found in metrics" if not defined $FONT; + +# +# Find the height of the tallest character, and print some warnings +# +my $maxhgt = 0; +foreach my $c (keys %CHARS) { + if(!defined $metrics{$c}) { + print STDERR "% WARNING: no metrics for char $c. Skipping.\n"; + next; + } + if($metrics{$c}->[4] > $maxhgt) { $maxhgt = $metrics{$c}->[4]; } +} +if($maxhgt == 0) { + print STDERR "No usable characters found. Bailing out.\n"; + exit 1; +} + +# +# Do the layout. Keep increasing the row count until the characters +# just fit. This isn't terribly elegant, but it's simple. +# +print STDERR "Laying out"; +my $rows = 1; +my $PS; +my $LINEHGT; +while(!defined ($PS = genPostscript($rows))) { $rows++; } +print STDERR " ($rows rows)\n"; + +# +# Call ghostscript to render +# +print STDERR "Rendering Postscript...\n"; +my $res = $TEXSIZ * $DOWNSAMPLE; +my $pid = open PS, "|gs -r$res -g${res}x${res} -sDEVICE=ppm -sOutputFile=$FONT.ppm > /dev/null"; +die "Couldn't spawn ghostscript interpreter" if !defined $pid; +print PS join("\n", @$PS), "\n"; +close PS; +waitpid($pid, 0); + +# +# Downsample with ImageMagick +# +print STDERR "Antialiasing image...\n"; +system("mogrify -geometry ${TEXSIZ}x${TEXSIZ} $FONT.ppm") == 0 + or die "Couldn't rescale $FONT.ppm"; + +# +# Generate the .txf file +# +print STDERR "Generating textured font file...\n"; + +# Prune undefined glyphs +foreach my $c (keys %metrics) { + delete $metrics{$c} if !defined $CHARS{$c}; +} + +sub round { sprintf "%.0f", $_[0] } +open TXF, ">$FONT.txf" or die; +print TXF pack "V", 0x667874ff; +print TXF pack "V", 0x12345678; +print TXF pack "V", 0; +print TXF pack "V", $TEXSIZ; +print TXF pack "V", $TEXSIZ; +print TXF pack "V", round($TEXSIZ * $LINEHGT); +print TXF pack "V", 0; +print TXF pack "V", scalar(keys(%metrics)); +my @chars = sort { $CHARS{$a} <=> $CHARS{$b} } (keys %metrics); +foreach my $c (@chars) { + my $m = $metrics{$c}; + my $p = $positions{$c}; + my $step = round($m->[0] * $LINEHGT * $TEXSIZ); + + # Pad the bounding box, to handle areas that outside. This can + # happen due to thick lines in the font path, or be an artifact of + # the downsampling. + my $w = round($m->[3] * $LINEHGT * $TEXSIZ + 2*$PADDING); + my $h = round($m->[4] * $LINEHGT * $TEXSIZ + 2*$PADDING); + my $xoff = -round($m->[1] * $LINEHGT * $TEXSIZ - $PADDING); + my $yoff = -round($m->[2] * $LINEHGT * $TEXSIZ - $PADDING); + my $x = round($p->[0] * $TEXSIZ - $PADDING); + my $y = round($p->[1] * $TEXSIZ - $PADDING); + + print TXF pack "v", $CHARS{$c}; + print TXF pack "C", $w; + print TXF pack "C", $h; + print TXF pack "c", $xoff; + print TXF pack "c", $yoff; + print TXF pack "C", $step; + print TXF pack "C", 0; + print TXF pack "v", $x; + print TXF pack "v", $y; +} + +# Read in the .ppm file, dump the duplicate color values (ghostscript +# won't generate pgm's) and write to the end of the .txf. Remember to +# swap the order of the rows; OpenGL textures are bottom-up. +open PPM, "$FONT.ppm" or die; +seek PPM, -3*$TEXSIZ*$TEXSIZ, 2 or die; +my @rows = (); +my $pixel; +for(my $r=0; $r<$TEXSIZ; $r++) { + my @row = (); + for(my $c=0; $c<$TEXSIZ; $c++) { + read PPM, $pixel, 3 or die; + push @row, substr($pixel, 0, 1); + } + push @rows, \@row; +} +close PPM; +for(my $r=(@rows - 1); $r>=0; $r--) { + print TXF join('', @{$rows[$r]}); +} +close TXF; + +# Clean up +#system("rm $FONT.ppm"); + +######################################################################## +######################################################################## +######################################################################## + +sub genPostscript { + my $rows = shift; + my $rowhgt = 1/$rows; + + my @PS = (); + + # The canonical "point size" number, in texture space + $LINEHGT = ($rowhgt - 2*$PADDING/$TEXSIZ) / $maxhgt; + + # Get to where we want. Draw the whole thing in a 1 inch square at + # the bottom left of the "page". + push @PS, "72 72 scale"; + + # Fill the square with black + push @PS, "0 setgray"; + push @PS, "-1 -1 moveto"; + push @PS, "-1 1 lineto 1 1 lineto 1 -1 lineto"; + push @PS, "closepath"; + push @PS, "fill"; + + # Draw in white + push @PS, "1 setgray"; + + # Generate our PUSH @PS, font + push @PS, "/$FONT findfont $LINEHGT scalefont setfont"; + + my $x = $PADDING/$TEXSIZ; + my $y = 1 - $rowhgt + $PADDING/$TEXSIZ; + my @chars = sort { $CHARS{$a} <=> $CHARS{$b} } (keys %CHARS); + foreach my $c (@chars) { + my $m = $metrics{$c}; + next if !defined $m; + + my $id = sprintf "%2.2x", $CHARS{$c}; + + # No space? + my $w = $m->[3]*$LINEHGT; + if($x + $w + $PADDING/$TEXSIZ > 1) { + $x = $PADDING/$TEXSIZ; + $y -= $rowhgt; + return undef if $y < 0; + } + + # Record where in the texture the box ended up + $positions{$c} = [$x, $y]; + + my $vx = $x + $m->[1]*$LINEHGT; + my $vy = $y + $m->[2]*$LINEHGT; + + push @PS, "$vx $vy moveto"; + push @PS, "<$id> show"; + + # Next box... + $x += $w + 2*$PADDING/$TEXSIZ; + } + + push @PS, "showpage"; + + return \@PS; +} + diff --git a/tools/src/plibconvert.cxx b/tools/src/plibconvert.cxx new file mode 100644 index 0000000..cd8d0a4 --- /dev/null +++ b/tools/src/plibconvert.cxx @@ -0,0 +1,99 @@ +// plibconvert.cxx +// +// Written by Bram Stolk +// Published as part of Steve Baker's PLIB under GPL +// +// Build with : +// g++ -o plibconvert plibconvert.cxx -lplibssg -lplibsg -lplibul -lGL + + +#include +#include +#include + +#include + +#include + + +int main(int argc, char *argv[]) +{ + if (argc!=3) + { + fprintf(stderr,"Usage: %s source destination\n", argv[0]); + exit(1); + } + + assert(strcmp(argv[1], argv[2])); + +#ifdef GLX_VERSION_1_3 + // Although we will not be doing any rendering, we must create + // a valid rendering context before we can call ssgInit() + Display *dpy = XOpenDisplay(0); + int scn=DefaultScreen(dpy); + int cnt; + GLXFBConfig *configs = glXGetFBConfigs(dpy, scn, &cnt); + if (!configs) + { + perror("glXGetFBConfigs"); + exit(1); + } + fprintf(stderr,"glXGetFBConfigs returned %p (%d matches)\n", configs, cnt); + assert(cnt); + GLXFBConfig fbc = configs[0]; + XVisualInfo *vinf = glXGetVisualFromFBConfig(dpy, fbc); + if (!vinf) + { + perror("glXGetVisualFromFBConfig"); + exit(1); + } + fprintf + ( + stderr, + "visualid=0x%lx (depth=%d,R/G/B=%lx/%lx/%lx)\n", + vinf->visualid, + vinf->depth, + vinf->red_mask, + vinf->green_mask, + vinf->blue_mask + ); + GLXContext cx = glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, 0, GL_TRUE); + if (!cx) + { + perror("glXCreateNewContext"); + exit(1); + } + else + { + fprintf(stderr,"glX context created\n"); + } + int attrlist[] = + { + GLX_PBUFFER_WIDTH, 1, + GLX_PBUFFER_HEIGHT, 1, + 0 + }; + GLXPbuffer pBuffer = glXCreatePbuffer(dpy, fbc, attrlist); + fprintf(stderr,"pBuffer = %lx\n", pBuffer); + + bool ok=glXMakeContextCurrent(dpy, pBuffer, pBuffer, cx); + if (!ok) + { + perror("glXMakeContextCurrent"); + exit(1); + } + + // Initialize plib + fprintf(stderr,"Initializing plib...\n"); + ssgInit(); + ssgTexturePath ( "." ) ; + ssgModelPath ( "." ) ; + + ssgEntity *e = ssgLoad(argv[1]); // Load + e->print(stdout); // Inform + ssgSave(argv[2], e); // Store +#else + fprintf(stderr,"This util uses a pbuffer as context for plib. To build the source, you need glx protocol 1.3 or higher.\n"); +#endif +} + diff --git a/tools/src/txfdump/txfdump.pl b/tools/src/txfdump/txfdump.pl new file mode 100755 index 0000000..401280a --- /dev/null +++ b/tools/src/txfdump/txfdump.pl @@ -0,0 +1,93 @@ +#!/usr/bin/perl -w +use strict; + +## txfdump.pl - A .txf font file parser and image extractor +## +## Usage: txfdump.pl blahblah.txf +## +## Human readable font metric information will be printed to stdout, +## and a blahblah.pgm file will be written containing the texture +## image from the file. Note that the resulting image will be +## vertically reflected, owing to the difference between cartesian x/y +## coordinates used by .txf and the screen raster convention of the +## .pgm format (well, and the author's laziness). Load it up in your +## favorite viewer and flip it if you care. +## +## Copyright (C) 2002 Andrew James Ross +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License version 2 as +## published by the Free Software Foundation. + +my $file = shift or die; +open TXF, $file or die; + +# Start out assuming intel byte order +# These get used in the getshort/getint functions below +my $shortType = "v"; +my $longType = "V"; + +sub getsbyte { my $v=getbyte(); if($v>127) { $v-=256; } return $v; } +sub getbyte { my $val; read TXF, $val, 1 or die; return ord $val; } +sub getshort { my $val; read TXF, $val, 2 or die; return unpack $shortType, $val; } +sub getint { my $val; read TXF, $val, 4 or die; return unpack $longType, $val; } + +# Check the magic number; it is endian-independant +my $magic = sprintf "0x%8.8x", getint(); +die "Bad magic number ($magic)" if $magic ne "0x667874ff"; + +# Read the next four bytes to determine endianness +my $endian = sprintf "0x%8.8x", getint(); +if($endian eq "0x12345678") { +} elsif($endian eq "0x78563412") { + # Network byte order + $shortType = "n"; + $longType = "N"; +} else { + die "Bad endianness tag ($endian)"; +} + +# Read the rest of the header +my $format = getint(); +my $texwid = getint(); +my $texhgt = getint(); +my $linehgt = getint(); +my $unk = getint(); +my $nglyph = getint(); + +printf "Magic $magic Endian $endian Format 0x%8.8x\n", $format; +print "Texture: ${texwid}x$texhgt Line: $linehgt Glyphs: $nglyph Unk: $unk\n"; + +# Read each glyph's metadata +for(my $i=0; $i<$nglyph; $i++) { + print "Glyph $i\n"; + my $c = getshort(); + print " char: $c ('", chr($c), "')\n"; + print " width: ", getbyte(), "\n"; + print " height: ", getbyte(), "\n"; + print " xoff: ", getsbyte(), "\n"; + print " yoff: ", getsbyte(), "\n"; + print " step: ", getbyte(), "\n"; + print " unk: ", getbyte(), "\n"; + print " X: ", getshort(), "\n"; + print " Y: ", getshort(), "\n"; +} + +# The rest of the file is an image. Write it out as a .pgm file with +# the same name as the font file. + +$file =~ s/\.txf$/.pgm/; +open PGM, ">$file" or die; +print PGM "P2\n$texwid $texhgt\n255\n"; + +if($format == 0) { + for(my $i=0; $i<($texwid*$texhgt); $i++) { print PGM getbyte(), " "; } +} else { + for(my $i=0; $i<($texwid*$texhgt/8); $i++) { + my $byte = getbyte(); + for(my $j=0; $j<8; $j++) { + print PGM ($byte & (1<<$j)) ? "255 " : "0 "; + } + } +} + diff --git a/tools/tools.dsw b/tools/tools.dsw new file mode 100644 index 0000000..7d23fe9 --- /dev/null +++ b/tools/tools.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "af2rgb"=.\SRC\AF2RGB\af2rgb.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/transferCvs2WebSite b/transferCvs2WebSite new file mode 100644 index 0000000..8aa19a8 --- /dev/null +++ b/transferCvs2WebSite @@ -0,0 +1,82 @@ +# transferCvs2WebSite for plib +# +# you must already be logged in !! +# That means, you must do the following ONCE (once is enough for a lifetime :-)): +# cvs -d:pserver:anonymous@cvs1:/cvsroot/plib login +# +# export doesnt create the "cvs"-directories. Update would. Therefore I use export + + +echo running cd +cd /home/groups/p/pl/plib/htdocs + +echo handling "sg" +rm -R sg +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d sg plib/doc/sg >/dev/null 2>&1 + +echo handling "ssg" +rm -R ssg +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d ssg plib/doc/ssg >/dev/null 2>&1 + +echo handling "sl" +rm -R sl +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d sl plib/doc/sl >/dev/null 2>&1 + +echo handling "js" +rm -R js +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d js plib/doc/js >/dev/null 2>&1 + +echo handling "fnt" +rm -R fnt +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d fnt plib/doc/fnt >/dev/null 2>&1 + +echo handling "pw" +rm -R pw +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d pw plib/doc/pw >/dev/null 2>&1 + +echo handling "psl" +rm -R psl +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d psl plib/doc/psl >/dev/null 2>&1 + +echo handling "puAux" +rm -R puAux +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d puAux plib/doc/puAux >/dev/null 2>&1 + +echo handling "ssgAux" +rm -R ssgAux +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d ssgAux plib/doc/ssgAux >/dev/null 2>&1 + +echo handling "util" +rm -R util +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d util plib/doc/util >/dev/null 2>&1 + +echo handling "pui" +rm -R pui +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d pui plib/doc/pui >/dev/null 2>&1 + +echo handling "net" +rm -R net +cvs -d:pserver:anonymous@cvs1:/cvsroot/plib export -Dtoday -d net plib/doc/net >/dev/null 2>&1 + +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/sg +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/ssg +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/sl +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/js +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/fnt +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/puAux +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/ssgAux +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/util +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/pui +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/psl +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/pw +chmod -R u=rwX,g=rwX,o=rX /home/groups/p/pl/plib/htdocs/net + +echo done. + + + + + + + +

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