From 0a6e372e7c39750c780afb21ff1d14762e23209c Mon Sep 17 00:00:00 2001 From: James Date: Thu, 5 Oct 2023 07:28:25 -0400 Subject: [PATCH 01/15] Add Bullet test demonstrating btHeightfieldTerrainShape (#7238) --- .../gdx/tests/BulletTestCollection.java | 3 +- .../gdx/tests/bullet/HeightFieldTest.java | 117 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/gdx-tests/src/com/badlogic/gdx/tests/bullet/HeightFieldTest.java diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/BulletTestCollection.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/BulletTestCollection.java index 41bcfc4796a..d07ed293f32 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/BulletTestCollection.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/BulletTestCollection.java @@ -40,7 +40,8 @@ public class BulletTestCollection extends GdxTest implements InputProcessor, Ges new RayCastTest(), new RayPickRagdollTest(), new InternalTickTest(), new CollisionWorldTest(), new CollisionTest(), new FrustumCullingTest(), new CollisionDispatcherTest(), new ContactCallbackTest(), new ContactCallbackTest2(), new ContactCacheTest(), new SoftBodyTest(), new SoftMeshTest(), new VehicleTest(), new VehicleFilterTest(), - new CharacterTest(), new ImportTest(), new TriangleRaycastTest(), new OcclusionCullingTest(), new PairCacheTest()}; + new CharacterTest(), new ImportTest(), new TriangleRaycastTest(), new OcclusionCullingTest(), new PairCacheTest(), + new HeightFieldTest()}; protected int testIndex = 0; diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/bullet/HeightFieldTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/bullet/HeightFieldTest.java new file mode 100644 index 00000000000..00f9524c5aa --- /dev/null +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/bullet/HeightFieldTest.java @@ -0,0 +1,117 @@ + +package com.badlogic.gdx.tests.bullet; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.VertexAttributes; +import com.badlogic.gdx.graphics.g3d.Material; +import com.badlogic.gdx.graphics.g3d.Model; +import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; +import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; +import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; +import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; +import com.badlogic.gdx.math.Matrix4; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.physics.bullet.collision.btHeightfieldTerrainShape; +import com.badlogic.gdx.physics.bullet.collision.btSphereShape; +import com.badlogic.gdx.tests.g3d.HeightField; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** Demonstration of Bullets {@link btHeightfieldTerrainShape}. + * @author JamesTKhan */ +public class HeightFieldTest extends BaseBulletTest { + private HeightField field; + private Texture texture; + private BulletEntity terrainEntity; + private FloatBuffer floatBuffer; + private btHeightfieldTerrainShape terrainShape; + + @Override + public void create () { + super.create(); + + texture = new Texture(Gdx.files.internal("data/badlogic.jpg")); + disposables.add(texture); + + createSpheres(); + createTerrain(25, 5); + } + + private void createTerrain (float size, float heightScale) { + // Create the height field model + Pixmap data = new Pixmap(Gdx.files.internal("data/g3d/heightmap.png")); + field = new HeightField(true, data, true, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal + | VertexAttributes.Usage.ColorUnpacked | VertexAttributes.Usage.TextureCoordinates); + data.dispose(); + field.corner00.set(-size / 2, 0, -size / 2); + field.corner10.set(size / 2, 0, -size / 2); + field.corner01.set(-size / 2, 0, size / 2); + field.corner11.set(size / 2, 0, size / 2); + field.magnitude.set(0f, heightScale, 0f); + field.update(); + + // Find the min/max height of our height field + float minHeight = Float.MAX_VALUE; + float maxHeight = Float.MIN_VALUE; + for (float f : field.data) { + if (f < minHeight) minHeight = f; + if (f > maxHeight) maxHeight = f; + } + + // Create the terrain model + ModelBuilder modelBuilder = new ModelBuilder(); + modelBuilder.begin(); + modelBuilder.part("terrain", field.mesh, GL20.GL_TRIANGLES, new Material(TextureAttribute.createDiffuse(texture))); + Model terrain = modelBuilder.end(); + disposables.add(terrain); + + // Convert our height data into a ByteBuffer/FloatBuffer for passing to Bullet + // We are responsible for maintaining the float buffer + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(field.data.length * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + floatBuffer = byteBuffer.asFloatBuffer(); + floatBuffer.put(field.data); + floatBuffer.position(0); + + // Create the terrain physics shape + terrainShape = new btHeightfieldTerrainShape(field.width, field.height, floatBuffer, 1f, minHeight, maxHeight, 1, true); + terrainShape.setLocalScaling(new Vector3((size) / ((field.width - 1)), heightScale, (size) / ((field.height - 1)))); + + world.addConstructor("terrain", new BulletConstructor(terrain, terrainShape)); + terrainEntity = world.add("terrain", 0, 0f, 0); + + // Align the physics body with the models height + float adjustedHeight = (maxHeight + minHeight) / 2f * heightScale; + Matrix4 adjustedTransform = terrainEntity.body.getWorldTransform().trn(0, adjustedHeight, 0); + terrainEntity.body.setWorldTransform(adjustedTransform); + } + + private void createSpheres () { + final Material material = new Material(ColorAttribute.createDiffuse(Color.GREEN), ColorAttribute.createSpecular(1, 1, 1, 1), + FloatAttribute.createShininess(8f)); + final long attributes = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal + | VertexAttributes.Usage.TextureCoordinates; + final Model sphere = modelBuilder.createSphere(1f, 1f, 1f, 24, 24, material, attributes); + disposables.add(sphere); + + world.addConstructor("sphere", new BulletConstructor(sphere, 5f, new btSphereShape(.5f))); + + for (int i = -2; i < 2; i++) { + for (int j = -2; j < 2; j++) { + world.add("sphere", i * 2, 10, j * 2); + } + } + } + + @Override + public boolean tap (float x, float y, int count, int button) { + shoot(x, y); + return true; + } +} From 85f0e6322adf8be3f8bf87e14b7248c7fed3247a Mon Sep 17 00:00:00 2001 From: theofficialgman <28281419+theofficialgman@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:07:52 -0400 Subject: [PATCH 02/15] Lower GLIBC requirements by using building linux natives on ubuntu 18.04 docker (#7178) --- .github/workflows/build-publish.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 97eb49665cf..48e39a51404 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -89,7 +89,35 @@ jobs: natives-linux: runs-on: ubuntu-20.04 + container: + image: ubuntu:18.04 steps: + - name: Install dependencies into minimal dockerfile + run: | + # ubuntu dockerfile is very minimal (only 122 packages are installed) + # need to install updated git (from official git ppa) + apt update + apt install -y software-properties-common + add-apt-repository ppa:git-core/ppa -y + # install dependencies expected by other steps + apt update + apt install -y git \ + curl \ + ca-certificates \ + wget \ + bzip2 \ + zip \ + unzip \ + xz-utils \ + maven \ + ant sudo locales + + # set Locale to en_US.UTF-8 (avoids hang during compilation) + locale-gen en_US.UTF-8 + echo "LANG=en_US.UTF-8" >> $GITHUB_ENV + echo "LANGUAGE=en_US.UTF-8" >> $GITHUB_ENV + echo "LC_ALL=en_US.UTF-8" >> $GITHUB_ENV + - uses: actions/checkout@v2 with: fetch-depth: 0 From ce9e6922519a7232dbefc8dd41694a9a923e9415 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 5 Oct 2023 09:18:35 -0400 Subject: [PATCH 03/15] Remove usage of deprecated tag on GL30 (#7196) --- .../android/AndroidApplicationConfiguration.java | 9 ++++----- .../backends/iosrobovm/IOSApplicationConfiguration.java | 7 +++---- .../backends/iosrobovm/IOSApplicationConfiguration.java | 7 +++---- .../gdx/backends/gwt/GwtApplicationConfiguration.java | 7 +++---- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidApplicationConfiguration.java b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidApplicationConfiguration.java index 836d8d6d654..b6e18d0516d 100644 --- a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidApplicationConfiguration.java +++ b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidApplicationConfiguration.java @@ -91,11 +91,10 @@ public class AndroidApplicationConfiguration { /** set this to true to enable Android 4.4 KitKat's 'Immersive mode' **/ public boolean useImmersiveMode = true; - /** Experimental, whether to enable OpenGL ES 3 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES - * 3* is enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access its functionality. Requires at least Android 4.3 (API - * level 18). - * @deprecated this option is currently experimental and not yet fully supported, expect issues. */ - @Deprecated public boolean useGL30 = false; + /** Whether to enable OpenGL ES 3.0 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES 3* is + * enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access its functionality. Requires at least Android 4.3 (API level + * 18). */ + public boolean useGL30 = false; /** The maximum number of threads to use for network requests. Default is {@link Integer#MAX_VALUE}. */ public int maxNetThreads = Integer.MAX_VALUE; diff --git a/backends/gdx-backend-robovm-metalangle/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java b/backends/gdx-backend-robovm-metalangle/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java index 5055ac9110e..e3123916c25 100644 --- a/backends/gdx-backend-robovm-metalangle/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java +++ b/backends/gdx-backend-robovm-metalangle/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java @@ -61,10 +61,9 @@ public class IOSApplicationConfiguration { /** whether or not the onScreenKeyboard should be closed on return key * */ public boolean keyboardCloseOnReturn = true; - /** Experimental, whether to enable OpenGL ES 3 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES 3 - * is enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access it's functionality. - * @deprecated this option is currently experimental and not yet fully supported, expect issues. */ - @Deprecated public boolean useGL30 = false; + /** Whether to enable OpenGL ES 3 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES 3 is enabled, + * {@link com.badlogic.gdx.Gdx#gl30} can be used to access it's functionality. */ + public boolean useGL30 = false; /** whether the status bar should be visible or not * */ public boolean statusBarVisible = false; diff --git a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java index dd47aef43c8..ea820f267cf 100644 --- a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java +++ b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplicationConfiguration.java @@ -70,10 +70,9 @@ public class IOSApplicationConfiguration { /** whether or not the onScreenKeyboard should be closed on return key **/ public boolean keyboardCloseOnReturn = true; - /** Experimental, whether to enable OpenGL ES 3 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES 3 - * is enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access it's functionality. - * @deprecated this option is currently experimental and not yet fully supported, expect issues. */ - @Deprecated public boolean useGL30 = false; + /** Whether to enable OpenGL ES 3 if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES 3 is enabled, + * {@link com.badlogic.gdx.Gdx#gl30} can be used to access it's functionality. */ + public boolean useGL30 = false; /** whether the status bar should be visible or not **/ public boolean statusBarVisible = false; diff --git a/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/GwtApplicationConfiguration.java b/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/GwtApplicationConfiguration.java index 640087c078b..b7632849c31 100644 --- a/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/GwtApplicationConfiguration.java +++ b/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/GwtApplicationConfiguration.java @@ -53,10 +53,9 @@ public class GwtApplicationConfiguration { public TextArea log; /** whether to use debugging mode for OpenGL calls. Errors will result in a RuntimeException being thrown. */ public boolean useDebugGL = false; - /** Experimental, whether to enable OpenGL ES 30 (aka WebGL2) if supported. If not supported it will fall-back to OpenGL ES - * 2.0. When GL ES 30 is enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access its functionality. - * @deprecated this option is currently experimental and not yet fully supported, expect issues. */ - @Deprecated public boolean useGL30 = false; + /** Whether to enable OpenGL ES 3.0 (aka WebGL2) if supported. If not supported it will fall-back to OpenGL ES 2.0. When GL ES + * 3.0 is enabled, {@link com.badlogic.gdx.Gdx#gl30} can be used to access its functionality. */ + public boolean useGL30 = false; /** preserve the back buffer, needed if you fetch a screenshot via canvas#toDataUrl, may have performance impact **/ public boolean preserveDrawingBuffer = false; /** whether to include an alpha channel in the color buffer to combine the color buffer with the rest of the webpage From 02202a37f4030b085769e0f4beee46b4571b229b Mon Sep 17 00:00:00 2001 From: John Lavender <82977289+JohnLavender474@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:27:44 -0400 Subject: [PATCH 04/15] 6901 - Support Tiled "Object" Type Map Property (#7229) --- CHANGES | 1 + .../gdx/maps/tiled/BaseTmxMapLoader.java | 48 +++- .../assets/data/maps/tiled-objects/grass.tsx | 4 + .../data/maps/tiled-objects/grass_1.png | Bin 0 -> 5706 bytes .../tiled-objects/test-object-properties.tmx | 66 ++++++ .../gdx/tests/gwt/client/GwtTestWrapper.java | 7 + .../gdx/tests/TiledMapObjectPropertyTest.java | 212 ++++++++++++++++++ .../badlogic/gdx/tests/utils/GdxTests.java | 1 + 8 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 tests/gdx-tests-android/assets/data/maps/tiled-objects/grass.tsx create mode 100644 tests/gdx-tests-android/assets/data/maps/tiled-objects/grass_1.png create mode 100644 tests/gdx-tests-android/assets/data/maps/tiled-objects/test-object-properties.tmx create mode 100644 tests/gdx-tests/src/com/badlogic/gdx/tests/TiledMapObjectPropertyTest.java diff --git a/CHANGES b/CHANGES index 73e505526dd..f0a85f82c55 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ - LWJGL3 Improvement: Audio device is automatically switched if it was changed in the operating system. - Tiled Fix: TiledLayer parallax default values fix - Android: Removed mouse catching added on 1.12.0 due to unintended effects (see #7187). +- API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. - Update to LWJGL 3.3.3 [1.12.0] diff --git a/gdx/src/com/badlogic/gdx/maps/tiled/BaseTmxMapLoader.java b/gdx/src/com/badlogic/gdx/maps/tiled/BaseTmxMapLoader.java index 3f9cfa777fa..c5167a7294f 100644 --- a/gdx/src/com/badlogic/gdx/maps/tiled/BaseTmxMapLoader.java +++ b/gdx/src/com/badlogic/gdx/maps/tiled/BaseTmxMapLoader.java @@ -64,6 +64,8 @@ public static class Parameters extends AssetLoaderParameters { protected int mapHeightInPixels; protected TiledMap map; + protected IntMap idToObject; + protected Array runOnEndOfLoadTiled; public BaseTmxMapLoader (FileHandleResolver resolver) { super(resolver); @@ -83,6 +85,15 @@ public Array getDependencies (String fileName, FileHandle tmxFi return getDependencyAssetDescriptors(tmxFile, textureParameter); } + /** Gets a map of the object ids to the {@link MapObject} instances. Returns null if + * {@link #loadTiledMap(FileHandle, Parameters, ImageResolver)} has not been called yet. + * + * @return the map of the ids to {@link MapObject}, or null if {@link #loadTiledMap(FileHandle, Parameters, ImageResolver)} + * method has not been called yet. */ + public @Null IntMap getIdToObject () { + return idToObject; + } + protected abstract Array getDependencyAssetDescriptors (FileHandle tmxFile, TextureLoader.TextureParameter textureParameter); @@ -94,6 +105,8 @@ protected abstract Array getDependencyAssetDescriptors (FileHan * @return the {@link TiledMap} */ protected TiledMap loadTiledMap (FileHandle tmxFile, P parameter, ImageResolver imageResolver) { this.map = new TiledMap(); + this.idToObject = new IntMap<>(); + this.runOnEndOfLoadTiled = new Array<>(); if (parameter != null) { this.convertObjectToTileSpace = parameter.convertObjectToTileSpace; @@ -179,6 +192,11 @@ protected TiledMap loadTiledMap (FileHandle tmxFile, P parameter, ImageResolver } } + for (Runnable runnable : runOnEndOfLoadTiled) { + runnable.run(); + } + runOnEndOfLoadTiled = null; + return map; } @@ -431,22 +449,44 @@ protected void loadObject (TiledMap map, MapObjects objects, Element element, fl if (properties != null) { loadProperties(object.getProperties(), properties); } + idToObject.put(id, object); objects.add(object); } } - protected void loadProperties (MapProperties properties, Element element) { + protected void loadProperties (final MapProperties properties, Element element) { if (element == null) return; if (element.getName().equals("properties")) { for (Element property : element.getChildrenByName("property")) { - String name = property.getAttribute("name", null); + final String name = property.getAttribute("name", null); String value = property.getAttribute("value", null); String type = property.getAttribute("type", null); if (value == null) { value = property.getText(); } - Object castValue = castProperty(name, value, type); - properties.put(name, castValue); + if (type != null && type.equals("object")) { + // Wait until the end of [loadTiledMap] to fetch the object + try { + // Value should be the id of the object being pointed to + final int id = Integer.parseInt(value); + // Create [Runnable] to fetch object and add it to props + Runnable fetch = new Runnable() { + @Override + public void run () { + MapObject object = idToObject.get(id); + properties.put(name, object); + } + }; + // [Runnable] should not run until the end of [loadTiledMap] + runOnEndOfLoadTiled.add(fetch); + } catch (Exception exception) { + throw new GdxRuntimeException( + "Error parsing property [\" + name + \"] of type \"object\" with value: [" + value + "]", exception); + } + } else { + Object castValue = castProperty(name, value, type); + properties.put(name, castValue); + } } } } diff --git a/tests/gdx-tests-android/assets/data/maps/tiled-objects/grass.tsx b/tests/gdx-tests-android/assets/data/maps/tiled-objects/grass.tsx new file mode 100644 index 00000000000..032019e2b58 --- /dev/null +++ b/tests/gdx-tests-android/assets/data/maps/tiled-objects/grass.tsx @@ -0,0 +1,4 @@ + + + + diff --git a/tests/gdx-tests-android/assets/data/maps/tiled-objects/grass_1.png b/tests/gdx-tests-android/assets/data/maps/tiled-objects/grass_1.png new file mode 100644 index 0000000000000000000000000000000000000000..75692cbe584bf10348e43b2d9981dfc7436b36a0 GIT binary patch literal 5706 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 literal 0 HcmV?d00001 diff --git a/tests/gdx-tests-android/assets/data/maps/tiled-objects/test-object-properties.tmx b/tests/gdx-tests-android/assets/data/maps/tiled-objects/test-object-properties.tmx new file mode 100644 index 00000000000..f9a7704fc5a --- /dev/null +++ b/tests/gdx-tests-android/assets/data/maps/tiled-objects/test-object-properties.tmx @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + diff --git a/tests/gdx-tests-gwt/src/com/badlogic/gdx/tests/gwt/client/GwtTestWrapper.java b/tests/gdx-tests-gwt/src/com/badlogic/gdx/tests/gwt/client/GwtTestWrapper.java index 5afc9e9f4af..27cb417c378 100644 --- a/tests/gdx-tests-gwt/src/com/badlogic/gdx/tests/gwt/client/GwtTestWrapper.java +++ b/tests/gdx-tests-gwt/src/com/badlogic/gdx/tests/gwt/client/GwtTestWrapper.java @@ -22,6 +22,7 @@ import com.badlogic.gdx.tests.Box2DTestCollection; import com.badlogic.gdx.tests.BufferUtilsTest; import com.badlogic.gdx.tests.ClipboardTest; +import com.badlogic.gdx.tests.TiledMapObjectPropertyTest; import com.badlogic.gdx.tests.ColorTest; import com.badlogic.gdx.tests.ComplexActionTest; import com.badlogic.gdx.tests.CustomShaderSpriteBatchTest; @@ -554,6 +555,12 @@ public GdxTest instance () { return new TiledMapObjectLoadingTest(); } }); + tests.add(new GwtInstancer() { + @Override + public GdxTest instance () { + return new TiledMapObjectPropertyTest(); + } + }); tests.add(new GwtInstancer() { public GdxTest instance () { return new UITest(); diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledMapObjectPropertyTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledMapObjectPropertyTest.java new file mode 100644 index 00000000000..c5fe6a0ca2b --- /dev/null +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledMapObjectPropertyTest.java @@ -0,0 +1,212 @@ + +package com.badlogic.gdx.tests; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.maps.MapObject; +import com.badlogic.gdx.maps.MapObjects; +import com.badlogic.gdx.maps.MapProperties; +import com.badlogic.gdx.maps.objects.RectangleMapObject; +import com.badlogic.gdx.maps.tiled.TiledMap; +import com.badlogic.gdx.maps.tiled.TiledMapRenderer; +import com.badlogic.gdx.maps.tiled.TmxMapLoader; +import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.tests.utils.GdxTest; +import com.badlogic.gdx.tests.utils.OrthoCamController; +import com.badlogic.gdx.utils.StringBuilder; +import com.badlogic.gdx.utils.*; + +import java.util.Iterator; + +public class TiledMapObjectPropertyTest extends GdxTest { + + private TiledMap map; + private SpriteBatch batch; + private ShapeRenderer shapeRenderer; + private OrthographicCamera camera; + private TiledMapRenderer mapRenderer; + private Array objects; + + private boolean error; + + @Override + public void create () { + try { + TmxMapLoader loader = new TmxMapLoader(); + // run multiple times to ensure reloading map works correctly + for (int i = 0; i < 3; i++) { + Gdx.app.log("-------------------------------------", "Running test " + (i + 1) + "/3\n"); + + StringBuilder builder = new StringBuilder(); + builder.append("Expected results:\n").append("- Object with id 1 should have \"object\" props:\n") + .append("\t- Points_To_ID_1 = id: 1\n").append("\t- Points_To_ID_2 = id: 2\n") + .append("\t- Points_To_ID_5 = id: 5\n").append("- Object with id 2 should have \"object\" props:\n") + .append("\t- Points_To_ID_3 = id: 3\n").append("\t- Points_To_ID_4 = id: 4\n") + .append("- Object with id 3 should have \"object\" props:\n").append("\t- Points_To_ID_2 = id: 2\n") + .append("- Object with id 4 should have \"object\" props:\n").append("\t- Points_To_ID_1 = id: 1\n") + .append("- Objects with id's 5 and 6 should have \"object\" props:\n").append("\t- Placeholder = 0\n"); + Gdx.app.log("TiledMapObjectPropertyTest", builder.toString()); + + float w = Gdx.graphics.getWidth(); + float h = Gdx.graphics.getHeight(); + + camera = new OrthographicCamera(); + camera.setToOrtho(false, (w / h) * 512, 512); + camera.zoom = .5f; + camera.update(); + + OrthoCamController cameraController = new OrthoCamController(camera); + Gdx.input.setInputProcessor(cameraController); + + map = loader.load("data/maps/tiled-objects/test-object-properties.tmx"); + + batch = new SpriteBatch(); + shapeRenderer = new ShapeRenderer(); + mapRenderer = new OrthogonalTiledMapRenderer(map); + + MapObjects objects1 = map.getLayers().get("Objects 1").getObjects(); + MapObjects objects2 = map.getLayers().get("Objects 2").getObjects(); + objects = new Array<>(); + for (MapObject object : objects1) { + objects.add(object); + } + for (MapObject object : objects2) { + objects.add(object); + } + + IntMap idToObject = loader.getIdToObject(); + + builder.clear(); + builder.append("\nidToObject: {"); + for (IntMap.Entry entry : idToObject) { + builder.append("\n\t").append(entry.key).append(" -> ").append(entry.value); + } + builder.append("\n}\n"); + Gdx.app.log("TiledMapObjectPropertyTest", builder.toString()); + + for (MapObject object1 : objects) { + int id = object1.getProperties().get("id", Integer.class); + MapObject object2 = idToObject.get(id); + if (object1 != object2) { + throw new RuntimeException( + "Error! Object with id " + id + " " + "is not the same object as the one in the idToObject map!"); + } + + MapProperties props = object1.getProperties(); + switch (id) { + case 1: + test(props, 2, idToObject); + test(props, 5, idToObject); + test(props, 1, idToObject); + break; + case 2: + test(props, 3, idToObject); + test(props, 4, idToObject); + break; + case 3: + test(props, 2, idToObject); + break; + case 4: + test(props, 1, idToObject); + break; + case 5: + case 6: + Iterator propKeysIterator = props.getKeys(); + ObjectSet propKeys = new ObjectSet<>(); + while (propKeysIterator.hasNext()) { + propKeys.add(propKeysIterator.next()); + } + if (propKeys.size != 6) { + throw new RuntimeException("Object with id " + id + " should " + "have six keys " + "but has " + propKeys); + } + } + } + + builder.clear(); + builder.append("Actual results:\n"); + for (IntMap.Entry entry : idToObject.entries()) { + int id = entry.key; + MapProperties props = entry.value.getProperties(); + + builder.append("- Object with id ").append(id).append(" has \"object\" props:\n"); + + Iterator propKeysIterator = props.getKeys(); + Iterator propValuesIterator = props.getValues(); + + while (propKeysIterator.hasNext() && propValuesIterator.hasNext()) { + Object value = propValuesIterator.next(); + String key = propKeysIterator.next(); + if (!key.contains("Points_To_ID_") && !key.contains("Placeholder")) { + continue; + } + + if (value instanceof MapObject) { + MapObject object = (MapObject)value; + int objectId = object.getProperties().get("id", Integer.class); + value = "id: " + objectId + ", object: " + object; + } + + builder.append("\t\t").append(key).append(" -> ").append(value).append("\n"); + } + } + Gdx.app.log("TiledMapObjectPropertyTest", builder.toString()); + } + } catch (Exception e) { + Gdx.app.error("TiledMapObjectPropertyTest", "Failed to run test!", e); + e.printStackTrace(); + error = true; + } + } + + @Override + public void render () { + if (error) { + Gdx.app.error("TiledMapObjectPropertyTest", "Failed to run test!"); + Gdx.app.exit(); + } + + ScreenUtils.clear(0.55f, 0.55f, 0.55f, 1f); + camera.update(); + mapRenderer.setView(camera); + mapRenderer.render(); + + shapeRenderer.setProjectionMatrix(camera.combined); + batch.setProjectionMatrix(camera.combined); + + shapeRenderer.setColor(Color.BLUE); + Gdx.gl20.glLineWidth(2); + for (MapObject object : objects) { + if (!object.isVisible()) continue; + if (object instanceof RectangleMapObject) { + shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); + Rectangle rectangle = ((RectangleMapObject)object).getRectangle(); + shapeRenderer.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + shapeRenderer.end(); + } + } + } + + @Override + public void dispose () { + map.dispose(); + shapeRenderer.dispose(); + } + + private void test (MapProperties props, int idToObjProp1, IntMap idToObjectMap) { + String key = "Points_To_ID_" + idToObjProp1; + if (!props.containsKey(key)) { + throw new GdxRuntimeException("Missing property: " + key); + } + + MapObject other1 = idToObjectMap.get(idToObjProp1); + MapObject other2 = props.get(key, MapObject.class); + + if (other1 != other2) { + throw new GdxRuntimeException("Property " + key + " does not point to the correct object"); + } + } +} diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java index 8759b8f264f..67dbf4c8bff 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java @@ -306,6 +306,7 @@ public class GdxTests { TiledMapDirectLoaderTest.class, TiledMapModifiedExternalTilesetTest.class, TiledMapObjectLoadingTest.class, + TiledMapObjectPropertyTest.class, TiledMapBench.class, TiledMapLayerOffsetTest.class, TimerTest.class, From f3412735a30fb19833642fb39cedad6c0cfac1cd Mon Sep 17 00:00:00 2001 From: Maximilian Wende <3714905+dasisdormax@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:05:30 +0200 Subject: [PATCH 05/15] Clear list pointers of child before adding it to a JsonValue (#7044) * Add test for removing and adding a json value * Clear list pointers of child before adding it to a JsonValue * Update CHANGES --- CHANGES | 1 + gdx/src/com/badlogic/gdx/utils/JsonValue.java | 6 ++-- .../com/badlogic/gdx/utils/JsonValueTest.java | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 gdx/test/com/badlogic/gdx/utils/JsonValueTest.java diff --git a/CHANGES b/CHANGES index f0a85f82c55..54aff94cda1 100644 --- a/CHANGES +++ b/CHANGES @@ -71,6 +71,7 @@ - API Fix: MathUtils.lerpAngle() fixed for extreme inputs - MathUtils trigonometry improvements - Various Scene2D fixes and improvements +- Fix: JsonValue#addChild now clears leftover list pointers, preventing inconsistent or looping JSON objects. [1.11.0] - [BREAKING CHANGE] iOS: Increased min supported iOS version to 9.0. Update your Info.plist file if necessary. diff --git a/gdx/src/com/badlogic/gdx/utils/JsonValue.java b/gdx/src/com/badlogic/gdx/utils/JsonValue.java index 2a78404cffe..3acef9e1279 100644 --- a/gdx/src/com/badlogic/gdx/utils/JsonValue.java +++ b/gdx/src/com/badlogic/gdx/utils/JsonValue.java @@ -912,11 +912,13 @@ public void addChild (JsonValue value) { if (type == ValueType.object && value.name == null) throw new IllegalStateException("An object child requires a name: " + value); value.parent = this; + value.next = null; size++; JsonValue current = child; - if (current == null) + if (current == null) { + value.prev = null; child = value; - else { + } else { while (true) { if (current.next == null) { current.next = value; diff --git a/gdx/test/com/badlogic/gdx/utils/JsonValueTest.java b/gdx/test/com/badlogic/gdx/utils/JsonValueTest.java new file mode 100644 index 00000000000..021a91f91ea --- /dev/null +++ b/gdx/test/com/badlogic/gdx/utils/JsonValueTest.java @@ -0,0 +1,32 @@ + +package com.badlogic.gdx.utils; + +import org.junit.Assert; +import org.junit.Test; + +public class JsonValueTest { + + @Test + public void testAddingRemovedValue () { + // Prepare two JSON objects + JsonValue firstObject = new JsonValue(JsonValue.ValueType.object); + JsonValue secondObject = new JsonValue(JsonValue.ValueType.object); + + firstObject.addChild("a", new JsonValue("A")); + secondObject.addChild("b", new JsonValue("B")); + secondObject.addChild("c", new JsonValue("C")); + + // Remove an item from one object and add it to the other + JsonValue b = secondObject.remove("b"); + firstObject.addChild(b); + + // Check if both objects have the expected children + Assert.assertNotNull(firstObject.get("a")); + Assert.assertNotNull(firstObject.get("b")); + Assert.assertNull(firstObject.get("c")); + + Assert.assertNull(secondObject.get("a")); + Assert.assertNull(secondObject.get("b")); + Assert.assertNotNull(secondObject.get("c")); + } +} From 99c10901f84aa5b3434c9359405b9c39c4725775 Mon Sep 17 00:00:00 2001 From: obigu <1794492+obigu@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:46:06 +0200 Subject: [PATCH 06/15] Upgrade MobiVM to 2.3.20 (#7249) --- CHANGES | 1 + .../gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java | 2 +- gradle/dependencies.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 54aff94cda1..152b73e38c5 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ - LWJGL3 Improvement: Audio device is automatically switched if it was changed in the operating system. - Tiled Fix: TiledLayer parallax default values fix - Android: Removed mouse catching added on 1.12.0 due to unintended effects (see #7187). +- iOS: Update to MobiVM 2.3.20 - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. - Update to LWJGL 3.3.3 diff --git a/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java b/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java index 6e335f9e6a7..36e0c614e1b 100644 --- a/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java +++ b/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java @@ -24,7 +24,7 @@ public class DependencyBank { static String libgdxVersion = "1.12.0"; // Temporary snapshot version, we need a more dynamic solution for pointing to the latest nightly static String libgdxNightlyVersion = "1.12.1-SNAPSHOT"; - static String roboVMVersion = "2.3.19"; + static String roboVMVersion = "2.3.20"; static String buildToolsVersion = "33.0.2"; static String androidAPILevel = "32"; static String androidMinAPILevel = "14"; diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index d8c74d00393..68f12c08fd9 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -23,7 +23,7 @@ ext { versions.java = project.hasProperty("javaVersion") ? Integer.parseInt(project.getProperty("javaVersion")) : 7 versions.javaLwjgl3 = project.hasProperty("javaVersion") ? Integer.parseInt(project.getProperty("javaVersion")) : 8 -versions.robovm = "2.3.19" +versions.robovm = "2.3.20" versions.gwt = "2.8.2" versions.gwtPlugin = "1.1.16" versions.gretty = "3.0.7" From f23b840f59763fb221f623dbee2d80cf22af4bb6 Mon Sep 17 00:00:00 2001 From: Thomas Creutzenberg Date: Fri, 6 Oct 2023 17:55:35 +0200 Subject: [PATCH 07/15] align for TiledDrawable (#7207) * TiledDrawable align * Removed obsolete ShapeRenderer from test * Apply formatter * Removed redundant loops --- CHANGES | 2 + .../scenes/scene2d/utils/TiledDrawable.java | 253 +++++++++++++++--- .../assets/data/testAtlas.atlas | 27 ++ .../assets/data/testAtlas.png | Bin 0 -> 1751 bytes .../badlogic/gdx/tests/TiledDrawableTest.java | 111 ++++++++ .../badlogic/gdx/tests/utils/GdxTests.java | 1 + 6 files changed, 357 insertions(+), 37 deletions(-) create mode 100644 tests/gdx-tests-android/assets/data/testAtlas.atlas create mode 100644 tests/gdx-tests-android/assets/data/testAtlas.png create mode 100644 tests/gdx-tests/src/com/badlogic/gdx/tests/TiledDrawableTest.java diff --git a/CHANGES b/CHANGES index 152b73e38c5..e1e0c977fd6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ [1.12.1] - LWJGL3 Improvement: Audio device is automatically switched if it was changed in the operating system. - Tiled Fix: TiledLayer parallax default values fix +- API Addition: TiledDrawable: Align can be set to manipulate the alignment of the rendering (TiledDrawable#setAlign, TiledDrawable#getAlign) +- API Addition: TiledDrawable#draw: Also available as a static function (with align) if you don't want to create an extra instance per texture region - Android: Removed mouse catching added on 1.12.0 due to unintended effects (see #7187). - iOS: Update to MobiVM 2.3.20 - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. diff --git a/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java b/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java index fc5138832a5..3b03ee7fa92 100644 --- a/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java +++ b/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java @@ -20,12 +20,15 @@ import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.Align; /** Draws a {@link TextureRegion} repeatedly to fill the area, instead of stretching it. - * @author Nathan Sweet */ + * @author Nathan Sweet + * @author Thomas Creutzenberg */ public class TiledDrawable extends TextureRegionDrawable { private final Color color = new Color(1, 1, 1, 1); private float scale = 1; + private int align = Align.bottomLeft; public TiledDrawable () { super(); @@ -43,50 +46,218 @@ public void draw (Batch batch, float x, float y, float width, float height) { float oldColor = batch.getPackedColor(); batch.setColor(batch.getColor().mul(color)); - TextureRegion region = getRegion(); - float regionWidth = region.getRegionWidth() * scale, regionHeight = region.getRegionHeight() * scale; - int fullX = (int)(width / regionWidth), fullY = (int)(height / regionHeight); - float remainingX = width - regionWidth * fullX, remainingY = height - regionHeight * fullY; - float startX = x, startY = y; - float endX = x + width - remainingX, endY = y + height - remainingY; - for (int i = 0; i < fullX; i++) { - y = startY; - for (int ii = 0; ii < fullY; ii++) { - batch.draw(region, x, y, regionWidth, regionHeight); - y += regionHeight; + draw(batch, getRegion(), x, y, width, height, scale, align); + + batch.setPackedColor(oldColor); + } + + public static void draw (Batch batch, TextureRegion textureRegion, float x, float y, float width, float height, float scale, + int align) { + final float regionWidth = textureRegion.getRegionWidth() * scale; + final float regionHeight = textureRegion.getRegionHeight() * scale; + + final Texture texture = textureRegion.getTexture(); + final float textureWidth = texture.getWidth() * scale; + final float textureHeight = texture.getHeight() * scale; + final float u = textureRegion.getU(); + final float v = textureRegion.getV(); + final float u2 = textureRegion.getU2(); + final float v2 = textureRegion.getV2(); + + int fullX = (int)(width / regionWidth); + final float leftPartialWidth; + final float rightPartialWidth; + if (Align.isLeft(align)) { + leftPartialWidth = 0f; + rightPartialWidth = width - (regionWidth * fullX); + } else if (Align.isRight(align)) { + leftPartialWidth = width - (regionWidth * fullX); + rightPartialWidth = 0f; + } else { + if (fullX != 0) { + fullX = fullX % 2 == 1 ? fullX : fullX - 1; + final float leftRight = 0.5f * (width - (regionWidth * fullX)); + leftPartialWidth = leftRight; + rightPartialWidth = leftRight; + } else { + leftPartialWidth = 0f; + rightPartialWidth = 0f; + } + } + int fullY = (int)(height / regionHeight); + final float topPartialHeight; + final float bottomPartialHeight; + if (Align.isTop(align)) { + topPartialHeight = 0f; + bottomPartialHeight = height - (regionHeight * fullY); + } else if (Align.isBottom(align)) { + topPartialHeight = height - (regionHeight * fullY); + bottomPartialHeight = 0f; + } else { + if (fullY != 0) { + fullY = fullY % 2 == 1 ? fullY : fullY - 1; + final float topBottom = 0.5f * (height - (regionHeight * fullY)); + topPartialHeight = topBottom; + bottomPartialHeight = topBottom; + } else { + topPartialHeight = 0f; + bottomPartialHeight = 0f; } - x += regionWidth; } - Texture texture = region.getTexture(); - float u = region.getU(); - float v2 = region.getV2(); - if (remainingX > 0) { - // Right edge. - float u2 = u + remainingX / (texture.getWidth() * scale); - float v = region.getV(); - y = startY; - for (int ii = 0; ii < fullY; ii++) { - batch.draw(texture, x, y, remainingX, regionHeight, u, v2, u2, v); - y += regionHeight; + + float drawX = x; + float drawY = y; + + // Left edge + if (leftPartialWidth > 0f) { + final float leftEdgeU = u2 - (leftPartialWidth / textureWidth); + + // Left bottom partial + if (bottomPartialHeight > 0f) { + final float leftBottomV = v + (bottomPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, leftPartialWidth, bottomPartialHeight, leftEdgeU, leftBottomV, u2, v); + drawY += bottomPartialHeight; } - // Upper right corner. - if (remainingY > 0) { - v = v2 - remainingY / (texture.getHeight() * scale); - batch.draw(texture, x, y, remainingX, remainingY, u, v2, u2, v); + + // Left center partials + if (fullY == 0 && Align.isCenterVertical(align)) { + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + final float leftCenterV = v2 - vOffset; + final float leftCenterV2 = v + vOffset; + batch.draw(texture, drawX, drawY, leftPartialWidth, height, leftEdgeU, leftCenterV, u2, leftCenterV2); + drawY += height; + } else { + for (int i = 0; i < fullY; i++) { + batch.draw(texture, drawX, drawY, leftPartialWidth, regionHeight, leftEdgeU, v2, u2, v); + drawY += regionHeight; + } + } + + // Left top partial + if (topPartialHeight > 0f) { + final float leftTopV = v2 - (topPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, leftPartialWidth, topPartialHeight, leftEdgeU, v2, u2, leftTopV); } } - if (remainingY > 0) { - // Top edge. - float u2 = region.getU2(); - float v = v2 - remainingY / (texture.getHeight() * scale); - x = startX; - for (int i = 0; i < fullX; i++) { - batch.draw(texture, x, y, regionWidth, remainingY, u, v2, u2, v); - x += regionWidth; + + // Center full texture regions + { + // Center bottom partials + if (bottomPartialHeight > 0f) { + drawX = x + leftPartialWidth; + drawY = y; + + final float centerBottomV = v + (bottomPartialHeight / textureHeight); + + if (fullX == 0 && Align.isCenterHorizontal(align)) { + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + final float centerBottomU = u + uOffset; + final float centerBottomU2 = u2 - uOffset; + batch.draw(texture, drawX, drawY, width, bottomPartialHeight, centerBottomU, centerBottomV, centerBottomU2, v); + drawX += width; + } else { + for (int i = 0; i < fullX; i++) { + batch.draw(texture, drawX, drawY, regionWidth, bottomPartialHeight, u, centerBottomV, u2, v); + drawX += regionWidth; + } + } + } + + // Center full texture regions + { + drawX = x + leftPartialWidth; + + final int originalFullX = fullX; + final int originalFullY = fullY; + + float centerCenterDrawWidth = regionWidth; + float centerCenterDrawHeight = regionHeight; + float centerCenterU = u; + float centerCenterU2 = u2; + float centerCenterV = v2; + float centerCenterV2 = v; + if (fullX == 0 && Align.isCenterHorizontal(align)) { + fullX = 1; + centerCenterDrawWidth = width; + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + centerCenterU = u + uOffset; + centerCenterU2 = u2 - uOffset; + } + if (fullY == 0 && Align.isCenterVertical(align)) { + fullY = 1; + centerCenterDrawHeight = height; + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + centerCenterV = v2 - vOffset; + centerCenterV2 = v + vOffset; + } + for (int i = 0; i < fullX; i++) { + drawY = y + bottomPartialHeight; + for (int ii = 0; ii < fullY; ii++) { + batch.draw(texture, drawX, drawY, centerCenterDrawWidth, centerCenterDrawHeight, centerCenterU, centerCenterV, + centerCenterU2, centerCenterV2); + drawY += centerCenterDrawHeight; + } + drawX += centerCenterDrawWidth; + } + + fullX = originalFullX; + fullY = originalFullY; + } + + // Center top partials + if (topPartialHeight > 0f) { + drawX = x + leftPartialWidth; + + final float centerTopV = v2 - (topPartialHeight / textureHeight); + + if (fullX == 0 && Align.isCenterHorizontal(align)) { + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + final float centerTopU = u + uOffset; + final float centerTopU2 = u2 - uOffset; + batch.draw(texture, drawX, drawY, width, topPartialHeight, centerTopU, v2, centerTopU2, centerTopV); + drawX += width; + } else { + for (int i = 0; i < fullX; i++) { + batch.draw(texture, drawX, drawY, regionWidth, topPartialHeight, u, v2, u2, centerTopV); + drawX += regionWidth; + } + } } } - batch.setPackedColor(oldColor); + // Right edge + if (rightPartialWidth > 0f) { + drawY = y; + + final float rightEdgeU2 = u + (rightPartialWidth / textureWidth); + + // Right bottom partial + if (bottomPartialHeight > 0f) { + final float rightBottomV = v + (bottomPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, rightPartialWidth, bottomPartialHeight, u, rightBottomV, rightEdgeU2, v); + drawY += bottomPartialHeight; + } + + // Right center partials + if (fullY == 0 && Align.isCenterVertical(align)) { + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + final float rightCenterV = v2 - vOffset; + final float rightCenterV2 = v + vOffset; + batch.draw(texture, drawX, drawY, rightPartialWidth, height, u, rightCenterV, rightEdgeU2, rightCenterV2); + drawY += height; + } else { + for (int i = 0; i < fullY; i++) { + batch.draw(texture, drawX, drawY, rightPartialWidth, regionHeight, u, v2, rightEdgeU2, v); + drawY += regionHeight; + } + } + + // Right top partial + if (topPartialHeight > 0f) { + final float rightTopV = v2 - (topPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, rightPartialWidth, topPartialHeight, u, v2, rightEdgeU2, rightTopV); + } + } } public void draw (Batch batch, float x, float y, float originX, float originY, float width, float height, float scaleX, @@ -106,6 +277,14 @@ public float getScale () { return scale; } + public int getAlign () { + return align; + } + + public void setAlign (int align) { + this.align = align; + } + public TiledDrawable tint (Color tint) { TiledDrawable drawable = new TiledDrawable(this); drawable.color.set(tint); diff --git a/tests/gdx-tests-android/assets/data/testAtlas.atlas b/tests/gdx-tests-android/assets/data/testAtlas.atlas new file mode 100644 index 00000000000..24079c4d95c --- /dev/null +++ b/tests/gdx-tests-android/assets/data/testAtlas.atlas @@ -0,0 +1,27 @@ +testAtlas.png + size: 256, 64 + filter: Linear, Linear +black + bounds: 126, 16, 4, 8 +darkBlue + bounds: 2, 33, 26, 26 +darkBlue2 + bounds: 2, 2, 26, 26 +darkGray + bounds: 33, 33, 26, 26 +darkGray2 + bounds: 33, 2, 26, 26 +darkGreen + bounds: 64, 33, 26, 26 +darkGreen2 + bounds: 64, 2, 26, 26 +grey + bounds: 126, 29, 22, 30 +pink + bounds: 95, 33, 26, 26 +purple + bounds: 153, 17, 16, 42 +tileTester + bounds: 95, 2, 26, 26 +white + bounds: 174, 57, 4, 2 diff --git a/tests/gdx-tests-android/assets/data/testAtlas.png b/tests/gdx-tests-android/assets/data/testAtlas.png new file mode 100644 index 0000000000000000000000000000000000000000..756483ac6bb7d515d4280fc49713cef9b43eb418 GIT binary patch literal 1751 zcmdT_e>l@?6o0>)jb>YO8(EE}o4P2!%CD3bDuhV#+m*^>7t<0&3|W3&JzbNUE$OyM zNk8aXja5WxO)TxM#t^xMT*B0H@6yxr-231C_x|&~=lPuXobx&7`JCs?_3_?@K`lW6 z05I-ut~&vM)FvQnA=T$pSXCncNUpo<=3U9*qnyprVO#J_O%1!ai#|_QYWx~wYa1yn zS+d+q?@>i&#^LkGi;=#yc@^61eIa-oX`vRRb7d<&zt}u)3l6_H)8|&Si5nt}k4xBN zd^Ef_Hh%6*%9|$wx;W+9=S@ly=dPWF*JL^iU+~h@sxN$0VqINbJ(gaNqEIM~b*kVA z*sm4d-NaanpKQZNr)e0IiWUJ2BXK&Z*%Klk`MI1U+PEd?H_P z+M@a1P`xVn#JRMC5s-4WGdlwjL8o`+9uI|U=3NjO_XzCi&3)vATMf;378`Pt?0tx_ z=6{ZLQ8ul`FjPI-r%Ou8+}dm+T||3^pUMY{;P##3&x(zC9(Eq=!qiv!CuPHYY^1#1a+_g* zyMZA_{q&njx7=5k!SCY*CT;TvZ<*{!xd9pwOlhSusI_3@ZF6w_pZ95q$eSLJ*hjP% zRR`fXkL#bULr{*t2;hSJKlDt|-zv_Ym8(c!NvP5ehX<_}S z5SO|i)MPzq%7E3ff{-LTd(T4W;ZZyrDP%E!Oml4EK{)65OKsrRDX0yDUCS$X$p3u> zFt=2y`K*L+>nK(P#%^oSaBokyiQ>%AF(W^?B8C}0(|EE9!hP+8Y(`+bQdm~w$I;I> z@GOmiw-rr2BtIBQLAZRx(+J+-@ul#SlQ6D-)v+tKzkQanD~hVJ^|9h0G`Rg~X9D+zSgBe%SR>4rhT@vYy{05n0YcljCTm zRW3Ew)2{+SS!A34L+(;-27?m3SG*qtKLW3jC#no;AMzdF!W7Hl-D-`50gVmY>&CF( zg0J`Sf)fM$L8yr4ca>jl3SeBDy0_-v+%RsP%^}JUpeC|57A+^B)VX6Zat9Pt%4XB4 zRBEBzo^}e-*Hw$$xJUHY8fD_^ft$29PdRZ>QM}HUmMypM-W|d4bC-=eia-Hu(884% z8`nOrnH73bm5?|pkrWru+wafl*%BDO<*JsPEKw|BF1mKk0_HO6F&kodLdJUUG~ood zL=`vbC(N(whjbnNn1ILW{R2LC)TMR01Db|sM>^f@(r5UoJIWUg#vLi_pTZg>KP@aO znkg$QJJi+HHL)?nyZFwaP2LMf$3im%7YrHstJ3y^gIWKnbt@gE zZ;#$I+!FT*($Dz}(9LPUb3~I={TWoQ@Sv6Ijqyjb!@;z-Me@LiOt8?YLtDt1>Fq1L zNu3doj3E^FpY0GN93Fk&+BkwxG{1e<6G%@{&AvDaDffN6V%N`0t5Y7c0O{eX_N6Iq e&RRw~bMPg3{K|cl)x%%@E%z Date: Sun, 15 Oct 2023 09:52:04 +0200 Subject: [PATCH 08/15] Use STBVorbis to decode ogg data for Sound (#7140) --- .../gdx/backends/lwjgl3/audio/Ogg.java | 49 ++++++++++++------- .../backends/lwjgl3/audio/OpenALSound.java | 28 ++++++----- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/Ogg.java b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/Ogg.java index aa058c60684..432182dd5bd 100644 --- a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/Ogg.java +++ b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/Ogg.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2011 See AUTHORS file. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,10 +16,16 @@ package com.badlogic.gdx.backends.lwjgl3.audio; -import java.io.ByteArrayOutputStream; - import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.BufferUtils; +import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.StreamUtils; +import org.lwjgl.stb.STBVorbis; +import org.lwjgl.system.MemoryStack; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; /** @author Nathan Sweet */ public class Ogg { @@ -61,19 +67,28 @@ static public class Sound extends OpenALSound { public Sound (OpenALLwjgl3Audio audio, FileHandle file) { super(audio); if (audio.noDevice) return; - OggInputStream input = null; - try { - input = new OggInputStream(file.read()); - ByteArrayOutputStream output = new ByteArrayOutputStream(4096); - byte[] buffer = new byte[2048]; - while (!input.atEnd()) { - int length = input.read(buffer); - if (length == -1) break; - output.write(buffer, 0, length); + + // put the encoded audio data in a ByteBuffer + byte[] streamData = file.readBytes(); + ByteBuffer encodedData = BufferUtils.newByteBuffer(streamData.length); + encodedData.put(streamData); + encodedData.flip(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + final IntBuffer channelsBuffer = stack.mallocInt(1); + final IntBuffer sampleRateBuffer = stack.mallocInt(1); + + // decode + final ShortBuffer decodedData = STBVorbis.stb_vorbis_decode_memory(encodedData, channelsBuffer, sampleRateBuffer); + int channels = channelsBuffer.get(0); + int sampleRate = sampleRateBuffer.get(0); + if (decodedData == null) { + throw new GdxRuntimeException("Error decoding OGG file: " + file); + } else if (channels < 1 || channels > 2) { + throw new GdxRuntimeException("Error decoding OGG file, unsupported number of channels: " + file); } - setup(output.toByteArray(), input.getChannels(), input.getSampleRate()); - } finally { - StreamUtils.closeQuietly(input); + + setup(decodedData, channels, sampleRate); } } } diff --git a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/OpenALSound.java b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/OpenALSound.java index 25796356622..8984b81d016 100644 --- a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/OpenALSound.java +++ b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/audio/OpenALSound.java @@ -16,11 +16,12 @@ package com.badlogic.gdx.backends.lwjgl3.audio; +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.utils.BufferUtils; + import java.nio.Buffer; import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import com.badlogic.gdx.audio.Sound; +import java.nio.ShortBuffer; import static org.lwjgl.openal.AL10.*; @@ -36,20 +37,23 @@ public OpenALSound (OpenALLwjgl3Audio audio) { } void setup (byte[] pcm, int channels, int sampleRate) { + int validBytes = pcm.length - (pcm.length % (channels > 1 ? 4 : 2)); + ByteBuffer buffer = BufferUtils.newByteBuffer(validBytes); + buffer.put(pcm, 0, validBytes); + ((Buffer)buffer).flip(); + + setup(buffer.asShortBuffer(), channels, sampleRate); + } + + void setup (ShortBuffer pcm, int channels, int sampleRate) { this.channels = channels; this.sampleRate = sampleRate; - int bytes = pcm.length - (pcm.length % (channels > 1 ? 4 : 2)); - int samples = bytes / (2 * channels); - duration = samples / (float)sampleRate; - - ByteBuffer buffer = ByteBuffer.allocateDirect(bytes); - buffer.order(ByteOrder.nativeOrder()); - buffer.put(pcm, 0, bytes); - ((Buffer)buffer).flip(); + int sampleFrames = pcm.limit() / channels; + duration = sampleFrames / (float)sampleRate; if (bufferID == -1) { bufferID = alGenBuffers(); - alBufferData(bufferID, channels > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, buffer.asShortBuffer(), sampleRate); + alBufferData(bufferID, channels > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, pcm, sampleRate); } } From 2dccb47f9a53f5eabf66ace3cded1f7cea4dfcd9 Mon Sep 17 00:00:00 2001 From: obigu <1794492+obigu@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:54:49 +0200 Subject: [PATCH 09/15] Upgrade some default Android backend versions (#7244) --- .../src/com/badlogic/gdx/setup/DependencyBank.java | 4 ++-- gradle/dependencies.gradle | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java b/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java index 36e0c614e1b..0bbbfd74c79 100644 --- a/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java +++ b/extensions/gdx-setup/src/com/badlogic/gdx/setup/DependencyBank.java @@ -26,7 +26,7 @@ public class DependencyBank { static String libgdxNightlyVersion = "1.12.1-SNAPSHOT"; static String roboVMVersion = "2.3.20"; static String buildToolsVersion = "33.0.2"; - static String androidAPILevel = "32"; + static String androidAPILevel = "33"; static String androidMinAPILevel = "14"; static String gwtVersion = "2.8.2"; @@ -42,7 +42,7 @@ public class DependencyBank { // Project plugins static String gwtPluginImport = "org.wisepersist:gwt-gradle-plugin:1.1.16"; static String grettyPluginImport = "org.gretty:gretty:3.0.7"; - static String androidPluginImport = "com.android.tools.build:gradle:7.2.2"; + static String androidPluginImport = "com.android.tools.build:gradle:7.3.1"; static String roboVMPluginImport = "com.mobidevelop.robovm:robovm-gradle-plugin:" + roboVMVersion; // Extension versions diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 68f12c08fd9..9767e11edf5 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -33,12 +33,12 @@ versions.lwjgl3 = "3.3.3" versions.jlayer = "1.0.1-gdx" versions.jorbis = "0.0.17" versions.junit = "4.13.2" -versions.androidPlugin = "7.2.2" +versions.androidPlugin = "7.3.1" versions.multiDex = "2.0.1" -versions.androidCompileSdk = 32 -versions.androidTargetSdk = 32 +versions.androidCompileSdk = 33 +versions.androidTargetSdk = 33 versions.androidMinSdk = 14 -versions.androidBuildTools = "33.0.0" +versions.androidBuildTools = "33.0.2" versions.androidFragment = "1.5.7" versions.javaparser = "2.3.0" versions.spotless = "6.7.1" From 5aad36eb97af778159447bda90616eeb0731b0b5 Mon Sep 17 00:00:00 2001 From: Berstanio Date: Mon, 16 Oct 2023 17:10:07 +0200 Subject: [PATCH 10/15] Fix ANGLE GLES renderer on dekstop (#7251) * fix(lwjgl3): Reset missingAddresses on ANGLE after GLES class init https://github.com/LWJGL/lwjgl3/issues/931 --- .../com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java index a9bbab2f58d..de905913f12 100644 --- a/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java +++ b/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java @@ -56,6 +56,7 @@ import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.SharedLibraryLoader; import org.lwjgl.system.Configuration; +import org.lwjgl.system.ThreadLocalUtil; public class Lwjgl3Application implements Lwjgl3ApplicationBase { private final Lwjgl3ApplicationConfiguration config; @@ -562,6 +563,8 @@ static long createGlfwWindow (Lwjgl3ApplicationConfiguration config, long shared try { Class gles = Class.forName("org.lwjgl.opengles.GLES"); gles.getMethod("createCapabilities").invoke(gles); + // TODO: Remove once https://github.com/LWJGL/lwjgl3/issues/931 is fixed + ThreadLocalUtil.setFunctionMissingAddresses(0); } catch (Throwable e) { throw new GdxRuntimeException("Couldn't initialize GLES", e); } From 7bcfbd42f1f7e8c26ff06144ac79a7ff38ffaf5b Mon Sep 17 00:00:00 2001 From: obigu <1794492+obigu@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:57:43 +0200 Subject: [PATCH 11/15] Fixes #7256 (#7259) --- CHANGES | 1 + .../badlogic/gdx/backends/android/AndroidTouchHandler.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index e1e0c977fd6..7701ba602e2 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ - API Addition: TiledDrawable: Align can be set to manipulate the alignment of the rendering (TiledDrawable#setAlign, TiledDrawable#getAlign) - API Addition: TiledDrawable#draw: Also available as a static function (with align) if you don't want to create an extra instance per texture region - Android: Removed mouse catching added on 1.12.0 due to unintended effects (see #7187). +- Android: Fixed touch state inconsistency when touching screen with 3 fingers on some devices (see #7256) - iOS: Update to MobiVM 2.3.20 - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. - Update to LWJGL 3.3.3 diff --git a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidTouchHandler.java b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidTouchHandler.java index 0e97c3df08f..710adaba7b6 100644 --- a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidTouchHandler.java +++ b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidTouchHandler.java @@ -23,6 +23,8 @@ import com.badlogic.gdx.Input.Buttons; import com.badlogic.gdx.backends.android.DefaultAndroidInput.TouchEvent; +import java.util.Arrays; + /** Multitouch handler for devices running Android >= 2.0. If device is capable of (fake) multitouch this will report additional * pointers. * @@ -82,6 +84,10 @@ public void onTouch (MotionEvent event, DefaultAndroidInput input) { input.touched[realPointerIndex] = false; input.button[realPointerIndex] = 0; input.pressure[realPointerIndex] = 0; + if (action == MotionEvent.ACTION_CANCEL) { + Arrays.fill(input.realId, -1); + Arrays.fill(input.touched, false); + } break; case MotionEvent.ACTION_MOVE: From 413eddccd2765607da15a1a928689863702cf02a Mon Sep 17 00:00:00 2001 From: Tommy Ettinger Date: Sun, 29 Oct 2023 01:08:04 -0700 Subject: [PATCH 12/15] Add Vector4 class and use it where fitting (#7260) * Add Vector4 by Antz, test. I've completed the Vector implementation with setToRandomDirection() and isOnLine(), plus I fixed a few minor issues, but the rest of the work was by Antz. * Fixed some typos in Vector-related docs. This also changes several methods to use Java-style array declaration, when internally they had been using C-style array declaration. Using the C-style arrays is a style warning in IDEA and probably could be in Spotless too. I wanted to clear up that idt() means "identical to" here, and not "identity" as it does in Quaternion, so I added one line of extra doc to each idt() in Vector3 and Vector4. I also mentioned that it compares with exact precision, not like MathUtils.isEqual() . * Vector2.idt(Vector2) was missing. * Apply formatter * Allow passing a Vector4 to a ShaderProgram. This is very similar to how Vector3 already works, and nearly identical to how Color can be used for the same thing. A Vector4 doesn't clamp its components like Color often does, so it might be useful for different things. * The .gwt.xml file needed updating. * Update CHANGES. --------- Co-authored-by: GitHub Action --- CHANGES | 1 + gdx/res/com/badlogic/gdx.gwt.xml | 1 + .../gdx/graphics/glutils/ShaderProgram.java | 13 + gdx/src/com/badlogic/gdx/math/Vector.java | 4 +- gdx/src/com/badlogic/gdx/math/Vector2.java | 11 +- gdx/src/com/badlogic/gdx/math/Vector3.java | 27 +- gdx/src/com/badlogic/gdx/math/Vector4.java | 679 ++++++++++++++++++ .../com/badlogic/gdx/math/Vector4Test.java | 18 + 8 files changed, 737 insertions(+), 17 deletions(-) create mode 100644 gdx/src/com/badlogic/gdx/math/Vector4.java create mode 100644 gdx/test/com/badlogic/gdx/math/Vector4Test.java diff --git a/CHANGES b/CHANGES index 7701ba602e2..8537dd7e1d4 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ - iOS: Update to MobiVM 2.3.20 - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. - Update to LWJGL 3.3.3 +- API Addition: Vector4 is just like Vector2 or Vector3, but with x, y, z, and w float components; a Vector4 can be passed as a shader uniform. [1.12.0] - [BREAKING CHANGE] Added #touchCancelled to InputProcessor interface, see #6871. diff --git a/gdx/res/com/badlogic/gdx.gwt.xml b/gdx/res/com/badlogic/gdx.gwt.xml index 08b0e721187..abfd619c19f 100644 --- a/gdx/res/com/badlogic/gdx.gwt.xml +++ b/gdx/res/com/badlogic/gdx.gwt.xml @@ -371,6 +371,7 @@ + diff --git a/gdx/src/com/badlogic/gdx/graphics/glutils/ShaderProgram.java b/gdx/src/com/badlogic/gdx/graphics/glutils/ShaderProgram.java index 809cb4e1b96..7efa0b1cd49 100644 --- a/gdx/src/com/badlogic/gdx/graphics/glutils/ShaderProgram.java +++ b/gdx/src/com/badlogic/gdx/graphics/glutils/ShaderProgram.java @@ -32,6 +32,7 @@ import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.math.Vector4; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.BufferUtils; import com.badlogic.gdx.utils.Disposable; @@ -622,6 +623,18 @@ public void setUniformf (int location, Vector3 values) { setUniformf(location, values.x, values.y, values.z); } + /** Sets the uniform with the given name. The {@link ShaderProgram} must be bound for this to work. + * + * @param name the name of the uniform + * @param values x, y, z, and w as the first, second, third, and fourth values respectively */ + public void setUniformf (String name, Vector4 values) { + setUniformf(name, values.x, values.y, values.z, values.w); + } + + public void setUniformf (int location, Vector4 values) { + setUniformf(location, values.x, values.y, values.z, values.w); + } + /** Sets the uniform with the given name. The {@link ShaderProgram} must be bound for this to work. * * @param name the name of the uniform diff --git a/gdx/src/com/badlogic/gdx/math/Vector.java b/gdx/src/com/badlogic/gdx/math/Vector.java index 9a6bade51ba..c1a2ffd8ece 100644 --- a/gdx/src/com/badlogic/gdx/math/Vector.java +++ b/gdx/src/com/badlogic/gdx/math/Vector.java @@ -23,12 +23,12 @@ public interface Vector> { /** @return a copy of this vector */ T cpy (); - /** @return The euclidean length */ + /** @return The Euclidean length */ float len (); /** This method is faster than {@link Vector#len()} because it avoids calculating a square root. It is useful for comparisons, * but not for getting exact lengths, as the return value is the square of the actual length. - * @return The squared euclidean length */ + * @return The squared Euclidean length */ float len2 (); /** Limits the length of this vector, based on the desired maximum length. diff --git a/gdx/src/com/badlogic/gdx/math/Vector2.java b/gdx/src/com/badlogic/gdx/math/Vector2.java index 5538ed1eb40..b58476bc5d3 100644 --- a/gdx/src/com/badlogic/gdx/math/Vector2.java +++ b/gdx/src/com/badlogic/gdx/math/Vector2.java @@ -100,7 +100,7 @@ public Vector2 sub (Vector2 v) { return this; } - /** Substracts the other vector from this vector. + /** Subtracts the other vector from this vector. * @param x The x-component of the other vector * @param y The y-component of the other vector * @return This vector for chaining */ @@ -186,6 +186,13 @@ public Vector2 mulAdd (Vector2 vec, Vector2 mulVec) { return this; } + /** Returns true if this vector and the vector parameter have identical components. + * @param vector The other vector + * @return Whether this and the other vector are equal with exact precision */ + public boolean idt (final Vector2 vector) { + return x == vector.x && y == vector.y; + } + public static float dst (float x1, float y1, float x2, float y2) { final float x_d = x2 - x1; final float y_d = y2 - y1; @@ -328,7 +335,7 @@ public float angle () { /** @return the angle in degrees of this vector (point) relative to the given vector. Angles are towards the negative y-axis * (typically clockwise) between -180 and +180 - * @deprecated use {@link #angleDeg(Vector2)} instead. Be ware of the changes in returned angle to counter-clockwise and the + * @deprecated use {@link #angleDeg(Vector2)} instead. Beware of the changes in returned angle to counter-clockwise and the * range. */ @Deprecated public float angle (Vector2 reference) { diff --git a/gdx/src/com/badlogic/gdx/math/Vector3.java b/gdx/src/com/badlogic/gdx/math/Vector3.java index c2c13684d29..633ed9f8376 100644 --- a/gdx/src/com/badlogic/gdx/math/Vector3.java +++ b/gdx/src/com/badlogic/gdx/math/Vector3.java @@ -218,7 +218,7 @@ public Vector3 mulAdd (Vector3 vec, Vector3 mulVec) { return this; } - /** @return The euclidean length */ + /** @return The Euclidean length */ public static float len (final float x, final float y, final float z) { return (float)Math.sqrt(x * x + y * y + z * z); } @@ -228,7 +228,7 @@ public float len () { return (float)Math.sqrt(x * x + y * y + z * z); } - /** @return The squared euclidean length */ + /** @return The squared Euclidean length */ public static float len2 (final float x, final float y, final float z) { return x * x + y * y + z * z; } @@ -238,13 +238,14 @@ public float len2 () { return x * x + y * y + z * z; } - /** @param vector The other vector - * @return Whether this and the other vector are equal */ + /** Returns true if this vector and the vector parameter have identical components. + * @param vector The other vector + * @return Whether this and the other vector are equal with exact precision */ public boolean idt (final Vector3 vector) { return x == vector.x && y == vector.y && z == vector.z; } - /** @return The euclidean distance between the two specified vectors */ + /** @return The Euclidean distance between the two specified vectors */ public static float dst (final float x1, final float y1, final float z1, final float x2, final float y2, final float z2) { final float a = x2 - x1; final float b = y2 - y1; @@ -351,7 +352,7 @@ public Vector3 mul4x3 (float[] matrix) { * @param matrix The matrix * @return This vector for chaining */ public Vector3 mul (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return this.set(x * l_mat[Matrix4.M00] + y * l_mat[Matrix4.M01] + z * l_mat[Matrix4.M02] + l_mat[Matrix4.M03], x * l_mat[Matrix4.M10] + y * l_mat[Matrix4.M11] + z * l_mat[Matrix4.M12] + l_mat[Matrix4.M13], x * l_mat[Matrix4.M20] + y * l_mat[Matrix4.M21] + z * l_mat[Matrix4.M22] + l_mat[Matrix4.M23]); @@ -361,7 +362,7 @@ public Vector3 mul (final Matrix4 matrix) { * @param matrix The matrix * @return This vector for chaining */ public Vector3 traMul (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return this.set(x * l_mat[Matrix4.M00] + y * l_mat[Matrix4.M10] + z * l_mat[Matrix4.M20] + l_mat[Matrix4.M30], x * l_mat[Matrix4.M01] + y * l_mat[Matrix4.M11] + z * l_mat[Matrix4.M21] + l_mat[Matrix4.M31], x * l_mat[Matrix4.M02] + y * l_mat[Matrix4.M12] + z * l_mat[Matrix4.M22] + l_mat[Matrix4.M32]); @@ -371,7 +372,7 @@ public Vector3 traMul (final Matrix4 matrix) { * @param matrix The matrix * @return This vector for chaining */ public Vector3 mul (Matrix3 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return set(x * l_mat[Matrix3.M00] + y * l_mat[Matrix3.M01] + z * l_mat[Matrix3.M02], x * l_mat[Matrix3.M10] + y * l_mat[Matrix3.M11] + z * l_mat[Matrix3.M12], x * l_mat[Matrix3.M20] + y * l_mat[Matrix3.M21] + z * l_mat[Matrix3.M22]); @@ -381,7 +382,7 @@ public Vector3 mul (Matrix3 matrix) { * @param matrix The matrix * @return This vector for chaining */ public Vector3 traMul (Matrix3 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return set(x * l_mat[Matrix3.M00] + y * l_mat[Matrix3.M10] + z * l_mat[Matrix3.M20], x * l_mat[Matrix3.M01] + y * l_mat[Matrix3.M11] + z * l_mat[Matrix3.M21], x * l_mat[Matrix3.M02] + y * l_mat[Matrix3.M12] + z * l_mat[Matrix3.M22]); @@ -399,7 +400,7 @@ public Vector3 mul (final Quaternion quat) { * @param matrix The matrix. * @return This vector for chaining */ public Vector3 prj (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; final float l_w = 1f / (x * l_mat[Matrix4.M30] + y * l_mat[Matrix4.M31] + z * l_mat[Matrix4.M32] + l_mat[Matrix4.M33]); return this.set((x * l_mat[Matrix4.M00] + y * l_mat[Matrix4.M01] + z * l_mat[Matrix4.M02] + l_mat[Matrix4.M03]) * l_w, (x * l_mat[Matrix4.M10] + y * l_mat[Matrix4.M11] + z * l_mat[Matrix4.M12] + l_mat[Matrix4.M13]) * l_w, @@ -411,7 +412,7 @@ public Vector3 prj (final Matrix4 matrix) { * @param matrix The matrix * @return This vector for chaining */ public Vector3 rot (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return this.set(x * l_mat[Matrix4.M00] + y * l_mat[Matrix4.M01] + z * l_mat[Matrix4.M02], x * l_mat[Matrix4.M10] + y * l_mat[Matrix4.M11] + z * l_mat[Matrix4.M12], x * l_mat[Matrix4.M20] + y * l_mat[Matrix4.M21] + z * l_mat[Matrix4.M22]); @@ -422,7 +423,7 @@ public Vector3 rot (final Matrix4 matrix) { * @param matrix The transformation matrix * @return The vector for chaining */ public Vector3 unrotate (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; return this.set(x * l_mat[Matrix4.M00] + y * l_mat[Matrix4.M10] + z * l_mat[Matrix4.M20], x * l_mat[Matrix4.M01] + y * l_mat[Matrix4.M11] + z * l_mat[Matrix4.M21], x * l_mat[Matrix4.M02] + y * l_mat[Matrix4.M12] + z * l_mat[Matrix4.M22]); @@ -434,7 +435,7 @@ public Vector3 unrotate (final Matrix4 matrix) { * @param matrix The transformation matrix * @return The vector for chaining */ public Vector3 untransform (final Matrix4 matrix) { - final float l_mat[] = matrix.val; + final float[] l_mat = matrix.val; x -= l_mat[Matrix4.M03]; y -= l_mat[Matrix4.M03]; z -= l_mat[Matrix4.M03]; diff --git a/gdx/src/com/badlogic/gdx/math/Vector4.java b/gdx/src/com/badlogic/gdx/math/Vector4.java new file mode 100644 index 00000000000..bd385daa600 --- /dev/null +++ b/gdx/src/com/badlogic/gdx/math/Vector4.java @@ -0,0 +1,679 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.badlogic.gdx.math; + +import com.badlogic.gdx.utils.GdxRuntimeException; +import com.badlogic.gdx.utils.NumberUtils; + +import java.io.Serializable; + +/** Encapsulates a 4D vector. Allows chaining operations by returning a reference to itself in all modification methods. + * @author badlogicgames@gmail.com + * @author Antz */ +public class Vector4 implements Serializable, Vector { + + private static final long serialVersionUID = -5394070284130414492L; + + /** the x-component of this vector **/ + public float x; + /** the y-component of this vector **/ + public float y; + /** the z-component of this vector **/ + public float z; + /** the w-component of this vector **/ + public float w; + + public final static Vector4 X = new Vector4(1, 0, 0, 0); + public final static Vector4 Y = new Vector4(0, 1, 0, 0); + public final static Vector4 Z = new Vector4(0, 0, 1, 0); + public final static Vector4 W = new Vector4(0, 0, 0, 1); + public final static Vector4 Zero = new Vector4(0, 0, 0, 0); + + /** Constructs a vector at (0,0,0,0) */ + public Vector4 () { + } + + /** Creates a vector with the given components + * @param x The x-component + * @param y The y-component + * @param z The z-component + * @param w The w-component * */ + public Vector4 (float x, float y, float z, float w) { + this.set(x, y, z, w); + } + + /** Creates a vector from the given Vector4 + * @param vector The vector */ + public Vector4 (final Vector4 vector) { + this.set(vector.x, vector.y, vector.z, vector.w); + } + + /** Creates a vector from the given array. The array must have at least 4 elements. + * + * @param values The array */ + public Vector4 (final float[] values) { + this.set(values[0], values[1], values[2], values[3]); + } + + /** Creates a vector from the given Vector2 and z- and w-components + * + * @param vector The vector + * @param z The z-component + * @param w The w-component */ + public Vector4 (final Vector2 vector, float z, float w) { + this.set(vector.x, vector.y, z, w); + } + + /** Creates a vector from the given Vector3 and w-component + * + * @param vector The vector + * @param w The w-component */ + public Vector4 (final Vector3 vector, float w) { + this.set(vector.x, vector.y, vector.z, w); + } + + /** Sets the vector to the given components + * + * @param x The x-component + * @param y The y-component + * @param z The z-component + * @param w The w-component + * @return this vector for chaining */ + public Vector4 set (float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public Vector4 set (final Vector4 vector) { + return this.set(vector.x, vector.y, vector.z, vector.w); + } + + /** Sets the components from the array. The array must have at least 4 elements + * + * @param values The array + * @return this vector for chaining */ + public Vector4 set (final float[] values) { + return this.set(values[0], values[1], values[2], values[3]); + } + + /** Sets the components to the given Vector2, z-component and w-component + * + * @param vector The vector2 holding the x- and y-components + * @param z The z-component + * @param w The w-component + * @return This vector for chaining */ + public Vector4 set (final Vector2 vector, float z, float w) { + return this.set(vector.x, vector.y, z, w); + } + + /** Sets the components of the given vector3 and w-component + * + * @param vector The vector + * @param w The w-component + * @return This vector for chaining */ + public Vector4 set (final Vector3 vector, float w) { + return this.set(vector.x, vector.y, vector.z, w); + } + + @Override + public Vector4 setToRandomDirection () { + // The algorithm here is #19 at + // https://extremelearning.com.au/how-to-generate-uniformly-random-points-on-n-spheres-and-n-balls/ . + // It is the only recommended way to randomly generate a point on the surface of the unit 4D hypersphere. + + // From the documentation of Random.nextGaussian(), but using float math. + float v1, v2, s, multiplier; + do { + v1 = (MathUtils.random() - 0.5f) * 2; // between -1.0 and 1.0 + v2 = (MathUtils.random() - 0.5f) * 2; // between -1.0 and 1.0 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + multiplier = (float)Math.sqrt(-2 * Math.log(s) / s); + x = v1 * multiplier; + y = v2 * multiplier; + // Each run of the Marsaglia polar method produces two normal-distributed variates. + do { + v1 = (MathUtils.random() - 0.5f) * 2; // between -1.0 and 1.0 + v2 = (MathUtils.random() - 0.5f) * 2; // between -1.0 and 1.0 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + multiplier = (float)Math.sqrt(-2 * Math.log(s) / s); + z = v1 * multiplier; + w = v2 * multiplier; + // Once we normalize four normal-distributed floats, we have a point on the unit hypersphere's surface. + return this.nor(); + } + + @Override + public Vector4 cpy () { + return new Vector4(this); + } + + @Override + public Vector4 add (final Vector4 vector) { + return this.add(vector.x, vector.y, vector.z, vector.w); + } + + /** Adds the given components to this vector + * @param x Added to the x-component + * @param y Added to the y-component + * @param z Added to the z-component + * @param w Added to the w-component + * @return This vector for chaining. */ + public Vector4 add (float x, float y, float z, float w) { + return this.set(this.x + x, this.y + y, this.z + z, this.w + w); + } + + /** Adds the given value to all four components of the vector. + * + * @param values The value + * @return This vector for chaining */ + public Vector4 add (float values) { + return this.set(this.x + values, this.y + values, this.z + values, this.w + values); + } + + @Override + public Vector4 sub (final Vector4 a_vec) { + return this.sub(a_vec.x, a_vec.y, a_vec.z, a_vec.w); + } + + /** Subtracts the given components from this vector. + * + * @param x Subtracted from the x-component + * @param y Subtracted from the y-component + * @param z Subtracted from the z-component + * @param w Subtracted from the w-component + * @return This vector for chaining */ + public Vector4 sub (float x, float y, float z, float w) { + return this.set(this.x - x, this.y - y, this.z - z, this.w - w); + } + + /** Subtracts the given value from all components of this vector + * + * @param value The value + * @return This vector for chaining */ + public Vector4 sub (float value) { + return this.set(this.x - value, this.y - value, this.z - value, this.w - value); + } + + /** Multiplies each component of this vector by the given scalar + * @param scalar Each component will be multiplied by this float + * @return This vector for chaining */ + @Override + public Vector4 scl (float scalar) { + return this.set(this.x * scalar, this.y * scalar, this.z * scalar, this.w * scalar); + } + + /** Multiplies each component of this vector by the corresponding component in other + * @param other Another Vector4 that will be used to scale this + * @return This vector for chaining */ + @Override + public Vector4 scl (final Vector4 other) { + return this.set(x * other.x, y * other.y, z * other.z, w * other.w); + } + + /** Scales this vector by the given values + * @param vx Multiplied with the X value + * @param vy Multiplied with the Y value + * @param vz Multiplied with the Z value + * @param vw Multiplied with the W value + * @return This vector for chaining */ + public Vector4 scl (float vx, float vy, float vz, float vw) { + return this.set(this.x * vx, this.y * vy, this.z * vz, this.w * vw); + } + + @Override + public Vector4 mulAdd (Vector4 vec, float scalar) { + this.x += vec.x * scalar; + this.y += vec.y * scalar; + this.z += vec.z * scalar; + this.w += vec.w * scalar; + return this; + } + + @Override + public Vector4 mulAdd (Vector4 vec, Vector4 mulVec) { + this.x += vec.x * mulVec.x; + this.y += vec.y * mulVec.y; + this.z += vec.z * mulVec.z; + this.w += vec.w * mulVec.w; + return this; + } + + /** @return The Euclidean length */ + public static float len (final float x, final float y, final float z, float w) { + return (float)Math.sqrt(x * x + y * y + z * z + w * w); + } + + @Override + public float len () { + return (float)Math.sqrt(x * x + y * y + z * z + w * w); + } + + /** @return The squared Euclidean length */ + public static float len2 (final float x, final float y, final float z, float w) { + return x * x + y * y + z * z + w * w; + } + + @Override + public float len2 () { + return x * x + y * y + z * z + w * w; + } + + /** Returns true if this vector and the vector parameter have identical components. + * @param vector The other vector + * @return Whether this and the other vector are equal with exact precision */ + public boolean idt (final Vector4 vector) { + return x == vector.x && y == vector.y && z == vector.z && w == vector.w; + } + + /** @return The Euclidean distance between the two specified vectors */ + public static float dst (final float x1, final float y1, final float z1, final float w1, final float x2, final float y2, + final float z2, final float w2) { + final float a = x2 - x1; + final float b = y2 - y1; + final float c = z2 - z1; + final float d = w2 - w1; + return (float)Math.sqrt(a * a + b * b + c * c + d * d); + } + + @Override + public float dst (final Vector4 vector) { + final float a = vector.x - x; + final float b = vector.y - y; + final float c = vector.z - z; + final float d = vector.w - w; + return (float)Math.sqrt(a * a + b * b + c * c + d * d); + } + + /** @return the distance between this point and the given point */ + public float dst (float x, float y, float z, float w) { + final float a = x - this.x; + final float b = y - this.y; + final float c = z - this.z; + final float d = w - this.w; + return (float)Math.sqrt(a * a + b * b + c * c + d * d); + } + + /** @return the squared distance between the given points */ + public static float dst2 (final float x1, final float y1, final float z1, final float w1, final float x2, final float y2, + final float z2, final float w2) { + final float a = x2 - x1; + final float b = y2 - y1; + final float c = z2 - z1; + final float d = w2 - w1; + return a * a + b * b + c * c + d * d; + } + + @Override + public float dst2 (Vector4 point) { + final float a = point.x - x; + final float b = point.y - y; + final float c = point.z - z; + final float d = point.w - w; + return a * a + b * b + c * c + d * d; + } + + /** Returns the squared distance between this point and the given point + * @param x The x-component of the other point + * @param y The y-component of the other point + * @param z The z-component of the other point + * @param w The w-component of the other point + * @return The squared distance */ + public float dst2 (float x, float y, float z, float w) { + final float a = x - this.x; + final float b = y - this.y; + final float c = z - this.z; + final float d = w - this.w; + return a * a + b * b + c * c + d * d; + } + + @Override + public Vector4 nor () { + final float len2 = this.len2(); + if (len2 == 0f || len2 == 1f) return this; + return this.scl(1f / (float)Math.sqrt(len2)); + } + + /** @return The dot product between the two vectors */ + public static float dot (float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2) { + return x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2; + } + + @Override + public float dot (final Vector4 vector) { + return x * vector.x + y * vector.y + z * vector.z + w * vector.w; + } + + /** Returns the dot product between this and the given vector (given as 4 components). + * @param x The x-component of the other vector + * @param y The y-component of the other vector + * @param z The z-component of the other vector + * @param w The w-component of the other vector + * @return The dot product */ + public float dot (float x, float y, float z, float w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + @Override + public boolean isUnit () { + return isUnit(0.000000001f); + } + + @Override + public boolean isUnit (final float margin) { + return Math.abs(len2() - 1f) < margin; + } + + @Override + public boolean isZero () { + return x == 0 && y == 0 && z == 0 && w == 0; + } + + @Override + public boolean isZero (final float margin) { + return len2() < margin; + } + + /** @return true if this vector is in line with the other vector (either in the same or the opposite direction) */ + @Override + public boolean isOnLine (Vector4 other, float epsilon) { + // The algorithm used here is based on the one in yama, a C++ math library. + // https://github.com/iboB/yama/blob/f08a71c6fd84df5eed62557000373f17f14e1ec7/include/yama/vector4.hpp#L566-L598 + // This code uses a flags variable to avoid allocating a float array. + int flags = 0; + float dx = 0, dy = 0, dz = 0, dw = 0; + + if (MathUtils.isZero(x, epsilon)) { + if (!MathUtils.isZero(other.x, epsilon)) { + return false; + } + } else { + dx = x / other.x; + flags |= 1; + } + if (MathUtils.isZero(y, epsilon)) { + if (!MathUtils.isZero(other.y, epsilon)) { + return false; + } + } else { + dy = y / other.y; + flags |= 2; + } + if (MathUtils.isZero(z, epsilon)) { + if (!MathUtils.isZero(other.z, epsilon)) { + return false; + } + } else { + dz = z / other.z; + flags |= 4; + } + if (MathUtils.isZero(w, epsilon)) { + if (!MathUtils.isZero(other.w, epsilon)) { + return false; + } + } else { + dw = w / other.w; + flags |= 8; + } + + switch (flags) { + case 0: + case 1: + case 2: + case 4: + case 8: + return true; + case 3: + return MathUtils.isEqual(dx, dy, epsilon); + case 5: + return MathUtils.isEqual(dx, dz, epsilon); + case 9: + return MathUtils.isEqual(dx, dw, epsilon); + case 6: + return MathUtils.isEqual(dy, dz, epsilon); + case 10: + return MathUtils.isEqual(dy, dw, epsilon); + case 12: + return MathUtils.isEqual(dz, dw, epsilon); + case 7: + return MathUtils.isEqual(dx, dy, epsilon) && MathUtils.isEqual(dx, dz, epsilon); + case 11: + return MathUtils.isEqual(dx, dy, epsilon) && MathUtils.isEqual(dx, dw, epsilon); + case 13: + return MathUtils.isEqual(dx, dz, epsilon) && MathUtils.isEqual(dx, dw, epsilon); + case 14: + return MathUtils.isEqual(dy, dz, epsilon) && MathUtils.isEqual(dy, dw, epsilon); + default: // this is essentially case 15: + return MathUtils.isEqual(dx, dy, epsilon) && MathUtils.isEqual(dx, dz, epsilon) && MathUtils.isEqual(dx, dw, epsilon); + } + } + + /** @return true if this vector is in line with the other vector (either in the same or the opposite direction) */ + @Override + public boolean isOnLine (Vector4 other) { + return isOnLine(other, MathUtils.FLOAT_ROUNDING_ERROR); + } + + /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector4, float)} && + * {@link #hasSameDirection(Vector4)}). */ + @Override + public boolean isCollinear (Vector4 other, float epsilon) { + return isOnLine(other, epsilon) && hasSameDirection(other); + } + + /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector4)} && + * {@link #hasSameDirection(Vector4)}). */ + @Override + public boolean isCollinear (Vector4 other) { + return isOnLine(other) && hasSameDirection(other); + } + + /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector4, float)} && + * {@link #hasSameDirection(Vector4)}). */ + @Override + public boolean isCollinearOpposite (Vector4 other, float epsilon) { + return isOnLine(other, epsilon) && hasOppositeDirection(other); + } + + /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector4)} && + * {@link #hasSameDirection(Vector4)}). */ + @Override + public boolean isCollinearOpposite (Vector4 other) { + return isOnLine(other) && hasOppositeDirection(other); + } + + @Override + public boolean isPerpendicular (Vector4 vector) { + return MathUtils.isZero(dot(vector)); + } + + @Override + public boolean isPerpendicular (Vector4 vector, float epsilon) { + return MathUtils.isZero(dot(vector), epsilon); + } + + @Override + public boolean hasSameDirection (Vector4 vector) { + return dot(vector) > 0; + } + + @Override + public boolean hasOppositeDirection (Vector4 vector) { + return dot(vector) < 0; + } + + @Override + public Vector4 lerp (final Vector4 target, float alpha) { + x += alpha * (target.x - x); + y += alpha * (target.y - y); + z += alpha * (target.z - z); + w += alpha * (target.w - w); + return this; + } + + @Override + public Vector4 interpolate (Vector4 target, float alpha, Interpolation interpolator) { + return lerp(target, interpolator.apply(alpha)); + } + + /** Converts this {@code Vector4} to a string in the format {@code (x,y,z,w)}. Strings with this exact format can be parsed + * with {@link #fromString(String)}. + * @return a string representation of this object. */ + @Override + public String toString () { + return "(" + x + "," + y + "," + z + "," + w + ")"; + } + + /** Sets this {@code Vector4} to the value represented by the specified string according to the format of {@link #toString()}. + * @param v the string. + * @return this vector, set with the value from v, for chaining */ + public Vector4 fromString (String v) { + int s0 = v.indexOf(',', 1); + int s1 = v.indexOf(',', s0 + 1); + int s2 = v.indexOf(',', s1 + 1); + if (s0 != -1 && s1 != -1 && v.charAt(0) == '(' && v.charAt(v.length() - 1) == ')') { + try { + float x = Float.parseFloat(v.substring(1, s0)); + float y = Float.parseFloat(v.substring(s0 + 1, s1)); + float z = Float.parseFloat(v.substring(s1 + 1, s2)); + float w = Float.parseFloat(v.substring(s2 + 1, v.length() - 1)); + return this.set(x, y, z, w); + } catch (NumberFormatException ex) { + // Throw a GdxRuntimeException... + } + } + throw new GdxRuntimeException("Malformed Vector4: " + v); + } + + @Override + public Vector4 limit (float limit) { + return limit2(limit * limit); + } + + @Override + public Vector4 limit2 (float limit2) { + float len2 = len2(); + if (len2 > limit2) { + scl((float)Math.sqrt(limit2 / len2)); + } + return this; + } + + @Override + public Vector4 setLength (float len) { + return setLength2(len * len); + } + + @Override + public Vector4 setLength2 (float len2) { + float oldLen2 = len2(); + return (oldLen2 == 0 || oldLen2 == len2) ? this : scl((float)Math.sqrt(len2 / oldLen2)); + } + + @Override + public Vector4 clamp (float min, float max) { + final float len2 = len2(); + if (len2 == 0f) return this; + float max2 = max * max; + if (len2 > max2) return scl((float)Math.sqrt(max2 / len2)); + float min2 = min * min; + if (len2 < min2) return scl((float)Math.sqrt(min2 / len2)); + return this; + } + + @Override + public int hashCode () { + final int prime = 31; + int result = 1; + result = prime * result + NumberUtils.floatToIntBits(x); + result = prime * result + NumberUtils.floatToIntBits(y); + result = prime * result + NumberUtils.floatToIntBits(z); + result = prime * result + NumberUtils.floatToIntBits(w); + return result; + } + + @Override + public boolean equals (Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Vector4 other = (Vector4)obj; + if (NumberUtils.floatToIntBits(x) != NumberUtils.floatToIntBits(other.x)) return false; + if (NumberUtils.floatToIntBits(y) != NumberUtils.floatToIntBits(other.y)) return false; + if (NumberUtils.floatToIntBits(z) != NumberUtils.floatToIntBits(other.z)) return false; + if (NumberUtils.floatToIntBits(w) != NumberUtils.floatToIntBits(other.w)) return false; + return true; + } + + @Override + public boolean epsilonEquals (final Vector4 other, float epsilon) { + if (other == null) return false; + if (Math.abs(other.x - x) > epsilon) return false; + if (Math.abs(other.y - y) > epsilon) return false; + if (Math.abs(other.z - z) > epsilon) return false; + if (Math.abs(other.w - w) > epsilon) return false; + return true; + } + + /** Compares this vector with the other vector, using the supplied epsilon for fuzzy equality testing. + * @param x x component of the other vector to compare + * @param y y component of the other vector to compare + * @param z z component of the other vector to compare + * @param w w component of the other vector to compare + * @param epsilon how much error to tolerate and still consider two floats equal + * @return whether the vectors are the same. */ + public boolean epsilonEquals (float x, float y, float z, float w, float epsilon) { + if (Math.abs(x - this.x) > epsilon) return false; + if (Math.abs(y - this.y) > epsilon) return false; + if (Math.abs(z - this.z) > epsilon) return false; + if (Math.abs(w - this.w) > epsilon) return false; + return true; + } + + /** Compares this vector with the other vector using {@link MathUtils#FLOAT_ROUNDING_ERROR} for its epsilon. + * + * @param other other vector to compare + * @return true if the vectors are equal, otherwise false */ + public boolean epsilonEquals (final Vector4 other) { + return epsilonEquals(other, MathUtils.FLOAT_ROUNDING_ERROR); + } + + /** Compares this vector with the other vector using {@link MathUtils#FLOAT_ROUNDING_ERROR} for its epsilon. + * + * @param x x component of the other vector to compare + * @param y y component of the other vector to compare + * @param z z component of the other vector to compare + * @param w w component of the other vector to compare + * @return true if the vectors are equal, otherwise false */ + public boolean epsilonEquals (float x, float y, float z, float w) { + return epsilonEquals(x, y, z, w, MathUtils.FLOAT_ROUNDING_ERROR); + } + + @Override + public Vector4 setZero () { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + return this; + } +} diff --git a/gdx/test/com/badlogic/gdx/math/Vector4Test.java b/gdx/test/com/badlogic/gdx/math/Vector4Test.java new file mode 100644 index 00000000000..b96bfaad700 --- /dev/null +++ b/gdx/test/com/badlogic/gdx/math/Vector4Test.java @@ -0,0 +1,18 @@ + +package com.badlogic.gdx.math; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Vector4Test { + @Test + public void testToString () { + assertEquals("(-5.0,42.00055,44444.32,-1.975)", new Vector4(-5f, 42.00055f, 44444.32f, -1.975f).toString()); + } + + @Test + public void testFromString () { + assertEquals(new Vector4(-5f, 42.00055f, 44444.32f, -1.975f), new Vector4().fromString("(-5,42.00055,44444.32,-1.9750)")); + } +} From 7fb396541a8c2777c92a5062aaa49afcab0bec1f Mon Sep 17 00:00:00 2001 From: damios Date: Mon, 30 Oct 2023 18:19:17 +0100 Subject: [PATCH 13/15] Add missing changelog entries. (#7266) #7140, #7164, #7172, #7176, #7178, #7179, #7181, #7193, #7196, #7213, #7217, #7239, #7251. --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index 8537dd7e1d4..378707055b0 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,19 @@ - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. - Update to LWJGL 3.3.3 - API Addition: Vector4 is just like Vector2 or Vector3, but with x, y, z, and w float components; a Vector4 can be passed as a shader uniform. +- Fix: Fix crash with ANGLE GLES renderer +- API Change: Use STBVorbis to decode ogg data for Sound +- API Change: Remove usage of deprecated tag on GL30 +- Lower GLIBC requirements to 2.17 in order to support older Linux systems +- Fix: Fix setCursor releasing the caught cursor on the LWJGL 3 backend. +- Fix crash in ParticleEditor and Flame +- Fix bitwise precedence in ModelBatch#addMesh (float[] vertices, short[] indices) +- Improve the default font's compatibility with GL30 +- Fix potential IndexOutOfBoundsException on AsynchronousAndroidSound +- API Addition: GwtGL20 & GwtGL30#glGetFramebufferAttachmentParameteriv +- Fix Tiled classes not being registered in the reflection cache +- Fix: Change androidx.fragment dependency to compileOnly +- Fix borderless fullscreen when taskbar is on the left/top [1.12.0] - [BREAKING CHANGE] Added #touchCancelled to InputProcessor interface, see #6871. From b3989cb385ab2f308756e6735eb90eb41cfec558 Mon Sep 17 00:00:00 2001 From: obigu <1794492+obigu@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:53:59 +0100 Subject: [PATCH 14/15] Fix longPress being triggered on back Android System gesture (#7265) --- gdx/src/com/badlogic/gdx/input/GestureDetector.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gdx/src/com/badlogic/gdx/input/GestureDetector.java b/gdx/src/com/badlogic/gdx/input/GestureDetector.java index 3cca0a470d5..b6d2a3abd90 100644 --- a/gdx/src/com/badlogic/gdx/input/GestureDetector.java +++ b/gdx/src/com/badlogic/gdx/input/GestureDetector.java @@ -236,6 +236,12 @@ public boolean touchUp (float x, float y, int pointer, int button) { return handled; } + @Override + public boolean touchCancelled (int screenX, int screenY, int pointer, int button) { + cancel(); + return super.touchCancelled(screenX, screenY, pointer, button); + } + /** No further gesture events will be triggered for the current touch, if any. */ public void cancel () { longPressTask.cancel(); From 78bbe3051c2a44ad19902300c94d661c580ec503 Mon Sep 17 00:00:00 2001 From: Tomski Date: Thu, 2 Nov 2023 22:01:31 +0000 Subject: [PATCH 15/15] Increment version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ff5e80fea99..4c57fa70f89 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.configureondemand=false android.useAndroidX=true group=com.badlogicgames.gdx -version=1.12.1 +version=1.12.2 POM_NAME=libGDX POM_DESCRIPTION=Android/Desktop/iOS/HTML5 game development framework POM_URL=https://github.com/libgdx/libgdx