From 7d3ebffc7cf72f42bfd2ac6a40f9d90a2bcc2360 Mon Sep 17 00:00:00 2001 From: Emanuel Rabina Date: Sat, 27 Jul 2024 13:14:26 +1200 Subject: [PATCH] Workaround for disappearing player unit (for now...) --- build.gradle | 2 +- .../extensions/CoordinateExtensions.groovy | 3 +- .../ultraq/redhorizon/classic/maps/Map.groovy | 19 +++++-------- .../graphics/pipeline/RenderPipeline.groovy | 17 ++++++++--- .../redhorizon/engine/scenegraph/Node.groovy | 6 ++-- .../engine/scenegraph/nodes/Camera.groovy | 11 +++----- .../explorer/scripts/MapViewerScript.groovy | 3 +- .../ultraq/redhorizon/shooter/Shooter.groovy | 12 +++++++- .../redhorizon/shooter/objects/Player.groovy | 28 +++++++++++++------ 9 files changed, 63 insertions(+), 38 deletions(-) diff --git a/build.gradle b/build.gradle index bf2be96d..5a30ebd5 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,7 @@ subprojects { ext.targetPlatform = targetPlatform dependencies { - implementation 'nz.net.ultraq.groovy:groovy-extensions:2.3.2' + implementation 'nz.net.ultraq.groovy:groovy-extensions:2.3.3' implementation 'nz.net.ultraq.groovy:groovy-profiling-extensions:0.10.0' implementation "org.apache.groovy:groovy:${groovyVersion}" implementation "org.slf4j:slf4j-api:${slf4jVersion}" diff --git a/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/extensions/CoordinateExtensions.groovy b/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/extensions/CoordinateExtensions.groovy index 5896c13e..fe6a371f 100644 --- a/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/extensions/CoordinateExtensions.groovy +++ b/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/extensions/CoordinateExtensions.groovy @@ -66,7 +66,8 @@ class CoordinateExtensions { * * @param self * @param objectHeightInCells - * Vertical space occupied by the objheheight of the object whose coordinates are being translated. + * Vertical space occupied by the objheheight of the object whose + * coordinates are being translated. * @return */ static Vector2f asWorldCoords(Vector2f self, int objectHeightInCells = 0) { diff --git a/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/maps/Map.groovy b/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/maps/Map.groovy index 6523e33e..9b4afd23 100644 --- a/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/maps/Map.groovy +++ b/redhorizon-classic/source/nz/net/ultraq/redhorizon/classic/maps/Map.groovy @@ -72,12 +72,7 @@ class Map extends Node { static final int TILE_HEIGHT = 24 static final int TILES_X = 128 static final int TILES_Y = 128 - static final Rectanglef MAX_BOUNDS = new Rectanglef( - -TILES_X * TILE_WIDTH / 2 as float, - -TILES_Y * TILE_HEIGHT / 2 as float, - TILES_X * TILE_WIDTH / 2 as float, - TILES_Y * TILE_HEIGHT / 2 as float - ) + static final Rectanglef MAX_BOUNDS = new Rectanglef(0, 0, TILES_X * TILE_WIDTH, TILES_Y * TILE_HEIGHT).center() private static final Logger logger = LoggerFactory.getLogger(Map) @@ -128,17 +123,13 @@ class Map extends Node { addChild(new MapBackground()) addChild(new MapPack()) addChild(new OverlayPack()) - - // The following should be rendered in top-left to bottom-right order so - // that "lower" objects get drawn over the "higher" ones addChild(new Terrain()) addChild(new Structures()) addChild(new Units()) addChild(new Infantry()) - addChild(new MapLines().tap { transform { -> - translate(0, 0, 0.4) + translate(0f, 0f, 0.1f) } }) } @@ -146,6 +137,7 @@ class Map extends Node { @Override CompletableFuture onSceneAddedAsync(Scene scene) { + // TODO: Need to handle if there are no tiles, ie: an empty map tileSetSpriteSheetFuture = CompletableFuture.supplyAsync { -> return tileSet.tileFileList .collect { tileFile -> @@ -188,7 +180,10 @@ class Map extends Node { @Override CompletableFuture onSceneRemovedAsync(Scene scene) { - return scene.requestDelete(tileSet.spriteSheet) + if (tileSet.numTiles) { + return scene.requestDelete(tileSet.spriteSheet) + } + return CompletableFuture.completedFuture() } /** diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/pipeline/RenderPipeline.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/pipeline/RenderPipeline.groovy index 3894cb20..54d69e86 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/pipeline/RenderPipeline.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/pipeline/RenderPipeline.groovy @@ -183,6 +183,12 @@ class RenderPipeline implements AutoCloseable { private final FrustumIntersection frustumIntersection = new FrustumIntersection() private final List queryResults = [] private final List drawCommands = [] + private final Comparator renderOrderComparator = new Comparator() { + @Override + int compare(Node node1, Node node2) { + return node1.globalPosition.z() <=> node2.globalPosition.z() + } + } Scene scene @@ -208,13 +214,16 @@ class RenderPipeline implements AutoCloseable { average('Gathering', 1f, logger) { -> queryResults.clear() drawCommands.clear() + drawCommands << scene.camera.renderCommand() frustumIntersection.set(enlargedViewProjection.scaling(0.9f, 0.9f, 1f).mul(scene.camera.viewProjection), false) - scene.query(frustumIntersection, queryResults).each { element -> - if (element instanceof GraphicsElement) { - drawCommands << element.renderCommand() + scene.query(frustumIntersection, queryResults) + .sort(true, renderOrderComparator) // Furthest-away objects first + .each { node -> + if (node instanceof GraphicsElement) { + drawCommands << node.renderCommand() + } } - } } } } diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/Node.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/Node.groovy index 01957449..eab67a36 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/Node.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/Node.groovy @@ -214,8 +214,8 @@ class Node implements SceneEvents, Scriptable { * for performance purposes. *

* The default behaviour is to inherit the partition hint of its parent, - * defaulting to {@link PartitionHint#NONE} if there are no hints in the node's ancestor - * tree. + * defaulting to {@link PartitionHint#NONE} if there are no hints in the + * node's ancestor tree. */ PartitionHint getPartitionHint() { @@ -392,7 +392,7 @@ class Node implements SceneEvents, Scriptable { /** * Set the local position of this node. */ - void setPosition(float x, float y, float z = 0) { + void setPosition(float x, float y, float z = position.z()) { transform.setTranslation( x * scale.x as float, diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/nodes/Camera.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/nodes/Camera.groovy index 85de8a7d..7279b088 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/nodes/Camera.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/scenegraph/nodes/Camera.groovy @@ -60,14 +60,11 @@ class Camera extends Node implements GraphicsElement { Camera(Dimension size) { viewportDef = [0, 0, size.width(), size.height()] - projection = new Matrix4f().setOrtho2D( - -size.width() / 2, size.width() / 2, - -size.height() / 2, size.height() / 2 - ) + projection = new Matrix4f().setOrthoSymmetric(size.width(), size.height(), 0f, 10f) logger.debug('Establishing an orthographic projection of {}x{}', size.width(), size.height()) view = new Matrix4f().setLookAt( - 0, 0, 1, + 0, 0, 10, 0, 0, 0, 0, 1, 0 ) @@ -168,7 +165,7 @@ class Camera extends Node implements GraphicsElement { } @Override - void setPosition(float x, float y, float z = 0) { + void setPosition(float x, float y, float z = position.z()) { // Positioning the camera is the opposite of what we would expect as we are // instead creating a transform matrix that moves the world around it @@ -179,7 +176,7 @@ class Camera extends Node implements GraphicsElement { void update(float delta) { if (tracking) { - setPosition(tracking.globalPosition) + setPosition(tracking.globalPosition.x(), tracking.globalPosition.y()) } } } diff --git a/redhorizon-explorer/source/nz/net/ultraq/redhorizon/explorer/scripts/MapViewerScript.groovy b/redhorizon-explorer/source/nz/net/ultraq/redhorizon/explorer/scripts/MapViewerScript.groovy index 7b652d0f..988da9ad 100644 --- a/redhorizon-explorer/source/nz/net/ultraq/redhorizon/explorer/scripts/MapViewerScript.groovy +++ b/redhorizon-explorer/source/nz/net/ultraq/redhorizon/explorer/scripts/MapViewerScript.groovy @@ -223,7 +223,7 @@ class MapViewerScript extends Script { private CompletableFuture moveCameraTo(Vector3f position) { var startPosition = new Vector3f(camera.position) - var nextPosition = new Vector3f() + var nextPosition = new Vector3f(0, 0, camera.position.z()) return new Transition(EasingFunctions::easeOutCubic, 800, { float delta -> camera.position = startPosition.lerp(position, delta, nextPosition) @@ -249,6 +249,7 @@ class MapViewerScript extends Script { void onSceneRemoved(Scene scene) { clearControls() + scene.removeChild(outline) } /** diff --git a/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/Shooter.groovy b/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/Shooter.groovy index 491601cd..d77b3d99 100644 --- a/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/Shooter.groovy +++ b/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/Shooter.groovy @@ -16,6 +16,8 @@ package nz.net.ultraq.redhorizon.shooter +import nz.net.ultraq.redhorizon.classic.filetypes.IniFile +import nz.net.ultraq.redhorizon.classic.filetypes.MapFile import nz.net.ultraq.redhorizon.classic.filetypes.PalFile import nz.net.ultraq.redhorizon.classic.maps.Map import nz.net.ultraq.redhorizon.classic.nodes.GlobalPalette @@ -83,10 +85,18 @@ class Shooter { globalPalette = new GlobalPalette(loadPalette()) scene << globalPalette - player = new Player(resourceManager) + player = new Player(resourceManager).tap { + transform { -> + translate(0f, 0f, 0.5f) + } + } scene << player camera.follow(player) + + var mapFile = resourceManager.loadFile('scg01ea.ini', IniFile) + var map = new Map(mapFile as MapFile, resourceManager) + scene << map } } diff --git a/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/objects/Player.groovy b/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/objects/Player.groovy index 9a7d8ab6..5ef14926 100644 --- a/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/objects/Player.groovy +++ b/redhorizon-shooter/source/nz/net/ultraq/redhorizon/shooter/objects/Player.groovy @@ -26,9 +26,12 @@ import nz.net.ultraq.redhorizon.engine.input.CursorPositionEvent import nz.net.ultraq.redhorizon.engine.input.GamepadControl import nz.net.ultraq.redhorizon.engine.input.KeyControl import nz.net.ultraq.redhorizon.engine.resources.ResourceManager +import nz.net.ultraq.redhorizon.engine.scenegraph.GraphicsElement import nz.net.ultraq.redhorizon.engine.scenegraph.Node +import nz.net.ultraq.redhorizon.engine.scenegraph.PartitionHint import nz.net.ultraq.redhorizon.engine.scenegraph.Scene import nz.net.ultraq.redhorizon.engine.scenegraph.Temporal +import nz.net.ultraq.redhorizon.engine.scenegraph.UpdateHint import nz.net.ultraq.redhorizon.engine.scenegraph.scripting.Script import org.joml.Math @@ -45,21 +48,22 @@ import java.util.concurrent.Executors * * @author Emanuel Rabina */ -class Player extends Node implements Rotatable, Temporal { +class Player extends Node implements GraphicsElement, Rotatable, Temporal { // private static final Logger logger = LoggerFactory.getLogger(Player) private static final Rectanglef MOVEMENT_RANGE = Map.MAX_BOUNDS - private static final float MOVEMENT_SPEED = 100f + private static final float MOVEMENT_SPEED = 200f private static final float ROTATION_SPEED = 180f private static final Vector2f up = new Vector2f(0, 1) + final PartitionHint partitionHint = PartitionHint.NONE + final UpdateHint updateHint = UpdateHint.ALWAYS private final Unit unit private final Vector2f screenPosition = new Vector2f() private Vector2f velocity = new Vector2f() private Vector2f direction = new Vector2f() private Vector2f movement = new Vector2f() - private Vector2f movementDiff = new Vector2f() private Vector2f lookAt = new Vector2f() private Vector2f lastLookAt = new Vector2f() private Vector2f relativeLookAt = new Vector2f() @@ -118,6 +122,15 @@ class Player extends Node implements Rotatable, Temporal { attachScript(new PlayerScript()) } + // TODO: Shouldn't need to implement this, but a Unit type is in the quadtree + // whereas Player is not. What should happen is that we update the unit + // position in the QuadTree. + @Override + RenderCommand renderCommand() { + + return unit.renderCommand() + } + @Override void setHeading(float newHeading) { @@ -136,11 +149,10 @@ class Player extends Node implements Rotatable, Temporal { } if (velocity.length()) { - movement.set(velocity).normalize().mul(MOVEMENT_SPEED).mul(delta) - movementDiff.set(position.x() + movement.x as float, position.y() + movement.y as float) + movement.set(velocity).normalize().mul(MOVEMENT_SPEED).mul(delta).add(position.x(), position.y()) setPosition( - Math.clamp(movementDiff.x, MOVEMENT_RANGE.minX, MOVEMENT_RANGE.maxX), - Math.clamp(movementDiff.y, MOVEMENT_RANGE.minY, MOVEMENT_RANGE.maxY) + java.lang.Math.clamp(movement.x, MOVEMENT_RANGE.minX, MOVEMENT_RANGE.maxX), + java.lang.Math.clamp(movement.y, MOVEMENT_RANGE.minY, MOVEMENT_RANGE.maxY) ) } @@ -150,7 +162,7 @@ class Player extends Node implements Rotatable, Temporal { heading = Math.toDegrees(relativeLookAt.set(lookAt).sub(screenPosition.x(), screenPosition.y()).angle(up)) // Adjust lookAt position with movement - lookAt.add(movementDiff) + lookAt.add(movement) lastLookAt.set(lookAt) } // Keyboard rotation