# HG changeset patch # User zzzzrrr # Date 1237545685 14400 # Node ID c10bc63824e75eb32d4239def565ea0048aa5acc Initial commit! diff -r 000000000000 -r c10bc63824e7 boundaryListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/boundaryListener.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * Based on Box2D by Erin Catto, http://www.box2d.org + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.boundaryListener; + +import melee.melee; + +// bzWorld boundary callback +class BoundaryListener : bzBoundaryListener +{ + Melee melee; + + this(Melee melee) + { + this.melee = melee; + } + + void violation(bzBody rBody) + { + melee.boundaryViolated(rBody); + } +} diff -r 000000000000 -r c10bc63824e7 build-dmd-win.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build-dmd-win.bat Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,4 @@ +::jake -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O main.d +rebuild -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O main.d +pause + diff -r 000000000000 -r c10bc63824e7 contactListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contactListener.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * Based on Box2D by Erin Catto, http://www.box2d.org + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.contactListener; + +import melee.melee; + +// bzWorld contact callback +class ContactListener : bzContactListener +{ + + Melee melee; + + this(Melee melee) { + this.melee = melee; + } + + void add(bzContactPoint point) + { + if (melee.pointCount == k_maxContactPoints) { + return; + } + + ContactPoint *cp = &melee.points[melee.pointCount]; + cp.shape1 = point.shape1; + cp.shape2 = point.shape2; + cp.position = point.position; + cp.normal = point.normal; + cp.id = point.id; + cp.state = ContactState.e_contactAdded; + + ++melee.pointCount; + } + + void persist(bzContactPoint point) + { + if (melee.pointCount == k_maxContactPoints) { + return; + } + + ContactPoint *cp = &melee.points[melee.pointCount]; + cp.shape1 = point.shape1; + cp.shape2 = point.shape2; + cp.position = point.position; + cp.normal = point.normal; + cp.id = point.id; + cp.state = ContactState.e_contactPersisted; + + ++melee.pointCount; + } + + void remove(bzContactPoint point) + { + if (melee.pointCount == k_maxContactPoints) { + return; + } + + ContactPoint *cp = &melee.points[melee.pointCount]; + cp.shape1 = point.shape1; + cp.shape2 = point.shape2; + cp.position = point.position; + cp.normal = point.normal; + cp.id = point.id; + cp.state = ContactState.e_contactRemoved; + + ++melee.pointCount; + } + + void result(bzContactResult point) {} + +} diff -r 000000000000 -r c10bc63824e7 gui.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,20 @@ +import "themes/default.cfg" + +new FramedTopLevelWindow main { + frame.text = "OpenMelee"; + showCursor = false; + size = 900 610; + [hexpand hfill vexpand vfill] new HBox { + [vexpand vfill] new VBox controls { + size = 175 0; + layout = { + padding = 5 5; + } + } + [hexpand hfill vexpand vfill] new GLViewport glview; + } +} @overlay { + [hexpand vexpand hfill vfill] new Group .overlay { + layout = Ghost; + } +} diff -r 000000000000 -r c10bc63824e7 main.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.main; + +import tango.io.Stdout : Stdout; + +version(distrib) import tango.io.vfs.ZipFolder; +import tango.time.StopWatch; +import fc = tango.text.convert.Float : toString; +import tango.util.log.Trace; +import tango.math.Math; + +import xf.core.JobHub; +import xf.hybrid.Hybrid; +import xf.hybrid.backend.GL; +import xf.omg.core.LinearAlgebra; + +import melee.melee; +import melee.render; + +const ITERS_PER_SECOND = 60; + +void main() { + + Settings settings; + float timeStep = settings.hz > 0.0f ? 1.0f / settings.hz : 0.0f; + version(distrib) gui.vfs.mount(new ZipFolder("./gui.zip")); + scope cfg = loadHybridConfig("./gui.cfg"); + scope renderer = new Renderer; + auto whut = new Render(&settings); + + gui.begin(cfg).retained; + gui.push(`main`); + GLViewport(`glview`).renderingHandler(&whut.draw) + .addHandler(&whut.onClick) + .addHandler(&whut.onMove) + .addHandler(&whut.onKey) + .addHandler(&whut.onDT) + .addHandler(&whut.onMouseEnter) + .addHandler(&whut.onMouseLeave) + .grabKeyboardFocus; + gui.pop(); + gui.immediate.end; + + StopWatch timer; + + jobHub.addRepeatableJob( { + whut.world.step(timeStep, settings.velocityIterations, + settings.positionIterations); + }, ITERS_PER_SECOND); + + bool running = true; + + jobHub.addPreFrameJob( { + // Clean out the kill list + uint[] key = whut.killList.keys; + foreach(k; key) { + whut.world.destroyBody(whut.killList[k]); + whut.killList.remove(k); + } + }); + + jobHub.addPostFrameJob( { + gui.begin(cfg); + gui.push(`main`); + if (gui().getProperty!(bool)("frame.closeClicked")) { + running = false; + } + + if(whut.thrust) { + whut.ship1.thrust(); + } + + gui().setProperty!(bool)("showCursor", true); + gui.pop(); + gui.end; + gui.render(renderer); + + { + vec2 p1 = vec2.from(whut.ship1.rBody.position); + vec2 p2 = vec2.from(whut.ship2.rBody.position); + vec2 distance = p1 - p2; + whut.viewCenter = p1 - (distance * 0.5f); + } + + }); + + while (running && !whut.quit) { + float delta = timer.stop; + timer.start; + jobHub.update(delta); + } +} diff -r 000000000000 -r c10bc63824e7 melee.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/melee.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.melee; + +import Integer = tango.text.convert.Integer; +import tango.math.Math; +import tango.math.random.Kiss; + +import xf.hybrid.Event; +import xf.input.KeySym; +import xf.omg.core.LinearAlgebra; + +public import blaze.all; + +import melee.boundaryListener; +import melee.contactListener; +import melee.ship; +import melee.urQuan; +import melee.orz; + +// Cursor scale factor +const CURSORSIZE = 0.05f; + +const INIT_SPAWN_SIZE = 0.5f; + +// Dragging stuffs +const BUNGEE_K = 1.5f; +// Damping factor for dragging +const DRAGDAMP = 20.0f; + +// Size of hinges +const HINGE_RADIUS = 0.05f; + +// Smallest allowed dimension +const MIN_DIMENSION = 0.1; + +// Water stuffs +const MAX_PARTICLES = 10000; +const WATER_BOUNCE = 0.01; +const WATER_FRICTION = 0.5; +const WATER_RADIUS = 0.05f; +const MAX_CIRCLE_RES = 32; + +const k_maxContactPoints = 2048; + +enum ContactState { + e_contactAdded, + e_contactPersisted, + e_contactRemoved +} + +struct ContactPoint { + bzShape shape1; + bzShape shape2; + bzVec2 normal; + bzVec2 position; + bzVec2 velocity; + bzContactID id; + ContactState state; +} + +// Melee settings. Some can be controlled in the GUI. +struct Settings { + float hz = 60; + int velocityIterations = 8; + int positionIterations = 2; + bool drawShapes = true; + bool drawJoints = true; + bool drawControllers; + bool drawCoreShapes; + bool drawAABBs; + bool drawOBBs; + bool drawPairs; + bool drawContactPoints; + bool drawContactNormals; + bool drawContactForces; + bool drawFrictionForces; + bool drawCOMs; + bool drawStats; + bool enableWarmStarting; + bool enableTOI; +} + +// Dirty, dirty hack for communicating config changes to Main +// TODO: Harass h3 to add .changed to hybrid so this isn't necessary +struct ConfigChange (T) +{ + protected + { + bool _pending = false; + T _value; + } + + T value() + { + _pending = false; + return _value; + } + + void value(T change) + { + _value = change; + _pending = true; + } + + bool pending() + { + return _pending; + } + + T opAssign(T change) + { + value(change); + return _value; + } + + bool opEquals(T other) + { + return _value == other; + } +} + +T randomRange(T = int) (T min, T max) +{ + return min + Kiss.instance.natural() % (max + 1 - min); +} + +class Melee +{ + + this(Settings *settings) + { + this.settings = settings; + init(); + spawnRect = vec2(INIT_SPAWN_SIZE, INIT_SPAWN_SIZE); + // bzWorld boundary callback + m_boundaryListener = new BoundaryListener(this); + // bzContact callback + m_contactListener = new ContactListener(this); + } + + void init() { + // Define world boundaries + bzAABB worldAABB; + worldAABB.lowerBound.set(-200.0f, -100.0f); + worldAABB.upperBound.set(200.0f, 200.0f); + world = new bzWorld(worldAABB, gravity, allowSleep); + world.boundaryListener = m_boundaryListener; + world.contactListener = m_contactListener; + viewCenter = vec2(10, 10); + ship1 = new Orz(world); + ship2 = new UrQuan(world); + } + + void drag() + { + + } + + EventHandling onClick(MouseButtonEvent e) + { + return EventHandling.Stop; + } + + EventHandling onKey(KeyboardEvent e) + { + if (e.unicode == '+') { // HACK: the windows input writer doesn't do plus correctly yet - h3 + e.keySym = KeySym.plus; + } + if (e.down) { + switch (e.keySym) { + case KeySym.Escape: + quit = true; + break; + default: + char key = cast(char) e.keySym; + if(key == 'w') { + thrust = true; + break; + } else { + ship1.turn(key); + } + break; + } + // Key released + } else { + char key = cast(char) e.keySym; + if(key == 'w') { + thrust = false; + } else if (key == 'd' || key == 'a') { + ship1.rBody.angularVelocity = 0.0f; + } + } + + return EventHandling.Stop; + } + + // Mouse move + EventHandling onMove(MouseMoveEvent e) + { + return EventHandling.Stop; + } + + EventHandling onDT(TimeUpdateEvent e) + { + return EventHandling.Continue; + } + + EventHandling onMouseEnter(MouseEnterEvent e) + { + return EventHandling.Continue; + } + + EventHandling onMouseLeave(MouseLeaveEvent e) + { + return EventHandling.Continue; + } + + protected + { + const bzVec2 gravity = bzVec2(0.0f, 0.0f); + bool allowSleep = false; + + vec2 spawnRect; + + vec2[] drawing; + + vec2i screenSize = vec2i.zero; + vec2 mousePos = vec2.zero; + + bool scaling = false; + bool full = false; + + vec2 spawnStart; + + bool preserveBullet = false; + + float waterDelta = 0; + } + + void boundaryViolated(bzBody rBody) + { + uint key = rBody.toHash(); + killList[key] = rBody; + } + + bool quit; + + // Ortho view zoom + float zoom = 40; + int pointCount; + vec2 viewCenter; + bzBody[uint] killList; + + bzWorld world; + Settings *settings; + + // bzWorld boundary listener. Destroy bodies that leave world bzAABB + bzBoundaryListener m_boundaryListener; + bzContactListener m_contactListener; + ContactPoint[k_maxContactPoints] points; + + ConfigChange!(vec2) editChange; + bool thrust; + + Ship ship1; + Ship ship2; +} + +// Utility functions +bzVec2 toBlaze(vec2 vec) +{ + auto ret = bzVec2(vec.x, vec.y); + return ret; +} + +vec2 rotate(vec2 point, float rad) +{ + return vec2(cos(rad) * point.x - sin(rad) * point.y, sin(rad) * point.x + cos(rad) * point.y); +} + +class Select +{ + bool select; +} diff -r 000000000000 -r c10bc63824e7 melee.geany --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/melee.geany Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,29 @@ + +[indentation] +indent_width=4 +indent_type=0 +indent_hard_tab_width=8 +detect_indent=false +indent_mode=2 + +[project] +name=melee +base_path=C:\\Documents and Settings\\greenma3\\workspace\\melee +make_in_base_path=false +description= +run_cmd= + +[files] +current_page=11 +FILE_NAME_0=3688;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\main.d;0 +FILE_NAME_1=108;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\boundaryListener.d;0 +FILE_NAME_2=108;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\contactListener.d;0 +FILE_NAME_3=6553;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\melee.d;0 +FILE_NAME_4=176;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\models.d;0 +FILE_NAME_5=0;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\orz.d;0 +FILE_NAME_6=9256;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\render.d;0 +FILE_NAME_7=2582;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\ship.d;0 +FILE_NAME_8=5220;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\steer.d;0 +FILE_NAME_9=762;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\urQuan.d;0 +FILE_NAME_10=143;None;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\melee\\build-dmd-win.bat;0 +FILE_NAME_11=13795;D;0;16;0;1;1;C:\\Documents and Settings\\greenma3\\workspace\\blaze\\examples\\testBed\\framework\\test.d;0 diff -r 000000000000 -r c10bc63824e7 models.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/models.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,239 @@ +void spathiEluder(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex) +{ + vertex.length = bodyVertex.length = 11; + + vertex[0].length = bodyVertex[0].length = 19; + // Hull + bodyVertex[0][0] = bzVec2(2.24,31.54); + bodyVertex[0][1] = bzVec2(14.14,28.28); + bodyVertex[0][2] = bzVec2(23.89,20.72); + bodyVertex[0][3] = bzVec2(30.00,10.00); + bodyVertex[0][4] = bzVec2(31.54, -2.24); + bodyVertex[0][5] = bzVec2(28.28,-14.14); + bodyVertex[0][6] = bzVec2(22.48,-21.62); + bodyVertex[0][7] = bzVec2(20.72,-23.89); + bodyVertex[0][8] = bzVec2(10.00,-30.00); + bodyVertex[0][9] = bzVec2(-2.24,-31.54); + bodyVertex[0][10] = bzVec2(-8.19,-29.91); + bodyVertex[0][11] = bzVec2(-19.02,-24.50); + bodyVertex[0][12] = bzVec2(-23.89,-20.72); + bodyVertex[0][13] = bzVec2(-30.00,-10.00); + bodyVertex[0][14] = bzVec2(-31.00,-5.00); + bodyVertex[0][15] = bzVec2(-31.00,5.00); + bodyVertex[0][16] = bzVec2(-28.28,14.14); + bodyVertex[0][17] = bzVec2(-20.72,23.89); + bodyVertex[0][18] = bzVec2(-10.00,30.00); + + vertex[1].length = bodyVertex[1].length = 4; + // Body + bodyVertex[1][0] = bzVec2(14.14,28.28); + bodyVertex[1][1] = bzVec2(23.89,20.72); + bodyVertex[1][2] = bzVec2(44.56,47.70); + bodyVertex[1][3] = bzVec2(35.97,54.29); + + vertex[2].length = bodyVertex[2].length = 4; + // Top Strut + bodyVertex[2][0] = bzVec2(22.48,-21.62); + bodyVertex[2][1] = bzVec2(28.28,-14.14); + bodyVertex[2][2] = bzVec2(53.41,-29.39); + bodyVertex[2][3] = bzVec2(46.67,-37.86); + + vertex[3].length = bodyVertex[3].length = 4; + // Top Wing + bodyVertex[3][0] = bzVec2(-19.02,-24.50); + bodyVertex[3][1] = bzVec2(-8.19,-29.91); + bodyVertex[3][2] = bzVec2(-36.26,-62.25); + bodyVertex[3][3] = bzVec2(-42.85,-53.66); + + vertex[4].length = bodyVertex[4].length = 4; + // Bottom Strut + bodyVertex[4][0] = bzVec2(-31.00,-5.00); + bodyVertex[4][1] = bzVec2(-31.00,5.00); + bodyVertex[4][2] = bzVec2(-46.00,5.00); + bodyVertex[4][3] = bzVec2(-46.00,-5.00); + + vertex[5].length = bodyVertex[5].length = 4; + // Bottom Wing + bodyVertex[5][0] = bzVec2(-10.00,30.00); + bodyVertex[5][1] = bzVec2(-20.72,23.89); + bodyVertex[5][2] = bzVec2(-50.00,65.86); + bodyVertex[5][3] = bzVec2(-40.00,70.00); + + vertex[6].length = bodyVertex[6].length = 8; + // Rear, top Pod + bodyVertex[6][0] = bzVec2(-40.00 ,70.00); + bodyVertex[6][1] = bzVec2(-35.86 , 80.00); + bodyVertex[6][2] = bzVec2( -40.00, 90.00); + bodyVertex[6][3] = bzVec2( -50.00,94.14); + bodyVertex[6][4] = bzVec2( -60.00, 90.00); + bodyVertex[6][5] = bzVec2(-64.14 , 80.00); + bodyVertex[6][6] = bzVec2(-60.00 , 70.00); + bodyVertex[6][7] = bzVec2(-50.00 ,65.86); + + vertex[7].length = bodyVertex[7].length = 8; + // Rear, middle pod + bodyVertex[7][0] = bzVec2(-46.00,5.00); + bodyVertex[7][1] = bzVec2( -46.00,-5.00); + bodyVertex[7][2] = bzVec2(-54.47 ,-12.02); + bodyVertex[7][3] = bzVec2( -65.30, -13.11); + bodyVertex[7][4] = bzVec2( -73.02, -5.53); + bodyVertex[7][5] = bzVec2( -73.11,5.30); + bodyVertex[7][6] = bzVec2( -65.53, 13.02); + bodyVertex[7][7] = bzVec2( -54.70,13.11); + + + vertex[8].length = bodyVertex[8].length = 8; + // Rear, bottom pod + bodyVertex[8][0] = bzVec2( -36.26,-62.25); + bodyVertex[8][1] = bzVec2( -42.85,-53.66); + bodyVertex[8][2] = bzVec2( -53.59,-52.25); + bodyVertex[8][3] = bzVec2( -62.17,-58.84); + bodyVertex[8][4] = bzVec2( -63.59,-69.57); + bodyVertex[8][5] = bzVec2( -57.00,-78.16); + bodyVertex[8][6] = bzVec2( -46.26,-79.57); + bodyVertex[8][7] = bzVec2( -37.68,-72.98); + + vertex[9].length = bodyVertex[9].length = 8; + + bodyVertex[9][0] = bzVec2( 53.41, -29.39); + bodyVertex[9][1] = bzVec2( 46.67,-37.86); + bodyVertex[9][2] = bzVec2( 47.89,-47.61); + bodyVertex[9][3] = bzVec2( 56.36, -55.35); + bodyVertex[9][4] = bzVec2( 67.12,-54.12); + bodyVertex[9][5] = bzVec2( 73.86, -45.65); + bodyVertex[9][6] = bzVec2( 72.63,-34.90); + bodyVertex[9][7] = bzVec2( 64.16,-28.16); + + vertex[10].length = bodyVertex[10].length = 8; + + bodyVertex[10][0] = bzVec2( 35.97, 54.29); + bodyVertex[10][1] = bzVec2( 44.56, 47.70); + bodyVertex[10][2] = bzVec2( 56.29, 49.12 ); + bodyVertex[10][3] = bzVec2( 61.88, 57.70); + bodyVertex[10][4] = bzVec2( 60.47, 68.44); + bodyVertex[10][5] = bzVec2( 51.88, 75.02); + bodyVertex[10][6] = bzVec2( 41.15, 73.61); + bodyVertex[10][7] = bzVec2( 34.56, 65.02); +} + +void urQuanDreadnought(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex) +{ + vertex.length = bodyVertex.length = 6; + + vertex[0].length = bodyVertex[0].length = 8; + // Head + bodyVertex[0][0] = bzVec2(42,49); + bodyVertex[0][1] = bzVec2(63,49); + bodyVertex[0][2] = bzVec2(70,45.5); + bodyVertex[0][3] = bzVec2(73.5,38.5); + bodyVertex[0][4] = bzVec2(73.5,-42); + bodyVertex[0][5] = bzVec2(70,-49); + bodyVertex[0][6] = bzVec2(63,-56); + bodyVertex[0][7] = bzVec2(42,-56); + + vertex[1].length = bodyVertex[1].length = 4; + // Body + bodyVertex[1][0] = bzVec2(-70,-28); + bodyVertex[1][1] = bzVec2(-70,24.5); + bodyVertex[1][2] = bzVec2(42,24.5); + bodyVertex[1][3] = bzVec2(42,-31.5); + + vertex[2].length = bodyVertex[2].length = 4; + // Top Strut + bodyVertex[2][0] = bzVec2(0,24.5); + bodyVertex[2][1] = bzVec2(-28,24.5); + bodyVertex[2][2] = bzVec2(-28,42); + bodyVertex[2][3] = bzVec2(0,42); + + vertex[3].length = bodyVertex[3].length = 4; + // Top Wing + bodyVertex[3][0] = bzVec2(-70,42); + bodyVertex[3][1] = bzVec2(-49,63); + bodyVertex[3][2] = bzVec2(28,63); + bodyVertex[3][3] = bzVec2(28,42); + + vertex[4].length = bodyVertex[4].length = 4; + // Bottom Strut + bodyVertex[4][0] = bzVec2(0,-31.5); + bodyVertex[4][1] = bzVec2(0,-49); + bodyVertex[4][2] = bzVec2(-28,-49); + bodyVertex[4][3] = bzVec2(-28,-31.5); + + vertex[5].length = bodyVertex[5].length = 4; + // Bottom Wing + bodyVertex[5][0] = bzVec2(-70,-49); + bodyVertex[5][1] = bzVec2(28,-49); + bodyVertex[5][2] = bzVec2(28,-70); + bodyVertex[5][3] = bzVec2(-42,-70); +} + +void yehatTerminator(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex) +{ + vertex.length = bodyVertex.length = 4; + vertex[0].length = bodyVertex[0].length = 3; + // Cockpit + bodyVertex[0][0] = bzVec2(-14,21); + bodyVertex[0][1] = bzVec2(0,42); + bodyVertex[0][2] = bzVec2(14,21); + + + vertex[1].length = bodyVertex[1].length = 4; + // Body + bodyVertex[1][0] = bzVec2(-14,21); + bodyVertex[1][1] = bzVec2(-14,-21); + bodyVertex[1][2] = bzVec2(14,-21); + bodyVertex[1][3] = bzVec2(14,21); + + vertex[2].length = bodyVertex[2].length = 4; + // Right Wing + bodyVertex[2][0] = bzVec2(14,21); + bodyVertex[2][1] = bzVec2(14,-21); + bodyVertex[2][2] = bzVec2(70,0); + bodyVertex[2][3] = bzVec2(84,59.5); + + vertex[3].length = bodyVertex[3].length = 4; + // Left Wing + bodyVertex[3][0] = bzVec2(-14,21); + bodyVertex[3][1] = bzVec2(-14,-21); + bodyVertex[3][2] = bzVec2(-70,0); + bodyVertex[3][3] = bzVec2(-84,59.5); +} + +void orzNemesis(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex) +{ + vertex.length = bodyVertex.length = 3; + vertex[0].length = bodyVertex[0].length = 4; + // Body + bodyVertex[0][0] = bzVec2(-28,21); + bodyVertex[0][1] = bzVec2(-28,-28); + bodyVertex[0][2] = bzVec2(42,-21); + bodyVertex[0][3] = bzVec2(42,14); + + vertex[1].length = bodyVertex[1].length = 5; + // Top Wing + bodyVertex[1][0] = bzVec2(-28,21); + bodyVertex[1][1] = bzVec2(-70,63); + bodyVertex[1][2] = bzVec2(-49,63); + bodyVertex[1][3] = bzVec2(70,14); + bodyVertex[1][4] = bzVec2(42,14); + + vertex[2].length = bodyVertex[2].length = 5; + // Bottom Wing + bodyVertex[2][0] = bzVec2(-28,-28); + bodyVertex[2][1] = bzVec2(-70,-63); + bodyVertex[2][2] = bzVec2(-49,-63); + bodyVertex[2][3] = bzVec2(70,-21); + bodyVertex[2][4] = bzVec2(42,-21); +} + +void triangle(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex) +{ + + vertex.length = bodyVertex.length = 1; + vertex[0].length = bodyVertex[0].length = 3; + + bodyVertex[0][0] = bzVec2(0.0f,7.0f); + bodyVertex[0][1] = bzVec2(7.0f,-7.0f); + bodyVertex[0][2] = bzVec2(-7.0f,-7.0f); + +} diff -r 000000000000 -r c10bc63824e7 orz.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orz.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.orz; + +import tango.io.Stdout; + +import blaze.dynamics.bzBody : bzBody; +import blaze.bzWorld: bzWorld; +import blaze.dynamics.bzBodyDef; +import blaze.collision.shapes.bzPolygon : bzPolyDef; +import blaze.common.bzMath: bzVec2, PI; + +import melee.ship; + +// UrQuan Dreadnought +class Orz : Ship +{ + + float scale = 0.01; + + this(bzWorld world) { + + super.engineForce = bzVec2(5, 0); + super.turnForce = bzVec2(0, 300); + super.rightTurnPoint = bzVec2(-0.1, 0); + super.leftTurnPoint = bzVec2(0.1, 0); + + auto bodyDef = new bzBodyDef; + bodyDef.position = bzVec2(10,10); + bodyDef.angle = PI/2; + + super.rBody = world.createBody(bodyDef); + + float density = 2.0f; + + // Body + auto b = new bzPolyDef(density); + b.vertices.length = 4; + b.vertices[0] = bzVec2(42,14) * scale; + b.vertices[1] = bzVec2(-28,21) * scale; + b.vertices[2] = bzVec2(-28,-28) * scale; + b.vertices[3] = bzVec2(42,-21) * scale; + super.shapes.add(rBody.createShape(b)); + + // Top Wing + auto tWing = new bzPolyDef(density); + tWing.vertices.length = 5; + tWing.vertices[4] = bzVec2(-28,21) * scale; + tWing.vertices[3] = bzVec2(-70,63) * scale; + tWing.vertices[2] = bzVec2(-49,63) * scale; + tWing.vertices[1] = bzVec2(70,14) * scale; + tWing.vertices[0] = bzVec2(42,14) * scale; + super.shapes.add(rBody.createShape(tWing)); + + // Bottom Wing + auto bWing = new bzPolyDef(density); + bWing.vertices.length = 5; + bWing.vertices[0] = bzVec2(-28,-28) * scale; + bWing.vertices[1] = bzVec2(-70,-63) * scale; + bWing.vertices[2] = bzVec2(-49,-63) * scale; + bWing.vertices[3] = bzVec2(70,-21) * scale; + bWing.vertices[4] = bzVec2(42,-21) * scale; + super.shapes.add(rBody.createShape(bWing)); + + super.rBody.setMassFromShapes(); + + } +} diff -r 000000000000 -r c10bc63824e7 render.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/render.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * Based on Box2D by Erin Catto, http://www.box2d.org + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.render; + +import xf.dog.Dog; +import xf.omg.core.LinearAlgebra; +import xf.hybrid.Event; +import xf.hybrid.Font; + +import melee.melee; + +/// Color for drawing. Each value has the range [0,1]. +struct Color { + static Color opCall(float r, float g, float b) + { + Color u; + u.r = r; + u.g = g; + u.b = b; + return u; + } + + float r = 0; + float g = 0; + float b = 0; +} + +class Render : Melee +{ + +this(Settings *settings) { + + super(settings); +} + +void drawCircle(GL gl, vec2 center, float radius, bool water = false, float theta = float.nan) +{ + int segs = cast(int)(radius) + 20; + if (segs > MAX_CIRCLE_RES) segs = MAX_CIRCLE_RES; + double coef = 2.0 * PI / segs; + + auto realTheta = (theta <>= 0 ? theta : 0); + if (water) { + gl.immediate(GL_TRIANGLE_FAN, + { + gl.Vertex2fv(center.ptr); + for (int n = 0; n <= segs; n++) { + double rads = n * coef; + gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y); + } + }); + } + + gl.immediate(GL_LINE_STRIP, + { + for (int n = 0; n <= segs; n++) { + double rads = n * coef; + gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y); + } + if (theta <>= 0) + gl.Vertex2fv(center.ptr); + }); +} + +void drawSolidCircle(GL gl, vec2 center, float radius, vec2 axis, Color color) +{ + const k_segments = 16.0f; + const k_increment = 2.0f * PI / k_segments; + float theta = 0.0f; + gl.Enable(GL_BLEND); + gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f); + gl.Begin(GL_TRIANGLE_FAN); + for (int i = 0; i < k_segments; ++i) { + vec2 v = center + radius * vec2(cos(theta), sin(theta)); + gl.Vertex2f(v.x, v.y); + theta += k_increment; + } + gl.End(); + gl.Disable(GL_BLEND); + + theta = 0.0f; + gl.Color4f(color.r, color.g, color.b, 1.0f); + gl.Begin(GL_LINE_LOOP); + for (int i = 0; i < k_segments; ++i) { + vec2 v = center + radius * vec2(cos(theta), sin(theta)); + gl.Vertex2f(v.x, v.y); + theta += k_increment; + } + gl.End(); + + vec2 p = center + radius * axis; + gl.Begin(GL_LINES); + gl.Vertex2f(center.x, center.y); + gl.Vertex2f(p.x, p.y); + gl.End(); +} + +void drawPolygon(GL gl, vec2[] glVerts, Color color) +{ + gl.Color3f(color.r, color.g, color.b); + gl.immediate(GL_LINE_LOOP, + { + foreach (v; glVerts) + gl.Vertex2fv(v.ptr); + }); +} + +void drawSolidPolygon(GL gl, vec2[] vertices, Color color) +{ + gl.Enable(GL_BLEND); + gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f); + gl.Begin(GL_TRIANGLE_FAN); + for (int i = 0; i < vertices.length; ++i) { + gl.Vertex2f(vertices[i].x, vertices[i].y); + } + gl.End(); + gl.Disable(GL_BLEND); + + gl.Color4f(color.r, color.g, color.b, 1.0f); + gl.Begin(GL_LINE_LOOP); + for (int i = 0; i < vertices.length; ++i) { + gl.Vertex2f(vertices[i].x, vertices[i].y); + } + gl.End(); +} + + +void drawPoint(GL gl, vec2 p, float size, Color color) +{ + gl.Color3f(color.r, color.g, color.b); + gl.PointSize(size); + gl.Begin(GL_POINTS); + gl.Vertex2f(p.x, p.y); + gl.End(); + gl.PointSize(1.0f); +} + +void drawSegment(GL gl, vec2 begin, vec2 end, Color color) +{ + gl.Color3f(color.r, color.g, color.b); + gl.immediate(GL_LINES, + { + gl.Vertex2fv(begin.ptr); + gl.Vertex2fv(end.ptr); + }); +} + +// TODO: handle inequal radii correctly +void connectCircles(GL gl, vec2 center1, float radius1, vec2 center2, float radius2) +{ + auto d = center2 - center1; + if (!d.length) + return; + d *= (d.length - radius1) / d.length; + center1 += d; + center2 -= d; + gl.immediate(GL_LINES, + { + gl.Vertex2fv(center1.ptr); + gl.Vertex2fv(center2.ptr); + }); +} + +void drawXForm(GL gl, bzXForm xf) +{ + bzVec2 p1 = xf.position, p2; + const k_axisScale = 0.4f; + + gl.Begin(GL_LINES); + { + gl.Color3f(1.0f, 0.0f, 0.0f); + gl.Vertex2f(p1.x, p1.y); + p2 = p1 + k_axisScale * xf.R.col1; + gl.Vertex2f(p2.x, p2.y); + + gl.Color3f(0.0f, 1.0f, 0.0f); + gl.Vertex2f(p1.x, p1.y); + p2 = p1 + k_axisScale * xf.R.col2; + gl.Vertex2f(p2.x, p2.y); + } + gl.End(); +} + +void drawSpring(GL gl, vec2 a, vec2 b, uint zigs) +{ + zigs++; + + // Portion of length dedicated to connectors + const float connPart = 0.2; + + vec2 inc = (b - a) / (zigs); + // One step from a to b + vec2 zigLen = inc * (1 - connPart); + // Length of a connector + vec2 connLen = inc * (connPart / 2) * zigs; + // Width of a zig + vec2 zigWidth = (b - a).rotatedHalfPi.normalized; + gl.immediate(GL_LINE_STRIP, + { + gl.Vertex2fv(a.ptr); + + a += connLen; + gl.Vertex2fv(a.ptr); + + bool dir = true; + a += zigWidth / 2 + zigLen / 2; + for (int i = 0; i < zigs; i++) { + gl.Vertex2fv(a.ptr); + a += zigLen; + if (dir) { + a -= zigWidth; + }else { + a += zigWidth; + } + dir = !dir; + } + + gl.Vertex2fv((b - connLen).ptr); + gl.Vertex2fv(b.ptr); + }); +} + +void drawShape(GL gl, bzShape shape, bzXForm xf, Color color, bool core) +{ + Color coreColor = Color(0.9f, 0.6f, 0.6f); + + switch (shape.type) { + case bzShapeType.CIRCLE: + auto circle = cast(bzCircle)shape; + + vec2 center = vec2.from(bzMul(xf, circle.localPosition)); + float radius = circle.radius; + vec2 axis = vec2.from(xf.R.col1); + + gl.drawSolidCircle(center, radius, axis, color); + + if (core) { + gl.Color3f(coreColor.r, coreColor.g, coreColor.b); + gl.drawCircle(center, radius - k_toiSlop); + } + break; + case bzShapeType.POLYGON: + { + bzPolygon poly = cast(bzPolygon)shape; + bzVec2[] vertices = poly.worldVertices; + vec2[] verts; + verts.length = vertices.length; + foreach (int i, v; vertices) { + verts[i] = vec2.from(v); + } + + gl.drawSolidPolygon(verts, color); + + if (core) { + bzVec2[] localCoreVertices = poly.coreVertices; + verts.length = localCoreVertices.length; + for (int i = 0; i < localCoreVertices.length; ++i) { + verts[i] = vec2.from(bzMul(xf, localCoreVertices[i])); + } + gl.drawPolygon(verts, coreColor); + } + } + break; + + case bzShapeType.EDGE: + { + bzEdge edge = cast(bzEdge)shape; + + vec2 p1 = vec2.from(bzMul(xf, edge.vertex1)); + vec2 p2 = vec2.from(bzMul(xf, edge.vertex2)); + gl.drawSegment(p1, p2, color); + + if (core) { + p1 = vec2.from(bzMul(xf, edge.coreVertex1)); + p2 = vec2.from(bzMul(xf, edge.coreVertex2)); + gl.drawSegment(p1, p2, coreColor); + } + } + break; + } +} + +void draw(vec2i screenSize, GL gl) +{ + this.screenSize = screenSize; + + gl.LoadIdentity(); + gl.MatrixMode(GL_PROJECTION); + gl.LoadIdentity(); + // Left, right, bottom, top + gl.gluOrtho2D(-screenSize.x / zoom, screenSize.x / zoom, -screenSize.y / zoom, screenSize.y / zoom); + gl.Translatef(-viewCenter.x, -viewCenter.y, 0); + gl.MatrixMode(GL_MODELVIEW); + gl.Disable(GL_DEPTH_TEST); + gl.LoadIdentity(); + gl.Clear(GL_COLOR_BUFFER_BIT); + + // Draw dynamic bodies + if (settings.drawShapes) { + for (bzBody b = world.bodyList; b; b = b.next) { + for (bzShape shape = b.shapeList; shape; shape = shape.next) { + + bzShape s = shape; + bzXForm xf = b.xf; + + if (b.isStatic) { + gl.drawShape(s, xf, Color(0.5f, 0.9f, 0.5f), settings.drawCoreShapes); + }else if (b.isSleeping) { + gl.drawShape(s, xf, Color(0.5f, 0.5f, 0.9f), settings.drawCoreShapes); + }else if (b.userData) { + auto ss = cast(Select)b.userData; + if (ss) { + gl.drawShape(s, xf, Color(0, .5, 1), settings.drawCoreShapes); + } + }else { + gl.drawShape(s, xf, Color(0.9f, 0.9f, 0.9f), settings.drawCoreShapes); + } + + gl.LoadIdentity(); + gl.Flush(); + } + } + + // Draw water + bzFluidParticle[] particles = world.particles; + gl.Color3f(0, 0, 1); + foreach (p; particles) { + gl.drawCircle(vec2.from(p.position), WATER_RADIUS, true); + } + } + + // Draw joints + if (settings.drawJoints) { + Color color = Color(0, 0, 1); + gl.Color3f(0, 0, 1); + + gl.LineWidth(1); + for (bzJoint joint = world.jointList; joint; joint = joint.next) { + auto distance = cast(bzDistanceJoint)joint; + auto pulley = cast(bzPulleyJoint)joint; + auto revolute = cast(bzRevoluteJoint)joint; + auto prismatic = cast(bzPrismaticJoint)joint; + auto line = cast(bzLineJoint)joint; + if (distance) { + color = Color(.5, .5, 0); + // Endpoints + vec2 a = vec2.from(distance.anchor1); + vec2 b = vec2.from(distance.anchor2); + // Circles + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + // Connecting line + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + }else if (pulley) { + auto a = vec2.from(pulley.anchor1); + auto b = vec2.from(pulley.groundAnchor1); + auto c = vec2.from(pulley.groundAnchor2); + auto d = vec2.from(pulley.anchor2); + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + gl.drawSegment(b, c, color); + gl.drawCircle(c, HINGE_RADIUS); + gl.drawCircle(d, HINGE_RADIUS); + gl.connectCircles(c, HINGE_RADIUS, d, HINGE_RADIUS); + }else if (revolute) { + auto a = vec2.from(revolute.rBody1.position); + auto b = vec2.from(revolute.anchor1); + auto c = vec2.from(revolute.rBody2.position); + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + gl.drawCircle(c, HINGE_RADIUS); + gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS); + }else if (prismatic) { + auto a = vec2.from(prismatic.rBody1.position); + auto b = vec2.from(prismatic.anchor1); + auto c = vec2.from(prismatic.rBody2.position); + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + gl.drawCircle(c, HINGE_RADIUS); + gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS); + }else if (line) { + auto a = vec2.from(line.rBody1.position); + auto b = vec2.from(line.anchor1); + auto c = vec2.from(line.rBody2.position); + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + gl.drawCircle(c, HINGE_RADIUS); + gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS); + } + } + + if (settings.drawControllers) { + bzForceGenerator[] forces = world.forces; + foreach (f; forces) { + auto spring1 = cast(bzSpring1) f; + auto spring2 = cast(bzSpring2) f; + auto buoyancy = cast(bzBuoyancy) f; + + if (spring1) { + auto bungee1 = cast(bzBungee1)spring1; + if (bungee1) { + gl.Color3f(.5, .5, 0); + // Endpoints + vec2 a = vec2.from(bungee1.rBody.position); + vec2 b = vec2.from(bungee1.anchor); + // Circles + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + // Connecting line + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + }else { + uint zigs = 10; + auto anchor1 = vec2.from(spring1.anchor); + auto anchor2 = vec2.from(spring1.rBody.position); + gl.drawSpring(anchor1, anchor2, zigs); + } + } + + if (spring2) { + auto bungee2 = cast(bzBungee2)spring2; + if (bungee2) { + gl.Color3f(.5, .5, 0); + // Endpoints + vec2 a = vec2.from(bungee2.rBody.position); + vec2 b = vec2.from(bungee2.otherBody.position); + // Circles + gl.drawCircle(a, HINGE_RADIUS); + gl.drawCircle(b, HINGE_RADIUS); + // Connecting line + gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS); + }else { + uint zigs = 10; + auto anchor1 = vec2.from(spring2.otherBody.position); + auto anchor2 = vec2.from(spring2.rBody.position); + gl.drawSpring(anchor1, anchor2, zigs); + } + } + + if(buoyancy) { + float plane = buoyancy.planeOffset; + vec2 p1 = vec2(-50, plane); + vec2 p2 = vec2(50, plane); + gl.drawSegment(p1, p2, color); + } + } + } + } + + if(settings.drawPairs) { + + bzBroadPhase bp = world.broadPhase; + bzVec2 invQ; + invQ.set(1.0f / bp.m_quantizationFactor.x, 1.0f / bp.m_quantizationFactor.y); + Color color = Color(0.9f, 0.9f, 0.3f); + + const k_tableCapacity = bzPairManager.TABLE_CAPACITY; + + for (int i = 0; i < k_tableCapacity; ++i) + { + ushort index = bp.m_pairManager.m_hashTable[i]; + while (index < bp.m_pairManager.m_pairs.length) + { + if(index == bzPairManager.NULL_PROXY) { + break; + } + bzPair pair = bp.m_pairManager.m_pairs[index]; + bzProxy p1 = bp.m_proxyPool[pair.proxyId1]; + bzProxy p2 = bp.m_proxyPool[pair.proxyId2]; + + bzAABB b1, b2; + b1.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.lowerBounds[0]].value; + b1.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.lowerBounds[1]].value; + b1.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.upperBounds[0]].value; + b1.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.upperBounds[1]].value; + b2.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.lowerBounds[0]].value; + b2.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.lowerBounds[1]].value; + b2.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.upperBounds[0]].value; + b2.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.upperBounds[1]].value; + + bzVec2 x1 = 0.5f * (b1.lowerBound + b1.upperBound); + bzVec2 x2 = 0.5f * (b2.lowerBound + b2.upperBound); + + gl.drawSegment(vec2.from(x1),vec2.from(x2), color); + + index = pair.next; + } + } + } + + // Draw axis aligned bounding boxes (bzAABB) + if (settings.drawAABBs) { + bzBroadPhase bp = world.broadPhase; + bzVec2 worldLower = bp.m_worldAABB.lowerBound; + bzVec2 worldUpper = bp.m_worldAABB.upperBound; + Color color; + bzVec2 invQ; + invQ.set(1.0f / bp.m_quantizationFactor.x, 1.0f / bp.m_quantizationFactor.y); + color = Color(1.0f, 1.0f, 1.0f); + + for (int i = 0; i < k_maxProxies; ++i) { + bzProxy p = bp.m_proxyPool[i]; + if (!p.isValid) { + continue; + } + + bzAABB b; + b.lowerBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.lowerBounds[0]].value; + b.lowerBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.lowerBounds[1]].value; + b.upperBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.upperBounds[0]].value; + b.upperBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.upperBounds[1]].value; + + vec2 vs[4]; + vs[0] = vec2(b.lowerBound.x, b.lowerBound.y); + vs[1] = vec2(b.upperBound.x, b.lowerBound.y); + vs[2] = vec2(b.upperBound.x, b.upperBound.y); + vs[3] = vec2(b.lowerBound.x, b.upperBound.y); + + drawPolygon(gl, vs, color); + } + + vec2 vs[4]; + vs[0] = vec2(worldLower.x, worldLower.y); + vs[1] = vec2(worldUpper.x, worldLower.y); + vs[2] = vec2(worldUpper.x, worldUpper.y); + vs[3] = vec2(worldLower.x, worldUpper.y); + + color = Color(0.3f, 0.9f, 0.9f); + drawPolygon(gl, vs, color); + } + + // Draw contact points + if (settings.drawContactPoints) { + const k_axisScale = 0.3f; + const k_forceScale = 0.01f; + + + for (int i = 0; i < pointCount; ++i) { + ContactPoint point = points[i]; + Color color; + + if (point.state == ContactState.e_contactAdded) { + // Add + color = Color(0.3f, 0.95f, 0.3f); + vec2 p = vec2.from(point.position); + gl.drawPoint(p, 10.0f, color); + }else if (point.state == ContactState.e_contactPersisted) { + // Persist + color = Color(0.3f, 0.3f, 0.95f); + vec2 p = vec2.from(point.position); + gl.drawPoint(p, 5.0f, color); + }else { + // Remove + color = Color(0.95f, 0.3f, 0.3f); + vec2 p = vec2.from(point.position); + gl.drawPoint(p, 10.0f, color); + } + + if (settings.drawContactNormals == 1) { + vec2 p1 = vec2.from(point.position); + vec2 p2 = p1 + k_axisScale * vec2.from(point.normal); + color = Color(0.4f, 0.9f, 0.4f); + gl.drawSegment(p1, p2, color); + }else if (settings.drawContactForces) { /* + vec2 p1 = vec2.from(point.position); + vec2 p2 = p1 + k_forceScale * vec2.from(point.normalForce * point.normal); + color = Color(0.9f, 0.9f, 0.3f); + gl.drawSegment(p1, p2, color);*/ + } + + if (settings.drawFrictionForces) { /* + vec2 tangent = vec2.from(bzCross(point.normal, 1.0f)); + vec2 p1 = point.position; + vec2 p2 = p1 + k_forceScale * vec2.from(point.tangentForce) * tangent; + color = Color(0.9f, 0.9f, 0.3f); + gl.drawSegment(p1, p2, color); */ + } + } + } + + if (settings.drawOBBs) { + Color color = Color(0.5f, 0.3f, 0.5f); + + for (bzBody b = world.bodyList; b; b = b.next) { + bzXForm xf = b.xf; + for (bzShape s = b.shapeList; s; s = s.next) { + if (s.type != bzShapeType.POLYGON) { + continue; + } + + bzPolygon poly = cast(bzPolygon)s; + bzOBB obb = poly.obb; + bzVec2 h = obb.extents; + bzVec2 vs[4]; + vs[0].set(-h.x, -h.y); + vs[1].set(h.x, -h.y); + vs[2].set(h.x, h.y); + vs[3].set(-h.x, h.y); + + vec2[4] v; + for (int i = 0; i < 4; ++i) { + vs[i] = obb.center + bzMul(obb.R, vs[i]); + v[i] = vec2.from(bzMul(xf, vs[i])); + } + + drawPolygon(gl, v, color); + } + } + } + + if (settings.drawCOMs) { + for (bzBody b = world.bodyList; b; b = b.next) { + bzXForm xf = b.xf; + xf.position = b.worldCenter; + drawXForm(gl, xf); + } + } + + // Nonphysical stuffs + // Universal '.' cursor + gl.Color3f(1, 1, 1); + gl.immediate(GL_POINTS, + { + gl.Vertex2fv(mousePos.ptr); + }); + + pointCount = 0; +} + +} diff -r 000000000000 -r c10bc63824e7 ship.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ship.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.ship; + +import tango.io.Stdout: Stdout; + +import tango.util.container.LinkedList : LinkedList; +import blaze.dynamics.bzBody : bzBody; +import blaze.collision.shapes.bzShape : bzShape; +import blaze.common.bzMath: bzVec2, bzCross; + +alias LinkedList!(bzShape) ShapeList; + +abstract class Ship +{ + + bzBody rBody; + ShapeList shapes; + bzVec2 engineForce; + bzVec2 turnForce; + bzVec2 leftTurnPoint; + bzVec2 rightTurnPoint; + + this() { + shapes = new ShapeList; + } + + void thrust() { + rBody.force += engineForce.rotate(rBody.angle); + } + + void turn(char key) { + switch(key) { + case 'd': + rBody.torque += bzCross(rightTurnPoint.rotate(rBody.angle), + turnForce.rotate(rBody.angle)); + break; + case 'a': + rBody.torque += bzCross(leftTurnPoint.rotate(rBody.angle), + turnForce.rotate(rBody.angle)); + break; + default: + break; + } + } +} diff -r 000000000000 -r c10bc63824e7 steer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/steer.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,480 @@ +// ---------------------------------------------------------------------------- +// +// +// OpenSteer -- Steering Behaviors for Autonomous Characters +// +// Copyright (c) 2002-2003, Sony Computer Entertainment America +// Original author: Craig Reynolds +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// +// ---------------------------------------------------------------------------- +// +// +// SteerLibraryMixin +// +// This mixin (class with templated superclass) adds the "steering library" +// functionality to a given base class. SteerLibraryMixin assumes its base +// class supports the Ship interface. +// +// 10-04-04 bk: put everything into the OpenSteer namespace +// 02-06-03 cwr: create mixin (from "SteerMass") +// 06-03-02 cwr: removed TS dependencies +// 11-21-01 cwr: created +// +// +// ---------------------------------------------------------------------------- +module melee.steer; + + class Steer + { + // Constructor: initializes state + this () + { + // set inital state + reset (); + } + + // reset state + void reset (void) + { + // initial state of wander behavior + wanderSide = 0; + wanderUp = 0; + + // default to non-gaudyPursuitAnnotation + gaudyPursuitAnnotation = false; + } + + // -------------------------------------------------- steering behaviors + + // Wander behavior + float wanderSide; + float wanderUp; + + bzVec2 steerForWander (float dt) { + // random walk wanderSide and wanderUp between -1 and +1 + float speed = 12 * dt; // maybe this (12) should be an argument? + wanderSide = scalarRandomWalk (wanderSide, speed, -1, +1); + wanderUp = scalarRandomWalk (wanderUp, speed, -1, +1); + + // return a pure lateral steering vector: (+/-Side) + (+/-Up) + return (side() * wanderSide) + (up() * wanderUp); + } + + // Seek behavior + bzVec2 steerForSeek (bzVec2 target) { + bzVec2 desiredVelocity = target - position; + return desiredVelocity - velocity; + } + + // Flee behavior + bzVec2 steerForFlee (bzVec2 target) { + bzVec2 desiredVelocity = position - target; + return desiredVelocity - velocity(); + } + + // xxx proposed, experimental new seek/flee [cwr 9-16-02] + bzVec2 xxxsteerForFlee (bzVec2 target) { + bzVec2 offset = position - target; + bzVec2 desiredVelocity = offset.truncateLength (maxSpeed ()); + return desiredVelocity - velocity(); + } + + bzVec2 xxxsteerForSeek (bzVec2 target) { + // bzVec2 offset = target - position; + bzVec2 offset = target - position; + bzVec2 desiredVelocity = offset.truncateLength (maxSpeed ()); //xxxnew + return desiredVelocity - velocity(); + } + + // ------------------------------------------------------------------------ + // Obstacle Avoidance behavior + // + // Returns a steering force to avoid a given obstacle. The purely + // lateral steering force will turn our vehicle towards a silhouette edge + // of the obstacle. Avoidance is required when (1) the obstacle + // intersects the vehicle's current path, (2) it is in front of the + // vehicle, and (3) is within minTimeToCollision seconds of travel at the + // vehicle's current velocity. Returns a zero vector value (bzVec2::zero) + // when no avoidance is required. + bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) { + + bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision); + // XXX more annotation modularity problems (assumes spherical obstacle) + if (avoidance != bzVec2::zero) + annotateAvoidObstacle (minTimeToCollision * speed()); + + return avoidance; + } + + // avoids all obstacles in an ObstacleGroup + + bzVec2 steerToAvoidObstacles (float minTimeToCollision, + ObstacleGroup obstacles) { + + bzVec2 avoidance; + PathIntersection nearest, next; + float minDistanceToCollision = minTimeToCollision * speed(); + + next.intersect = false; + nearest.intersect = false; + + // test all obstacles for intersection with my forward axis, + // select the one whose point of intersection is nearest + for (ObstacleIterator o = obstacles.begin(); o != obstacles.end(); o++) + { + // xxx this should be a generic call on Obstacle, rather than + // xxx this code which presumes the obstacle is spherical + findNextIntersectionWithSphere ((SphericalObstacle)**o, next); + + if ((nearest.intersect == false) || + ((next.intersect != false) + (next.distance < nearest.distance))) + nearest = next; + } + + // when a nearest intersection was found + if ((nearest.intersect != false) + (nearest.distance < minDistanceToCollision)) + { + // show the corridor that was checked for collisions + annotateAvoidObstacle (minDistanceToCollision); + + // compute avoidance steering force: take offset from obstacle to me, + // take the component of that which is lateral (perpendicular to my + // forward direction), set length to maxForce, add a bit of forward + // component (in capture the flag, we never want to slow down) + bzVec2 offset = position - nearest.obstacle.center; + avoidance = offset.perpendicularComponent (forward()); + avoidance = avoidance.normalize (); + avoidance *= maxForce (); + avoidance += forward() * maxForce () * 0.75; + } + + return avoidance; + } + + // ------------------------------------------------------------------------ + // Unaligned collision avoidance behavior: avoid colliding with other + // nearby vehicles moving in unconstrained directions. Determine which + // (if any) other other vehicle we would collide with first, then steers + // to avoid the site of that potential collision. Returns a steering + // force vector, which is zero length if there is no impending collision. + + bzVec2 steerToAvoidNeighbors (float minTimeToCollision, AVGroup others) { + + // first priority is to prevent immediate interpenetration + bzVec2 separation = steerToAvoidCloseNeighbors (0, others); + if (separation != bzVec2::zero) return separation; + + // otherwise, go on to consider potential future collisions + float steer = 0; + Ship* threat = NULL; + + // Time (in seconds) until the most immediate collision threat found + // so far. Initial value is a threshold: don't look more than this + // many frames into the future. + float minTime = minTimeToCollision; + + // xxx solely for annotation + bzVec2 xxxThreatPositionAtNearestApproach; + bzVec2 xxxOurPositionAtNearestApproach; + + // for each of the other vehicles, determine which (if any) + // pose the most immediate threat of collision. + for (AVIterator i = others.begin(); i != others.end(); i++) + { + Ship other = **i; + if (other != this) + { + // avoid when future positions are this close (or less) + float collisionDangerThreshold = radius() * 2; + + // predicted time until nearest approach of "this" and "other" + float time = predictNearestApproachTime (other); + + // If the time is in the future, sooner than any other + // threatened collision... + if ((time >= 0) (time < minTime)) + { + // if the two will be close enough to collide, + // make a note of it + if (computeNearestApproachPositions (other, time) + < collisionDangerThreshold) + { + minTime = time; + threat = other; + xxxThreatPositionAtNearestApproach + = hisPositionAtNearestApproach; + xxxOurPositionAtNearestApproach + = ourPositionAtNearestApproach; + } + } + } + } + + // if a potential collision was found, compute steering to avoid + if (threat) + { + // parallel: +1, perpendicular: 0, anti-parallel: -1 + float parallelness = forward.dot(threat.forward); + float angle = 0.707f; + + if (parallelness < -angle) + { + // anti-parallel "head on" paths: + // steer away from future threat position + bzVec2 offset = xxxThreatPositionAtNearestApproach - position; + float sideDot = offset.dot(side()); + steer = (sideDot > 0) ? -1.0f : 1.0f; + } + else + { + if (parallelness > angle) + { + // parallel paths: steer away from threat + bzVec2 offset = threat.position - position; + float sideDot = offset.dot(side()); + steer = (sideDot > 0) ? -1.0f : 1.0f; + } + else + { + // perpendicular paths: steer behind threat + // (only the slower of the two does this) + if (threat.speed() <= speed()) + { + float sideDot = side().dot(threat.velocity); + steer = (sideDot > 0) ? -1.0f : 1.0f; + } + } + } + } + + return side() * steer; + } + + // Given two vehicles, based on their current positions and velocities, + // determine the time until nearest approach + float predictNearestApproachTime (Ship other) { + + // imagine we are at the origin with no velocity, + // compute the relative velocity of the other vehicle + bzVec2 myVelocity = velocity; + bzVec2 otherVelocity = other.velocity; + bzVec2 relVelocity = otherVelocity - myVelocity; + float relSpeed = relVelocity.length; + + // for parallel paths, the vehicles will always be at the same distance, + // so return 0 (aka "now") since "there is no time like the present" + if (relSpeed == 0) return 0; + + // Now consider the path of the other vehicle in this relative + // space, a line defined by the relative position and velocity. + // The distance from the origin (our vehicle) to that line is + // the nearest approach. + + // Take the unit tangent along the other vehicle's path + bzVec2 relTangent = relVelocity / relSpeed; + + // find distance from its path to origin (compute offset from + // other to us, find length of projection onto path) + bzVec2 relPosition = position - other.position; + float projection = relTangent.dot(relPosition); + + return projection / relSpeed; + } + + // Given the time until nearest approach (predictNearestApproachTime) + // determine position of each vehicle at that time, and the distance + // between them + float computeNearestApproachPositions (Ship other, float time) { + + bzVec2 myTravel = forward * speed * time; + bzVec2 otherTravel = other.forward * other.speed * time; + + bzVec2 myFinal = position + myTravel; + bzVec2 otherFinal = other.position + otherTravel; + + // xxx for annotation + ourPositionAtNearestApproach = myFinal; + hisPositionAtNearestApproach = otherFinal; + + return bzVec2::distance (myFinal, otherFinal); + } + + // otherwise return zero + return bzVec2::zero; + } + + // ------------------------------------------------------------------------ + // pursuit of another vehicle ( version with ceiling on prediction time) + + bzVec2 steerForPursuit (Ship quarry) { + return steerForPursuit (quarry, FLT_MAX); + } + + bzVec2 steerForPursuit (Ship quarry, float maxPredictionTime) { + + // offset from this to quarry, that distance, unit vector toward quarry + bzVec2 offset = quarry.position - position; + float distance = offset.length (); + bzVec2 unitOffset = offset / distance; + + // how parallel are the paths of "this" and the quarry + // (1 means parallel, 0 is pependicular, -1 is anti-parallel) + float parallelness = forward.dot(quarry.forward()); + + // how "forward" is the direction to the quarry + // (1 means dead ahead, 0 is directly to the side, -1 is straight back) + float forwardness = forward.dot(unitOffset); + + float directTravelTime = distance / speed; + int f = intervalComparison (forwardness, -0.707f, 0.707f); + int p = intervalComparison (parallelness, -0.707f, 0.707f); + + float timeFactor = 0; // to be filled in below + bzVec2 color; // to be filled in below (xxx just for debugging) + + // Break the pursuit into nine cases, the cross product of the + // quarry being [ahead, aside, or behind] us and heading + // [parallel, perpendicular, or anti-parallel] to us. + switch (f) + { + case +1: + switch (p) + { + case +1: // ahead, parallel + timeFactor = 4; + color = gBlack; + break; + case 0: // ahead, perpendicular + timeFactor = 1.8f; + color = gGray50; + break; + case -1: // ahead, anti-parallel + timeFactor = 0.85f; + color = gWhite; + break; + } + break; + case 0: + switch (p) + { + case +1: // aside, parallel + timeFactor = 1; + color = gRed; + break; + case 0: // aside, perpendicular + timeFactor = 0.8f; + color = gYellow; + break; + case -1: // aside, anti-parallel + timeFactor = 4; + color = gGreen; + break; + } + break; + case -1: + switch (p) + { + case +1: // behind, parallel + timeFactor = 0.5f; + color= gCyan; + break; + case 0: // behind, perpendicular + timeFactor = 2; + color= gBlue; + break; + case -1: // behind, anti-parallel + timeFactor = 2; + color = gMagenta; + break; + } + break; + } + + // estimated time until intercept of quarry + float et = directTravelTime * timeFactor; + + // xxx experiment, if kept, this limit should be an argument + float etl = (et > maxPredictionTime) ? maxPredictionTime : et; + + // estimated position of quarry at intercept + bzVec2 target = quarry.predictFuturePosition (etl); + + // annotation + annotationLine (position, + target, + gaudyPursuitAnnotation ? color : gGray40); + + return steerForSeek (target); + } + + // ------------------------------------------------------------------------ + // evasion of another vehicle + bzVec2 steerForEvasion (Ship menace, float maxPredictionTime) { + + // offset from this to menace, that distance, unit vector toward menace + bzVec2 offset = menace.position - position; + float distance = offset.length; + + float roughTime = distance / menace.speed; + float predictionTime = ((roughTime > maxPredictionTime) ? maxPredictionTime : roughTime); + bzVec2 target = menace.predictFuturePosition (predictionTime); + + return steerForFlee (target); + } + + + // ------------------------------------------------------------------------ + // tries to maintain a given speed, returns a maxForce-clipped steering + // force along the forward/backward axis + bzVec2 steerForTargetSpeed (float targetSpeed) { + float mf = maxForce(); + float speedError = targetSpeed - speed (); + return forward () * clip (speedError, -mf, +mf); + } + + + // ----------------------------------------------------------- utilities + bool isAhead (bzVec2 target) {return isAhead (target, 0.707f);}; + bool isAside (bzVec2 target) {return isAside (target, 0.707f);}; + bool isBehind (bzVec2 target) {return isBehind (target, -0.707f);}; + + bool isAhead (bzVec2 target, float cosThreshold) + { + bzVec2 targetDirection = (target - position ()).normalize (); + return forward().dot(targetDirection) > cosThreshold; + } + + bool isAside (bzVec2 target, float cosThreshold) + { + bzVec2 targetDirection = (target - position ()).normalize (); + float dp = forward().dot(targetDirection); + return (dp < cosThreshold) (dp > -cosThreshold); + } + + bool isBehind (bzVec2 target, float cosThreshold) + { + bzVec2 targetDirection = (target - position).normalize (); + return forward().dot(targetDirection) < cosThreshold; + } + } diff -r 000000000000 -r c10bc63824e7 themes/default.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,21 @@ +import "themes/default/Button.cfg" +import "themes/default/Check.cfg" +import "themes/default/Combo.cfg" +import "themes/default/FramedTopLevelWindow.cfg" +import "themes/default/Input.cfg" +import "themes/default/Menu.cfg" +import "themes/default/Picker.cfg" +import "themes/default/Progressbar.cfg" +import "themes/default/Scrollbar.cfg" +import "themes/default/ScrollbarButton.cfg" +import "themes/default/ScrollView.cfg" +import "themes/default/TabButton.cfg" +import "themes/default/TabView.cfg" +import "themes/default/TextList.cfg" +import "themes/default/TickBox.cfg" +import "themes/default/WindowFrame.cfg" +import "themes/default/WindowFrameButton.cfg" +import "themes/default/XCheck.cfg" +import "themes/default/Slider.cfg" +import "themes/default/Spinner.cfg" +import "themes/default/Angle.cfg" \ No newline at end of file diff -r 000000000000 -r c10bc63824e7 themes/default/Angle.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Angle.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,13 @@ +widget Angle { + layout = Layered; + size = 40 40; + shape = Rectangle; + + [hexpand hfill vexpand vfill] new GLViewport glView; + glView = sub(glView); + + radians = false; + filled = true; + angleWidth = 2; + limitWidth = 1; +} diff -r 000000000000 -r c10bc63824e7 themes/default/Button.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Button.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,52 @@ +// define what a Button really is... +widget Button { + layout = Layered; + size = 30 0; + + [hfill vfill] new Graphic body { + size = 0 23; + + style.normal = { + image = grid("themes/default/img/button.png", hline(2, 73), vline(2, 21)); + background = solid(white); + } + + style.active = { + image = grid("themes/default/img/button_active.png", hline(2, 73), vline(2, 21)); + } + } + + [hfill vfill] new Graphic bodyOver { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + } + + style.hover = { + background = solid(rgba(1, 1, 1, .07)); + deactivation = .3; + } + } + + [hfill vfill] new HBox { + layout = { + padding = 6 3; + spacing = 2; + } + + [hexpand vexpand] new HBox leftExtra; + [vexpand] new Label text { + style.normal = { + color = rgb(.8, .8, .8); + } + + text = "Button"; + fontSize = 11; + } + [hexpand vexpand] new HBox rightExtra; + } + + leftExtra = sub(leftExtra); + rightExtra = sub(rightExtra); + label = sub(text); + text = prop(text.text); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Check.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Check.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,12 @@ +widget Check { + layout = HBox; + layout = { + spacing = 5; + } + + [vexpand] new TickBox tick; + new Label label; + + text = prop(label.text); + checked = prop(tick.checked); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Combo.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Combo.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,39 @@ +widget Combo { + layout = VBox; + size = 100 0; + + [hexpand hfill] new HBox { + [hexpand hfill] new Input input { + size = 80 0; + } + + [vexpand vfill] new ScrollbarButton button { + arrowDir = 3; + } + } + + + new HBox popup { + [hexpand hfill vexpand vfill] new ScrollView { + children.useChildSize = 1; + + [hexpand hfill vexpand vfill] new VBox { + shape = Rectangle; + style.normal = { + background = solid(rgba(.1, .1, .1, .8)); + border = 1 rgba(.4, .4, .4, .7); + } + layout = { + padding = 1 1; + } + + [hexpand hfill vexpand vfill] new TextList textList; + } + } + } + + input = sub(input); + popup = sub(popup); + button = sub(button); + textList = sub(popup.textList); +} diff -r 000000000000 -r c10bc63824e7 themes/default/FramedTopLevelWindow.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/FramedTopLevelWindow.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,27 @@ +widget FramedTopLevelWindow { + layout = Layered; + + [hfill vfill] new WindowFrame frame { + text = "Hybrid test app 1"; + + shape = Rectangle; + style.normal = { + background = solid(rgb(.22, .22, .22)); + } + + layout = Bin; + new Group clientArea { + shape = Rectangle; + style.normal = { + background = solid(rgb(.22, .22, .22)); + } + } + } + + [hfill vfill] new Group overlay { + } + + frame = sub(frame); + children = sub(frame.clientArea); + overlay = sub(overlay); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Input.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Input.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,27 @@ +widget Input { + shape = Rectangle; + layout = Bin; + layout = { + padding = 3 3; + } + + style.normal = { + background = solid(rgba(.3, .3, .3, 1)); + border = 1 black; + } + + [hexpand vexpand hfill vfill] new InputArea area { + style.normal = { + textInput = { + caretColor = white; + } + } + } + + text = prop(area.text); + font = prop(area.font); + fontFace = prop(area.fontFace); + fontSize = prop(area.fontSize); + hasFocus = prop(area.hasFocus); + inputArea = sub(area); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Menu.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Menu.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,96 @@ +widget HMenu { + new HBox children { + layout = { + spacing = 8; + padding = 8 2; + } + } + + children = sub(children); +} + + +widget HMenuItem { + [hexpand hfill] new Group { + shape = Rectangle; + style.active = { + background = solid(rgba(1, 1, 1, .1)); + border = 1 black; + } + layout = { + padding = 2 2; + } + + new Label label { + fontSize = 11; + style.normal = { + color = rgb(.8, .8, .8); + } + } + } + + label = sub(label); + text = prop(label.text); + + childDir = 3; +} + + +widget VMenu { + [hexpand hfill vexpand vfill] new Graphic { + renderOversize = 8 8; + renderOffset = 2 2; + style.normal = { + background = solid(rgba(0, 0, 0, .5)); + image = grid("themes/default/img/menuShadow.png", hline(14, 66), vline(14, 66)); + } + + [hexpand hfill vexpand vfill] new Group { + size = 80 0; + shape = Rectangle; + style.normal = { + background = solid(rgb(.18, .18, .18)); + border = 1 rgb(.25, .25, .25); + } + layout = { + padding = 4 3; + } + + [hfill hexpand] new VBox children { + layout = { + spacing = 2; + padding = 2 2; + attribs = "hexpand hfill"; + } + } + } + } + + children = sub(children); +} + + +widget VMenuItem { + [hexpand hfill] new Group { + shape = Rectangle; + style.active = { + background = solid(rgba(1, 1, 1, .05)); + border = 1 black; + } + layout = { + padding = 2 2; + } + + new Label label { + fontSize = 11; + style.normal = { + color = rgb(.8, .8, .8); + } + } + } + + label = sub(label); + text = prop(label.text); + + childDir = 0; +} diff -r 000000000000 -r c10bc63824e7 themes/default/Picker.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Picker.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,22 @@ +widget Picker { + layout = Layered; + + [hfill vfill] new Group { + layout = Free; + new Group background { + } + } + + [hfill vfill] new VBox main { + } + + [hfill vfill] new Group { + layout = Free; + new Group foreground { + } + } + + children = sub(main); + background = sub(background); + foreground = sub(foreground); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Progressbar.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Progressbar.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,18 @@ +widget Progressbar { + layout = HBox; + layout = { + padding = 2 2; + } + shape = Rectangle; + style.normal = { + background = solid(white); + image = grid("themes/default/img/progress.png", hline(1, 20), vline(1, 11)); + } + + [hexpand hfill vexpand vfill] new HTiledImage img { + size = 0 13; + file = "themes/default/img/progress_box.png"; + } + + img = sub(img); +} \ No newline at end of file diff -r 000000000000 -r c10bc63824e7 themes/default/ScrollView.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/ScrollView.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,27 @@ +widget ScrollView { + layout = HBox; + + //[hexpand vexpand hfill vfill] new HBox { + [hexpand vexpand hfill vfill] new VBox { + [hexpand vexpand hfill vfill] new ClipView clipView { + } + + [hexpand hfill] new HScrollbar hscroll; + } + + [vexpand vfill] new VBox { + [vexpand vfill] new VScrollbar vscroll; + + new Graphic corner { + size = 17 17; + } + } + //} + + hscroll = sub(hscroll); + vscroll = sub(vscroll); + clipView = sub(clipView); + corner = sub(corner); + + children = sub(clipView); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Scrollbar.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Scrollbar.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,79 @@ +widget HScrollbar { + [hexpand hfill] new HBox { + shape = Rectangle; + style.normal = { + background = solid(rgba(0.26, 0.26, 0.26, 1)); + } + + new ScrollbarButton button1 { + arrowDir = 2; + } + + [hexpand hfill] new HWidgetSlider slider { + new Graphic { + size = 17 17; + style.normal = { + image = grid("themes/default/img/scrollhandle.png", hline(3, 14), vline(3, 14)); + background = solid(white); + border = 1 rgba(.1, .1, .1, .8); + } + } + + handleSize = .2; + skip = false; + } + + new ScrollbarButton button2 { + arrowDir = 0; + } + } + + handleSize = prop(slider.handleSize); + position = prop(slider.position); + skipSize = prop(slider.skipSize); + + button1 = sub(button1); + button2 = sub(button2); + slider = sub(slider); +} + + + +widget VScrollbar { + [vexpand vfill] new VBox { + shape = Rectangle; + style.normal = { + background = solid(rgba(0.26, 0.26, 0.26, 1)); + } + + new ScrollbarButton button1 { + arrowDir = 1; + } + + [vexpand vfill] new VWidgetSlider slider { + new Graphic { + size = 17 17; + style.normal = { + image = grid("themes/default/img/scrollhandle.png", hline(3, 14), vline(3, 14)); + background = solid(white); + border = 1 rgba(.1, .1, .1, .8); + } + } + + handleSize = .2; + skip = false; + } + + new ScrollbarButton button2 { + arrowDir = 3; + } + } + + handleSize = prop(slider.handleSize); + position = prop(slider.position); + skipSize = prop(slider.skipSize); + + button1 = sub(button1); + button2 = sub(button2); + slider = sub(slider); +} diff -r 000000000000 -r c10bc63824e7 themes/default/ScrollbarButton.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/ScrollbarButton.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,38 @@ +widget ScrollbarButton { + layout = Layered; + + [hfill vfill] new Graphic { + size = 17 17; + + style.normal = { + image = grid("themes/default/img/scrollbutton.png", hline(3, 14), vline(3, 14)); + background = solid(white); + } + + style.active = { + background = solid(rgba(1, 1, 1, 0)); + } + } + + [hfill vfill] new Graphic { + size = 17 17; + + style.normal = { + image = grid("themes/default/img/scrollbutton_active.png", hline(3, 14), vline(3, 14)); + background = solid(rgba(1, 1, 1, 0)); + } + + style.active = { + background = solid(white); + } + } + + new Icon icon { + addIcon = "themes/default/img/arrow0.png"; + addIcon = "themes/default/img/arrow1.png"; + addIcon = "themes/default/img/arrow2.png"; + addIcon = "themes/default/img/arrow3.png"; + } + + arrowDir = prop(icon.iconIndex); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Slider.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Slider.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,49 @@ +widget HSlider { + shape = Rectangle; + style.normal = { + border = 1 rgb(.118, .118, .118); + background = hgradient(rgb(0, 0, 0), rgb(.4, .4, .4)); + } + + [hexpand hfill] new HWidgetSlider wslider { + shape = Rectangle; + style.normal = { + } + + new Graphic { + size = 13 13; + style.normal = { + background = solid(white); + image = file("themes/default/img/hsliderarrow.png"); + } + } + } + + wslider = sub(wslider); +} + + + +widget VSlider { + shape = Rectangle; + style.normal = { + border = 1 rgb(.118, .118, .118); + background = vgradient(rgb(.4, .4, .4), rgb(0, 0, 0)); + } + + [vexpand vfill] new VWidgetSlider wslider { + shape = Rectangle; + style.normal = { + } + + new Graphic { + size = 13 13; + style.normal = { + background = solid(white); + image = file("themes/default/img/vsliderarrow.png"); + } + } + } + + wslider = sub(wslider); +} diff -r 000000000000 -r c10bc63824e7 themes/default/Spinner.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/Spinner.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,80 @@ +widget Spinner { + new VBox { + new SpinnerButton button2 { + icon.iconIndex = 0; + } + + new SpinnerButton button1 { + icon.iconIndex = 1; + } + } + + button1 = sub(button1); + button2 = sub(button2); +} + + +widget SpinnerButton { + layout = Layered; + + [hfill vfill] new Icon icon { + addIcon = "themes/default/img/spinner_up.png"; + addIcon = "themes/default/img/spinner_down.png"; + } + + [hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + } + style.hover = { + background = solid(rgba(1, 1, 1, .2)); + deactivation = .3; + } + style.active = { + background = solid(rgba(1, 1, 1, .4)); + } + } + + icon = sub(icon); +} + + +widget InputSpinner { + size = 60 0; + shape = Rectangle; + style.normal = { + border = 1 black; + background = solid(rgb(.2, .21, .24)); + } + layout = HBox; + layout = { + spacing = 2; + } + + [hexpand hfill vexpand hfill] new Group { + layout = { + padding = 2 2; + } + + [hexpand hfill vexpand] new InputArea input { + shape = Rectangle; + style.normal = { + background = solid(black); + textInput = { + caretColor = rgb(.6, .7, 1); + } + } + + fontSize = 11; + text = "0"; + } + } + new Spinner spinner; + + + spinner = sub(spinner); + input = sub(input); + value = prop(spinner.value); + + value = 0; +} diff -r 000000000000 -r c10bc63824e7 themes/default/TabButton.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/TabButton.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,50 @@ +widget TabButton { + layout = HBox; + layout = { + spacing = -1; + } + size = 80 0; + + [vexpand vfill hexpand hfill] new Group { + layout = Layered; + + [hfill vfill] new Graphic { + size = 0 22; + style.normal = { + background = solid(white); + image = grid("themes/default/img/tbw.png", hline(1, 47), vline(3, 19)); + } + style.active = { + image = grid("themes/default/img/tbw_active.png", hline(1, 72), vline(1, 21)); + } + style.hover = { + image = grid("themes/default/img/tbw_hover.png", hline(1, 51), vline(3, 19)); + } + } + + [hfill vfill] new HBox { + layout = { + padding = 6 3; + spacing = 2; + } + + [hexpand vexpand] new HBox leftExtra; + [vexpand] new Label label { + style.normal = { + color = rgb(.6, .6, .6); + } + style.active = { + color = rgb(.8, .8, .8); + } + } + [hexpand vexpand] new HBox rightExtra; + } + } + + leftExtra = sub(leftExtra); + rightExtra = sub(rightExtra); + label = sub(label); + text = prop(label.text); + + text = "Tab"; +} diff -r 000000000000 -r c10bc63824e7 themes/default/TabView.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/TabView.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,26 @@ +widget TabView { + layout = VBox; + layout = { + spacing = -2; + } + + new HBox tabList { + layout = { + spacing = -1; + } + } + + [hexpand hfill vexpand vfill] new Group clientArea { + shape = Rectangle; + style.normal = { + background = solid(rgb(.255, .255, .255)); + border = 1 rgb(.118, .118, .118); + } + layout = { + padding = 5 5; + } + } + + tabList = sub(tabList); + clientArea = sub(clientArea); +} diff -r 000000000000 -r c10bc63824e7 themes/default/TextList.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/TextList.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,19 @@ +widget TextList { + [hexpand hfill] new Picker picker { + } @background { + [hexpand vexpand hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, .4)); + } + } + } @foreground { + [hexpand vexpand hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(0, 0, 0, .2)); + } + } + } + + picker = sub(picker); + pickedIdx = prop(picker.pickedIdx); +} diff -r 000000000000 -r c10bc63824e7 themes/default/TickBox.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/TickBox.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,37 @@ +widget TickBox { + layout = Layered; + + [hfill vfill] new Graphic { + size = 13 13; + + style.normal = { + background = solid(white); + image = file("themes/default/img/tick.png"); + } + } + + [hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + image = file("themes/default/img/tick_active.png"); + } + + style.active = { + background = solid(white); + deactivation = .2; + } + } + + [hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + image = file("themes/default/img/tick_hover.png"); + } + + style.hover = { + background = solid(white); + activation = .1; + deactivation = .7; + } + } +} diff -r 000000000000 -r c10bc63824e7 themes/default/WindowFrame.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/WindowFrame.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,50 @@ +widget WindowFrame { + layout = VBox; + shape = Rectangle; + style.normal = { + border = 1 black; + } + + [hexpand hfill] new HBox handle { + size = 0 23; + shape = Rectangle; + layout = { + padding = 5 0; + spacing = 4; + } + + style.normal = { + border = 1 black; + background = solid(rgb(.6, .6, .6)); + image = grid("themes/default/img/winframe_bg.png", hline(1, 9), vline(0, 23)); + } + style.active = { + background = solid(white); + } + + [hexpand vexpand] new Label caption { + } + + [vexpand] new WindowFrameButton minimizeButton { + addIcon = "themes/default/img/winframe_minimize.png"; + } + [vexpand] new WindowFrameButton maximizeButton { + addIcon = "themes/default/img/winframe_maximize.png"; + addIcon = "themes/default/img/winframe_restore.png"; + } + [vexpand] new WindowFrameButton closeButton { + addIcon = "themes/default/img/winframe_close.png"; + } + } + + [hexpand hfill vexpand vfill] new VBox clientArea { + } + + children = sub(clientArea); + handle = sub(handle); + + minimizeClicked = prop(handle.minimizeButton.clicked); + maximizeClicked = prop(handle.maximizeButton.clicked); + closeClicked = prop(handle.closeButton.clicked); + text = prop(handle.caption.text); +} diff -r 000000000000 -r c10bc63824e7 themes/default/WindowFrameButton.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/WindowFrameButton.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,20 @@ +widget WindowFrameButton { + layout = Layered; + + new Icon icon { + } + + [hfill vfill] new Graphic hlight { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + } + + style.active = { + background = solid(rgba(1, 1, 1, .2)); + } + } + + addIcon = prop(icon.addIcon); + icon = sub(icon); + hlight = sub(hlight); +} diff -r 000000000000 -r c10bc63824e7 themes/default/XCheck.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/default/XCheck.cfg Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,47 @@ +widget XCheck { + layout = HBox; + layout = { + spacing = 5; + } + + [vexpand] new Group tick { + layout = Layered; + + [hfill vfill] new Graphic { + size = 13 13; + + style.normal = { + background = solid(white); + image = file("themes/default/img/radio.png"); + } + } + + [hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + image = file("themes/default/img/radio_active.png"); + } + + style.active = { + background = solid(white); + deactivation = .2; + } + } + + [hfill vfill] new Graphic { + style.normal = { + background = solid(rgba(1, 1, 1, 0)); + image = file("themes/default/img/radio_hover.png"); + } + + style.hover = { + background = solid(white); + activation = .5; + deactivation = .7; + } + } + } + new Label label; + + text = prop(label.text); +} diff -r 000000000000 -r c10bc63824e7 themes/default/img/Thumbs.db Binary file themes/default/img/Thumbs.db has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/arrow0.png Binary file themes/default/img/arrow0.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/arrow1.png Binary file themes/default/img/arrow1.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/arrow2.png Binary file themes/default/img/arrow2.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/arrow3.png Binary file themes/default/img/arrow3.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/button.png Binary file themes/default/img/button.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/button_active.png Binary file themes/default/img/button_active.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/hsliderarrow.png Binary file themes/default/img/hsliderarrow.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/menuShadow.png Binary file themes/default/img/menuShadow.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/progress.png Binary file themes/default/img/progress.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/progress_box.png Binary file themes/default/img/progress_box.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/radio.png Binary file themes/default/img/radio.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/radio_active.png Binary file themes/default/img/radio_active.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/radio_hover.png Binary file themes/default/img/radio_hover.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/scrollbutton.png Binary file themes/default/img/scrollbutton.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/scrollbutton_active.png Binary file themes/default/img/scrollbutton_active.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/scrollhandle.png Binary file themes/default/img/scrollhandle.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/spinner_down.png Binary file themes/default/img/spinner_down.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/spinner_up.png Binary file themes/default/img/spinner_up.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tbw.png Binary file themes/default/img/tbw.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tbw_active.png Binary file themes/default/img/tbw_active.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tbw_hover.png Binary file themes/default/img/tbw_hover.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tick.png Binary file themes/default/img/tick.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tick_active.png Binary file themes/default/img/tick_active.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/tick_hover.png Binary file themes/default/img/tick_hover.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/vsliderarrow.png Binary file themes/default/img/vsliderarrow.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/winframe_bg.png Binary file themes/default/img/winframe_bg.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/winframe_close.png Binary file themes/default/img/winframe_close.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/winframe_maximize.png Binary file themes/default/img/winframe_maximize.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/winframe_minimize.png Binary file themes/default/img/winframe_minimize.png has changed diff -r 000000000000 -r c10bc63824e7 themes/default/img/winframe_restore.png Binary file themes/default/img/winframe_restore.png has changed diff -r 000000000000 -r c10bc63824e7 urQuan.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urQuan.d Fri Mar 20 06:41:25 2009 -0400 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009, Mason Green (zzzzrrr) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the polygonal nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module melee.urQuan; + +import blaze.dynamics.bzBody : bzBody; +import blaze.bzWorld: bzWorld; +import blaze.dynamics.bzBodyDef; +import blaze.collision.shapes.bzPolygon : bzPolyDef; +import blaze.common.bzMath: bzVec2; + +import melee.ship; + +// UrQuan Dreadnought +class UrQuan : Ship +{ + + float scale = 0.01; + + this(bzWorld world) { + + auto bodyDef = new bzBodyDef; + bodyDef.position = bzVec2(10,5); + super.rBody = world.createBody(bodyDef); + float density = 2.0f; + + // Head + auto head = new bzPolyDef; + head.vertices.length = 8; + head.vertices[0] = bzVec2(42,49) * scale; + head.vertices[7] = bzVec2(63,49) * scale; + head.vertices[6] = bzVec2(70,45.5) * scale; + head.vertices[5] = bzVec2(73.5,38.5) * scale; + head.vertices[4] = bzVec2(73.5,-42) * scale; + head.vertices[3] = bzVec2(70,-49) * scale; + head.vertices[2] = bzVec2(63,-56) * scale; + head.vertices[1] = bzVec2(42,-56) * scale; + super.shapes.add(rBody.createShape(head)); + + // Body + auto b = new bzPolyDef(density); + b.vertices.length = 4; + b.vertices[0] = bzVec2(-70,-28) * scale; + b.vertices[3] = bzVec2(-70,24.5) * scale; + b.vertices[2] = bzVec2(42,24.5) * scale; + b.vertices[1] = bzVec2(42,-31.5) * scale; + super.shapes.add(rBody.createShape(b)); + + // Top Strut + auto tStrut = new bzPolyDef(density); + tStrut.vertices.length = 4; + tStrut.vertices[0] = bzVec2(0,24.5) * scale; + tStrut.vertices[3] = bzVec2(-28,24.5) * scale; + tStrut.vertices[2] = bzVec2(-28,42) * scale; + tStrut.vertices[1] = bzVec2(0,42) * scale; + super.shapes.add(rBody.createShape(tStrut)); + + // Top Wing + auto tWing = new bzPolyDef(density); + tWing.vertices.length = 4; + tWing.vertices[0] = bzVec2(-70,42) * scale; + tWing.vertices[3] = bzVec2(-49,63) * scale; + tWing.vertices[2] = bzVec2(28,63) * scale; + tWing.vertices[1] = bzVec2(28,42) * scale; + super.shapes.add(rBody.createShape(tWing)); + + // Bottom Strut + auto bStrut = new bzPolyDef(density); + bStrut.vertices.length = 4; + bStrut.vertices[0] = bzVec2(0,-31.5) * scale; + bStrut.vertices[3] = bzVec2(0,-49) * scale; + bStrut.vertices[2] = bzVec2(-28,-49) * scale; + bStrut.vertices[1] = bzVec2(-28,-31.5) * scale; + super.shapes.add(rBody.createShape(bStrut)); + + // Bottom Wing + auto bWing = new bzPolyDef(density); + bWing.vertices.length = 4; + bWing.vertices[0] = bzVec2(-70,-49) * scale; + bWing.vertices[3] = bzVec2(28,-49) * scale; + bWing.vertices[2] = bzVec2(28,-70) * scale; + bWing.vertices[1] = bzVec2(-42,-70) * scale; + super.shapes.add(rBody.createShape(bWing)); + + super.rBody.setMassFromShapes(); + } +} diff -r 000000000000 -r c10bc63824e7 verdana.ttf Binary file verdana.ttf has changed