From 6f1b787a10d8ab1a3117a4b8c004dd2d90599608 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Mon, 26 Jul 2010 22:36:54 -0600 Subject: [PATCH] mesh and other random adjustments --- arch/gentoo/yoink.ebuild | 20 +- configure | 17 +- data/models/classic.ac | 2456 ++++++++++++++++++------------------ data/models/classic.blend | Bin 80789 -> 80861 bytes data/scenes/Classic.lua | 827 +----------- src/Animation.cc | 6 +- src/Animation.hh | 3 +- src/GameLayer.cc | 58 +- src/GameLayer.hh | 9 +- src/Main.cc | 180 ++- src/Main.hh | 16 +- src/Scene.cc | 22 +- src/Scene.hh | 3 +- src/moof/entity.hh | 8 +- src/moof/image.cc | 20 +- src/moof/log.cc | 50 +- src/moof/log.hh | 30 +- src/moof/mesh.cc | 299 +++-- src/moof/mesh.hh | 51 +- src/moof/mesh_bindings.cc | 136 ++ src/moof/modal_dialog.hh | 8 +- src/moof/opengl.hh | 32 + src/moof/resource.cc | 330 ++--- src/moof/resource.hh | 140 +- src/moof/script.hh | 806 ++++++++---- src/moof/sound.cc | 45 +- src/moof/sound.hh | 5 +- src/moof/sound_bindings.cc | 112 ++ src/moof/string.cc | 323 +++-- src/moof/string.hh | 150 ++- src/moof/timer.cc | 13 +- src/moof/timer.hh | 5 +- 32 files changed, 3251 insertions(+), 2929 deletions(-) create mode 100644 src/moof/mesh_bindings.cc create mode 100644 src/moof/sound_bindings.cc diff --git a/arch/gentoo/yoink.ebuild b/arch/gentoo/yoink.ebuild index 16781c7..6e167d0 100644 --- a/arch/gentoo/yoink.ebuild +++ b/arch/gentoo/yoink.ebuild @@ -4,17 +4,17 @@ EAPI=2 -inherit autotools eutils games +inherit eutils games DESCRIPTION="Alien-smashing action game" HOMEPAGE="http://www.dogcows.com/yoink/" -SRC_URI="http://www.dogcows.com/yoink/${P}.tar.bz2 +SRC_URI="http://www.dogcows.com/yoink/chrome/site/${P}.tar.bz2 http://eng.utah.edu/~mcgarvey/yoink/${P}.tar.bz2" LICENSE="BSD-2 BSD LGPL-2.1 ZLIB" SLOT="0" KEYWORDS="amd64 x86" -IUSE="debug double-precision gtk qt4 threads" +IUSE="debug double-precision gtk hotload qt4 threads" RDEPEND="dev-lang/lua media-libs/libpng @@ -22,30 +22,26 @@ RDEPEND="dev-lang/lua media-libs/libvorbis media-libs/openal virtual/glu - virtual/opengl" + virtual/opengl + gtk? ( x11-libs/gtk+:2 ) + qt4? ( !gtk? ( x11-libs/qt-core:4 ) )" DEPEND="${RDEPEND} dev-libs/boost dev-util/pkgconfig" -src_prepare() { - sed -i -e "/apps/d" -e "/pixmap/d" data/Makefile.am || die "sed failed" - sed -i -e "/man/d" Makefile.am || die "sed failed" - - eautoreconf -} - src_configure() { egamesconf \ --disable-dependency-tracking \ $(use_enable debug) \ $(use_enable double-precision) \ + $(use_enable hotload) \ $(use_enable threads) \ $(use_with gtk) \ $(use_with qt4) } src_install() { - emake DESTDIR="${D}" install || die "emake install failed" + emake DESTDIR="${D}" install_man=no install || die "emake failed" dodoc AUTHORS COPYING README TODO doman doc/yoink.6 doicon data/yoink.png diff --git a/configure b/configure index bea16f4..292639c 100755 --- a/configure +++ b/configure @@ -58,7 +58,7 @@ Program options: --enable-clock_gettime use a very accurate timing function --enable-debug compile in assertion checks and other debug helps --enable-double-precision use larger floating-point numbers - --enable-hotloading watch assets and automatically reload them + --enable-hotload automatically reload modified game assets --enable-threads use threads for concurrency where appropriate --with-gtk use the gtk2 toolkit (overrides --with-qt4) @@ -468,17 +468,16 @@ end config.USE_CLOCK_GETTIME = get_feature("clock_gettime") config.USE_DOUBLE_PRECISION = get_feature("double-precision") -config.USE_HOTLOADING = get_feature("hotloading") +config.USE_HOTLOADING = get_feature("hotload") config.USE_THREADS = get_feature("threads") -config.PROFILING_ENABLED = get_feature("profile") and add_cflag("-pg") - -if get_package("gtk") then - -- TODO +if get_feature("profile") then + config.PROFILING_ENABLED = true + add_cflag("-pg") + LDFLAGS = LDFLAGS .. "-pg" end -if get_package("qt4") then - -- TODO -end +if get_package("gtk") then config.USE_GTK = true end +if get_package("qt4") then config.USE_QT4 = true end -- diff --git a/data/models/classic.ac b/data/models/classic.ac index ef7f632..7b13176 100644 --- a/data/models/classic.ac +++ b/data/models/classic.ac @@ -15,40 +15,40 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 34 -23 1 -7 -23 0 -7 -12 0 -7 -12 1 -7 -29 1 -7 -29 0 -7 -33 0 -7 -33 1 -7 -22 0 -7 -21 0 -7 -20 0 -7 -19 0 -7 -18 0 -7 -17 0 -7 -16 0 -7 -15 0 -7 -14 0 -7 -13 0 -7 -22 1 -7 -21 1 -7 -20 1 -7 -19 1 -7 -18 1 -7 -17 1 -7 -16 1 -7 -15 1 -7 -14 1 -7 -13 1 -7 -30 1 -7 -31 1 -7 -32 1 -7 -30 0 -7 -31 0 -7 -32 0 -7 +23 1 -3 +23 0 -3 +12 0 -3 +12 1 -3 +29 1 -3 +29 0 -3 +33 0 -3 +33 1 -3 +22 0 -3 +21 0 -3 +20 0 -3 +19 0 -3 +18 0 -3 +17 0 -3 +16 0 -3 +15 0 -3 +14 0 -3 +13 0 -3 +22 1 -3 +21 1 -3 +20 1 -3 +19 1 -3 +18 1 -3 +17 1 -3 +16 1 -3 +15 1 -3 +14 1 -3 +13 1 -3 +30 1 -3 +31 1 -3 +32 1 -3 +30 0 -3 +31 0 -3 +32 0 -3 numsurf 15 SURF 0x00 mat 1 @@ -164,80 +164,80 @@ texture "Building.png" texrep 1 1 crease 30.000000 numvert 74 -18 0 -6 18 0 -2 +18 0 2 +15 0 2 15 0 -2 -15 0 -6 +18 3 2 +15 3 2 18 3 -2 15 3 -2 -18 3 -6 -15 3 -6 +18 1 2 +18 2 2 +15 1 2 +15 2 2 18 1 -2 18 2 -2 15 1 -2 15 2 -2 -18 1 -6 -18 2 -6 -15 1 -6 -15 2 -6 -17 0 -2 -16 0 -2 -17 3 -2 -16 3 -2 -16 1 -2 -17 1 -2 -16 2 -2 -17 2 -2 -18 0 -5 -18 0 -4 -18 0 -3 -18 3 -3 -18 3 -4 -18 3 -5 -18 1 -3 -18 1 -4 -18 1 -5 -18 2 -3 -18 2 -4 -18 2 -5 -15 0 -3 -15 0 -4 -15 0 -5 -15 3 -3 -15 3 -4 -15 3 -5 -15 1 -3 -15 1 -4 -15 1 -5 -15 2 -3 -15 2 -4 -15 2 -5 -15 5.499999 -4.000002 -18 5.499999 -4.000002 -16 5.499999 -4.000002 -17 5.499999 -4.000002 -15 4 -5 -15 4 -4 -15 4 -3 -18 4 -5 -18 4 -4 -18 4 -3 -18 5 -4 -18 2.8 -1.8 -15 2.8 -1.8 -15 5 -4 -17 2.8 -1.8 -16 2.8 -1.8 -18 4.266666 -3.266667 -18 3.533333 -2.533333 -17 5 -4 -16 5 -4 -15 3.533333 -2.533334 -15 4.266666 -3.266667 -16 3.533333 -2.533334 -17 3.533333 -2.533333 -16 4.266666 -3.266667 -17 4.266666 -3.266667 +17 0 2 +16 0 2 +17 3 2 +16 3 2 +16 1 2 +17 1 2 +16 2 2 +17 2 2 +18 0 -1 +18 0 0 +18 0 1 +18 3 1 +18 3 0 +18 3 -1 +18 1 1 +18 1 0 +18 1 -1 +18 2 1 +18 2 0 +18 2 -1 +15 0 1 +15 0 0 +15 0 -1 +15 3 1 +15 3 0 +15 3 -1 +15 1 1 +15 1 0 +15 1 -1 +15 2 1 +15 2 0 +15 2 -1 +15 5.499999 -2e-06 +18 5.499999 -2e-06 +16 5.499999 -2e-06 +17 5.499999 -2e-06 +15 4 -1 +15 4 0 +15 4 1 +18 4 -1 +18 4 0 +18 4 1 +18 5 0 +18 2.8 2.2 +15 2.8 2.2 +15 5 0 +17 2.8 2.2 +16 2.8 2.2 +18 4.266666 0.733333 +18 3.533333 1.466667 +17 5 0 +16 5 0 +15 3.533333 1.466666 +15 4.266666 0.733333 +16 3.533333 1.466666 +17 3.533333 1.466667 +16 4.266666 0.733333 +17 4.266666 0.733333 numsurf 57 SURF 0x00 mat 2 @@ -540,9 +540,9 @@ refs 3 SURF 0x00 mat 2 refs 3 -5 0.375 0.75 -54 0.5 0.5 -39 0.375 0.5 +5 0.499525219202 0.500041484833 +54 0.374525249004 0.750041484833 +39 0.374525249004 0.500041425228 SURF 0x00 mat 2 refs 3 @@ -553,19 +553,19 @@ SURF 0x00 mat 2 refs 3 56 0.25 0.5 -58 0.124999992549 0.5 -57 0.25 0.75 +58 0.25 0.75 +57 0.125 0.5 SURF 0x00 mat 2 refs 3 -55 0.375 0.75 -58 0.5 0.5 +55 0.5 0.5 +58 0.375 0.75 56 0.375 0.5 SURF 0x00 mat 2 refs 3 -6 0.375 0.75 -55 0.5 0.5 +6 0.5 0.5 +55 0.375 0.75 29 0.375 0.5 SURF 0x00 mat 2 @@ -637,17 +637,17 @@ data 6 L-Lawn crease 30.000000 numvert 11 +6 0 2 +2 0 4 +2 0 2 +14 0 2 +5 0 -3 +15 0 -3 6 0 -2 -2 0 0 -2 0 -2 14 0 -2 -5 0 -7 -15 0 -7 -6 0 -6 -14 0 -6 -5 0 -2 -15 0 0 -5 0 0 +5 0 2 +15 0 4 +5 0 4 numsurf 6 SURF 0x30 mat 3 @@ -699,78 +699,50 @@ texture "TowerBlock1.png" texrep 1 1 crease 30.000000 numvert 182 -12 0 -7 -5 0 -7 -5 8 -7 -12 8 -7 -3 8 -7 -12 0 -13 -12 0 -12 -12 0 -11 -12 0 -10 +12 0 -3 +5 0 -3 +5 8 -3 +12 8 -3 +3 8 -3 12 0 -9 12 0 -8 +12 0 -7 +12 0 -6 +12 0 -5 +12 0 -4 +12 8 -4 +12 8 -5 +12 8 -6 +12 8 -7 12 8 -8 12 8 -9 -12 8 -10 -12 8 -11 -12 8 -12 -12 8 -13 -3 8 -13 -3 8 -12 -3 8 -11 -3 8 -10 3 8 -9 3 8 -8 -5 1 -7 -5 2 -7 -5 3 -7 -5 4 -7 -5 5 -7 -5 6 -7 -5 7 -7 -12 1 -7 -12 2 -7 -12 3 -7 -12 4 -7 -12 5 -7 -12 6 -7 -12 7 -7 -12 1 -13 -12 2 -13 -12 3 -13 -12 4 -13 -12 5 -13 -12 6 -13 -12 7 -13 -12 7 -12 -12 6 -12 -12 5 -12 -12 4 -12 -12 3 -12 -12 2 -12 -12 1 -12 -12 7 -11 -12 6 -11 -12 5 -11 -12 4 -11 -12 3 -11 -12 2 -11 -12 1 -11 -12 1 -10 -12 2 -10 -12 3 -10 -12 4 -10 -12 5 -10 -12 6 -10 -12 7 -10 -12 7 -9 -12 6 -9 -12 5 -9 -12 4 -9 -12 3 -9 -12 2 -9 +3 8 -7 +3 8 -6 +3 8 -5 +3 8 -4 +5 1 -3 +5 2 -3 +5 3 -3 +5 4 -3 +5 5 -3 +5 6 -3 +5 7 -3 +12 1 -3 +12 2 -3 +12 3 -3 +12 4 -3 +12 5 -3 +12 6 -3 +12 7 -3 12 1 -9 +12 2 -9 +12 3 -9 +12 4 -9 +12 5 -9 +12 6 -9 +12 7 -9 12 7 -8 12 6 -8 12 5 -8 @@ -778,86 +750,82 @@ numvert 182 12 3 -8 12 2 -8 12 1 -8 -11 0 -7 -10 0 -7 -9 0 -7 -8 0 -7 -7 0 -7 -6 0 -7 -6 1 -7 -7 1 -7 -8 1 -7 -9 1 -7 -10 1 -7 -11 1 -7 -6 2 -7 -7 2 -7 -8 2 -7 -9 2 -7 -10 2 -7 -11 2 -7 -6 3 -7 -7 3 -7 -8 3 -7 -9 3 -7 -10 3 -7 -11 3 -7 -6 4 -7 -7 4 -7 -8 4 -7 -9 4 -7 -10 4 -7 -11 4 -7 -6 5 -7 -7 5 -7 -8 5 -7 -9 5 -7 -10 5 -7 -11 5 -7 -6 6 -7 -7 6 -7 -8 6 -7 -9 6 -7 -10 6 -7 -11 6 -7 -6 7 -7 -7 7 -7 -8 7 -7 -9 7 -7 -10 7 -7 -11 7 -7 -11 8 -13 -10 8 -13 -9 8 -13 -8 8 -13 -7 8 -13 -6 8 -13 -5 8 -13 -4 8 -13 -11 8 -12 -10 8 -12 -9 8 -12 -8 8 -12 -7 8 -12 -6 8 -12 -5 8 -12 -4 8 -12 -11 8 -11 -10 8 -11 -9 8 -11 -8 8 -11 -7 8 -11 -6 8 -11 -5 8 -11 -4 8 -11 -11 8 -10 -10 8 -10 -9 8 -10 -8 8 -10 -7 8 -10 -6 8 -10 -5 8 -10 -4 8 -10 +12 7 -7 +12 6 -7 +12 5 -7 +12 4 -7 +12 3 -7 +12 2 -7 +12 1 -7 +12 1 -6 +12 2 -6 +12 3 -6 +12 4 -6 +12 5 -6 +12 6 -6 +12 7 -6 +12 7 -5 +12 6 -5 +12 5 -5 +12 4 -5 +12 3 -5 +12 2 -5 +12 1 -5 +12 7 -4 +12 6 -4 +12 5 -4 +12 4 -4 +12 3 -4 +12 2 -4 +12 1 -4 +11 0 -3 +10 0 -3 +9 0 -3 +8 0 -3 +7 0 -3 +6 0 -3 +6 1 -3 +7 1 -3 +8 1 -3 +9 1 -3 +10 1 -3 +11 1 -3 +6 2 -3 +7 2 -3 +8 2 -3 +9 2 -3 +10 2 -3 +11 2 -3 +6 3 -3 +7 3 -3 +8 3 -3 +9 3 -3 +10 3 -3 +11 3 -3 +6 4 -3 +7 4 -3 +8 4 -3 +9 4 -3 +10 4 -3 +11 4 -3 +6 5 -3 +7 5 -3 +8 5 -3 +9 5 -3 +10 5 -3 +11 5 -3 +6 6 -3 +7 6 -3 +8 6 -3 +9 6 -3 +10 6 -3 +11 6 -3 +6 7 -3 +7 7 -3 +8 7 -3 +9 7 -3 +10 7 -3 +11 7 -3 11 8 -9 10 8 -9 9 8 -9 @@ -874,13 +842,45 @@ numvert 182 6 8 -8 5 8 -8 4 8 -8 -4 8 -7 11 8 -7 10 8 -7 9 8 -7 8 8 -7 7 8 -7 6 8 -7 +5 8 -7 +4 8 -7 +11 8 -6 +10 8 -6 +9 8 -6 +8 8 -6 +7 8 -6 +6 8 -6 +5 8 -6 +4 8 -6 +11 8 -5 +10 8 -5 +9 8 -5 +8 8 -5 +7 8 -5 +6 8 -5 +5 8 -5 +4 8 -5 +11 8 -4 +10 8 -4 +9 8 -4 +8 8 -4 +7 8 -4 +6 8 -4 +5 8 -4 +4 8 -4 +4 8 -3 +11 8 -3 +10 8 -3 +9 8 -3 +8 8 -3 +7 8 -3 +6 8 -3 numsurf 158 SURF 0x00 mat 4 @@ -1997,10 +1997,17 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 45 -14 0 -6 14 0 -2 +14 0 2 +6 0 2 6 0 -2 -6 0 -6 +13 0 2 +12 0 2 +11 0 2 +10 0 2 +9 0 2 +8 0 2 +7 0 2 13 0 -2 12 0 -2 11 0 -2 @@ -2008,40 +2015,33 @@ numvert 45 9 0 -2 8 0 -2 7 0 -2 -13 0 -6 -12 0 -6 -11 0 -6 -10 0 -6 -9 0 -6 -8 0 -6 -7 0 -6 -14 0 -5 -14 0 -4 -14 0 -3 -6 0 -3 -6 0 -4 -6 0 -5 -13 0 -5 -13 0 -4 -13 0 -3 -12 0 -5 -12 0 -4 -12 0 -3 -11 0 -5 -11 0 -4 -11 0 -3 -10 0 -5 -10 0 -4 -10 0 -3 -9 0 -3 -9 0 -4 -9 0 -5 -8 0 -3 -8 0 -4 -8 0 -5 -7 0 -3 -7 0 -4 -7 0 -5 +14 0 -1 +14 0 0 +14 0 1 +6 0 1 +6 0 0 +6 0 -1 +13 0 -1 +13 0 0 +13 0 1 +12 0 -1 +12 0 0 +12 0 1 +11 0 -1 +11 0 0 +11 0 1 +10 0 -1 +10 0 0 +10 0 1 +9 0 1 +9 0 0 +9 0 -1 +8 0 1 +8 0 0 +8 0 -1 +7 0 1 +7 0 0 +7 0 -1 numsurf 32 SURF 0x00 mat 1 @@ -2276,137 +2276,67 @@ texture "TowerBlock1.png" texrep 1 1 crease 30.000000 numvert 201 -5 0 -7 -5 0 -2 -0 0 -2 -0 15 -7 -0 15 -2 -5 15 -2 -5 15 -7 -0 15 -6 -0 15 -5 -0 15 -4 +5 0 -3 +5 0 2 +0 0 2 0 15 -3 +0 15 2 +5 15 2 5 15 -3 -5 15 -4 -5 15 -5 -5 15 -6 -5 0 -6 -5 0 -5 -5 0 -4 -5 0 -3 -1 15 -7 -2 15 -7 -3 15 -7 -4 15 -7 -1 15 -2 -2 15 -2 -3 15 -2 -4 15 -2 -1 0 -2 -2 0 -2 -3 0 -2 -4 0 -2 -1 15 -6 -2 15 -6 -3 15 -6 -4 15 -6 -1 15 -5 -2 15 -5 -3 15 -5 -4 15 -5 -1 15 -4 -2 15 -4 -3 15 -4 -4 15 -4 +0 15 -2 +0 15 -1 +0 15 0 +0 15 1 +5 15 1 +5 15 0 +5 15 -1 +5 15 -2 +5 0 -2 +5 0 -1 +5 0 0 +5 0 1 1 15 -3 2 15 -3 3 15 -3 4 15 -3 -0 14 -2 -0 13 -2 -0 12 -2 -0 11 -2 -0 10 -2 -0 9 -2 -0 8 -2 -0 7 -2 -0 6 -2 -0 5 -2 -0 4 -2 -0 3 -2 -0 2 -2 -0 1 -2 -5 14 -7 -5 13 -7 -5 12 -7 -5 11 -7 -5 10 -7 -5 9 -7 -5 8 -7 -5 7 -7 -5 6 -7 -5 5 -7 -5 4 -7 -5 3 -7 -5 2 -7 -5 1 -7 -5 14 -2 -5 13 -2 -5 12 -2 -5 11 -2 -5 10 -2 -5 9 -2 -5 8 -2 -5 7 -2 -5 6 -2 -5 5 -2 -5 4 -2 -5 3 -2 -5 2 -2 -5 1 -2 -5 14 -6 -5 13 -6 -5 12 -6 -5 11 -6 -5 10 -6 -5 9 -6 -5 8 -6 -5 7 -6 -5 6 -6 -5 5 -6 -5 4 -6 -5 3 -6 -5 2 -6 -5 1 -6 -5 14 -5 -5 13 -5 -5 12 -5 -5 11 -5 -5 10 -5 -5 9 -5 -5 8 -5 -5 7 -5 -5 6 -5 -5 5 -5 -5 4 -5 -5 3 -5 -5 2 -5 -5 1 -5 -5 14 -4 -5 13 -4 -5 12 -4 -5 11 -4 -5 10 -4 -5 9 -4 -5 8 -4 -5 7 -4 -5 6 -4 -5 5 -4 -5 4 -4 -5 3 -4 -5 2 -4 -5 1 -4 +1 15 2 +2 15 2 +3 15 2 +4 15 2 +1 0 2 +2 0 2 +3 0 2 +4 0 2 +1 15 -2 +2 15 -2 +3 15 -2 +4 15 -2 +1 15 -1 +2 15 -1 +3 15 -1 +4 15 -1 +1 15 0 +2 15 0 +3 15 0 +4 15 0 +1 15 1 +2 15 1 +3 15 1 +4 15 1 +0 14 2 +0 13 2 +0 12 2 +0 11 2 +0 10 2 +0 9 2 +0 8 2 +0 7 2 +0 6 2 +0 5 2 +0 4 2 +0 3 2 +0 2 2 +0 1 2 5 14 -3 5 13 -3 5 12 -3 @@ -2421,101 +2351,133 @@ numvert 201 5 3 -3 5 2 -3 5 1 -3 -4 1 -2 -4 2 -2 -4 3 -2 -4 4 -2 -4 5 -2 -4 6 -2 -4 7 -2 -4 8 -2 -4 9 -2 -4 10 -2 -4 11 -2 -4 12 -2 -4 13 -2 -4 14 -2 -3 14 -2 -3 13 -2 -3 12 -2 -3 11 -2 -3 10 -2 -3 9 -2 -3 8 -2 -3 7 -2 -3 6 -2 -3 5 -2 -3 4 -2 -3 3 -2 -3 2 -2 -3 1 -2 -2 1 -2 -2 2 -2 -2 3 -2 -2 4 -2 -2 5 -2 -2 6 -2 -2 7 -2 -2 8 -2 -2 9 -2 -2 10 -2 -2 11 -2 -2 12 -2 -2 13 -2 -2 14 -2 -1 1 -2 -1 2 -2 -1 3 -2 -1 4 -2 -1 5 -2 -1 6 -2 -1 7 -2 -1 8 -2 -1 9 -2 -1 10 -2 -1 11 -2 -1 12 -2 -1 13 -2 -1 14 -2 -numsurf 181 -SURF 0x00 -mat 4 -refs 4 -1 -1.49011611938e-08 1.0 -0 0.0 0.75 -16 0.25 0.75 -17 0.25 1.0 -SURF 0x00 -mat 4 -refs 3 -16 -1.49011611938e-08 1.0 -0 0.0 0.75 -15 0.25 0.75 -SURF 0x00 -mat 4 -refs 3 -17 -1.49011611938e-08 1.0 -18 0.0 0.75 -1 0.25 0.75 -SURF 0x00 -mat 4 -refs 4 -29 -1.49011611938e-08 1.0 -28 0.0 0.75 -2 0.25 0.75 -1 0.25 1.0 -SURF 0x00 -mat 4 -refs 3 -30 -1.49011611938e-08 1.0 -29 0.0 0.75 -1 0.25 0.75 -SURF 0x00 -mat 4 -refs 3 -28 -1.49011611938e-08 1.0 -27 0.0 0.75 -2 0.25 0.75 +5 14 2 +5 13 2 +5 12 2 +5 11 2 +5 10 2 +5 9 2 +5 8 2 +5 7 2 +5 6 2 +5 5 2 +5 4 2 +5 3 2 +5 2 2 +5 1 2 +5 14 -2 +5 13 -2 +5 12 -2 +5 11 -2 +5 10 -2 +5 9 -2 +5 8 -2 +5 7 -2 +5 6 -2 +5 5 -2 +5 4 -2 +5 3 -2 +5 2 -2 +5 1 -2 +5 14 -1 +5 13 -1 +5 12 -1 +5 11 -1 +5 10 -1 +5 9 -1 +5 8 -1 +5 7 -1 +5 6 -1 +5 5 -1 +5 4 -1 +5 3 -1 +5 2 -1 +5 1 -1 +5 14 0 +5 13 0 +5 12 0 +5 11 0 +5 10 0 +5 9 0 +5 8 0 +5 7 0 +5 6 0 +5 5 0 +5 4 0 +5 3 0 +5 2 0 +5 1 0 +5 14 1 +5 13 1 +5 12 1 +5 11 1 +5 10 1 +5 9 1 +5 8 1 +5 7 1 +5 6 1 +5 5 1 +5 4 1 +5 3 1 +5 2 1 +5 1 1 +4 1 2 +4 2 2 +4 3 2 +4 4 2 +4 5 2 +4 6 2 +4 7 2 +4 8 2 +4 9 2 +4 10 2 +4 11 2 +4 12 2 +4 13 2 +4 14 2 +3 14 2 +3 13 2 +3 12 2 +3 11 2 +3 10 2 +3 9 2 +3 8 2 +3 7 2 +3 6 2 +3 5 2 +3 4 2 +3 3 2 +3 2 2 +3 1 2 +2 1 2 +2 2 2 +2 3 2 +2 4 2 +2 5 2 +2 6 2 +2 7 2 +2 8 2 +2 9 2 +2 10 2 +2 11 2 +2 12 2 +2 13 2 +2 14 2 +1 1 2 +1 2 2 +1 3 2 +1 4 2 +1 5 2 +1 6 2 +1 7 2 +1 8 2 +1 9 2 +1 10 2 +1 11 2 +1 12 2 +1 13 2 +1 14 2 +numsurf 175 SURF 0x00 mat 4 refs 4 @@ -3750,10 +3712,10 @@ texture "Trees.png" texrep 1 1 crease 30.000000 numvert 4 -12.7 3 -6.46203 -12.7 0 -6.46203 -15.7 0 -6.46203 -15.7 3 -6.46203 +12.7 3 -2.46203 +12.7 0 -2.46203 +15.7 0 -2.46203 +15.7 3 -2.46203 numsurf 1 SURF 0x00 mat 5 @@ -3771,14 +3733,21 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 48 +14 0.5 2 14 0.5 -2 -14 0.5 -6 -6 0.5 -6 6 0.5 -2 -14 0 -6 -6 0 -6 +6 0.5 2 14 0 -2 6 0 -2 +14 0 2 +6 0 2 +13 0.5 2 +12 0.5 2 +11 0.5 2 +10 0.5 2 +9 0.5 2 +8 0.5 2 +7 0.5 2 13 0.5 -2 12 0.5 -2 11 0.5 -2 @@ -3786,13 +3755,13 @@ numvert 48 9 0.5 -2 8 0.5 -2 7 0.5 -2 -13 0.5 -6 -12 0.5 -6 -11 0.5 -6 -10 0.5 -6 -9 0.5 -6 -8 0.5 -6 -7 0.5 -6 +13 0 2 +12 0 2 +11 0 2 +10 0 2 +9 0 2 +8 0 2 +7 0 2 13 0 -2 12 0 -2 11 0 -2 @@ -3800,25 +3769,18 @@ numvert 48 9 0 -2 8 0 -2 7 0 -2 -13 0 -6 -12 0 -6 -11 0 -6 -10 0 -6 -9 0 -6 -8 0 -6 -7 0 -6 -14 0.5 -3 -14 0.5 -4 -14 0.5 -5 -6 0.5 -5 -6 0.5 -4 -6 0.5 -3 -14 0 -5 -14 0 -4 -14 0 -3 -6 0 -5 -6 0 -4 -6 0 -3 +14 0.5 1 +14 0.5 0 +14 0.5 -1 +6 0.5 -1 +6 0.5 0 +6 0.5 1 +14 0 -1 +14 0 0 +14 0 1 +6 0 -1 +6 0 0 +6 0 1 numsurf 24 SURF 0x30 mat 1 @@ -3997,96 +3959,96 @@ texture "Building.png" texrep 1 1 crease 30.000000 numvert 90 -28 0 -10 -28 0 -7 -24 0 -7 -24 0 -10 -28 4 -7 -24 4 -7 -28 4 -10 -24 4 -10 -26 6 -7 -26 6 -10 -26 6.5 -7 -26 6.5 -10 -28 0 -9 -28 0 -8 -24 0 -8 -24 0 -9 -28 4 -8 -28 4 -9 -24 4 -8 -24 4 -9 -26 6 -8 -26 6 -9 -26 6.5 -8 -26 6.5 -9 -28 1 -7 -28 2 -7 -28 3 -7 -24 1 -7 -24 2 -7 -24 3 -7 -28 1 -10 -28 2 -10 -28 3 -10 -24 1 -10 -24 2 -10 -24 3 -10 -28 1 -8 -28 2 -8 -28 3 -8 -28 3 -9 -28 2 -9 -28 1 -9 -24 1 -9 -24 2 -9 -24 3 -9 -24 1 -8 -24 2 -8 -24 3 -8 -27 0 -7 -26 0 -7 -25 0 -7 -27 4 -7 -26 4 -7 -25 4 -7 -25 1 -7 -26 1 -7 -27 1 -7 -27 2 -7 -26 2 -7 -25 2 -7 -25 3 -7 -26 3 -7 -27 3 -7 -25 5 -7 -27 5 -7 -26 5 -7 -23.878679 3.87868 -7 -23.878679 3.87868 -10 -28.121321 3.87868 -7 -28.121321 3.87868 -10 -24.585787 4.585787 -7 -25.292892 5.292893 -7 -25.292892 5.292893 -10 -24.585787 4.585787 -10 -23.878679 3.87868 -8 -23.878679 3.87868 -9 -24.585787 4.585787 -8 -25.292892 5.292893 -8 -24.585787 4.585787 -9 -25.292892 5.292893 -9 -27.414213 4.585786 -10 -26.707108 5.292893 -10 -28.121321 3.87868 -8 -28.121321 3.87868 -9 -27.414213 4.585787 -7 -26.707108 5.292893 -7 -26.707108 5.292893 -8 -27.414213 4.585786 -8 -26.707108 5.292893 -9 -27.414213 4.585786 -9 +28 0 -6 +28 0 -3 +24 0 -3 +24 0 -6 +28 4 -3 +24 4 -3 +28 4 -6 +24 4 -6 +26 6 -3 +26 6 -6 +26 6.5 -3 +26 6.5 -6 +28 0 -5 +28 0 -4 +24 0 -4 +24 0 -5 +28 4 -4 +28 4 -5 +24 4 -4 +24 4 -5 +26 6 -4 +26 6 -5 +26 6.5 -4 +26 6.5 -5 +28 1 -3 +28 2 -3 +28 3 -3 +24 1 -3 +24 2 -3 +24 3 -3 +28 1 -6 +28 2 -6 +28 3 -6 +24 1 -6 +24 2 -6 +24 3 -6 +28 1 -4 +28 2 -4 +28 3 -4 +28 3 -5 +28 2 -5 +28 1 -5 +24 1 -5 +24 2 -5 +24 3 -5 +24 1 -4 +24 2 -4 +24 3 -4 +27 0 -3 +26 0 -3 +25 0 -3 +27 4 -3 +26 4 -3 +25 4 -3 +25 1 -3 +26 1 -3 +27 1 -3 +27 2 -3 +26 2 -3 +25 2 -3 +25 3 -3 +26 3 -3 +27 3 -3 +25 5 -3 +27 5 -3 +26 5 -3 +23.878679 3.87868 -3 +23.878679 3.87868 -6 +28.121321 3.87868 -3 +28.121321 3.87868 -6 +24.585787 4.585787 -3 +25.292892 5.292893 -3 +25.292892 5.292893 -6 +24.585787 4.585787 -6 +23.878679 3.87868 -4 +23.878679 3.87868 -5 +24.585787 4.585787 -4 +25.292892 5.292893 -4 +24.585787 4.585787 -5 +25.292892 5.292893 -5 +27.414213 4.585786 -6 +26.707108 5.292893 -6 +28.121321 3.87868 -4 +28.121321 3.87868 -5 +27.414213 4.585787 -3 +26.707108 5.292893 -3 +26.707108 5.292893 -4 +27.414213 4.585786 -4 +26.707108 5.292893 -5 +27.414213 4.585786 -5 numsurf 67 SURF 0x20 mat 2 @@ -4404,9 +4366,9 @@ refs 3 SURF 0x00 mat 2 refs 3 -8 0.5 0.5 +8 0.375 0.75 65 0.375 0.5 -64 0.375 0.75 +64 0.5 0.5 SURF 0x00 mat 2 refs 4 @@ -4418,14 +4380,14 @@ SURF 0x00 mat 2 refs 4 52 0.375 0.5 -65 0.25 0.5 +65 0.375 0.75 63 0.25 0.75 -53 0.375 0.75 +53 0.25 0.5 SURF 0x00 mat 2 refs 3 -4 0.375 0.75 -64 0.5 0.5 +4 0.5 0.5 +64 0.375 0.75 51 0.375 0.5 SURF 0x00 mat 2 @@ -4560,30 +4522,30 @@ data 6 M-Lawn crease 30.000000 numvert 24 -31 0 -2 -31 0 0 -15 0 0 +31 0 2 +31 0 4 +15 0 4 +15 0 2 +23 0 -3 +23 0 -2 15 0 -2 -23 0 -7 -23 0 -6 -15 0 -6 -15 0 -7 -18 0 -6 -18 0 -5 -19 0 -5 -19 0 -6 -19 0 -4 -19 0 -2 +15 0 -3 18 0 -2 -18 0 -4 -28 0 -5 -28 0 -3 -24 0 -3 -24 0 -5 -33 0 -7 -33 0 -6 -29 0 -6 -29 0 -7 +18 0 -1 +19 0 -1 +19 0 -2 +19 0 0 +19 0 2 +18 0 2 +18 0 0 +28 0 -1 +28 0 1 +24 0 1 +24 0 -1 +33 0 -3 +33 0 -2 +29 0 -2 +29 0 -3 numsurf 6 SURF 0x00 mat 3 @@ -4636,174 +4598,174 @@ texture "TowerBlock1.png" texrep 1 1 crease 30.000000 numvert 168 -38 0 -9 -38 0 -3 +38 0 -5 +38 0 1 +33 0 1 +33 0 -5 +33 0 0 +33 0 -1 +33 0 -2 33 0 -3 -33 0 -9 33 0 -4 -33 0 -5 -33 0 -6 -33 0 -7 -33 0 -8 -33 1 -3 -33 2 -3 -33 3 -3 -33 4 -3 -33 5 -3 +33 1 1 +33 2 1 +33 3 1 +33 4 1 +33 5 1 +33 6 1 +38 1 -5 +38 2 -5 +38 3 -5 +38 4 -5 +38 5 -5 +38 6 -5 +38 1 1 +38 2 1 +38 3 1 +38 4 1 +38 5 1 +38 6 1 +33 6 -5 +33 5 -5 +33 4 -5 +33 3 -5 +33 2 -5 +33 1 -5 +33 6 0 +33 5 0 +33 4 0 +33 3 0 +33 2 0 +33 1 0 +33 6 -1 +33 5 -1 +33 4 -1 +33 3 -1 +33 2 -1 +33 1 -1 +33 6 -2 +33 5 -2 +33 4 -2 +33 3 -2 +33 2 -2 +33 1 -2 33 6 -3 -38 1 -9 -38 2 -9 -38 3 -9 -38 4 -9 -38 5 -9 -38 6 -9 -38 1 -3 -38 2 -3 -38 3 -3 -38 4 -3 -38 5 -3 -38 6 -3 -33 6 -9 -33 5 -9 -33 4 -9 -33 3 -9 -33 2 -9 -33 1 -9 +33 5 -3 +33 4 -3 +33 3 -3 +33 2 -3 +33 1 -3 33 6 -4 33 5 -4 33 4 -4 33 3 -4 33 2 -4 33 1 -4 -33 6 -5 -33 5 -5 -33 4 -5 -33 3 -5 -33 2 -5 -33 1 -5 -33 6 -6 -33 5 -6 -33 4 -6 -33 3 -6 -33 2 -6 -33 1 -6 -33 6 -7 -33 5 -7 -33 4 -7 -33 3 -7 -33 2 -7 -33 1 -7 -33 6 -8 -33 5 -8 -33 4 -8 -33 3 -8 -33 2 -8 -33 1 -8 -37 0 -3 -36 0 -3 -35 0 -3 -34 0 -3 -34 1 -3 -35 1 -3 -36 1 -3 -37 1 -3 -34 2 -3 -35 2 -3 -36 2 -3 -37 2 -3 -37 3 -3 -36 3 -3 -35 3 -3 -34 3 -3 -34 4 -3 -35 4 -3 -36 4 -3 -37 4 -3 -34 5 -3 -35 5 -3 -36 5 -3 -37 5 -3 -37 6 -3 -36 6 -3 -35 6 -3 -34 6 -3 -38 0 -8 -38 0 -7 -38 0 -6 -38 0 -5 +37 0 1 +36 0 1 +35 0 1 +34 0 1 +34 1 1 +35 1 1 +36 1 1 +37 1 1 +34 2 1 +35 2 1 +36 2 1 +37 2 1 +37 3 1 +36 3 1 +35 3 1 +34 3 1 +34 4 1 +35 4 1 +36 4 1 +37 4 1 +34 5 1 +35 5 1 +36 5 1 +37 5 1 +37 6 1 +36 6 1 +35 6 1 +34 6 1 38 0 -4 +38 0 -3 +38 0 -2 +38 0 -1 +38 0 0 +38 1 0 +38 1 -1 +38 1 -2 +38 1 -3 38 1 -4 -38 1 -5 -38 1 -6 -38 1 -7 -38 1 -8 +38 2 0 +38 2 -1 +38 2 -2 +38 2 -3 38 2 -4 -38 2 -5 -38 2 -6 -38 2 -7 -38 2 -8 +38 3 0 +38 3 -1 +38 3 -2 +38 3 -3 38 3 -4 -38 3 -5 -38 3 -6 -38 3 -7 -38 3 -8 -38 4 -8 -38 4 -7 -38 4 -6 -38 4 -5 38 4 -4 -38 5 -8 -38 5 -7 -38 5 -6 -38 5 -5 +38 4 -3 +38 4 -2 +38 4 -1 +38 4 0 38 5 -4 +38 5 -3 +38 5 -2 +38 5 -1 +38 5 0 +38 6 0 +38 6 -1 +38 6 -2 +38 6 -3 38 6 -4 -38 6 -5 -38 6 -6 -38 6 -7 -38 6 -8 -33 7 -9 -34 7 -9 -35 7 -9 -36 7 -9 -37 7 -9 -38 7 -9 -38 7 -8 -38 7 -7 -38 7 -6 +33 7 -5 +34 7 -5 +35 7 -5 +36 7 -5 +37 7 -5 38 7 -5 38 7 -4 38 7 -3 +38 7 -2 +38 7 -1 +38 7 0 +38 7 1 +33 7 1 +34 7 1 +35 7 1 +36 7 1 +37 7 1 +33 7 -4 33 7 -3 -34 7 -3 -35 7 -3 +33 7 -2 +33 7 -1 +33 7 0 36 7 -3 37 7 -3 -33 7 -8 -33 7 -7 -33 7 -6 -33 7 -5 -33 7 -4 -36 7 -7 -37 7 -7 -37 7 -8 -36 7 -8 -35 7 -7 -35 7 -8 -34 7 -7 -34 7 -8 -36 7 -6 -37 7 -6 -35 7 -6 -34 7 -6 -36 7 -5 -37 7 -5 -35 7 -5 -34 7 -5 -36 7 -4 37 7 -4 +36 7 -4 +35 7 -3 35 7 -4 +34 7 -3 34 7 -4 +36 7 -2 +37 7 -2 +35 7 -2 +34 7 -2 +36 7 -1 +37 7 -1 +35 7 -1 +34 7 -1 +36 7 0 +37 7 0 +35 7 0 +34 7 0 numsurf 149 SURF 0x00 mat 4 @@ -5857,26 +5819,79 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 87 +33 0 4 +33 0 3 +33 0 2 +33 0 1 33 0 0 33 0 -1 33 0 -2 -33 0 -3 -33 0 -4 -33 0 -5 -33 0 -6 -18 0 -4 -18 0 -5 -31 0 0 +18 0 0 +18 0 -1 +31 0 4 +32 0 4 +29 0 -3 +28 0 -3 +27 0 -3 +26 0 -3 +25 0 -3 +24 0 -3 +23 0 -3 +32 0 3 +31 0 3 +19 0 2 +20 0 2 +21 0 2 +22 0 2 +23 0 2 +24 0 2 +25 0 2 +26 0 2 +27 0 2 +28 0 2 +29 0 2 +30 0 2 +31 0 2 +32 0 2 +32 0 1 +31 0 1 +30 0 1 +29 0 1 +28 0 1 +27 0 1 +26 0 1 +25 0 1 +24 0 1 +23 0 1 +22 0 1 +21 0 1 +20 0 1 +19 0 1 32 0 0 -29 0 -7 -28 0 -7 -27 0 -7 -26 0 -7 -25 0 -7 -24 0 -7 -23 0 -7 +31 0 0 +30 0 0 +29 0 0 +28 0 0 +24 0 0 +23 0 0 +22 0 0 +21 0 0 +20 0 0 +19 0 0 32 0 -1 31 0 -1 +30 0 -1 +29 0 -1 +28 0 -1 +27 0 -1 +26 0 -1 +25 0 -1 +24 0 -1 +23 0 -1 +22 0 -1 +21 0 -1 +20 0 -1 +19 0 -1 19 0 -2 20 0 -2 21 0 -2 @@ -5891,59 +5906,6 @@ numvert 87 30 0 -2 31 0 -2 32 0 -2 -32 0 -3 -31 0 -3 -30 0 -3 -29 0 -3 -28 0 -3 -27 0 -3 -26 0 -3 -25 0 -3 -24 0 -3 -23 0 -3 -22 0 -3 -21 0 -3 -20 0 -3 -19 0 -3 -32 0 -4 -31 0 -4 -30 0 -4 -29 0 -4 -28 0 -4 -24 0 -4 -23 0 -4 -22 0 -4 -21 0 -4 -20 0 -4 -19 0 -4 -32 0 -5 -31 0 -5 -30 0 -5 -29 0 -5 -28 0 -5 -27 0 -5 -26 0 -5 -25 0 -5 -24 0 -5 -23 0 -5 -22 0 -5 -21 0 -5 -20 0 -5 -19 0 -5 -19 0 -6 -20 0 -6 -21 0 -6 -22 0 -6 -23 0 -6 -24 0 -6 -25 0 -6 -26 0 -6 -27 0 -6 -28 0 -6 -29 0 -6 -30 0 -6 -31 0 -6 -32 0 -6 numsurf 59 SURF 0x10 mat 1 @@ -6367,22 +6329,22 @@ texture "Trees.png" texrep 1 1 crease 30.000000 numvert 16 -27 3 -4.5 -27 0 -4.5 -24 0 -4.5 -24 3 -4.5 -24 3 -4.5 -24 0 -4.5 -27 0 -4.5 -27 3 -4.5 -28.299999 3 -3.46203 -28.299999 0 -3.46203 -25.299999 0 -3.46203 -25.299999 3 -3.46203 -25.299999 3 -3.46203 -25.299999 0 -3.46203 -28.299999 0 -3.46203 -28.299999 3 -3.46203 +27 3 -0.5 +27 0 -0.5 +24 0 -0.5 +24 3 -0.5 +24 3 -0.5 +24 0 -0.5 +27 0 -0.5 +27 3 -0.5 +28.299999 3 0.53797 +28.299999 0 0.53797 +25.299999 0 0.53797 +25.299999 3 0.53797 +25.299999 3 0.53797 +25.299999 0 0.53797 +28.299999 0 0.53797 +28.299999 3 0.53797 numsurf 4 SURF 0x00 mat 5 @@ -6421,94 +6383,94 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 88 -24 0.5 -4 -28 0.5 -4 -24 0.5 -5 -25 0.5 -5 -26 0.5 -5 -27 0.5 -5 -28 0.5 -5 -24 0.5 -3 -25 0.5 -3 -26 0.5 -3 -27 0.5 -3 -28 0.5 -3 -24 0 -5 -25 0 -5 -26 0 -5 -27 0 -5 -28 0 -5 -24 0 -4 -28 0 -4 -24 0 -3 -25 0 -3 -26 0 -3 -27 0 -3 -28 0 -3 -23 0.5 -7 -29 0.5 -7 -19 0.5 -3 -31 0.5 -1 -31 0.5 0 -33 0.5 -6 -32 0.5 -6 -31 0.5 -6 -30 0.5 -6 -29 0.5 -6 -23 0.5 -6 -22 0.5 -6 -21 0.5 -6 -20 0.5 -6 -19 0.5 -6 -19 0.5 -5 -18 0.5 -5 -19 0.5 -4 -18 0.5 -4 +24 0.5 0 +28 0.5 0 +24 0.5 -1 +25 0.5 -1 +26 0.5 -1 +27 0.5 -1 +28 0.5 -1 +24 0.5 1 +25 0.5 1 +26 0.5 1 +27 0.5 1 +28 0.5 1 +24 0 -1 +25 0 -1 +26 0 -1 +27 0 -1 +28 0 -1 +24 0 0 +28 0 0 +24 0 1 +25 0 1 +26 0 1 +27 0 1 +28 0 1 +23 0.5 -3 +29 0.5 -3 +19 0.5 1 +31 0.5 3 +31 0.5 4 +33 0.5 -2 +32 0.5 -2 31 0.5 -2 30 0.5 -2 29 0.5 -2 -28 0.5 -2 -27 0.5 -2 -26 0.5 -2 -25 0.5 -2 -24 0.5 -2 23 0.5 -2 22 0.5 -2 21 0.5 -2 20 0.5 -2 19 0.5 -2 -32 0 -6 -31 0 -6 -30 0 -6 -29 0 -6 -23 0 -6 -22 0 -6 -21 0 -6 -20 0 -6 -19 0 -6 -19 0 -5 -19 0 -4 -19 0 -3 +19 0.5 -1 +18 0.5 -1 +19 0.5 0 +18 0.5 0 +31 0.5 2 +30 0.5 2 +29 0.5 2 +28 0.5 2 +27 0.5 2 +26 0.5 2 +25 0.5 2 +24 0.5 2 +23 0.5 2 +22 0.5 2 +21 0.5 2 +20 0.5 2 +19 0.5 2 +32 0 -2 31 0 -2 30 0 -2 29 0 -2 -28 0 -2 -27 0 -2 -26 0 -2 -25 0 -2 -24 0 -2 23 0 -2 22 0 -2 21 0 -2 20 0 -2 19 0 -2 -31 0 -1 -23 0 -7 -29 0 -7 -31 0 0 -18 0 -5 -18 0 -4 -33 0 -6 +19 0 -1 +19 0 0 +19 0 1 +31 0 2 +30 0 2 +29 0 2 +28 0 2 +27 0 2 +26 0 2 +25 0 2 +24 0 2 +23 0 2 +22 0 2 +21 0 2 +20 0 2 +19 0 2 +31 0 3 +23 0 -3 +29 0 -3 +31 0 4 +18 0 -1 +18 0 0 +33 0 -2 numsurf 41 SURF 0x30 mat 1 @@ -6804,14 +6766,14 @@ data 6 R-Lawn crease 30.000000 numvert 8 -48 0 -2 -48 0 0 -40 0 0 -40 0 -2 -45 0 -7 -45 0 -6 -39 0 -6 -39 0 -7 +48 0 2 +48 0 4 +40 0 4 +40 0 2 +45 0 -3 +45 0 -2 +39 0 -2 +39 0 -3 numsurf 2 SURF 0x20 mat 3 @@ -6836,74 +6798,74 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 68 -33 0 0 -34 0 0 -35 0 0 -36 0 0 -37 0 0 -38 0 0 -39 0 0 -40 0 0 -38 0 -7 -39 0 -7 +33 0 4 +34 0 4 +35 0 4 +36 0 4 +37 0 4 +38 0 4 +39 0 4 +40 0 4 +38 0 -3 +39 0 -3 +44 0 2 +44 0 1 +44 0 0 +44 0 -1 44 0 -2 -44 0 -3 -44 0 -4 -44 0 -5 -44 0 -6 -33 0 -1 -33 0 -2 -33 0 -3 -34 0 -3 -34 0 -2 -34 0 -1 -35 0 -3 -35 0 -2 -35 0 -1 -36 0 -1 -36 0 -2 -36 0 -3 -37 0 -3 -37 0 -2 -37 0 -1 +33 0 3 +33 0 2 +33 0 1 +34 0 1 +34 0 2 +34 0 3 +35 0 1 +35 0 2 +35 0 3 +36 0 3 +36 0 2 +36 0 1 +37 0 1 +37 0 2 +37 0 3 +38 0 3 +38 0 2 +38 0 1 +38 0 0 38 0 -1 38 0 -2 -38 0 -3 -38 0 -4 -38 0 -5 -38 0 -6 -39 0 -6 -39 0 -5 -39 0 -4 -39 0 -3 39 0 -2 39 0 -1 -40 0 -6 -40 0 -5 -40 0 -4 -40 0 -3 +39 0 0 +39 0 1 +39 0 2 +39 0 3 40 0 -2 40 0 -1 -41 0 -6 -41 0 -5 -41 0 -4 -41 0 -3 +40 0 0 +40 0 1 +40 0 2 +40 0 3 41 0 -2 -42 0 -6 -42 0 -5 -42 0 -4 -42 0 -3 +41 0 -1 +41 0 0 +41 0 1 +41 0 2 42 0 -2 -43 0 -6 -43 0 -5 -43 0 -4 -43 0 -3 +42 0 -1 +42 0 0 +42 0 1 +42 0 2 43 0 -2 +43 0 -1 +43 0 0 +43 0 1 +43 0 2 +45 0 2 +45 0 1 +45 0 0 +45 0 -1 45 0 -2 -45 0 -3 -45 0 -4 -45 0 -5 -45 0 -6 numsurf 48 SURF 0x00 mat 1 @@ -7250,112 +7212,56 @@ texture "TowerBlock1.png" texrep 1 1 crease 30.000000 numvert 201 -50 0 -2 -45 0 -2 -45 15 -7 -45 15 -2 -50 15 -2 -45 0 -7 -45 15 -6 -45 15 -5 -45 15 -4 +50 0 2 +45 0 2 45 15 -3 +45 15 2 +50 15 2 45 0 -3 -45 0 -4 -45 0 -5 -45 0 -6 -49 0 -2 -48 0 -2 -47 0 -2 -46 0 -2 -46 15 -2 -47 15 -2 -48 15 -2 -49 15 -2 -45 1 -2 -45 2 -2 -45 3 -2 -45 4 -2 -45 5 -2 -45 6 -2 -45 7 -2 -45 8 -2 -45 9 -2 -45 10 -2 -45 11 -2 -45 12 -2 -45 13 -2 -45 14 -2 -50 1 -2 -50 2 -2 -50 3 -2 -50 4 -2 -50 5 -2 -50 6 -2 -50 7 -2 -50 8 -2 -50 9 -2 -50 10 -2 -50 11 -2 -50 12 -2 -50 13 -2 -50 14 -2 -45 14 -7 -45 13 -7 -45 12 -7 -45 11 -7 -45 10 -7 -45 9 -7 -45 8 -7 -45 7 -7 -45 6 -7 -45 5 -7 -45 4 -7 -45 3 -7 -45 2 -7 -45 1 -7 -45 14 -6 -45 13 -6 -45 12 -6 -45 11 -6 -45 10 -6 -45 9 -6 -45 8 -6 -45 7 -6 -45 6 -6 -45 5 -6 -45 4 -6 -45 3 -6 -45 2 -6 -45 1 -6 -45 14 -5 -45 13 -5 -45 12 -5 -45 11 -5 -45 10 -5 -45 9 -5 -45 8 -5 -45 7 -5 -45 6 -5 -45 5 -5 -45 4 -5 -45 3 -5 -45 2 -5 -45 1 -5 -45 14 -4 -45 13 -4 -45 12 -4 -45 11 -4 -45 10 -4 -45 9 -4 -45 8 -4 -45 7 -4 -45 6 -4 -45 5 -4 -45 4 -4 -45 3 -4 -45 2 -4 -45 1 -4 +45 15 -2 +45 15 -1 +45 15 0 +45 15 1 +45 0 1 +45 0 0 +45 0 -1 +45 0 -2 +49 0 2 +48 0 2 +47 0 2 +46 0 2 +46 15 2 +47 15 2 +48 15 2 +49 15 2 +45 1 2 +45 2 2 +45 3 2 +45 4 2 +45 5 2 +45 6 2 +45 7 2 +45 8 2 +45 9 2 +45 10 2 +45 11 2 +45 12 2 +45 13 2 +45 14 2 +50 1 2 +50 2 2 +50 3 2 +50 4 2 +50 5 2 +50 6 2 +50 7 2 +50 8 2 +50 9 2 +50 10 2 +50 11 2 +50 12 2 +50 13 2 +50 14 2 45 14 -3 45 13 -3 45 12 -3 @@ -7370,87 +7276,143 @@ numvert 201 45 3 -3 45 2 -3 45 1 -3 -49 1 -2 -49 2 -2 -49 3 -2 -49 4 -2 -49 5 -2 -49 6 -2 -49 7 -2 -49 8 -2 -49 9 -2 -49 10 -2 -49 11 -2 -49 12 -2 -49 13 -2 -49 14 -2 -48 1 -2 -48 2 -2 -48 3 -2 -48 4 -2 -48 5 -2 -48 6 -2 -48 7 -2 -48 8 -2 -48 9 -2 -48 10 -2 -48 11 -2 -48 12 -2 -48 13 -2 -48 14 -2 -47 14 -2 -47 13 -2 -47 12 -2 -47 11 -2 -47 10 -2 -47 9 -2 -47 8 -2 -47 7 -2 -47 6 -2 -47 5 -2 -47 4 -2 -47 3 -2 -47 2 -2 -47 1 -2 -46 1 -2 -46 2 -2 -46 3 -2 -46 4 -2 -46 5 -2 -46 6 -2 -46 7 -2 -46 8 -2 -46 9 -2 -46 10 -2 -46 11 -2 -46 12 -2 -46 13 -2 -46 14 -2 +45 14 -2 +45 13 -2 +45 12 -2 +45 11 -2 +45 10 -2 +45 9 -2 +45 8 -2 +45 7 -2 +45 6 -2 +45 5 -2 +45 4 -2 +45 3 -2 +45 2 -2 +45 1 -2 +45 14 -1 +45 13 -1 +45 12 -1 +45 11 -1 +45 10 -1 +45 9 -1 +45 8 -1 +45 7 -1 +45 6 -1 +45 5 -1 +45 4 -1 +45 3 -1 +45 2 -1 +45 1 -1 +45 14 0 +45 13 0 +45 12 0 +45 11 0 +45 10 0 +45 9 0 +45 8 0 +45 7 0 +45 6 0 +45 5 0 +45 4 0 +45 3 0 +45 2 0 +45 1 0 +45 14 1 +45 13 1 +45 12 1 +45 11 1 +45 10 1 +45 9 1 +45 8 1 +45 7 1 +45 6 1 +45 5 1 +45 4 1 +45 3 1 +45 2 1 +45 1 1 +49 1 2 +49 2 2 +49 3 2 +49 4 2 +49 5 2 +49 6 2 +49 7 2 +49 8 2 +49 9 2 +49 10 2 +49 11 2 +49 12 2 +49 13 2 +49 14 2 +48 1 2 +48 2 2 +48 3 2 +48 4 2 +48 5 2 +48 6 2 +48 7 2 +48 8 2 +48 9 2 +48 10 2 +48 11 2 +48 12 2 +48 13 2 +48 14 2 +47 14 2 +47 13 2 +47 12 2 +47 11 2 +47 10 2 +47 9 2 +47 8 2 +47 7 2 +47 6 2 +47 5 2 +47 4 2 +47 3 2 +47 2 2 +47 1 2 +46 1 2 +46 2 2 +46 3 2 +46 4 2 +46 5 2 +46 6 2 +46 7 2 +46 8 2 +46 9 2 +46 10 2 +46 11 2 +46 12 2 +46 13 2 +46 14 2 +46 15 1 +47 15 1 +48 15 1 +49 15 1 +50 15 1 +46 15 0 +47 15 0 +48 15 0 +49 15 0 +50 15 0 +46 15 -1 +47 15 -1 +48 15 -1 +49 15 -1 +50 15 -1 +46 15 -2 +47 15 -2 +48 15 -2 +49 15 -2 +50 15 -2 46 15 -3 47 15 -3 48 15 -3 49 15 -3 50 15 -3 -46 15 -4 -47 15 -4 -48 15 -4 -49 15 -4 -50 15 -4 -46 15 -5 -47 15 -5 -48 15 -5 -49 15 -5 -50 15 -5 -46 15 -6 -47 15 -6 -48 15 -6 -49 15 -6 -50 15 -6 -46 15 -7 -47 15 -7 -48 15 -7 -49 15 -7 -50 15 -7 numsurf 175 SURF 0x00 mat 4 @@ -8686,18 +8648,18 @@ texture "Trees.png" texrep 1 1 crease 30.000000 numvert 12 -40.900002 3 -7.4 -40.900002 0 -7.4 -43.900002 0 -7.4 -43.900002 3 -7.4 -38.800003 3 -6.76203 -38.800003 0 -6.76203 -41.800003 0 -6.76203 -41.800003 3 -6.76203 -45 3 -6.16203 -45 0 -6.16203 -42 0 -6.16203 -42 3 -6.16203 +40.900002 3 -3.4 +40.900002 0 -3.4 +43.900002 0 -3.4 +43.900002 3 -3.4 +38.800003 3 -2.76203 +38.800003 0 -2.76203 +41.800003 0 -2.76203 +41.800003 3 -2.76203 +45 3 -2.16203 +45 0 -2.16203 +42 0 -2.16203 +42 3 -2.16203 numsurf 3 SURF 0x00 mat 5 @@ -8729,38 +8691,38 @@ texture "Scenery.png" texrep 1 1 crease 30.000000 numvert 32 -44 0.5 -6 44 0.5 -2 -43 0.5 -6 +44 0.5 2 43 0.5 -2 -42 0.5 -6 +43 0.5 2 42 0.5 -2 -41 0.5 -6 +42 0.5 2 41 0.5 -2 -40 0.5 -6 -40 0.5 0 -40 0.5 -1 +41 0.5 2 40 0.5 -2 -39 0.5 -6 -39 0.5 -7 +40 0.5 4 +40 0.5 3 +40 0.5 2 +39 0.5 -2 +39 0.5 -3 +43 0 2 43 0 -2 -43 0 -6 +42 0 2 42 0 -2 -42 0 -6 +41 0 2 41 0 -2 -41 0 -6 -40 0 -1 +40 0 3 +40 0 2 40 0 -2 -40 0 -6 -39 0 -6 -44 0 -6 +39 0 -2 44 0 -2 -39 0 -7 -40 0 0 +44 0 2 +39 0 -3 +40 0 4 +45 0 2 45 0 -2 -45 0 -6 +45 0.5 2 45 0.5 -2 -45 0.5 -6 numsurf 14 SURF 0x30 mat 1 diff --git a/data/models/classic.blend b/data/models/classic.blend index 8ed07734cfc81af37262790133c07b0bbf6520a4..122fc036f6ec2b6f64d465cb23b6cd959cb4ca55 100644 GIT binary patch literal 80861 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%0DDuf<-Xi`MF5PE15AV8=IB!s+!&vWl{ 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 SwCTx-2DskX$ LOW then - ResetTransform() - Translate(0, 0, -3) - DrawTilemap({ - width = 7, - 2, 2, 2, 2, 2, 2, 2, - 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 6, 1, 0, - 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, - 4, 4, 5, 5, 5, 4, 4}) - - -- Right side - - ResetTransform() - Rotate(Y, 90) - Translate(7, 0, -3) - DrawTilemap({ - width = 6, - 2, 2, 2, 2, 2, 2, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 4, 4, 4, 4, 4, 4}) - - -- Top - - ResetTransform() - Rotate(X, 90) - Translate(-2, 8, -9) - DrawTilemap({ - width = 9, - 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3}) -end - --- Foreground building with pitched roof --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Left wall - -ResetTransform() -Rotate(Y, -90) -Translate(10, 0, -2) -SetTexture("Building") -DrawTilemap({ - width = 4, - surface = LEFT, - -1, 9, 11, -1, - 9, 10, 12, 11, - 15, 7, 7, 16, - 3, 5, 6, 4, - 3, 6, 5, 4}) - --- Right wall - -ResetTransform() -Rotate(Y, -90) -Translate(13, 0, -2) -DrawTilemap({ - width = 4, - surface = RIGHT, - -1, 9, 11, -1, - 9, 10, 12, 11, - 15, 7, 7, 16, - 3, 5, 6, 4, - 3, 8, 5, 4}) - --- Front wall - -ResetTransform() -Translate(10, 0, 2) -DrawTilemap({ - width = 3, - 15, 7, 16, - 3, 5, 4, - 3, 6, 4}) - --- Pitched roof - -ResetTransform() -Rotate(X, 135) -Scale(1, 1.5, 1.5) -Translate(10, 5, 0) -DrawTilemap({ - width = 3, - 13, 13, 13, - 13, 13, 13}) - --- Finial - -ResetTransform() -Translate(10, 5, -0.00001) -DrawTilemap({ - width = 3, - 18, 18, 18}) - --- Cheaty invisible platform --- This draws nothing but creates a platform on the roof for walking. - -ResetTransform() -Rotate(X, 90) -Translate(10, 5, 0) -DrawTilemap({ - width = 3, - surface = TOP, - -1, -1, -1}) - --- The ground --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Courtyard - -ResetTransform() -Rotate(X, 90) -Translate(-3, 0, -3) -SetTexture("Scenery") -DrawTilemap({ - width = 13, - surface = TOP, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, - -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, - -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, - -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, - -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) - --- Front grass - -if detail > MEDIUM then - ResetTransform() - Scale(8, 1, 1) - Translate(1, -0.5, 2) - DrawTile({ - 2, - u_scale = 8}) - - -- Back grass - - ResetTransform() - Scale(8, 1, 1) - Translate(1, -0.5, -2) - DrawTile({ - 2, - u_scale = 8 - }) - - -- Left grass - - ResetTransform() - Scale(4, 1, 1) - Rotate(Y, -90) - Translate(1, -0.5, -2) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Right grass - - ResetTransform() - Scale(4, 1, 1) - Rotate(Y, -90) - Translate(9, -0.5, -2) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Fence behind house - - ResetTransform() - Scale(11, 1, 1) - Translate(7, 0, -3) - DrawTile({ - 4, - u_scale = 11 - }) -end - --- Background building with pitched roof --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Front wall - -if detail > LOW then - ResetTransform() - Translate(19, 0, -3) - SetTexture("Building") - DrawTilemap({ - width = 4, - -1, 9, 11, -1, - 9, 10, 12, 11, - 15, 7, 7, 16, - 3, 6, 5, 4, - 3, 5, 6, 4, - 3, 8, 5, 4}) - - -- Left wall - - ResetTransform() - Rotate(Y, -90) - Translate(19, 0, -6) - DrawTilemap({ - width = 3, - 15, 1, 16, - 3, 7, 4, - 3, 5, 4, - 3, 0, 4}) - - -- Right wall - - ResetTransform() - Rotate(Y, -90) - Translate(23, 0, -6) - DrawTilemap({ - width = 3, - 15, 0, 16, - 3, 7, 4, - 3, 6, 4, - 3, 2, 4}) - - -- Left pitched roof - - ResetTransform() - Rotate(X, 135) - Scale(1, 1.5, 1.5) - Rotate(Y, -90) - Translate(21, 6, -6) - DrawTilemap({ - width = 3, - 13, 13, 13, - 13, 13, 13}) - - -- Right pitched roof - - ResetTransform() - Rotate(X, -135) - Scale(1, 1.5, 1.5) - Rotate(Y, -90) - Translate(21, 6, -6) - DrawTilemap({ - width = 3, - 13, 13, 13, - 13, 13, 13}) - - -- Finial - - ResetTransform() - Rotate(Y, -90) - Translate(21, 6, -6) - DrawTilemap({ - width = 3, - 18, 18, 18}) -end - --- More ground to the right --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Ground under house - -ResetTransform() -Rotate(X, 90) -Translate(10, 0, -3) -SetTexture("Scenery") -DrawTilemap({ - width = 3, - surface = TOP, - 1, 1, 1, - 1, 1, 1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - 1, 1, 1}) - --- Left part of center courtyard - -ResetTransform() -Rotate(X, 90) -Translate(13, 0, -3) -DrawTilemap({ - width = 8, - surface = TOP, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0}) - --- Front grass - -if detail > MEDIUM then - ResetTransform() - Scale(12, 1, 1) - Translate(14, -0.5, 2) - DrawTile({ - 2, - u_scale = 12 - }) - - -- Back grass - - ResetTransform() - Scale(4, 1, 1) - Translate(14, -0.5, -2) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Front grass next to door - - ResetTransform() - Translate(13, -0.5, 0) - DrawTile({ - 2, - u_scale = 1 - }) - - -- Back grass next to door - - ResetTransform() - Translate(13, -0.5, -1) - DrawTile({ - 2, - u_scale = 1 - }) - - -- Left grass - - ResetTransform() - Rotate(Y, -90) - Translate(14, -0.5, -2) - DrawTilemap({ - width = 4, - 2, -1, 2, 2}) - - -- Grass left of house - - ResetTransform() - Rotate(Y, -90) - Translate(18, -0.5, -3) - DrawTile({ - 2, - u_scale = 1 - }) - - -- Grass right of house - - ResetTransform() - Rotate(Y, -90) - Translate(24, -0.5, -3) - DrawTile({ - 2, - u_scale = 1 - }) - - -- Front grass in center - - ResetTransform() - Scale(4, 1, 1) - Translate(19, -0.5, 1) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Back grass in center - - ResetTransform() - Scale(4, 1, 1) - Translate(19, -0.5, -1) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Left grass in center - - ResetTransform() - Scale(2, 1, 1) - Rotate(Y, -90) - Translate(19, -0.5, -1) - DrawTile({ - 2, - u_scale = 2 - }) - - -- Right grass in center - - ResetTransform() - Scale(2, 1, 1) - Rotate(Y, -90) - Translate(23, -0.5, -1) - DrawTile({ - 2, - u_scale = 2 - }) -end - --- Right part of center courtyard - -ResetTransform() -Rotate(X, 90) -Translate(21, 0, -3) -DrawTilemap({ - width = 7, - surface = TOP, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1}) - --- Fence to right of back house - -if detail > MEDIUM then - ResetTransform() - Scale(4, 1, 1) - Translate(24, 0, -3) - DrawTile({ - 4, - u_scale = 4 - }) - - -- Grass in front of fence - - ResetTransform() - Scale(4, 1, 1) - Translate(24, -0.5, -2) - DrawTile({ - 2, - u_scale = 4 - }) - - -- Grass to left of tower block - - ResetTransform() - Scale(2, 1, 1) - Rotate(Y, -90) - Translate(26, -0.5, 2) - DrawTile({ - 2, - u_scale = 2 - }) - - -- Grass to right of tower block - - ResetTransform() - Scale(2, 1, 1) - Rotate(Y, -90) - Translate(35, -0.5, 2) - DrawTile({ - 2, - u_scale = 2 - }) - - -- Next bit of grass - - ResetTransform() - Scale(5, 1, 1) - Translate(35, -0.5, 2) - DrawTile({ - 2, - u_scale = 5 - }) - - -- Back grass - - ResetTransform() - Scale(6, 1, 1) - Translate(34, -0.5, -2) - DrawTile({ - 2, - u_scale = 6 - }) - - -- Extra bit of back grass - - ResetTransform() - Rotate(Y, -90) - Translate(34, -0.5, -3) - DrawTile({ - 2, - u_scale = 1 - }) -end - --- Ground around tower block - -ResetTransform() -Rotate(X, 90) -Translate(28, 0, 1) -DrawTilemap({ - width = 5, - surface = TOP, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0}) - --- Rightmost ground - -ResetTransform() -Rotate(X, 90) -Translate(33, 0, -3) -DrawTilemap({ - width = 10, - surface = TOP, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, - 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, - 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, - 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, - 0, 1, 1, 1, 1, 1, 1, -1, -1, -1}) - --- Right foreground tower block --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Front - -ResetTransform() -Translate(28, 0, 1) -SetTexture("TowerBlock1") -DrawTilemap({ - width = 5, - 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 6, 0, - 4, 4, 4, 4, 4}) - --- Right side - -ResetTransform() -Rotate(Y, 90) -Translate(33, 0, 1) -DrawTilemap({ - width = 6, - surface = RIGHT, - 2, 2, 2, 2, 2, 2, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 5, 4, 5, 5, 4, 5}) - --- Left side - -ResetTransform() -Rotate(Y, 90) -Translate(28, 0, 1) -DrawTilemap({ - width = 6, - surface = LEFT, - 2, 2, 2, 2, 2, 2, - 0, 1, 6, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, - 5, 4, 5, 5, 4, 5}) - --- Top - -ResetTransform() -Rotate(X, 90) -Translate(28, 7, -5) -DrawTilemap({ - width = 5, - surface = TOP, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3}) - --- Right end tower block --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- Front - -ResetTransform() -Translate(40, 0, 2) -DrawTilemap({ - width = 5, - 2, 2, 2, 2, 2, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 6, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 6, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 0, 1, 0, 0, 1, - 4, 4, 4, 4, 4}) - --- Left side - -ResetTransform() -Rotate(Y, 90) -Translate(40, 0, 2) -DrawTilemap({ - width = 5, - surface = LEFT, - 2, 2, 2, 2, 2, - 6, 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, 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, - 4, 5, 5, 5, 4}) - --- Top - -ResetTransform() -Rotate(X, 90) -Translate(40, 15, -3) -DrawTilemap({ - width = 5, - surface = TOP, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3}) - --- Background --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -ResetTransform() -Translate(-0.3, -0.3, -35) -Scale(105, 52, 1) -SetTexture("BackgroundFar") -DrawTile() - -Translate(0, 0, 5) -SetTexture("BackgroundNear") -DrawTile({ - blend = detail > LOW and true or false -}) - --- Trees --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -SetTexture("Trees") - --- Left courtyard - -if detail > LOW then - ResetTransform() - Scale(3) - Translate(7.75, -0.1, -2.5) - DrawTile(1) -end - --- Center courtyard - -ResetTransform() -Scale(3) -Translate(19, -0.1, -0.5) -DrawTile(0) - -ResetTransform() -Scale(3) -Translate(20.25, -0.1, 0.5) -DrawTile(1) - --- Right courtyard - -if detail > LOW then - ResetTransform() - Scale(3) - Translate(34, -0.1, -2.75) - DrawTile(1) - - ResetTransform() - Scale(3) - Translate(36, -0.1, -3.5) - DrawTile(0) - - ResetTransform() - Scale(3) - Translate(37, -0.1, -2.25) - DrawTile(1) -end +--geometry = yoink.mesh("classic.ac") +--geometry:draw() +--geometry = yoink.mesh.fromstring([[ +--AC3Db +--OBJECT poly +--name "Hello World" +--... +--]] -- Functions: @@ -845,7 +66,37 @@ end Event = {} -function Event:Think() +do + local mysound = yoink.sound("Explosion") + local count = 0 + function Event.Think() + if count % 300 == 0 then + --mysound:play() + end + count = count + 1 + end +end + + +classic_mesh = yoink.mesh("classic") + +--drawme = {} + +--world = classic_mesh:object(1) +--for i = 1, 19 do + --local object = world:kid(i) + --if object then table.insert(drawme, object) end +--end + +--lawn = classic_mesh:object(1):kid("M-Lawn") + +--Event.Draw = function() tower:draw(false) end +Event.Draw = function() + --for i,object in ipairs(drawme) do + --object:draw() + --end + --lawn:draw() + classic_mesh:draw() end function Event:BadGuyDied(enemy) diff --git a/src/Animation.cc b/src/Animation.cc index fead5ca..a9710ce 100644 --- a/src/Animation.cc +++ b/src/Animation.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include "Animation.hh" @@ -139,11 +140,6 @@ public: moof::script script; std::string path = moof::resource::find_file("animations/"+name, "lua"); - //if (!resource::find(path)) - //{ - //throw std::runtime_error("cannot find resource " + name); - //} - script.import_base_library(); moof::log::import(script); importAnimationBindings(script); diff --git a/src/Animation.hh b/src/Animation.hh index 3875b45..97e916d 100644 --- a/src/Animation.hh +++ b/src/Animation.hh @@ -22,7 +22,6 @@ #include #include -#include class Animation; @@ -36,7 +35,7 @@ typedef boost::shared_ptr AnimationP; * about anything to whatever drawing context is used to render the frame. */ -class Animation : public moof::resource +class Animation { public: diff --git a/src/GameLayer.cc b/src/GameLayer.cc index 5e95b76..8ff3a75 100644 --- a/src/GameLayer.cc +++ b/src/GameLayer.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -24,8 +25,10 @@ void GameLayer::loadSceneLoader() { - state_.script.import_standard_libraries(); + state_.script.import_safe_standard_libraries(); moof::log::import(state_.script); + moof::mesh::import(state_.script, "yoink"); + moof::sound::import(state_.script, "yoink"); std::string path = moof::resource::find_file("scenes/loader.lua"); if (path.empty()) @@ -45,7 +48,7 @@ void GameLayer::loadSceneLoader() state_.script.top().get(state_.sceneList); if (state_.sceneList.size() == 0) { - throw std::runtime_error("no variable `scenes' in script loader."); + throw std::runtime_error("no variable `scenes' in script loader"); } } @@ -73,27 +76,31 @@ void GameLayer::advanceScene(moof::settings& settings) state_.script.registry().set_field(); } state_.script.pop(); + + table = state_.script.globals().push_field("Event"); + if (table.is_table()) + { + table.push_field("Draw"); + state_.script.registry().set_field("Draw"); + } + state_.script.pop(); } } GameLayer::GameLayer() { - moof::log_info("about to load sound resource..."); - music_.sample("sounds/NightFusionIntro.ogg"); + music_.sample("NightFusionIntro.ogg"); music_.loop(true); - music_.enqueue("sounds/NightFusionLoop.ogg"); + music_.enqueue("NightFusionLoop.ogg"); music_.position(moof::vector3(10.0, 5.0, 0.0)); - mThinkTimer.init(boost::bind(&GameLayer::thinkTimer, this), - 0.1, moof::timer::repeat); + punch_sound_.sample("RobotPunch"); state_.heroine = Heroine::alloc(); state_.heroine->animation.startSequence("FlyDiagonallyUp"); state_.interp.init(0.0, 1.0, 4.0, moof::lerp_scalar::oscillate); - - sceneMesh = moof::resource::load("classic.ac"); } @@ -101,7 +108,7 @@ void GameLayer::did_add_to_view() { bool isMute = false; settings().get("nomusic", isMute); - music_.stream(); + music_.play(); loadSceneLoader(); advanceScene(settings()); // load the first scene @@ -137,16 +144,12 @@ void GameLayer::update(moof::scalar t, moof::scalar dt) mRay.point = state_.heroine->state().position; - moof::view::update(t, dt); -} - -void GameLayer::thinkTimer() -{ state_.script.registry().push_field("Think"); if (state_.script[-1].is_function()) state_.script.call(); - else state_.script.pop(); -} + else state_.script.pop(); + moof::view::update(t, dt); +} void GameLayer::rayTimer() { @@ -189,7 +192,7 @@ void GameLayer::draw(moof::scalar alpha) const state_.camera.upload_to_gl(alpha); float pos[] = {state_.heroine->state().position[0], - state_.heroine->state().position[1], 0.0f}; + state_.heroine->state().position[1], 0.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_POSITION, pos); // DRAW THE SCENE @@ -199,7 +202,24 @@ void GameLayer::draw(moof::scalar alpha) const glEnableClientState(GL_TEXTURE_COORD_ARRAY); //state_.scene->draw_if_visible(alpha, state_.camera.frustum()); - sceneMesh->draw(alpha); + + size_t meh = 23; + state_.script.push(meh); + state_.script.pop(); + + state_.script.registry().push_field("Draw"); + if (state_.script[-1].is_function()) + { + moof::script::status result = state_.script.call(); + if (result != moof::script::success) + { + std::string str; + state_.script[-1].get(str); + throw std::runtime_error("script error: " + str); + } + } + else state_.script.pop(); + state_.heroine->draw(alpha); mRay.draw(); diff --git a/src/GameLayer.hh b/src/GameLayer.hh index 54b4996..f381443 100644 --- a/src/GameLayer.hh +++ b/src/GameLayer.hh @@ -24,7 +24,6 @@ #include #include -#include #include #include // TODO @@ -62,22 +61,16 @@ private: void loadSceneLoader(); void advanceScene(moof::settings& settings); - void thinkTimer(); - void projection(); void projection(moof::scalar width, moof::scalar height); - GameState state_; - moof::timer mThinkTimer; + mutable GameState state_; HudP mHud; - //moof::sound_stream mMusic; moof::sound punch_sound_; moof::sound music_; - moof::mesh_handle sceneMesh; - moof::ray2 mRay; moof::line2 mLine; moof::circle mCircle; diff --git a/src/Main.cc b/src/Main.cc index ae183ec..13681ee 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -17,6 +17,12 @@ #include #include +#if !defined(__WIN32) +#include +#else +int isatty(int dummy) { return 0; } +#endif + #include #include @@ -25,6 +31,7 @@ #include #include #include +#include #include #include "GameLayer.hh" @@ -37,9 +44,15 @@ Main::Main(moof::settings& settings, moof::video& video) : moof::view(settings, video) { moof::dispatcher& dispatcher = moof::dispatcher::global(); - mNewContextDispatch = dispatcher.add_target("video.newcontext", - boost::bind(&Main::setupGL)); - setupGL(); + video_reloaded_ = dispatcher.add_target("video.newcontext", + boost::bind(&Main::setup_opengl)); + setup_opengl(); + +#if USE_HOTLOADING + hotload_timer_.init(boost::bind(&moof::resource::reload_as_needed), + SCALAR(0.25), + moof::timer::repeat); +#endif } @@ -100,7 +113,7 @@ bool Main::handle_event(const moof::event& event) } -std::string Main::getSearchPath() +std::string Main::search_paths() { // Add search paths; they should be searched in this order: // 1. YOINK_DATADIR (environment) @@ -118,7 +131,7 @@ std::string Main::getSearchPath() return path; } -std::string Main::getConfigPath() +std::string Main::config_paths() { // Build the list of config files to search for, in this order: // 1. YOINK_DATADIR/yoinkrc @@ -144,17 +157,19 @@ std::string Main::getConfigPath() } -void Main::setupGL() +void Main::setup_opengl() { glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); + //glEnable(GL_CULL_FACE); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_POLYGON_SMOOTH); + //glEnable(GL_POINT_SMOOTH); + //glEnable(GL_LINE_SMOOTH); + //glEnable(GL_POLYGON_SMOOTH); glShadeModel(GL_SMOOTH); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.0); @@ -163,21 +178,21 @@ void Main::setupGL() //glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); - float light[] = {1.0f, 1.0f, 1.0f, 1.0f}; - glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, light); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + float amb[] = {0.1f, 0.1f, 0.1f, 1.0f}; + float dif[] = {0.6f, 0.6f, 0.6f, 1.0f}; + //glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); - //glMatrixMode(GL_PROJECTION); - //glLoadIdentity(); - //moof::scalar ratio = moof::core.getVideo()->width() / - //moof::core.getVideo()->height(); - //gluPerspective(60.0, ratio, 1.0, 250.0); - - //glMatrixMode(GL_MODELVIEW); + float spec[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_SPECULAR, spec); } -void Main::printUsage() +void Main::print_usage() { std::cout << "Usage: " << PACKAGE" [-h|--help] [-i|--info] [OPTION=VALUE]..." @@ -199,18 +214,33 @@ void Main::printUsage() << "See documentation for more options." << std::endl; } -void Main::printInfo(int argc, char* argv[]) + +void Main::print_info(int argc, char* argv[]) { + bool color = (isatty(1) == 1); + stlplus::env_vector environment; std::string assets; assets.assign(YOINK_DATADIR); - if (!stlplus::file_readable(assets)) assets += " (no access)"; + if (!stlplus::file_readable(assets)) + { + assets += " "; + if (color) assets += "\033[1;91m"; + assets += "(no access)"; + if (color) assets += "\033[0m"; + } std::string datadir = environment["YOINK_DATADIR"]; if (!datadir.empty()) { - if (!stlplus::folder_readable(datadir)) datadir += " (no access)"; + if (!stlplus::folder_readable(datadir)) + { + datadir += " "; + if (color) datadir += "\033[1;91m"; + datadir += "(no access)"; + if (color) datadir += "\033[0m"; + } } std::string rc_file = environment["YOINKRC"]; @@ -227,55 +257,75 @@ void Main::printInfo(int argc, char* argv[]) << " Built: " << COMPILE_TIME << std::endl << " Compiler: " << COMPILER_STRING << std::endl << " Assets: " << assets << std::endl - << "Build options: " -#if !USE_CLOCK_GETTIME - << "-" -#endif - << "clock_gettime " -#if !DEBUG - << "-" + << "Build options: "; + +#if USE_CLOCK_GETTIME + print_option("clock_gettime", true); +#else + print_option("clock_gettime", false); #endif - << "debug " -#if !USE_DOUBLE_PRECISION - << "-" +#if DEBUG + print_option("debug", true); +#else + print_option("debug", false); #endif - << "double-precision " -#if !USE_GTK - << "-" +#if USE_GTK + print_option("gtk", true); +#else + print_option("gtk", false); #endif - << "gtk " -#if !USE_HOTLOADING - << "-" +#if USE_HOTLOADING + print_option("hotload", true); +#else + print_option("hotload", false); #endif - << "hotloading " -#if !PROFILING_ENABLED - << "-" +#if PROFILING_ENABLED + print_option("profile", true); +#else + print_option("profile", false); #endif - << "profile " -#if !USE_QT4 - << "-" +#if USE_QT4 + print_option("qt4", true); +#else + print_option("qt4", false); #endif - << "qt4 " -#if !USE_THREADS - << "-" +#if USE_THREADS + print_option("threads", true); +#else + print_option("threads", false); #endif - << "threads" << std::endl - << " YOINKRC: " << rc_file << std::endl + std::cout << std::endl; + std::cout << " YOINKRC: " << rc_file << std::endl << "YOINK_DATADIR: " << datadir << std::endl; } +void Main::print_option(const std::string& option, bool enabled) +{ + if (isatty(1) == 1) + { + if (enabled) std::cout << "\033[1;94m"; + else std::cout << "\033[1m"; + } + if (!enabled) std::cout << "-"; + std::cout << option; + if (isatty(1) == 1) std::cout << "\033[0m"; + std::cout << " "; +} + void hello() { + if (isatty(1) == 1) std::cout << "\033[94m"; std::cout << std::endl << PACKAGE_STRING << std::endl << "Compiled " << __TIME__ " " __DATE__ << std::endl << "Send patches and bug reports to <" - PACKAGE_BUGREPORT << ">." << std::endl << std::endl; + PACKAGE_BUGREPORT << ">." << std::endl << moof::log::endl; } void goodbye() { - std::cout << std::endl << "Goodbye..." << std::endl << std::endl; + if (isatty(1) == 1) std::cout << "\033[94m"; + std::cout << std::endl << "Goodbye." << std::endl << moof::log::endl; } @@ -283,22 +333,17 @@ int main(int argc, char* argv[]) { moof::backend backend; - // FIXME: This is temporary. - moof::timer reloadTimer(boost::bind(&moof::resource::reload_as_needed), - SCALAR(2.0), - moof::timer::repeat); - if (argc > 1) { std::string arg(argv[1]); if (arg == "-h" || arg == "--help") { - Main::printUsage(); + Main::print_usage(); return 0; } else if (arg == "-i" || arg == "--info") { - Main::printInfo(argc, argv); + Main::print_info(argc, argv); return 0; } } @@ -306,9 +351,9 @@ int main(int argc, char* argv[]) hello(); atexit(goodbye); - moof::resource::add_search_paths(Main::getSearchPath()); + moof::resource::set_search_paths(Main::search_paths()); - moof::settings settings(argc, argv, Main::getConfigPath()); + moof::settings settings(argc, argv, Main::config_paths()); enum moof::log::level logLevel = moof::log::info; settings.get("loglevel", logLevel); @@ -335,11 +380,18 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { moof::modal_dialog dialog(moof::modal_dialog::error, - PACKAGE_STRING, "Unhandled Exception", + PACKAGE_STRING, "unhandled exception", e.what()); - dialog.run(); - return 1; } + catch (const char* e) + { + moof::modal_dialog dialog(moof::modal_dialog::error, + PACKAGE_STRING, "unhandled exception", + e); + dialog.run(); + } + + return 1; } diff --git a/src/Main.hh b/src/Main.hh index 0a3c616..4532e41 100644 --- a/src/Main.hh +++ b/src/Main.hh @@ -24,6 +24,7 @@ #include #include +#include #include @@ -47,20 +48,23 @@ public: void draw(moof::scalar alpha) const; bool handle_event(const moof::event& event); - static std::string getSearchPath(); - static std::string getConfigPath(); + static std::string search_paths(); + static std::string config_paths(); - static void printUsage(); - static void printInfo(int argc, char* argv[]); + static void print_usage(); + static void print_info(int argc, char* argv[]); private: /** * Set OpenGL to a state we can know and depend on. */ - static void setupGL(); + static void setup_opengl(); - moof::dispatcher::handle mNewContextDispatch; + static void print_option(const std::string& option, bool enabled); + + moof::dispatcher::handle video_reloaded_; + moof::timer hotload_timer_; }; diff --git a/src/Scene.cc b/src/Scene.cc index a1218f2..af54c28 100644 --- a/src/Scene.cc +++ b/src/Scene.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,7 @@ struct Scene::impl : public moof::manager }; Quad(const moof::vector3* vertices[4], - const std::string& texture, + const moof::image_handle& texture, int tileIndex) : mTilemap(texture, tileIndex), mBlending(false), @@ -119,7 +120,7 @@ struct Scene::impl : public moof::manager moof::matrix4 mTransform; - std::string mTexture; + moof::image_handle mTexture; std::list< boost::shared_ptr > mObjects; std::list mLines; @@ -191,8 +192,8 @@ struct Scene::impl : public moof::manager static int loadBox(moof::script& script, moof::aabb3& aabb) { - script[1].require_table(); - script[2].require_table(); + script[1].require_table("point"); + script[2].require_table("point"); script[1].push_field(1).get(aabb.min[0]); script[1].push_field(2).get(aabb.min[1]); @@ -206,8 +207,7 @@ struct Scene::impl : public moof::manager int setBounds(moof::script& script) { - int ret = loadBox(script, mBounds); - return ret; + return loadBox(script, mBounds); } int resetTransform(moof::script& script) @@ -264,10 +264,10 @@ struct Scene::impl : public moof::manager int rotate(moof::script& script) { - size_t index = 0; - script[1].require_number().get(index); + size_t index = 0; + moof::scalar value; - moof::scalar value; + script[1].require_number().get(index); script[2].require_number().get(value); moof::matrix_rotate_about_world_axis(mTransform, index, @@ -278,7 +278,9 @@ struct Scene::impl : public moof::manager int setTexture(moof::script& script) { - script[1].require_string().get(mTexture); + std::string texture_name; + script[1].require_string().get(texture_name); + mTexture = moof::resource::load(texture_name, "png"); return 0; } diff --git a/src/Scene.hh b/src/Scene.hh index 3c14b47..8c60cc5 100644 --- a/src/Scene.hh +++ b/src/Scene.hh @@ -19,7 +19,6 @@ #include #include -#include #include @@ -33,7 +32,7 @@ class settings; class Scene; typedef boost::shared_ptr SceneP; -class Scene : public moof::cullable, public moof::drawable, public moof::resource +class Scene : public moof::cullable, public moof::drawable { class impl; boost::shared_ptr impl_; diff --git a/src/moof/entity.hh b/src/moof/entity.hh index b503551..f95a94c 100644 --- a/src/moof/entity.hh +++ b/src/moof/entity.hh @@ -79,7 +79,7 @@ public: * Get the axis-aligned bounding box surrounding the entity. * \return The AABB. */ - const moof::aabb<3>& aabb() const + const moof::aabb3& aabb() const { return aabb_; } @@ -87,7 +87,7 @@ public: /** Get the bounding sphere surrounding the entity. * \return The sphere. */ - const moof::sphere<3>& sphere() const + const moof::sphere3& sphere() const { return sphere_; } @@ -95,8 +95,8 @@ public: protected: - moof::aabb<3> aabb_; - moof::sphere<3> sphere_; + moof::aabb3 aabb_; + moof::sphere3 sphere_; }; diff --git a/src/moof/image.cc b/src/moof/image.cc index 64b2f2e..7453b48 100644 --- a/src/moof/image.cc +++ b/src/moof/image.cc @@ -27,6 +27,8 @@ namespace moof { +MOOF_REGISTER_RESOURCE(image, png, textures); + //static int power_of_two(int input) //{ //int value = 1; @@ -342,23 +344,5 @@ void image::set_texture_info(const std::string& info) } -class image_resource_loader -{ -public: - - image_resource_loader() - { - resource::register_type("png", "textures"); - } - - ~image_resource_loader() - { - resource::unregister_type("png"); - } -}; - -static image_resource_loader loader; - - } // namespace moof diff --git a/src/moof/log.cc b/src/moof/log.cc index 017ce36..f9a3ad9 100644 --- a/src/moof/log.cc +++ b/src/moof/log.cc @@ -11,6 +11,13 @@ #include +#if !defined(__WIN32) +#include +#else +int isatty(int dummy) { return 0; } +#endif + + #include "log.hh" #include "script.hh" @@ -32,14 +39,49 @@ enum log::level log::level() } -std::ostream& log(std::clog); +log::log(enum level level) : + level_(level) +{ + if (isatty(1) == 0) + switch (level) + { + case log::error: prefix_ = " error: "; break; + case log::warning: prefix_ = "warning: "; break; + case log::info: prefix_ = " info: "; break; + case log::none: break; + } + else + switch (level) + { + case log::error: prefix_ = "\033[101m error: "; break; + case log::warning: prefix_ = "\033[103mwarning: "; break; + case log::info: prefix_ = " info: "; break; + case log::none: break; + } +} + +std::ostream& operator << (class log& log, log::endl_ endl) +{ + if (log::global_level_ < log.level_) return null_log; + if (isatty(1) == 0) return moof::log << std::endl; + else return moof::log << "\033[0m" << std::endl; +} + +std::ostream& operator << (std::ostream& stream, log::endl_ endl) +{ + if (isatty(1) == 0) return stream << std::endl; + else return stream << "\033[0m" << std::endl; +} + + +std::ostream& log(std::cout); static std::ofstream null_log_; std::ostream& null_log(null_log_); -class log log_error( log::error, " error: "); -class log log_warning(log::warning, "warning: "); -class log log_info( log::info, " info: "); +class log log_error( log::error); +class log log_warning(log::warning); +class log log_info( log::info); static int log_script(script& script, enum log::level level) diff --git a/src/moof/log.hh b/src/moof/log.hh index a32daad..e6e1a80 100644 --- a/src/moof/log.hh +++ b/src/moof/log.hh @@ -21,6 +21,7 @@ #include // exit #include +#include namespace moof { @@ -79,33 +80,40 @@ public: * \param level The log level. * \param prefix The string printed before each log message. */ - log(enum level level, const char* prefix) : - level_(level), - prefix_(prefix) /* only pass literal strings */ {} + log(enum level level); + + + /** + * Output this to end a line. This is equivalent to std::endl except + * this will also reset terminal format attributes. + */ + static struct endl_ + { + } endl; template void operator () (const A& a) { - *this << a << std::endl; + *this << a << endl; } template void operator () (const A& a, const B& b) { - *this << a << " " << b << std::endl; + *this << a << " " << b << endl; } template void operator () (const A& a, const B& b, const C& c) { - *this << a << " " << b << " " << c << std::endl; + *this << a << " " << b << " " << c << endl; } template void operator () (const A& a, const B& b, const C& c, const D& d) { - *this << a << " " << b << " " << c << " " << d << std::endl; + *this << a << " " << b << " " << c << " " << d << endl; } template @@ -119,18 +127,19 @@ public: << b << " " << c << " " << d << " " - << e << std::endl; + << e << endl; } private: template friend std::ostream& operator << (log&, const T&); + friend std::ostream& operator << (log&, endl_); static enum level global_level_; enum level level_; - const char* prefix_; + std::string prefix_; }; @@ -149,6 +158,9 @@ inline std::ostream& operator << (class log& log, const T& item) return moof::log << log.prefix_ << item; } +std::ostream& operator << (class log& log, log::endl_ endl); +std::ostream& operator << (std::ostream& stream, log::endl_ endl); + } // namespace moof diff --git a/src/moof/mesh.cc b/src/moof/mesh.cc index b41c377..023138b 100644 --- a/src/moof/mesh.cc +++ b/src/moof/mesh.cc @@ -25,6 +25,8 @@ #include "mesh.hh" #include "opengl.hh" +// TODO: this file needs to be cleaned up + #define AC3D_FORMAT_VERSION (0x0b) #define ZLIB_BUF_SIZE (262114) @@ -33,6 +35,9 @@ namespace moof { +MOOF_REGISTER_RESOURCE(mesh, ac, models); + + static std::string read_string(std::istream& stream) { std::string str; @@ -85,15 +90,21 @@ inline vector2 read_pair(std::istream& stream) return triplet; } - -template -inline vector< scalar, fixed > read_triplet(std::istream& stream) +inline vector3 read_triplet(std::istream& stream) { - vector< scalar, fixed > triplet; + vector3 triplet; stream >> triplet[0] >> triplet[1] >> triplet[2]; return triplet; } +inline vector4 read_color(std::istream& stream) +{ + vector4 color; + stream >> color[0] >> color[1] >> color[2]; + color[3] = SCALAR(1.0); + return color; +} + void mesh::import(std::istream& stream) { @@ -130,20 +141,18 @@ void mesh::import(std::istream& stream) materials_.push_back(material(read_string(stream))); stream >> atom; - materials_.back().diffuse = read_triplet<4>(stream); + materials_.back().diffuse = read_color(stream); stream >> atom; - materials_.back().ambient = read_triplet<3>(stream); + materials_.back().ambient = read_color(stream); stream >> atom; - materials_.back().emissive = read_triplet<3>(stream); + materials_.back().emissive = read_color(stream); stream >> atom; - materials_.back().specular = read_triplet<3>(stream); + materials_.back().specular = read_color(stream); stream >> atom >> materials_.back().shininess; - stream >> atom >> materials_.back().diffuse[3]; // transparency - materials_.back().diffuse[3] = 1.0; // FIXME: temporary - - log_info("read material", materials_.back().name, - materials_.back().diffuse); + stream >> atom >> materials_.back().diffuse[3]; + materials_.back().diffuse[3] = SCALAR(1.0) - + materials_.back().diffuse[3]; } else if (atom == "OBJECT") { @@ -153,7 +162,7 @@ void mesh::import(std::istream& stream) throw std::runtime_error("unexpected object type " + atom); } - object_ptr newObj = object::alloc(); + object_ptr newObj = object::alloc(*this); if (obj) { @@ -169,7 +178,12 @@ void mesh::import(std::istream& stream) } else if (atom == "name") { - if (obj) obj->name = read_string(stream); + if (obj) + { + obj->name = read_string(stream); + object_ptr parent = obj->parent.lock(); + if (parent) parent->kids_byname.insert(std::make_pair(obj->name, obj)); + } else throw std::runtime_error("unexpected atom: " + atom); } else if (atom == "data") @@ -208,12 +222,9 @@ void mesh::import(std::istream& stream) int numvert = 0; stream >> numvert; - log_warning("adding verts", numvert); - for (int i = 0; i < numvert; ++i) { - obj->verts.push_back(read_triplet<3>(stream)); - log_error("vert", obj->verts.back()); + obj->verts.push_back(read_triplet(stream)); } } else if (atom == "numsurf") @@ -228,8 +239,7 @@ void mesh::import(std::istream& stream) stream >> atom; if (atom != "SURF") throw std::runtime_error("uh oh"); - int flags = read_hex(stream); - log_info(flags); + read_hex(stream); int material = 0; stream >> atom; @@ -246,77 +256,104 @@ void mesh::import(std::istream& stream) if ((int)obj->faces.size() <= material) { - log_info("inserting face..."); - //obj->faces.insert(obj->faces.begin() + material, - //material_group()); obj->faces.resize(material + 1); - log_info("inserted face", material, obj->faces.size()); } - material_group& face = obj->faces[material]; - int vert; + unsigned vert; stream >> vert; vector2 uv = read_pair(stream); + if (vert < face.triangles_uv.size()) + { + if (uv != face.triangles_uv[vert]) + { + obj->verts.push_back(obj->verts[vert]); + face.triangles_uv.resize(obj->verts.size()); + vert = obj->verts.size() - 1; + } + } + else face.triangles_uv.resize(vert + 1); + face.triangles_uv[vert] = uv; face.triangles.push_back(vert); - face.triangles_uv.push_back(uv); - unsigned first = face.triangles.back(); - vector2 first_uv = face.triangles_uv.back(); + unsigned first = vert; stream >> vert; uv = read_pair(stream); + if (vert < face.triangles_uv.size()) + { + if (uv != face.triangles_uv[vert]) + { + obj->verts.push_back(obj->verts[vert]); + face.triangles_uv.resize(obj->verts.size()); + vert = obj->verts.size() - 1; + } + } + else face.triangles_uv.resize(vert + 1); + face.triangles_uv[vert] = uv; face.triangles.push_back(vert); - face.triangles_uv.push_back(uv); stream >> vert; uv = read_pair(stream); + if (vert < face.triangles_uv.size()) + { + if (uv != face.triangles_uv[vert]) + { + obj->verts.push_back(obj->verts[vert]); + face.triangles_uv.resize(obj->verts.size()); + vert = obj->verts.size() - 1; + } + } + else face.triangles_uv.resize(vert + 1); + face.triangles_uv[vert] = uv; face.triangles.push_back(vert); - face.triangles_uv.push_back(uv); - unsigned last = face.triangles.back(); - vector2 last_uv = face.triangles_uv.back(); + unsigned last = vert; for (int j = 3; j < numrefs; ++j) { - // first face.triangles.push_back(first); - face.triangles_uv.push_back(first_uv); - - // last face.triangles.push_back(last); - face.triangles_uv.push_back(last_uv); stream >> vert; uv = read_pair(stream); + if (vert < face.triangles_uv.size()) + { + if (uv != face.triangles_uv[vert]) + { + obj->verts.push_back(obj->verts[vert]); + face.triangles_uv.resize(obj->verts.size()); + vert = obj->verts.size() - 1; + } + } + else face.triangles_uv.resize(vert + 1); + face.triangles_uv[vert] = uv; face.triangles.push_back(vert); - face.triangles_uv.push_back(uv); last = face.triangles.back(); - last_uv = face.triangles_uv.back(); } } } else if (atom == "kids") { - for (int i = kids.size(); i > 0; --i) - { - if (--kids.top() <= 0) - { - ASSERT(obj && "should be an object"); - obj = obj->parent; - kids.pop(); - } - else break; - } + //while (0 < kids.size()) + //{ + //if (--kids.top() <= 0) + //{ + //ASSERT(obj && "should be an object"); + //obj = obj->parent; + //kids.pop(); + //} + //else break; + //} int numkids = 0; stream >> numkids; - if (numkids > 0) kids.push(numkids); - - if (kids.size() > 0) + if (0 < numkids) kids.push(numkids); + else { - log_info("KIDS", kids.top(), "|", kids.size()); + if (0 < kids.size() && 0 < --kids.top()) kids.pop(); + obj = obj->parent.lock(); } } else @@ -325,8 +362,41 @@ void mesh::import(std::istream& stream) } } while (stream); + + std::vector::iterator meh; + for (meh = objects_.begin(); meh != objects_.end(); ++meh) + { + object_ptr cow = *meh; + log_info("OBJ: -", cow->name, cow->kids.size()); + + std::vector::iterator foo; + for (foo = cow->kids.begin(); foo != cow->kids.end(); ++foo) + { + log_info("OBJ: -", (*foo)->name, (*foo)->kids.size()); + } + } } +//unsigned mesh::read_vertex_line(std::istream& stream) +//{ + //unsigned vert; + //stream >> vert; + //vector2 uv = read_pair(stream); + //if (vert < face.triangles_uv.size()) + //{ + //if (uv != face.triangles_uv[vert]) + //{ + //obj->verts.push_back(obj->verts[vert]); + //face.triangles_uv.resize(obj->verts.size()); + //vert = obj->verts.size() - 1; + //} + //} + //else face.triangles_uv.resize(vert + 1); + //face.triangles_uv[vert] = uv; + //face.triangles.push_back(vert); +//} + + mesh::mesh(const std::string& path) { @@ -405,16 +475,15 @@ mesh::mesh(const std::string& path) void mesh::draw(scalar alpha) const { - //glEnableClientState(GL_VERTEX_ARRAY); - //glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - std::vector::const_iterator it; for (it = objects_.begin(); it != objects_.end(); ++it) { - (*it)->draw(*this, alpha); + (*it)->draw(alpha); } + + // TODO: disable vertex array? } @@ -425,94 +494,68 @@ void mesh::set_material(int index) const void mesh::set_material(const material& material) const { - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material.diffuse.data()); - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material.ambient.data()); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material.specular.data()); - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material.emissive.data()); - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.shininess); - //glColor(material.diffuse); + glColor(material.diffuse); + glMaterial(GL_FRONT, GL_DIFFUSE, material.diffuse); + glMaterial(GL_FRONT, GL_AMBIENT, material.ambient); + glMaterial(GL_FRONT, GL_SPECULAR, material.specular); + glMaterial(GL_FRONT, GL_EMISSION, material.emissive); + glMaterial(GL_FRONT, GL_SHININESS, material.shininess); } -void mesh::object::draw(const mesh& mesh, scalar alpha) const +void mesh::object::draw(scalar alpha, bool recurse) const { - //log_info("cool", verts.size()); - //{ - //image::reset_binding(); - //std::vector::const_iterator it; - //glBegin(GL_LINE_STRIP); - //for (it = verts.begin(); it != verts.end(); ++it) - //{ - //glVertex(*it); - //} - //glEnd(); - //} - - //glPolygonMode(GL_BACK, GL_LINE); - //glVertexPointer(3, GL_SCALAR, 0, verts[0].data()); - if (texture) texture->bind(); - else image::reset_binding(); + glVertexPointer(verts); + + if (texture) + { + texture->bind(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + else + { + image::reset_binding(); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } for (size_t i = 0; i < faces.size(); ++i) { const material_group& face = faces[i]; + if (face.triangles.size() == 0) continue; + mesh.set_material(i); - //it->draw(alpha); - //std::vector::const_iterator jt; - int count = face.triangles.size(); - for (int j = 0; j < count; j += 3) - { - glBegin(GL_TRIANGLES); - glTexCoord(face.triangles_uv[j]); - glVertex(verts[face.triangles[j]]); - glTexCoord(face.triangles_uv[j+1]); - glVertex(verts[face.triangles[j+1]]); - glTexCoord(face.triangles_uv[j+2]); - glVertex(verts[face.triangles[j+2]]); - glEnd(); - } - } - std::vector::const_iterator jt; - for (jt = kids.begin(); jt != kids.end(); ++jt) - { - (*jt)->draw(mesh, alpha); + if (texture) glTexCoordPointer(face.triangles_uv); + glDrawElements(GL_TRIANGLES, face.triangles); } -} -void mesh::material_group::draw(scalar alpha) const -{ - // TODO: setup material - - /* - if (triangles.size() > 0) + if (recurse) { - //log_info("drawing triangles:", triangles.size()/3); - glTexCoordPointer(2, GL_SCALAR, 0, triangles_uv[0].data()); - glDrawElements(GL_TRIANGLES, - triangles.size(), GL_UNSIGNED_INT, - &triangles[0]); + std::vector::const_iterator jt; + for (jt = kids.begin(); jt != kids.end(); ++jt) + { + (*jt)->draw(alpha); + } } - */ } -class mesh_resource_loader -{ -public: +//class mesh_resource_loader +//{ +//public: - mesh_resource_loader() - { - resource::register_type("ac", "models"); - } + //mesh_resource_loader() + //{ + //resource::register_type("ac", "models"); + //} - ~mesh_resource_loader() - { - resource::unregister_type("ac"); - } -}; + //~mesh_resource_loader() + //{ + //resource::unregister_type("ac"); + //} +//}; -static mesh_resource_loader loader; +//static mesh_resource_loader loader; } // namespace moof diff --git a/src/moof/mesh.hh b/src/moof/mesh.hh index cf267fb..0fbc3cc 100644 --- a/src/moof/mesh.hh +++ b/src/moof/mesh.hh @@ -18,11 +18,14 @@ */ #include +#include +#include #include #include +#include -#include +#include #include #include #include @@ -31,7 +34,10 @@ namespace moof { -class mesh : public boost::noncopyable, public drawable +class script; + + +class mesh : public boost::noncopyable, public entity { public: @@ -49,9 +55,9 @@ public: std::string name; vector4 diffuse; - vector3 ambient; - vector3 emissive; - vector3 specular; + vector4 ambient; + vector4 emissive; + vector4 specular; scalar shininess; }; @@ -59,13 +65,8 @@ public: void set_material(int index) const; void set_material(const material& material) const; - struct material_group : public drawable + struct material_group { - material_group() {} - - void draw(scalar alpha = SCALAR(0.0)) const; - - std::vector triangles; std::vector triangles_uv; }; @@ -73,20 +74,23 @@ public: class object; typedef boost::shared_ptr object_ptr; + typedef boost::weak_ptr object_weakptr; struct object { - object() : + object(const mesh& m) : + mesh(m), texrep(SCALAR(1.0), SCALAR(1.0)) {} - static object_ptr alloc() + static object_ptr alloc(const mesh& m) { - return object_ptr(new object); + return object_ptr(new object(m)); } + void draw(scalar alpha = SCALAR(0.0), bool recurse = true) const; - void draw(const mesh& mesh, scalar alpha = SCALAR(0.0)) const; + const moof::mesh& mesh; std::string name; std::string data; @@ -99,10 +103,25 @@ public: std::vector faces; std::vector kids; - object_ptr parent; + std::map kids_byname; + object_weakptr parent; }; + object_ptr operator [] (unsigned index) const + { + return objects_[index]; + } + + + /** + * Import script bindings for the mesh class. + * \param The script. + * \param The name of the namespace to import to. + */ + static void import(script& script, const std::string& nspace = ""); + + private: void import(std::istream& stream); diff --git a/src/moof/mesh_bindings.cc b/src/moof/mesh_bindings.cc new file mode 100644 index 0000000..02138ad --- /dev/null +++ b/src/moof/mesh_bindings.cc @@ -0,0 +1,136 @@ + +/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +**] All rights reserved. +* +* vi:ts=4 sw=4 tw=75 +* +* Distributable under the terms and conditions of the 2-clause BSD license; +* see the file COPYING for a complete text of the license. +* +**************************************************************************/ + +#include "log.hh" +#include "mesh.hh" +#include "script.hh" + + +namespace moof { + + +static int mesh_new(script& script) +{ + script::slot name = script[2].require_string("mesh name"); + + std::string str; + name.get(str); + + mesh_handle mesh = resource::load(str, "ac"); + if (!mesh) name.raise("unable to load mesh from file"); + + script.push(mesh); + return 1; +} + +static int mesh_draw(script& script) +{ + mesh_handle* mesh; + script[1].require_object("mesh").get(mesh); + + scalar alpha; + if (script[2].get(alpha)) (*mesh)->draw(alpha); + else (*mesh)->draw(); + + return 0; +} + +static int mesh_index(script& script) +{ + mesh_handle* mesh; + script[1].require_object("mesh").get(mesh); + + unsigned index; + script[2].require_number("index").get(index); + --index; + + script.push((**mesh)[index]); + script::slot e = script[-1].push_environment(); + e.set_field("mesh", *mesh); + e.pop(); + return 1; +} + + +static int object_new(script& script) +{ + return script.raise("cannot construct a mesh object without a mesh"); +} + +static int object_draw(script& script) +{ + mesh::object_ptr* object; + script[1].require_object("mesh object").get(object); + + scalar alpha; + bool recurse; + if (script[2].get(alpha)) + { + if (script[3].get(recurse)) (*object)->draw(alpha, recurse); + else (*object)->draw(alpha); + } + else + { + if (script[2].get(recurse)) (*object)->draw(SCALAR(0.0), recurse); + else (*object)->draw(); + } + + return 0; +} + +static int object_index(script& script) +{ + mesh::object_ptr* object; + script[1].require_object("mesh object").get(object); + + if (script[2].is_number()) + { + unsigned index; + script[2].get(index); + --index; + script.push((*object)->kids[index]); + } + else if (script[2].is_string()) + { + std::string name; + script[2].get(name); + script.push((*object)->kids_byname[name]); + } + else + { + script[2].raise_type_error("index or name"); + } + return 1; +} + + +void mesh::import(script& script, const std::string& nspace) +{ + script.check_stack(8); + + script::slot parent = script.push_table(nspace); + script::slot meta = script.push_class(mesh_new); + + meta.set_field("draw", mesh_draw); + meta.set_field("object", mesh_index); + + script::slot object_meta = script.push_class(object_new); + object_meta.set_field("draw", object_draw); + object_meta.set_field("kid", object_index); + meta.set_field("__object"); + + parent.set_field("mesh"); + parent.pop(); +} + + +} // namespace moof + diff --git a/src/moof/modal_dialog.hh b/src/moof/modal_dialog.hh index 23e929b..e1d7739 100644 --- a/src/moof/modal_dialog.hh +++ b/src/moof/modal_dialog.hh @@ -140,8 +140,8 @@ struct modal_dialog "%s", text2.c_str()); gtk_window_set_title(GTK_WINDOW(dialog), title.c_str()); - std::string icon_path(PACKAGE".png"); - if (resource::find(icon_path)) + std::string icon_path = resource::find_file(PACKAGE".png"); + if (!icon_path.empty()) { GdkPixbuf* iconPixbuf = gdk_pixbuf_new_from_file(icon_path.c_str(), NULL); @@ -180,8 +180,8 @@ struct modal_dialog dialog.setInformativeText(text2.c_str()); dialog.setStandardButtons(QMessageBox::Close); - std::string icon_path(PACKAGE".png"); - if (resource::find(icon_path)) + std::string icon_path = resource::find_file(PACKAGE".png"); + if (!icon_path.empty()) { QIcon icon(icon_path.c_str()); dialog.setWindowIcon(icon); diff --git a/src/moof/opengl.hh b/src/moof/opengl.hh index 347f3e5..06ffbaa 100644 --- a/src/moof/opengl.hh +++ b/src/moof/opengl.hh @@ -20,6 +20,8 @@ #include "config.h" +#include + #include #include @@ -105,6 +107,36 @@ OPENGL_GENERIC_FUNC(void, Rect, S4); OPENGL_GENERIC_FUNC(void, Rect, V4); +inline void glMaterial(GLenum face, GLenum pname, moof::scalar s) +{ + glMaterialf(face, pname, float(s)); +} + +inline void glMaterial(GLenum face, GLenum pname, const moof::vector4& v) +{ +#if USE_DOUBLE_PRECISION + float f[] = {v[0], v[1], v[2], v[3]}; + glMaterialfv(face, pname, f); +#else + glMaterialfv(face, pname, v.data()); +#endif +} + +inline void glVertexPointer(const std::vector& v) +{ + glVertexPointer(3, GL_SCALAR, 0, v[0].data()); +} +inline void glTexCoordPointer(const std::vector& v) +{ + glTexCoordPointer(2, GL_SCALAR, 0, v[0].data()); +} + +inline void glDrawElements(GLenum type, const std::vector& v) +{ + glDrawElements(type, v.size(), GL_UNSIGNED_INT, &v[0]); +} + + #if USE_DOUBLE_PRECISION inline void glGetScalar(GLenum a, GLscalar* b) { glGetDoublev(a, b); } #else diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 57bd99b..3cf901b 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -11,13 +11,13 @@ #include "config.h" +#include + #ifdef USE_HOTLOADING #include #include #endif -#include - #include #include #include @@ -34,239 +34,261 @@ namespace moof { -bool resource::call_registry(const std::string& extension, - loader_ptr& loader, - registry_action action) +/*] Filesystem searching + *************************************************************************/ + +static std::vector search_paths_; + +void resource::set_search_paths(const std::string& paths) { - static type_lookup table; + boost::split(search_paths_, paths, boost::is_any_of(":")); +} - switch (action) + +std::string resource::find_file(const std::string& name) +{ + std::string ext = stlplus::extension_part(name); + std::string prefix; + std::string path; + + loader_ptr loader; + call_registry(ext, loader, lookup); + if (loader) prefix = loader->prefix(); + + std::vector::iterator it; + for (it = search_paths_.begin(); it != search_paths_.end(); ++it) { - case set: + if (!prefix.empty()) { - if (loader) table[extension] = loader; - else table.erase(extension); - break; + // try it with the prefix first + path = stlplus::filespec_to_path(*it, prefix); + path = stlplus::filespec_to_path(path, name); + log_info("looking for", name, "at", path); + if (stlplus::file_exists(path)) return path; } - case lookup: - { - std::map::iterator it; - it = table.find(extension); - if (it != table.end()) loader = (*it).second; - break; - } + path = stlplus::filespec_to_path(*it, name); + log_info("looking for", name, "at", path); + if (stlplus::file_exists(path)) return path; } - return loader; + log_error("cannot find resource file:", name); + return std::string(); } -static std::string search_paths_; - -typedef boost::weak_ptr resource_weakptr; -static hash resource_table_; - -static hash prefix_table_; +std::string resource::find_file(const std::string& name, + const std::string& ext) +{ + std::string actual_ext = stlplus::extension_part(name); + if (actual_ext != ext) + { + // try the explicit extension first + return find_file(stlplus::create_filename(name, ext)); + } + return find_file(name); +} -#ifdef USE_HOTLOADING -static hash monitor_lookup_; -static int monitor_fd_ = inotify_init1(IN_NONBLOCK); -#endif +/*] Resource loading + *************************************************************************/ -int resource::reload_as_needed() +typedef boost::weak_ptr resource_weakptr; +static struct rsrc_list { - int num_resources = 0; + // this table holds weak references to any and all loaded resources + hash table; -#ifdef USE_HOTLOADING - log_info("hotloading?"); - char bytes[BUF_SIZE]; - int num_bytes; - if (0 < (num_bytes = read(monitor_fd_, bytes, num_bytes))) +#ifdef DEBUG + // this destructor will let us know if the program exits while + // resources are still being held + ~rsrc_list() { - char* end = bytes + num_bytes; - char* byte = bytes; - - log_warning("num_bytes:", num_bytes); - log_error("1"); - - while (byte < end) + hash::iterator it; + for (it = table.begin(); it != table.end(); ++it) { - struct inotify_event* event = (struct inotify_event*)byte; + log_warning("leaked resource:", (*it).first); + } + } +#endif +} rsrc_list; - if (event->mask & IN_IGNORED) - { - log_warning("watch", event->wd, "removed"); - } +#ifdef USE_HOTLOADING +static struct watch_list +{ + // this table associates a watch descriptor with a loaded resource + hash table; - log_error("2"); - hash::iterator it; - it = monitor_lookup_.find(event->wd); - if (it != monitor_lookup_.end()) - { - log_error("3"); - std::string path = (*it).second; - monitor_lookup_.erase(it); - resource::reload(path); - } + int fd; // the inotify file descriptor - byte += sizeof(*event) + event->len; + watch_list() : + fd(inotify_init1(IN_NONBLOCK)) {} - ++num_resources; - } + ~watch_list() + { + close(fd); } -#endif - return num_resources; -} + int add(resource_ptr rsrc) + { + int wd = inotify_add_watch(fd, rsrc->path().c_str(), + IN_DELETE_SELF | IN_MODIFY); + table[wd] = rsrc; + return wd; + } + void remove(int wd) + { + if (wd != -1) inotify_rm_watch(fd, wd); + } +} watch_list; +#endif -resource::~resource() +resource_ptr resource::load(const std::string& name) { -#ifdef USE_HOTLOADING - inotify_rm_watch(monitor_fd_, wd_); -#endif + std::string ext = stlplus::extension_part(name); + return load_with_path(find_file(name, ext), ext); } - resource_ptr resource::load(const std::string& name, const std::string& ext) { - return load_with_path(find_file(name, ext)); -} - -resource_ptr resource::load(const std::string& name) -{ - return load_with_path(find_file(name)); + return load_with_path(find_file(name, ext), ext); } -resource_ptr resource::load_with_path(const std::string& path) +resource_ptr resource::load_with_path(const std::string& path, + const std::string& ext) { - std::string extension = stlplus::extension_part(path); + if (path.empty()) return resource_ptr(); + // first try to lookup the resource hash::iterator it; - it = resource_table_.find(path); - if (it != resource_table_.end()) + it = rsrc_list.table.find(path); + if (it != rsrc_list.table.end()) { - resource_weakptr rsrc = (*it).second; - resource_ptr locked = rsrc.lock(); - if (locked) return locked; + resource_ptr rsrc = (*it).second.lock(); + if (rsrc) return rsrc; } + // then proceed to use the registered loader to get the resource loader_ptr loader; - call_registry(extension, loader, lookup); + call_registry(ext, loader, lookup); if (loader) { resource_ptr rsrc(loader->load(path)); - rsrc->set_loader(path, loader); - resource_table_[path] = rsrc; - + rsrc_list.table[path] = rsrc; + rsrc->path_ = path; + rsrc->type_ = ext; #ifdef USE_HOTLOADING - int wd = inotify_add_watch(monitor_fd_, path.c_str(), IN_MODIFY); - rsrc->set_watch_descriptor(wd); - monitor_lookup_[wd] = path; + rsrc->wd_ = watch_list.add(rsrc); #endif - - log_info("loaded", rsrc.get()); return rsrc; } - log_warning("cannot load resource of unknown type:", path); + log_warning("cannot load resource of unregistered type:", path); return resource_ptr(); } -resource_ptr resource::reload(std::string& path) +/*] Hotloading + *************************************************************************/ + +int resource::reload_as_needed() { - log_info("reloading...", path); - hash::iterator it; - it = resource_table_.find(path); - if (it != resource_table_.end()) + int count = 0; + +#ifdef USE_HOTLOADING + char bytes[BUF_SIZE]; + int num_bytes; + // an inotify file descriptor lets your read inotify_event structures + if (0 < (num_bytes = read(watch_list.fd, bytes, sizeof(bytes)))) { - resource_weakptr rsrc = (*it).second; - resource_ptr locked = rsrc.lock(); - if (locked) + char* end = bytes + num_bytes; + char* byte = bytes; + + while (byte < end) { - locked->reload(); - return locked; + struct inotify_event* event = (struct inotify_event*)byte; + byte += sizeof(*event) + event->len; + + hash::iterator it; + it = watch_list.table.find(event->wd); + if (it != watch_list.table.end()) + { + resource_ptr rsrc = (*it).second.lock(); + if (rsrc) + { + rsrc->reload(); + ++count; + + if (event->mask & IN_IGNORED) + { + watch_list.table.erase(event->wd); + rsrc->wd_ = watch_list.add(rsrc); + } + } + else + { + // if we can't lock the resource, it was unloaded so we + // don't need to watch it anymore + watch_list.table.erase(event->wd); + } + } } } +#endif - return load(path); + return count; } void resource::reload() { - log_info("reloaded", path_); + loader_ptr loader; + call_registry(type_, loader, lookup); - resource* resource = loader_->load(path_); - //*this = *resource; + resource_ptr resource(loader->load(path_)); resource_ = resource->resource_; typeinfo_ = resource->typeinfo_; unloader_ = resource->unloader_; +} +resource::~resource() +{ + rsrc_list.table.erase(path_); #ifdef USE_HOTLOADING - int wd = inotify_add_watch(monitor_fd_, path_.c_str(), IN_MODIFY); - set_watch_descriptor(wd); - monitor_lookup_[wd] = path_; + watch_list.remove(wd_); #endif - - delete resource; } -void resource::add_search_paths(const std::string& paths) -{ - search_paths_ = paths; -} +/*] Resource registration + *************************************************************************/ - -std::string resource::find_file(const std::string& name) +bool resource::call_registry(const std::string& ext, + loader_ptr& loader, + registry_action action) { - std::vector paths; - boost::split(paths, search_paths_, boost::is_any_of(":")); - - std::string ext = stlplus::extension_part(name); - std::string prefix("hi"); - - loader_ptr loader; - call_registry(ext, loader, lookup); - if (loader) prefix = loader->prefix(); + static std::map table; - log_info("find_file:", ext, prefix); - - std::vector::iterator it; - for (it = paths.begin(); it != paths.end(); ++it) + switch (action) { - std::string path = stlplus::create_filespec(*it, name); - log_info("looking for", name, "at", path); - if (stlplus::file_exists(path)) return path; - - // try it with the prefix added - if (!prefix.empty()) + case set: { - *it = stlplus::create_filespec(*it, prefix); - path = stlplus::create_filespec(*it, name); - log_info("looking for", name, "at", path); - if (stlplus::file_exists(path)) return path; + if (loader) table[ext] = loader; + else table.erase(ext); + break; } - } - - log_error("cannot find resource file:", name); - return std::string(); -} - -std::string resource::find_file(const std::string& name, - const std::string& ext) -{ - std::string actual_ext = stlplus::extension_part(name); - if (actual_ext != ext) - { - return find_file(stlplus::create_filename(name, ext)); + case lookup: + { + std::map::iterator it; + it = table.find(ext); + if (it != table.end()) loader = (*it).second; + break; + } } - return find_file(name); + + return loader; } diff --git a/src/moof/resource.hh b/src/moof/resource.hh index 9d99105..094791c 100644 --- a/src/moof/resource.hh +++ b/src/moof/resource.hh @@ -17,8 +17,6 @@ * Interface for textures, sounds, and other types of resources. */ -#include "config.h" - #include #include #include @@ -45,25 +43,30 @@ class resource { public: - // XXX: this won't be necessary once the existing code is modified to - // use the resource handles - resource() {} - /** * Add a directory to search when looking for resource files. * \param paths A colon-separated list of directory paths. */ - static void add_search_paths(const std::string& paths); + static void set_search_paths(const std::string& paths); /** - * Get the path to a resource of a given name. - * \param path The name of the resource to find. Upon successful - * return, this is changed to an absolute path to the resource. - * \return True if a path to a resource was found, false otherwise. + * Get the path to a resource of a given name. This uses the search + * path(s) and resources prefixes to locate resource files. + * \param name The name or partial path of the resource to find. + * \return The full path of the resource. */ static std::string find_file(const std::string& name); + /** + * Get the path to a resource of a given name and explicit type. This + * uses the search path(s) and resources prefixes to locate resource + * files. + * \param name The name or partial path of the resource to find. + * \param ext The extension is appended to the name if the same + * extension is not already a part of name. + * \return The full path of the resource. + */ static std::string find_file(const std::string& name, const std::string& ext); @@ -85,7 +88,7 @@ public: * Unregister the type associated with a file extension. Resources of * this type will no longer be loadable, although resources which are * already loaded will remain loaded. - * \param extension The file extension + * \param extension The file extension. */ static void unregister_type(const std::string& extension) { @@ -94,34 +97,46 @@ public: } + /** + * Find and load a resource by name or path. + * \param name The name or partial path of the resource. This should + * include the extension so that the correct loader can be chosen. + * \return The resource. + */ static resource_ptr load(const std::string& name); + + /** + * Find and load a resource by name or path. + * \param name The name or partial path of the resource. This should + * include the extension so that the correct loader can be chosen. + * \param + * \return The resource. + */ static resource_ptr load(const std::string& name, const std::string& ext); - static resource_ptr reload(std::string& path); - /** - * Construct a resource container. - * \param ptr A pointer to the underlying resource data. + * Reload the resource data. This will cause the resource file to be + * reread, and the underlying resource data will change. */ - template - explicit resource(T* ptr) : - resource_(ptr), - typeinfo_(const_cast(&typeid(T))), - unloader_(new specific_unloader(ptr)) {} + void reload(); /** - * Deconstruct a resource container. + * Get the path of file from which this resource was loaded. + * \return The path. */ - virtual ~resource(); - + std::string path() const + { + return path_; + } /** - * Reload the resource data. This will cause the resource file to be - * reread, and the underlying resource data will change. + * Reloads some resources which have been modified on disk since they + * were loaded. Hotloading must have been enabled at compile-time. + * \return The number of resources reloaded. */ - void reload(); + static int reload_as_needed(); /** @@ -149,22 +164,28 @@ public: /** - * Reloads some resources which have been modified on disk since they - * were loaded. Hotloading must have been enabled at compile-time. - * \return The number of resources reloaded. + * Deconstruct a resource container. */ - static int reload_as_needed(); + virtual ~resource(); private: - static resource_ptr load_with_path(const std::string& path); + template + explicit resource(T* ptr) : + resource_(ptr), + typeinfo_(const_cast(&typeid(T))), + unloader_(new specific_unloader(ptr)), + wd_(-1) {} + + static resource_ptr load_with_path(const std::string& path, + const std::string& extension); + class loader { public: - //loader() {} loader(const std::string& prefix) : prefix_(prefix) {} @@ -193,13 +214,11 @@ private: { public: - //specific_loader() {} specific_loader(const std::string& prefix) : loader(prefix) {} virtual resource* load(const std::string& path) { - log_info("loading resource of type ", typeid(T).name()); return new resource(new T(path)); } }; @@ -224,7 +243,6 @@ private: virtual ~specific_unloader() { - log_info("unloading resource of type ", typeid(T).name()); delete object_; } @@ -235,25 +253,6 @@ private: }; - void set_loader(const std::string& path, loader_ptr loader) - { - path_ = path; - loader_ = loader; - } - - - void* resource_; - std::type_info* typeinfo_; - unloader_ptr unloader_; - - std::string path_; - loader_ptr loader_; - - typedef std::map type_lookup; - //typedef boost::shared_ptr type_lookup_ptr; - //static type_lookup_ptr type_lookup_; - //static type_lookup type_lookup_; - enum registry_action { lookup, @@ -264,14 +263,13 @@ private: loader_ptr& loader, registry_action action); -#ifdef USE_HOTLOADING - int wd_; - void set_watch_descriptor(int wd) - { - wd_ = wd; - } -#endif + void* resource_; + std::type_info* typeinfo_; + unloader_ptr unloader_; + int wd_; + std::string path_; + std::string type_; }; @@ -304,7 +302,6 @@ public: resource_(resource::load(name, ext)) {} - /** * Get whether or not the handle is dereferenceable to the type of this * handle. A resource handle is dereferenceable if it is not a null @@ -376,6 +373,23 @@ private: }; +/** + * This macro easily registers types to act as resources. It should be + * used in a module file in global scope. + * \param TYPE The type (class), qualified as needed for the scope. + * \param EXT The file extension the resource uses. + * \param PREFIX The path prefix where a resource of this type could be. + */ +#define MOOF_REGISTER_RESOURCE(TYPE, EXT, PREFIX) \ +namespace { \ + struct EXT { \ + EXT() { moof::resource::register_type(#EXT, #PREFIX); } \ + ~EXT() { moof::resource::unregister_type(#EXT); } \ + }; \ + static EXT EXT; \ +} + + } // namespace moof #endif // _MOOF_RESOURCE_HH_ diff --git a/src/moof/script.hh b/src/moof/script.hh index 85973dd..35e80d2 100644 --- a/src/moof/script.hh +++ b/src/moof/script.hh @@ -14,7 +14,7 @@ /** * \file script.hh - * A thin wrapper over Lua. This is not meant as a complicated binding + * A thin wrapper over Lua 5.1. This is not meant as a complicated binding * package between C++ and Lua. It is not meant to obscure the division * between C++ and Lua but rather to clarify it and make it more * manageable. It does not hide the concept of the Lua stack, but rather @@ -22,10 +22,13 @@ * providing a cleaner, more consistent API. */ +#include #include -#include #include +#include #include +#include +#include #include #include @@ -46,6 +49,7 @@ class script public: typedef boost::function function; + typedef int (*cfunction)(script&); enum status { @@ -65,6 +69,15 @@ public: globals_index = LUA_GLOBALSINDEX }; + + template + static int object_finalizer(script& script) + { + reinterpret_cast(script[1].get_data())->~T(); + return 0; + } + + /** * This is the most prominent abstraction on top of the standard Lua * API. A slot object represents a value on the stack. More @@ -74,14 +87,12 @@ public: * value of which will change as things are pushed onto and popped from * the stack). */ - struct slot { /** * You have direct access to the index of the value on the stack * being represented. */ - int index; @@ -99,140 +110,128 @@ public: thread = LUA_TTHREAD }; + static std::string type_name(type type) + { + switch (type) + { + case none: return "none"; + case nil: return "nil"; + case boolean: return "boolean"; + case light_data: + case data: return "userdata"; + case number: return "number"; + case string: return "string"; + case table: return "table"; + case function: return "function"; + case thread: return "thread"; + } + return "?"; + } + slot(const class script& s, int i = 0) : index(i), - script_(const_cast(s)) {} - - /** - * A copied value presently points to the same value, except the - * real index is used. That means that if a value that refers to a - * frame referenced from the top of the stack will have its - * normalized index copied into the new value object. - */ - - //slot(const slot& copy) : - //index(copy.positiveIndex()), - //script_(copy.script_) {} + script_(const_cast(&s)) {} // check the type of the value bool is_boolean() const - { return (bool)lua_isboolean(script_.state_, index); } + { return (bool)lua_isboolean(script_->state_, index); } bool is_imported_function() const - { return (bool)lua_iscfunction(script_.state_, index); } + { return (bool)lua_iscfunction(script_->state_, index); } bool is_function() const - { return (bool)lua_isfunction(script_.state_, index); } + { return (bool)lua_isfunction(script_->state_, index); } bool is_nil() const - { return (bool)lua_isnil(script_.state_, index); } + { return (bool)lua_isnil(script_->state_, index); } bool is_none() const - { return (bool)lua_isnone(script_.state_, index); } + { return (bool)lua_isnone(script_->state_, index); } bool is_none_or_nil() const - { return (bool)lua_isnoneornil(script_.state_, index); } + { return (bool)lua_isnoneornil(script_->state_, index); } bool is_number() const - { return (bool)lua_isnumber(script_.state_, index); } + { return (bool)lua_isnumber(script_->state_, index); } bool is_string() const - { return (bool)lua_isstring(script_.state_, index); } + { return (bool)lua_isstring(script_->state_, index); } bool is_table() const - { return (bool)lua_istable(script_.state_, index); } + { return (bool)lua_istable(script_->state_, index); } bool is_thread() const - { return (bool)lua_isthread(script_.state_, index); } + { return (bool)lua_isthread(script_->state_, index); } bool is_data() const - { return (bool)lua_isuserdata(script_.state_, index); } + { return (bool)lua_isuserdata(script_->state_, index); } bool is_light_data() const - { return (bool)lua_islightuserdata(script_.state_, index); } + { return (bool)lua_islightuserdata(script_->state_, index); } /** * Check the value and throw an error if its the wrong type. - * There's a little caveat: This method never returns because it - * does a long jump. Consequently, constructed C++ objects which - * exist on the stack between the current frame and some lua - * function will not be destructed. That's not a problem for - * objects that only exist on the stack, but any objects that - * allocate memory on the heap (such as containers or strings) will - * leak. Therefore, you should only call this method after - * cleaning up such objects. The best thing to do for defining - * functions is to simply check all the parameters at the get-go - * before any C++ objects are even constructed. */ - - void require_type(type t) const + const slot& require_type(type t) const { - if (t != type()) - { - luaL_typerror(script_.state_, index, - lua_typename(script_.state_, t)); - } + if (t != type()) raise_type_error(type_name(t)); + return *this; } - void raise(const char* error) + const slot& require_boolean(const std::string& what = "boolean") const { - luaL_argerror(script_.state_, index, error); + if (!is_boolean()) raise_type_error(what); + return *this; } - - - slot& require_boolean() + const slot& require_number(const std::string& what = "number") const { - if (!is_boolean()) - { - luaL_typerror(script_.state_, index, "boolean"); - } + if (!is_number()) raise_type_error(what); return *this; } - slot& require_number() + const slot& require_string(const std::string& what = "string") const { - if (!is_number()) - { - luaL_typerror(script_.state_, index, "number"); - } + if (!is_string()) raise_type_error(what); return *this; } - slot& require_string() + const slot& require_table(const std::string& what = "table") const { - if (!is_string()) - { - luaL_typerror(script_.state_, index, "string"); - } + if (!is_table()) raise_type_error(what); return *this; } - slot& require_table() + const slot& require_function(const std::string& what = "function") const { - if (!is_table()) - { - luaL_typerror(script_.state_, index, "table"); - } + if (!is_function()) raise_type_error(what); return *this; } - slot& require_function() + const slot& require_data(const std::string& what = "userdata") const { - if (!is_function()) - { - luaL_typerror(script_.state_, index, "function"); - } + if (!is_data()) raise_type_error(what); return *this; } - slot& require_data() + const slot& require_nil(const std::string& what = "nil") const { - if (!is_data()) - { - luaL_typerror(script_.state_, index, "data"); - } + if (!is_nil()) raise_type_error(what); return *this; } - slot& require_nil() + const slot& require_thread(const std::string& what = "thread") const { - if (!is_nil()) - { - luaL_typerror(script_.state_, index, "nil"); - } + if (!is_thread()) raise_type_error(what); return *this; } - slot& require_thread() + + template + const slot& require_object(const std::string& what = typeid(T).name()) const { - if (!is_thread()) + if (!is_data()) raise_type_error(what); + + slot metatable = push_metatable(); + if (!metatable.is_table()) { - luaL_typerror(script_.state_, index, "thread"); + metatable.pop(); + raise_type_error(what); } + + slot type = metatable.push_field("__cxxtype"); + std::type_info* typeinfo; + if (!type.get(typeinfo)) + { + metatable.pop(); + raise_type_error(what); + } + + metatable.pop(); + if (*typeinfo != typeid(T)) raise_type_error(what); return *this; } @@ -240,19 +239,38 @@ public: /** * Get the type of the value. */ - enum type type() const { - return (enum type)lua_type(script_.state_, index); + return (enum type)lua_type(script_->state_, index); } /** * Get the name of the type of the value as a string. */ - std::string type_name() const { - return std::string(luaL_typename(script_.state_, index)); + if (is_none()) return "none"; + else if (is_data() && !is_light_data()) + { + slot metatable = push_metatable(); + if (!metatable.is_table()) + { + metatable.pop(); + return "userdata"; + } + + slot type = metatable.push_field("__cxxtype"); + std::type_info* typeinfo; + if (!type.get(typeinfo)) + { + metatable.pop(); + return "userdata"; + } + + metatable.pop(); + return typeinfo->name(); + } + return luaL_typename(script_->state_, index); } @@ -260,15 +278,19 @@ public: * Get the length of the value according to the definition given by * Lua. */ + size_t size() const + { + return lua_objlen(script_->state_, index); + } size_t length() const { - return lua_objlen(script_.state_, index); + return size(); } - int positiveIndex() const + int positive_index() const { - if (index < 0) return index + lua_gettop(script_.state_) + 1; + if (index < 0) return index + lua_gettop(script_->state_) + 1; else return index; } @@ -277,15 +299,14 @@ public: * Get a pointer value (for userdata, tables, threads, and * functions). */ - const void* id() const { - return lua_topointer(script_.state_, index); + return lua_topointer(script_->state_, index); } bool is_identical(const slot& rhs) const { - return &script_ == &(rhs.script_) && index == rhs.index; + return script_ == rhs.script_ && index == rhs.index; } operator bool () const @@ -296,41 +317,24 @@ public: bool operator == (const slot& rhs) const { - return (bool)lua_equal(script_.state_, index, rhs.index); - } - bool operator != (const slot& rhs) const - { - return !(*this == rhs); + return (bool)lua_equal(script_->state_, index, rhs.index); } bool operator < (const slot& rhs) const { - return (bool)lua_lessthan(script_.state_, index, rhs.index); - } - bool operator <= (const slot& rhs) const - { - return *this < rhs || *this == rhs; - } - bool operator > (const slot& rhs) const - { - return !(*this <= rhs); - } - bool operator >= (const slot& rhs) const - { - return !(*this < rhs); + return (bool)lua_lessthan(script_->state_, index, rhs.index); } /** * Convert the underlying value to a C++ type. */ - template bool get(T& value) const { if (is_number()) { - value = (T)lua_tointeger(script_.state_, index); + value = (T)lua_tointeger(script_->state_, index); return true; } return false; @@ -340,7 +344,7 @@ public: { if (is_number()) { - value = (float)lua_tonumber(script_.state_, index); + value = (float)lua_tonumber(script_->state_, index); return true; } return false; @@ -349,7 +353,7 @@ public: { if (is_number()) { - value = (double)lua_tonumber(script_.state_, index); + value = (double)lua_tonumber(script_->state_, index); return true; } return false; @@ -359,7 +363,7 @@ public: { if (is_boolean()) { - value = (bool)lua_toboolean(script_.state_, index); + value = (bool)lua_toboolean(script_->state_, index); return true; } return false; @@ -369,7 +373,7 @@ public: { if (is_string()) { - value = lua_tolstring(script_.state_, index, &size); + value = lua_tolstring(script_->state_, index, &size); return true; } return false; @@ -387,15 +391,20 @@ public: return false; } - bool get(void*& value) const + template + bool get(T*& value) const { if (is_data()) { - value = lua_touserdata(script_.state_, index); + value = reinterpret_cast(lua_touserdata(script_->state_, index)); return true; } return false; } + void* get_data() const + { + return lua_touserdata(script_->state_, index); + } template bool get(std::vector& array) const @@ -404,19 +413,19 @@ public: array.clear(); - slot value = script_[-1]; - int realIndex = positiveIndex(); + slot value = (*script_)[-1]; + int realIndex = positive_index(); bool done = false; for (int i = 1; !done; ++i) { - lua_rawgeti(script_.state_, realIndex, i); + lua_rawgeti(script_->state_, realIndex, i); T v; if (value.get(v)) array.push_back(v); else done = true; - script_.pop(); + script_->pop(); } return true; @@ -429,12 +438,12 @@ public: dictionary.clear(); - slot key = script_[-2]; - slot value = script_[-1]; - int realIndex = positiveIndex(); + slot key = (*script_)[-2]; + slot value = (*script_)[-1]; + int realIndex = positive_index(); - script_.push_nil(); - while (lua_next(script_.state_, realIndex) != 0) + script_->push_nil(); + while (lua_next(script_->state_, realIndex) != 0) { std::string k; if (!key.is_number() && key.get(k)) @@ -442,9 +451,9 @@ public: T v; if (value.get(v)) dictionary[k] = v; } - script_.pop(); + script_->pop(); } - script_.pop(); + script_->pop(); return true; } @@ -452,42 +461,58 @@ public: /** * Get the value of a field from the table. */ - template - bool get(T& value, V field) const + bool get(T& value, const V& field) const { bool ret = push_field(field).get(value); - script_.pop(); + script_->pop(); return ret; } template - void set_field(T field, V value) + void set_field(const T& field, const V& value) { - script_.push(field); - script_.push(value); + script_->push(field); + script_->push(value); set_field(); } void set_field() { - lua_settable(script_.state_, index); + lua_settable(script_->state_, index); + } + + + void set_field(const std::string& field) + { + set_field(field.c_str()); + } + void set_field(const char* field) + { + lua_setfield(script_->state_, index, field); } template - void set_field(const std::string& field, T value) + void set_field(const std::string& field, const T& value) { set_field(field.c_str(), value); } template - void set_field(const char* field, T value) + void set_field(const char* field, const T& value) { - script_.push(value); - lua_setfield(script_.state_, index, field); + script_->push(value); + set_field(field); } + /** + * Set the top value to be the metatable of this value. + */ + void set_metatable() + { + lua_setmetatable(script_->state_, index); + } /** * This set method, as opposed to the others, sets the value of the @@ -496,80 +521,81 @@ public: template void set(T value) { - script_.push(value); - replace(); + script_->push(value); + set(); } - void set() - { - replace(); - } - - /** * Replace this value with the value at the top of the stack. */ + void set() + { + lua_replace(script_->state_, index); + } - void replace() + void set_nil() { - lua_replace(script_.state_, index); + script_->push_nil(); + set(); } + void remove() { - lua_remove(script_.state_, index); + lua_remove(script_->state_, index); } + /** + * Remove this value and everything above it. + */ void pop() { - // removes this slot, taking with it everything above it - script_.pop(script_.stack_size() - index + 1); + if (index < 0) script_->pop(-index); + else script_->pop(script_->stack_size() - index + 1); } /** * Inserts the top-most value on the stack at position index, * shifting other values as needed. */ - void insert_top_here() { - lua_insert(script_.state_, index); + lua_insert(script_->state_, index); } /** * Copy the value and push the copy to the stack. */ - slot push_copy() const { - lua_pushvalue(script_.state_, index); - return script_.top(); + lua_pushvalue(script_->state_, index); + return script_->top(); } slot push_metatable() const { - lua_getmetatable(script_.state_, index); - return script_.top(); + lua_getmetatable(script_->state_, index); + return script_->top(); } slot push_environment() const { - lua_getfenv(script_.state_, index); - return script_.top(); + lua_getfenv(script_->state_, index); + return script_->top(); } slot push_field() const { - lua_gettable(script_.state_, index); - return script_.top(); + lua_gettable(script_->state_, index); + return script_->top(); } template slot push_field(T index) const { - script_.push(index); + script_->push(index); return push_field(); } @@ -579,25 +605,64 @@ public: } slot push_field(const char* name) const { - lua_getfield(script_.state_, index, name); - return script_.top(); + lua_getfield(script_->state_, index, name); + return script_->top(); } class script& script() { - return script_; + return *script_; } const class script& script() const { - return script_; + return *script_; + } + + + /** + * Throw an exception with a message formatted to communicate a + * type mismatch with the argument represented by this slot. + */ + int raise_type_error(const std::string& expected) const + { + lua_Debug ar; + lua_getstack(script_->state_, 0, &ar); + lua_getinfo(script_->state_, "n", &ar); + const char* func = ar.name ? ar.name : "unknown function"; + + std::ostringstream stream; + stream << "bad argument " << index << " to '" << func + << "' (" << expected << " expected, got " + << type_name() << ")"; + + throw std::invalid_argument(stream.str()); + return 0; + } + + /** + * Throw a generic error concerning this particular slot. + */ + int raise(const std::string& message) const + { + lua_Debug ar; + lua_getstack(script_->state_, 0, &ar); + lua_getinfo(script_->state_, "n", &ar); + const char* func = ar.name ? ar.name : "unknown function"; + + std::ostringstream stream; + stream << "bad argument " << index << " to '" << func + << "' (" << message << ")"; + + throw std::invalid_argument(stream.str()); + return 0; } private: - class script& script_; + mutable class script* script_; }; @@ -622,7 +687,6 @@ public: { if (state_) destroy(); state_ = luaL_newstate(); - registry().set_field("Script_hh_Object", (void*)this); } @@ -631,58 +695,84 @@ public: luaL_openlibs(state_); } + void import_safe_standard_libraries() + { + import_base_library(); + import_string_library(); + import_table_library(); + import_math_library(); + import_os_library(); + import_debug_library(); + + slot g = globals(); + + push_nil(); g.set_field("dofile"); + push_nil(); g.set_field("loadfile"); + push_nil(); g.set_field("require"); + push_nil(); g.set_field("io"); + push_nil(); g.set_field("package"); + slot os = g.push_field("os"); + push_nil(); os.set_field("execute"); + push_nil(); os.set_field("exit"); + push_nil(); os.set_field("getenv"); + push_nil(); os.set_field("remove"); + push_nil(); os.set_field("rename"); + push_nil(); os.set_field("tmpname"); + pop(); + } + void import_base_library() { - lua_pushcfunction(state_, luaopen_base); + push(luaopen_base); push(LUA_COLIBNAME); call(1, 0); } void import_package_library() { - lua_pushcfunction(state_, luaopen_package); + push(luaopen_package); push(LUA_LOADLIBNAME); call(1, 0); } void import_string_library() { - lua_pushcfunction(state_, luaopen_string); + push(luaopen_string); push(LUA_STRLIBNAME); call(1, 0); } void import_table_library() { - lua_pushcfunction(state_, luaopen_table); + push(luaopen_table); push(LUA_TABLIBNAME); call(1, 0); } void import_math_library() { - lua_pushcfunction(state_, luaopen_math); + push(luaopen_math); push(LUA_MATHLIBNAME); call(1, 0); } void import_io_library() { - lua_pushcfunction(state_, luaopen_io); + push(luaopen_io); push(LUA_IOLIBNAME); call(1, 0); } void import_os_library() { - lua_pushcfunction(state_, luaopen_os); + push(luaopen_os); push(LUA_OSLIBNAME); call(1, 0); } void import_debug_library() { - lua_pushcfunction(state_, luaopen_debug); + push(luaopen_debug); push(LUA_DBLIBNAME); call(1, 0); } @@ -705,7 +795,7 @@ public: } - /** + /* * Thread-handling methods. */ @@ -714,9 +804,10 @@ public: return script(state_); } - void push_thread() + slot push_thread() { lua_pushthread(state_); + return top(); } status resume(int nargs) @@ -724,7 +815,7 @@ public: return status(lua_resume(state_, nargs)); } - status getStatus() const + status thread_status() const { return status(lua_status(state_)); } @@ -736,28 +827,36 @@ public: bool is_main_thread() const { - return is_main_thread_; + bool is_main = lua_pushthread(state_); + lua_pop(state_, 1); + return is_main; } /** - * Throw an error with the value at the top of the stack. This method - * never returns because it does a long jump. Consequently, - * constructed C++ objects which exist on the stack between the - * current frame and some lua function will not be destructed. That's - * not a problem for objects that only exist on the stack, but any - * objects that allocate memory on the heap (such as containers or - * strings) will leak. Therefore, you should only call this method - * after cleaning up such objects. + * Throw an error with the value at the top of the stack. If this is + * called from an imported function, the error will be caught and + * returned on the stack when the execution aborts. */ + int raise() + { + throw std::runtime_error(""); + return 0; + } - void raise() + /** + * Throw an error with a given message describing the problem. If this + * is called from an imported function, the error will be caught and + * returned on the stack when the execution aborts. + */ + int raise(const std::string& message) { - lua_error(state_); + throw std::runtime_error(message); + return 0; } - /** + /* * Get significant values. */ @@ -817,7 +916,7 @@ public: * enough to hold whatever you want to push on it. This is usually * only an issue if you're pushing stuff in a loop. */ - bool checkStack(int extra) + bool check_stack(int extra) { return (bool)lua_checkstack(state_, extra); } @@ -832,11 +931,56 @@ public: } - /** + /* * Push some values onto the stack. */ - template - slot push(T value) + + slot push(char value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned char value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(short value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned short value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(int value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned int value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(long long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned long long value) { lua_pushinteger(state_, lua_Integer(value)); return top(); @@ -850,12 +994,12 @@ public: slot push(float value) { - lua_pushnumber(state_, (lua_Number)value); + lua_pushnumber(state_, lua_Number(value)); return top(); } slot push(double value) { - lua_pushnumber(state_, (lua_Number)value); + lua_pushnumber(state_, lua_Number(value)); return top(); } @@ -877,18 +1021,56 @@ public: slot push(const function& function) { - functions_.push_back(function); - lua_pushlightuserdata(state_, (void*)&functions_.back()); - lua_pushcclosure(state_, dispatch_call, 1); + push(function); + push_pointer(this); + push(call_functor, 2); return top(); } - - slot push(void* data) + slot push(cfunction function) { - lua_pushlightuserdata(state_, data); + push_pointer(function); + push_pointer(this); + push(call_function, 2); return top(); } + template + slot push(const T& object) + { + void* storage; + slot copy = push_data(storage, sizeof(T)); + new(storage) T(object); + + push_class_metatable(); + copy.set_metatable(); + return copy; + } + + template + slot push_class(const function& ctor) + { + slot metatable = push_class_metatable(); + + slot constructor = push_table(); + push(ctor); + constructor.set_field("__call"); + metatable.set_metatable(); + + return metatable; + } + template + slot push_class(cfunction ctor) + { + slot metatable = push_class_metatable(); + + slot constructor = push_table(); + push(ctor); + constructor.set_field("__call"); + metatable.set_metatable(); + + return metatable; + } + slot push_nil() { lua_pushnil(state_); @@ -917,18 +1099,59 @@ public: return top(); } - slot push_new_data(void*& data, size_t size) + slot push_data(void*& data, size_t size) { data = lua_newuserdata(state_, size); return top(); } - slot push_new_table(int narr = 0, int nrec = 0) + template + slot push_pointer(const T* ptr) + { + lua_pushlightuserdata(state_, const_cast((const void*)ptr)); + return top(); + } + slot push_pointer(cfunction function) + { + return push_pointer((void*)function); + } + + slot push_table(const std::string& name, int narr = 0, int nrec = 0) + { + if (name.empty()) return globals().push_field("_G"); + + slot table = globals().push_field(name); + if (table.is_table()) return table; + + pop(); + push_table(narr, nrec); + globals().set_field(name); + + return globals().push_field(name); + } + slot push_table(int narr = 0, int nrec = 0) { lua_createtable(state_, narr, nrec); return top(); } + slot push_metatable(const std::string& type, bool& is_new) + { + is_new = luaL_newmetatable(state_, type.c_str()); + return top(); + } + slot push_metatable(const std::string& type) + { + luaL_newmetatable(state_, type.c_str()); + return top(); + } + + template + slot push_type() + { + return push_pointer(&typeid(T)); + } + /** * Call a function on the stack. The correct procedure is to push a @@ -936,7 +1159,6 @@ public: * will pop them off upon return, leaving up to nresults return values * (default is any number of return values, depending on the callee). */ - status call(int nargs = 0, int nresults = LUA_MULTRET) { return status(lua_pcall(state_, nargs, nresults, 0)); @@ -946,7 +1168,6 @@ public: /** * Pops n values from the top of the stack. */ - void pop(int n = 1) { lua_pop(state_, n); @@ -956,44 +1177,42 @@ public: /** * Index into the stack to get a slot. */ - slot operator [] (int index) const { return slot(*this, index); } - /** + /* * Control over the garbage collection process. */ - void collect_all() + void collect_garbage() { lua_gc(state_, LUA_GCCOLLECT, 0); } + void collect_garbage(int step) + { + lua_gc(state_, LUA_GCSTEP, step); + } - void stop_collector() + void disable_garbage_collector() { lua_gc(state_, LUA_GCSTOP, 0); } - - void restart_collector() + void enable_garbage_collector() { lua_gc(state_, LUA_GCRESTART, 0); } - int memory_used() const + float memory_used() const { // in kilobytes - return lua_gc(state_, LUA_GCCOUNT, 0); + return lua_gc(state_, LUA_GCCOUNT, 0) + + lua_gc(state_, LUA_GCCOUNTB, 0) / 1024.0f; } - void collect(int step) - { - lua_gc(state_, LUA_GCSTEP, step); - } - - void tune_collector(int pause, int step) + void tune_garbage_collector(int pause, int step = 200) { lua_gc(state_, LUA_GCSETPAUSE, pause); lua_gc(state_, LUA_GCSETSTEPMUL, step); @@ -1003,32 +1222,139 @@ public: private: script(lua_State* state) : - state_(lua_newthread(state)), - is_main_thread_(false) {} + state_(lua_newthread(state)) {} + + slot push(lua_CFunction function, int upvalues = 0) + { + lua_pushcclosure(state_, function, upvalues); + return top(); + } + + template + slot push_class_metatable() + { + bool is_new; + slot metatable = push_metatable(typeid(T).name(), is_new); + if (is_new) + { + metatable.push_copy(); // class behavior + metatable.set_field("__index"); + + push_type(); + metatable.set_field("__cxxtype"); // type_info + + push(object_finalizer_); + metatable.set_field("__gc"); // finalizer + + //push(object_tostring_); + //metatable.set_field("__tostring"); // tostring + } + return metatable; + } + + template + static int object_tostring_(lua_State* state) + { + std::ostringstream stream; + stream << *reinterpret_cast(lua_touserdata(state, 1)); + lua_pushlstring(state, stream.str().c_str(), stream.str().length()); + return 1; + } + + template + static int object_finalizer_(lua_State* state) + { + reinterpret_cast(lua_touserdata(state, 1))->~T(); + return 0; + } - static int dispatch_call(lua_State* state) + static int call_functor(lua_State* state) { - const function* function = (const script::function*)lua_touserdata(state, + function* function = (script::function*)lua_touserdata(state, lua_upvalueindex(1)); - lua_getfield(state, LUA_REGISTRYINDEX, "Script_hh_Object"); - script* script = (moof::script*)lua_touserdata(state, -1); - lua_pop(state, 1); + script* script = (moof::script*)lua_touserdata(state, + lua_upvalueindex(2)); - return (*function)(*script); + try + { + return (*function)(*script); + } + catch (const std::exception& e) + { + if (0 < std::strlen(e.what())) + { + luaL_where(state, 1); + lua_pushstring(state, e.what()); + lua_concat(state, 2); + } + return lua_error(state); + } + catch (const char* e) + { + luaL_where(state, 1); + lua_pushstring(state, e); + lua_concat(state, 2); + return lua_error(state); + } + catch (...) + { + return lua_error(state); + } + } + + + static int call_function(lua_State* state) + { + cfunction function = (cfunction)lua_touserdata(state, + lua_upvalueindex(1)); + + script* script = (moof::script*)lua_touserdata(state, + lua_upvalueindex(2)); + + try + { + return function(*script); + } + catch (const std::exception& e) + { + if (0 < std::strlen(e.what())) + { + luaL_where(state, 1); + lua_pushstring(state, e.what()); + lua_concat(state, 2); + } + return lua_error(state); + } + catch (const char* e) + { + luaL_where(state, 1); + lua_pushstring(state, e); + lua_concat(state, 2); + return lua_error(state); + } + catch (...) + { + return lua_error(state); + } } + void destroy() { - if (is_main_thread_) lua_close(state_); + if (is_main_thread()) lua_close(state_); } + lua_State* state_; - bool is_main_thread_; - std::list functions_; }; +using namespace std::rel_ops; + +/** + * Output a script value to a stream. + */ inline std::ostream& operator << (std::ostream& stream, const script::slot& slot) { diff --git a/src/moof/sound.cc b/src/moof/sound.cc index 0714698..2c19d26 100644 --- a/src/moof/sound.cc +++ b/src/moof/sound.cc @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -25,22 +24,24 @@ #include "hash.hh" #include "log.hh" -#include "manager.hh" #include "sound.hh" #include "resource.hh" #include "timer.hh" #ifndef BUF_SIZE -#define BUF_SIZE (4096) +#define BUF_SIZE (4096 * 64) #endif -#define NUM_BUFFERS (8) +#define NUM_BUFFERS (4) namespace moof { +/*] Sound backend + *************************************************************************/ + class sound_backend { public: @@ -104,27 +105,11 @@ ALCcontext* sound_backend::al_context; class sound_resource; typedef resource_handle sound_handle; - -class sound_resource_loader -{ -public: - - sound_resource_loader() - { - resource::register_type("ogg", "sounds"); - } - - ~sound_resource_loader() - { - resource::unregister_type("ogg"); - } -}; - -static sound_resource_loader loader; - +MOOF_REGISTER_RESOURCE(sound_resource, ogg, sounds); -// SOUND BUFFER +/*] Sound buffer + *************************************************************************/ class buffer { @@ -226,7 +211,8 @@ buffer::retcount_lookup buffer::retain_counts_; -// SOUND RESOURCE +/*] Sound resource + *************************************************************************/ class sound_resource : public boost::noncopyable { @@ -334,6 +320,8 @@ private: }; +/*] Sound class + *************************************************************************/ class sound::impl { @@ -419,7 +407,7 @@ public: if (!stream_timer_.is_valid()) { stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2), - 0.01, timer::repeat); + SCALAR(0.5), timer::repeat); } alSourcePlay(source_); @@ -603,12 +591,6 @@ void sound::enqueue(const std::string& path) void sound::play() -{ - // pass through - impl_->play(); -} - -void sound::stream() { // pass through impl_->stream(); @@ -637,7 +619,6 @@ void sound::toggle() { if (is_playing()) pause(); else play(); - // TODO: what about streaming sources? } bool sound::is_playing() const diff --git a/src/moof/sound.hh b/src/moof/sound.hh index e9d4bcf..f920f49 100644 --- a/src/moof/sound.hh +++ b/src/moof/sound.hh @@ -27,6 +27,9 @@ namespace moof { +class script; + + class sound { public: @@ -38,7 +41,6 @@ public: void enqueue(const std::string& name); void play(); - void stream(); void stop(); void pause(); void rewind(); @@ -58,6 +60,7 @@ public: static void listener_orientation(const vector3& forward, const vector3& up); + static void import(script& script, const std::string& nspace = ""); private: diff --git a/src/moof/sound_bindings.cc b/src/moof/sound_bindings.cc new file mode 100644 index 0000000..9e81aa3 --- /dev/null +++ b/src/moof/sound_bindings.cc @@ -0,0 +1,112 @@ + +/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +**] All rights reserved. +* +* vi:ts=4 sw=4 tw=75 +* +* Distributable under the terms and conditions of the 2-clause BSD license; +* see the file COPYING for a complete text of the license. +* +**************************************************************************/ + +#include "script.hh" +#include "sound.hh" + + +namespace moof { + + +static int sound_new(script& script) +{ + script::slot name = script[2].require_string("sound name"); + + std::string str; + name.get(str); + + script.push(sound(str)); + return 1; +} + +static int sound_enqueue(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + + std::string name; + script[2].require_string("sound name").get(name); + + sound->enqueue(name); + return 0; +} + +static int sound_play(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + sound->play(); + return 0; +} + +static int sound_stop(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + sound->stop(); + return 0; +} + +static int sound_pause(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + sound->pause(); + return 0; +} + +static int sound_rewind(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + sound->rewind(); + return 0; +} + +static int sound_toggle(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + sound->toggle(); + return 0; +} + +static int sound_is_playing(script& script) +{ + sound* sound; + script[1].require_object("sound").get(sound); + script.push(sound->is_playing()); + return 1; +} + + +void sound::import(script& script, const std::string& nspace) +{ + script.check_stack(4); + + script::slot parent = script.push_table(nspace); + script::slot meta = script.push_class(sound_new); + + meta.set_field("enqueue", sound_enqueue); + meta.set_field("play", sound_play); + meta.set_field("stop", sound_stop); + meta.set_field("pause", sound_pause); + meta.set_field("rewind", sound_rewind); + meta.set_field("toggle", sound_toggle); + meta.set_field("is_playing", sound_is_playing); + + parent.set_field("sound"); + parent.pop(); +} + + +} // namespace moof + diff --git a/src/moof/string.cc b/src/moof/string.cc index a2c27bb..61bbf8f 100644 --- a/src/moof/string.cc +++ b/src/moof/string.cc @@ -11,129 +11,308 @@ #include +#include + #include "ConvertUTF.h" +#include "script.hh" #include "string.hh" namespace moof { -// TODO this code is ugly - wstring multi_to_wide(const string& multi) { - size_t length = multi.length(); + typedef boost::shared_array buffer; if (sizeof(wchar_t) == 2) { - wchar_t* wide = new wchar_t[length + 1]; - - const UTF8* srcStart = reinterpret_cast(multi.c_str()); - const UTF8* srcEnd = srcStart + length; - UTF16* targetStart = reinterpret_cast(wide); - UTF16* targetEnd = targetStart + length+1; + size_t length = multi.length(); + buffer wide(new wchar_t[length + 1]); + const UTF8* src1 = (const UTF8*)multi.c_str(); + const UTF8* src2 = src1 + length; + UTF16* dst1 = (UTF16*)wide.get(); + UTF16* dst2 = dst1 + length+1; - ConversionResult res = ConvertUTF8toUTF16(&srcStart, srcEnd, - &targetStart, targetEnd, lenientConversion); - if (res != conversionOK) + if (ConvertUTF8toUTF16(&src1, src2, + &dst1, dst2, lenientConversion) != conversionOK) { - delete[] wide; - throw std::runtime_error("bad conversion from multi to wide characters"); + throw std::runtime_error("bad string conversion"); } - *targetStart = 0; - wstring convertedStr(wide); - delete[] wide; - - return convertedStr; + *dst1 = 0; + wstring str(wide.get()); + return str; } else if (sizeof(wchar_t) == 4) { - wchar_t* wide = new wchar_t[length]; - - const UTF8* srcStart = reinterpret_cast(multi.c_str()); - const UTF8* srcEnd = srcStart + length; - UTF32* targetStart = reinterpret_cast(wide); - UTF32* targetEnd = targetStart + length; + size_t length = multi.length(); + buffer wide(new wchar_t[length + 1]); + const UTF8* src1 = (const UTF8*)multi.c_str(); + const UTF8* src2 = src1 + length; + UTF32* dst1 = (UTF32*)wide.get(); + UTF32* dst2 = dst1 + length+1; - ConversionResult res = ConvertUTF8toUTF32(&srcStart, srcEnd, - &targetStart, targetEnd, lenientConversion); - if (res != conversionOK) + if (ConvertUTF8toUTF32(&src1, src2, + &dst1, dst2, lenientConversion) != conversionOK) { - delete[] wide; - throw std::runtime_error("bad conversion from multi to wide characters"); + throw std::runtime_error("bad string conversion"); } - *targetStart = 0; - wstring convertedStr(wide); - delete[] wide; - - return convertedStr; + *dst1 = 0; + wstring str(wide.get()); + return str; } else { throw std::runtime_error("unknown size of wide characters"); } - return L""; } string wide_to_multi(const wstring& wide) { - size_t length = wide.length(); + typedef boost::shared_array buffer; if (sizeof(wchar_t) == 2) { - size_t multiLength = 3 * length + 1; - char* multi = new char[multiLength]; - - const UTF16* srcStart = reinterpret_cast(wide.c_str()); - const UTF16* srcEnd = srcStart + length; - UTF8* targetStart = reinterpret_cast(multi); - UTF8* targetEnd = targetStart + multiLength; + size_t length = wide.length(); + size_t multi_length = 3 * length + 1; + buffer multi(new char[multi_length]); + const UTF16* src1 = (const UTF16*)wide.c_str(); + const UTF16* src2 = src1 + length; + UTF8* dst1 = (UTF8*)multi.get(); + UTF8* dst2 = dst1 + multi_length; - ConversionResult res = ConvertUTF16toUTF8(&srcStart, srcEnd, - &targetStart, targetEnd, lenientConversion); - if (res != conversionOK) + if (ConvertUTF16toUTF8(&src1, src2, + &dst1, dst2, lenientConversion) != conversionOK) { - delete[] multi; - throw std::runtime_error("bad conversion from wide to multi-characters"); + throw std::runtime_error("bad string conversion"); } - *targetStart = 0; - string convertedStr(multi); - delete[] multi; - - return convertedStr; + *dst1 = 0; + string str(multi.get()); + return str; } else if (sizeof(wchar_t) == 4) { - size_t multiLength = 4 * length + 1; - char* multi = new char[multiLength]; + size_t length = wide.length(); + size_t multi_length = 4 * length + 1; + buffer multi(new char[multi_length]); + const UTF32* src1 = (const UTF32*)wide.c_str(); + const UTF32* src2 = src1 + length; + UTF8* dst1 = (UTF8*)multi.get(); + UTF8* dst2 = dst1 + multi_length; - const UTF32* srcStart = reinterpret_cast(wide.c_str()); - const UTF32* srcEnd = srcStart + length; - UTF8* targetStart = reinterpret_cast(multi); - UTF8* targetEnd = targetStart + multiLength; - - ConversionResult res = ConvertUTF32toUTF8(&srcStart, srcEnd, - &targetStart, targetEnd, lenientConversion); - if (res != conversionOK) + if (ConvertUTF32toUTF8(&src1, src2, + &dst1, dst2, lenientConversion) != conversionOK) { - delete[] multi; - throw std::runtime_error("bad conversion from wide to multi-characters"); + throw std::runtime_error("bad string conversion"); } - *targetStart = 0; - string convertedStr(multi); - delete[] multi; - - return convertedStr; + *dst1 = 0; + string str(multi.get()); + return str; } else { throw std::runtime_error("unknown size of wide characters"); } - return ""; +} + + +static script& regex_script() +{ + static script script; + static bool init = true; + if (init) + { + script.import_string_library(); + script.globals().push_field("string").push_field("match"); + script.globals().set_field("match"); + script.top().push_field("gmatch"); + script.globals().set_field("gmatch"); + script.top().push_field("gsub"); + script.globals().set_field("gsub"); + script.push_nil(); + script.globals().set_field("string"); + script.pop(); + init = false; + } + return script; +} + + +regex::regex(const string& pattern) +{ + regex::pattern(pattern); +} + +regex::regex(const string& pattern, const string& source) +{ + regex::pattern(pattern); + match(source); +} + +regex::~regex() +{ + script& script = regex_script(); + + script.push_pointer(this); + script.push_nil(); + script.globals().set_field(); + script.push_pointer(this); + script.push_nil(); + script.registry().set_field(); +} + + +string regex::pattern() const +{ + script& script = regex_script(); + script.push_pointer(this); + script::slot saved = script.registry().push_field(); + + string pattern; + saved.get(pattern); + + saved.pop(); + return pattern; +} + +void regex::pattern(const string& pattern) +{ + script& script = regex_script(); + script.push_pointer(this); + script.push(pattern); + script.registry().set_field(); +} + + +void regex::match(const string& source) +{ + script& script = regex_script(); + script.push_pointer(this); + + script.globals().push_field("gmatch"); + script.push(source); + script.push_pointer(this); + script.registry().push_field(); + script.call(2, 1); + + script.globals().set_field(); +} + +bool regex::get(string& match) +{ + script& script = regex_script(); + script.push_pointer(this); + script::slot value = script.globals().push_field(); + if (!value.is_function()) + { + script.clear_stack(); + return false; + } + + script.call(0, 1); + bool result = value.get(match); + script.pop(); + return result; +} + +bool regex::get(std::vector& captures) +{ + script& script = regex_script(); + script.push_pointer(this); + script::slot value = script.globals().push_field(); + if (!value.is_function()) + { + script.clear_stack(); + return false; + } + + script.call(); + captures.clear(); + + while (value.is_string()) + { + captures.resize(captures.size() + 1); + value.get(captures.back()); + ++value.index; + } + + script.clear_stack(); + return 0 < captures.size(); +} + + +bool regex::match(string& match, + const string& pattern, + const string& source, + int position) +{ + script& script = regex_script(); + + script::slot value = script.globals().push_field("match"); + script.push(source); + script.push(pattern); + ++position; // Lua indices count from one. + script.push(position); + script.call(3, 1); + + bool result = value.get(match); + script.clear_stack(); + return result; +} + +bool regex::match(std::vector& captures, + const string& pattern, + const string& source, + int position) +{ + script& script = regex_script(); + + script::slot value = script.globals().push_field("match"); + script.push(source); + script.push(pattern); + ++position; // Lua indices count from one. + script.push(position); + script.call(3); + + captures.clear(); + + while (value.is_string()) + { + captures.resize(captures.size() + 1); + value.get(captures.back()); + ++value.index; + } + + script.clear_stack(); + return 0 < captures.size(); +} + + +int regex::sub(string& substitution, + const string& pattern, + const string& source, + const string& replacement) +{ + script& script = regex_script(); + + script::slot value = script.globals().push_field("gsub"); + script.push(source); + script.push(pattern); + script.push(replacement); + script.call(3, 2); + + value.get(substitution); + + ++value.index; + int count = 0; + value.get(count); + + script.clear_stack(); + return count; } diff --git a/src/moof/string.hh b/src/moof/string.hh index d666aa1..dd9d791 100644 --- a/src/moof/string.hh +++ b/src/moof/string.hh @@ -14,11 +14,13 @@ /** * \file string.hh - * Functions related to string manipulations. + * Functions and classes related to string manipulation. */ #include +#include + namespace moof { @@ -42,8 +44,152 @@ wstring multi_to_wide(const string& multi); string wide_to_multi(const wstring& wide); -} // namespace moof +/** + * Class exposing the pattern-matching and substitution methods used in + * Lua. + */ +class regex : public boost::noncopyable +{ +public: + + /** + * Construct a regex object. + */ + regex() {} + + /** + * Construct a regex object with a pattern. + * \param pattern The pattern. + */ + regex(const string& pattern); + + /** + * Construct a regex object with a pattern and source to match. + * \param pattern The pattern. + * \param source The source string. + */ + regex(const string& pattern, const string& source); + + /** + * Deconstruct the regex. + */ + ~regex(); + + + /** + * Get the regex pattern. + */ + string pattern() const; + + /** + * Set the regex pattern. + */ + void pattern(const string& pattern); + + + /** + * Match a string against the pattern iteratively. + * \param source The source string. + */ + void match(const string& source); + + /** + * Get the next match. If the pattern contains captures, this version + * will only get the first capture. + * \param match Reference to a string to be assigned the match. + * \return True if there was a match to get, false otherwise. + */ + bool get(string& match); + + /** + * Get the next match. Use this version if the pattern contains more + * than one capture to get all of the captures. + * \param captures Reference to a vector of strings to hold the result. + * \return True if there was a match to get, false otherwise. + */ + bool get(std::vector& captures); + + + /** + * Match a string against a pattern all at one time. + * \param pattern The pattern. + * \param source The source string. + * \param position The index of the first character of source to match. + * \return The match. + */ + static string match(const string& pattern, + const string& source, + int position = 0) + { + string match; + regex::match(match, pattern, source, position); + return match; + } + + /** + * Match a string against a pattern all at one time. + * \param match A reference to a string to be assigned the match. + * \param pattern The pattern. + * \param source The source string. + * \param position The index of the first character of source to match. + * \return True if a match was made, false otherwise. + */ + static bool match(string& match, + const string& pattern, + const string& source, + int position = 0); + + /** + * Match a string against a pattern all at one time. If the pattern + * contains captures, the resulting vector will contain all of the + * captures. + * \param captures A reference to a vector of strings to contain the + * result. + * \param pattern The pattern. + * \param source The source string. + * \param position The index of the first character of source to match. + * \return True if a match was made, false otherwise. + */ + static bool match(std::vector& captures, + const string& pattern, + const string& source, + int position = 0); + + + /** + * Substitute a string using a pattern and replacement string. + * \param pattern The pattern. + * \param source The source string. + * \param replacement The replacement string; may also contain + * references to captures. + * \return The string with any substitutions made. + */ + static string sub(const string& pattern, + const string& source, + const string& replacement) + { + string substitution; + regex::sub(substitution, pattern, source, replacement); + return substitution; + } + + /** + * Substitute a string using a pattern and replacement string. + * \param substitution A reference to a string to contain the result. + * \param pattern The pattern. + * \param source The source string. + * \param replacement The replacement string; may also contain + * references to captures. + * \return The number of substitutions made. + */ + static int sub(string& substitution, + const string& pattern, + const string& source, + const string& replacement); +}; + +} // namespace moof #endif // _MOOF_STRINGTOOLS_HH_ diff --git a/src/moof/timer.cc b/src/moof/timer.cc index 89f3e9d..74acc1c 100644 --- a/src/moof/timer.cc +++ b/src/moof/timer.cc @@ -139,11 +139,6 @@ bool timer::is_repeating() const } -void timer::fire_expired_timers() -{ - fire_expired_timers(ticks()); -} - void timer::fire_expired_timers(scalar t) { if (next_expiration_ > t) return; @@ -191,7 +186,7 @@ scalar timer::ticks() ASSERT(result == 0 && "cannot access clock"); return scalar(ts.tv_sec - reference_) + - scalar(ts.tv_nsec) / 1000000000.0; + scalar(ts.tv_nsec) * SCALAR(0.000000001); } void timer::sleep(scalar seconds, mode mode) @@ -201,7 +196,7 @@ void timer::sleep(scalar seconds, mode mode) if (mode == absolute) seconds -= ticks(); ts.tv_sec = time_t(seconds); - ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0); + ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0)); do { @@ -221,13 +216,13 @@ void timer::sleep(scalar seconds, mode mode) scalar timer::ticks() { Uint32 ms = SDL_GetTicks(); - return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0; + return scalar(ms / 1000) + scalar(ms % 1000) * SCALAR(0.001); } void timer::sleep(scalar seconds, mode mode) { if (mode == absolute) seconds -= ticks(); - SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000))); + SDL_Delay(Uint32(clamp(Uint32(seconds * SCALAR(1000.0)), 0, 1000))); } #endif // USE_CLOCK_GETTIME diff --git a/src/moof/timer.hh b/src/moof/timer.hh index a86f445..cb50c3b 100644 --- a/src/moof/timer.hh +++ b/src/moof/timer.hh @@ -201,7 +201,10 @@ public: * Fire any timers which are not yet invalidated but have an expiration * time in the past. */ - static void fire_expired_timers(); + static void fire_expired_timers() + { + fire_expired_timers(ticks()); + } /** * Fire any timers which are not yet invalidated but have an expiration -- 2.45.2